---
title: "TAPS Node and SoveDocs"
description: "Use a self-hosted or hosted TAPS Node, or SoveDocs, for protected files with main.yaml, a local install, revoke, activity and sharing."
section: "integrations"
canonical: "https://mainsdk.dev/docs/integrations/taps"
updated: "2026-10-28"
---

# TAPS Node and SoveDocs

> Use a self-hosted or hosted TAPS Node, or SoveDocs, for protected files with main.yaml, a local install, revoke, activity and sharing.

Canonical: https://mainsdk.dev/docs/integrations/taps · Updated: 2026-10-28

TAPS (Trust Anchor Provenance System) is HubXero Labs' open protocol for files that carry their own identity, provenance and policy and open only when a TAPS Node issues a licence. These three drivers serve [`protect`](https://mainsdk.dev/docs/interfaces/protect): a self-hosted TAPS Node (the container from the SDK), a hosted TAPS Node, or SoveDocs. Pick them for exam papers nobody can open early, tenders, results or any document you may need to revoke.

> **Note:** The drivers are planned for milestone 0.4 (spec `protect-taps`) and experimental. They live in `@mainsdk/protect-taps`, a private package that installs the TAPS SDK from a local folder until `@taps/*` is on npm. The `local` driver in `mainsdk` needs none of this.

## Interfaces it serves

### Protected files (`protect`)

- Status: Building for 0.4 · ships as a experimental API
- Driver: `taps-node` (Self-hosted TAPS Node). In @mainsdk/protect-taps
- Docs: https://mainsdk.dev/docs/interfaces/protect.md

```yaml
protect:
  driver: taps-node
```

Swap to another provider: `local`, `taps-hosted`, `sovedocs`.

### Protected files (`protect`)

- Status: Building for 0.4 · ships as a experimental API
- Driver: `taps-hosted` (Hosted TAPS Node). In @mainsdk/protect-taps
- Docs: https://mainsdk.dev/docs/interfaces/protect.md

```yaml
protect:
  driver: taps-hosted
```

Swap to another provider: `local`, `taps-node`, `sovedocs`.

### Protected files (`protect`)

- Status: Building for 0.4 · ships as a experimental API
- Driver: `sovedocs` (SoveDocs). In @mainsdk/protect-taps; conformance follows taps-node
- Docs: https://mainsdk.dev/docs/interfaces/protect.md

```yaml
protect:
  driver: sovedocs
```

Swap to another provider: `local`, `taps-node`, `taps-hosted`.

### Connected devices (`device`)

- Status: Building for 1.3 · ships as a beta API
- Driver: `taps` (TAPS credentials, attestation and delegation). In @mainsdk/device-taps
- Docs: https://mainsdk.dev/docs/interfaces/device.md

```yaml
device:
  driver: taps
```

Swap to another provider: `local`.

## Before you start

1. Choose where the node runs: your own `taps-node` container, a hosted TAPS Node, or SoveDocs.
2. Make the app's issuer identity with `mainsdk seal keygen` and keep it as a secret: `MAINSDK_PROTECT_KEY`.
3. Hardware attestation roots, HSMs and identity providers are ports your deployment supplies; MainSDK does not run them.

## Install

`@mainsdk/protect-taps` is private and is not published to npm. It depends on `@taps/*` (the TAPS TypeScript SDK) through `link:` folders, which the owner approved for this stage.

1. Check out the TAPS SDK next to your project: `taps-sdk-typescript` at tag `v0.2.0`.
2. Add `@mainsdk/protect-taps` from your MainSDK workspace and run `pnpm install`.
3. Pass the driver to the app: `createApp({ protect: tapsNode })`.

```ts title="app/mainsdk.ts"
import { createApp } from "mainsdk";
import { tapsNode } from "@mainsdk/protect-taps";

export const app = createApp({ protect: tapsNode });
```

CI checks out `taps-sdk-typescript` at `v0.2.0` in its own job, so a folder that moves is caught there. Application code never imports `@taps/*`: `mainsdk check` reports one that does as `MAIN-PROTECT-0020`. In production, `@taps/*` must come from the registry, not a folder: a build that resolves it from `link:` or `file:` fails with `MAIN-PROTECT-0022`.

