Skip to content

StorageProvider seam + private SEAL-encrypted remember/recall (public→Tatum, private→Harbor) #19

Description

@harrymove-ctrl

Context

Today "private" is only a D1 read-token check (CloudflareNamespaceStore.authorizeNamespace, apps/api/src/worker.ts:502) — bytes are uploaded plaintext and readable by anyone with the blobId/aggregator URL — and WalrusStorageReceipt.provider is hard-wired to the literal 'tatum' (packages/core/src/types.ts:655) with packages/walrus/src/storage.ts as the only provider. The roadmap mandates coexistence + real encryption: a StorageProvider seam routes public→Tatum (mainnet, plaintext) and private→Harbor+SEAL (testnet, encrypted), where a private namespace gets an owned Seal policy and bytes are SEAL-encrypted before upload and only recallable by decrypt via SessionKey + seal_approve.

Goal / user story

As a user with a private namespace, when I remember an artifact it goes through a StorageProvider that SEAL-encrypts it and stores ciphertext on Harbor, and recall fetches + decrypts — while public namespaces keep going to Tatum plaintext, with the chosen provider recorded honestly in the receipt.

Acceptance criteria

  • Define a StorageProvider interface { upload, status, fetch } in packages/walrus; refactor the existing Tatum functions behind a TatumStorageProvider implementing it, with no behavior change for current CLI/MCP callers.
  • Widen WalrusStorageReceipt.provider (core/types.ts:655) from 'tatum' to 'tatum' | 'harbor' and add optional Harbor fields (spaceId, bucketId, fileId, sealPolicyId, sealIdentity).
  • New packages/walrus/src/seal.ts wraps @mysten/seal: encrypt(bytes, { sealPolicyId, identity }) → ciphertext; decrypt(ciphertext, { sessionKey, sealPolicyId }) builds the seal_approve PTB.
  • A selectStorageProvider({ visibility, network }) picks Harbor+SEAL for private namespaces and Tatum for public; private writes encrypt the proof bundle with a per-file identity against the namespace's sealPolicyId, then upload ciphertext via HarborClient; the chosen provider is written into the receipt, and rememberStorageIndex/renderStorageIndex (packages/memwal/src/index.ts:164/281) carry bucketId/fileId/sealPolicyId/sealIdentity (keep back-compat parsing of the [ctxm-storage] format).
  • New GET /api/memwal/artifact (worker.ts) resolves provider + refs from D1, fetches ciphertext, decrypts via SessionKey + seal_approve, and streams plaintext — token-gated through authorizeNamespace (worker.ts:502); public namespaces fall back to the plaintext aggregator path.
  • Fail-closed: non-private writes fall back to Tatum on Harbor failure, but private writes NEVER silently fall back to plaintext; creating/revoking a namespace read token maps to granting/revoking SEAL decrypt for that policy (wired near authorizeNamespace and token create/revoke worker.ts:992/:1013).
  • Tests cover provider selection by visibility, public-only Tatum fallback, and a private round-trip (remember → recall artifact → bytes match; a request without a valid token cannot decrypt).

Implementation notes

  • Land the interface + receipt widening early (no deps); uploadProofBundle (storage.ts:183) currently always returns provider: 'tatum' — route it through selectStorageProvider.
  • SessionKey needs an Ed25519 signer (service key, or per-owner per the mapping issue's decision); execute the seal_approve PTB via SuiGrpcClient devInspect (same client as proof.ts). Per-file identity deterministic from namespace + run/chunk id so recall can reconstruct it.
  • Public share pages CANNOT do SessionKey+seal_approve — confirm public namespaces stay plaintext (Tatum) and only private go through SEAL. Network reality: Tatum=mainnet, Harbor=testnet; document that public-mainnet and private-testnet blobs live on different networks.
  • Encryption on write likely runs in the queue consumer / Node CLI+MCP path (packages/cli/src/index.ts:347, packages/mcp/src/index.ts:200) since packProofBundle is Node-only — gated by the seal Worker spike. Keep packProofBundle (tar) Node-only; the Worker private path needs an in-memory zip from R2 (separate roadmap item, out of scope here).

Sui Overflow angle

Turns "private" from a DB flag into real on-chain access control: decryption is gated by an on-chain seal_approve Move call. The headline Sui-native privacy demo — "revoke the token → recall can no longer decrypt" — lands here, shown side-by-side with a public Tatum/Walrus blob whose receipt names the provider.

Dependencies

The seal Worker spike, the HarborClient issue, and the namespace→Space/Bucket/Seal mapping + service-key issue. The interface + receipt widening have no deps and can land first.

Part of the ContextMEM roadmap (#4) • Sui Overflow build.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Important: hardens the demo and core productharborHarbor encrypted Walrus storage on Sui (Space/Bucket/File)seal@mysten/seal client-side encryption + on-chain access controlwalrusWalrus blob storage / Sites / aggregator

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions