SYS // v0.1.0-ALPHAHEALTH // Nodes (6/6) OK
UNTRACE
Documentation

ZK Data Vaults

A ZK Data Vault is the fundamental storage unit of the Untrace protocol. Every piece of data stored on Untrace lives in a vault — an encrypted, sharded, ZK-gated container that only the owner can open.


What Is a Vault?

A vault is not a file. It is not a folder. It is a cryptographic container with an on-chain identity, an access policy, and no physical home.

When you create a vault:

  1. Your data is encrypted client-side with an ephemeral AES-256-GCM key
  2. That key — and the encrypted data — are sharded using Shamir's Secret Sharing
  3. The fragments are distributed to independent nodes across the network
  4. An on-chain commitment anchors the vault's identity, policy, and shard manifest
  5. Reconstruction is possible only by producing a valid Zero-Knowledge Proof of ownership

At no point does the complete data exist anywhere outside your device — not on our servers, not on any single node, not on the blockchain.


Vault Structure

Vault {
  id:            bytes32          // Unique on-chain identifier
  owner:         DID              // Decentralized Identifier of the owner
  policy:        AccessPolicy     // ZK-gated access rules
  shardManifest: ShardManifest    // Node assignments and shard commitments
  commitment:    bytes32          // Pedersen commitment to all shards
  metadata:      VaultMetadata    // Schema hint, size, timestamps (encrypted)
  createdAt:     uint256          // Block timestamp of vault creation
}

The shardManifest is stored on-chain as a set of commitments — not as the shards themselves. Nodes know which shards they hold, but the manifest reveals nothing about the data content.


Vault Types

Untrace supports three vault types out of the box:

| Type | Use Case | Key Behavior | | --------------------- | ------------------------------------ | ---------------------------------------------- | | Private Vault | Single-owner data storage | Only owner DID can reconstruct | | Shared Vault | Collaborative or delegated access | Owner grants access to additional DIDs | | Conditional Vault | Compliance, escrow, time-locked data | Access gated by ZK conditions (age, KYC, time) |

All three types share the same underlying sharding and encryption architecture. The difference is in the access policy layer.


Creating a Vault

import { UntraceClient } from "@untrace/sdk"

const client = new UntraceClient({ network: "mainnet", signer })

// Store a document
const vaultId = await client.vault.store({
  data: { contract: "...", signedAt: "2026-05-08" },
  type: "private",
})

// Store a file (Buffer or Blob)
const vaultId = await client.vault.storeFile(fileBuffer, {
  mimeType: "application/pdf",
  name: "agreement.pdf",
})

The SDK handles all encryption, sharding, and distribution automatically. You receive a vaultId — the on-chain identifier you use to retrieve or manage the vault later.


Accessing Vault Data

// Reconstruct data — triggers local ZK proof generation
const data = await client.vault.retrieve(vaultId)

// Retrieve a file
const file = await client.vault.retrieveFile(vaultId)

When you call retrieve, the SDK:

  1. Generates a ZK proof of your identity locally (private key never leaves your device)
  2. Submits the proof to the on-chain verifier
  3. Collects K-of-N encrypted shards from authorized nodes
  4. Reconstructs the symmetric key via SSS
  5. Decrypts the data locally

The entire flow is end-to-end — the reconstructed data never transits through Untrace infrastructure.


Sharing and Access Control

// Grant read access to another identity
await client.vault.grantAccess(vaultId, {
  grantee: "did:untrace:0x4f3e...a8b1",
  permissions: ["read"],
  expiresAt: Date.now() + 30 * 24 * 60 * 60 * 1000, // 30 days
})

// Revoke access instantly
await client.vault.revokeAccess(vaultId, "did:untrace:0x4f3e...a8b1")

Access grants are enforced on-chain. Revocation takes effect in one block — no token expiry, no cache invalidation window.


Vault Versioning

Vaults support versioned writes. Each write creates a new shard generation anchored to the same vault ID:

// Update vault contents
await client.vault.update(vaultId, { data: updatedPayload })

// Access a specific version
const v1 = await client.vault.retrieve(vaultId, { version: 1 })

Previous versions are retained by default (configurable). Version history is anchored on-chain, creating a tamper-evident audit trail.


Security Properties

| Property | Guarantee | | ------------------------- | ---------------------------------------------------------------------- | | Confidentiality | AES-256-GCM + SSS; nodes see only encrypted fragments | | Integrity | Each shard is MAC'd; tampering detected at reconstruction | | Availability | Survives up to N−K simultaneous node failures | | Non-repudiation | Vault creation, access grants, and revocations are on-chain and signed | | Forward secrecy | Ephemeral keys rotated per vault write | | Zero-knowledge access | Proof reveals only authorization, not identity details or data |


Further Reading