## Configure

```yaml title="main.yaml"
protect:
  driver: taps-node                 # taps-node | taps-hosted | sovedocs
  url: https://taps.acme.dev        # the TAPS Node
  share-domain: open.acme.dev       # where the Web Reader runs
  key: secret:MAINSDK_PROTECT_KEY   # the app's issuer identity
  mode: viewer                      # viewer | wrapper
  suite: TAPS-CS1                   # TAPS-CS1 | TAPS-CS2
  renew: 10s                        # licence renewal, 5s to 5m
```

`url`, `share-domain` and `key` are required for all three drivers. The hosts (the Node, the share domain, any threshold party and the sealing service) are added to the [network](https://mainsdk.dev/docs/interfaces/network) allowlist, and every request goes through it with its timeouts and retries. For every key, see [Protected files](https://mainsdk.dev/docs/interfaces/protect).

## Use it

```ts title="app/papers/actions.ts"
"use server";
import { auth, protect, share } from "mainsdk";

export async function publishPaper(req: Request, paper: File, examiners: string[]) {
  await auth.require(req, { role: "exam-admin" });
  const asset = await protect(paper, {
    label: "sensitive",
    policy: { releaseAt: "2027-03-01T09:00:00Z" },   // no licence before this time
  });
  const { url } = await share(asset, { to: examiners });
  return url;   // a getqr.id link that opens the TAPS Web Reader
}

export async function pullPaper(req: Request, assetId: string) {
  await auth.require(req, { role: "exam-admin", fresh: "5m" });
  await protect.revoke(assetId, { reason: "paper leaked" });   // the next open and renew fail
  return protect.activity(assetId);                             // who opened it
}
```

## Local development and testing

Use `createTestApp()` from [`@mainsdk/testing`](https://mainsdk.dev/docs/interfaces/testing). It runs the `local` driver offline with the test clock and adds `app.protect.signals()`, `app.protect.party(name)` and `app.protect.chain()`. Nothing in a test reaches a TAPS Node. Run the `mainsdk` suite against `taps-node` in your own CI, where the Node container is available.

## What MainSDK adds on top

- Ciphertext without a licence, wherever a copy ends up.
- Revocation within one `renew` interval.
- Opens and revocations in the MainSDK decision log as well as the TAPS ledger, on the eight `mainsdk:protect.*` channels.
- Each user is a tenant-pairwise principal to the Node: no user id, email or name reaches it in clear.
- Share links and QR codes through getqr.id, with "Powered by getqr.id".
- Check codes `MAIN-PROTECT-0020` to `0022` for imports, the section and production installs.

See [Protected files](https://mainsdk.dev/docs/interfaces/protect).

## Limits and notes

- The TAPS ledger runs in process in this release.
- A suite of `TAPS-CS3` is refused with `MAIN-PROTECT-0010` until it is registered. MainSDK claims no post-quantum protection for `protect`.
- The package installs from a local folder, so it is private and not on npm. That changes when `@taps/*` is published.
- Gaps in a driver (`query`, `threshold`, `timelock`, `inspect`, `web3`, `delivery`) are listed in the driver profile; a call it lacks fails with `MAIN-PROTECT-0019`.
- A TAPS Node, threshold party or sealing service is not run inside MainSDK. An unreachable Node fails opens with `MAIN-PROTECT-0014` and the rest of the app keeps serving.
- Fleet adds an estate view of protected files and a revoke-since-a-date response action.

## Swap to or from it

```yaml title="main.yaml"
protect:
  driver: taps-hosted   # was taps-node
```

> **Warning:** Protected files and their licences live on the node. Moving assets between nodes, or from `local`, is not covered by the spec.

## FAQ

**Do readers need to install anything?** No. Links open in the TAPS Web Reader.

**Can I use the TAPS SDK directly?** Not in app code. Import `protect` from `mainsdk`; the driver package is the only place that imports `@taps/*`.

## Next

- [Protected files](https://mainsdk.dev/docs/interfaces/protect): every call and key.
- [Share links and QR codes](https://mainsdk.dev/docs/interfaces/share): how protected files are shared.
- [Errors](https://mainsdk.dev/docs/reference/errors): every `MAIN-PROTECT-*` code.
