MainSDK
Search⌘K
Integrations

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.

2 interfaces4 driversWritten guide
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: 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.

Interfaces it serves

Pick TAPS Node and SoveDocs in main.yaml for any of these. Your calls stay the same, and you can swap later.

Protected filesprotectBuilding for 0.4

Driver taps-node Self-hosted TAPS Node

In @mainsdk/protect-taps

main.yaml
protect:
  driver: taps-node

Swap to another provider

Local fake TAPS NodeHosted TAPS NodeSoveDocs
Protected filesprotectBuilding for 0.4

Driver taps-hosted Hosted TAPS Node

In @mainsdk/protect-taps

main.yaml
protect:
  driver: taps-hosted

Swap to another provider

Local fake TAPS NodeSelf-hosted TAPS NodeSoveDocs
Protected filesprotectBuilding for 0.4

Driver sovedocs SoveDocs

In @mainsdk/protect-taps; conformance follows taps-node

main.yaml
protect:
  driver: sovedocs

Swap to another provider

Local fake TAPS NodeSelf-hosted TAPS NodeHosted TAPS Node

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 }).
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

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 allowlist, and every request goes through it with its timeouts and retries. For every key, see Protected files.

Use it

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. 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.

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

main.yaml
protect:
  driver: taps-hosted   # was taps-node

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

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet