You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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 — andWalrusStorageReceipt.provideris hard-wired to the literal'tatum'(packages/core/src/types.ts:655) withpackages/walrus/src/storage.tsas the only provider. The roadmap mandates coexistence + real encryption: aStorageProviderseam 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 viaSessionKey+seal_approve.Goal / user story
As a user with a private namespace, when I remember an artifact it goes through a
StorageProviderthat 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
StorageProviderinterface{ upload, status, fetch }inpackages/walrus; refactor the existing Tatum functions behind aTatumStorageProviderimplementing it, with no behavior change for current CLI/MCP callers.WalrusStorageReceipt.provider(core/types.ts:655) from'tatum'to'tatum' | 'harbor'and add optional Harbor fields (spaceId,bucketId,fileId,sealPolicyId,sealIdentity).packages/walrus/src/seal.tswraps@mysten/seal:encrypt(bytes, { sealPolicyId, identity })→ ciphertext;decrypt(ciphertext, { sessionKey, sealPolicyId })builds theseal_approvePTB.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'ssealPolicyId, then upload ciphertext viaHarborClient; the chosen provider is written into the receipt, andrememberStorageIndex/renderStorageIndex(packages/memwal/src/index.ts:164/281) carrybucketId/fileId/sealPolicyId/sealIdentity(keep back-compat parsing of the[ctxm-storage]format).GET /api/memwal/artifact(worker.ts) resolves provider + refs from D1, fetches ciphertext, decrypts viaSessionKey+seal_approve, and streams plaintext — token-gated throughauthorizeNamespace(worker.ts:502); public namespaces fall back to the plaintext aggregator path.authorizeNamespaceand token create/revokeworker.ts:992/:1013).Implementation notes
uploadProofBundle(storage.ts:183) currently always returnsprovider: 'tatum'— route it throughselectStorageProvider.SessionKeyneeds an Ed25519 signer (service key, or per-owner per the mapping issue's decision); execute theseal_approvePTB viaSuiGrpcClientdevInspect (same client asproof.ts). Per-fileidentitydeterministic from namespace + run/chunk id so recall can reconstruct it.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.packages/cli/src/index.ts:347,packages/mcp/src/index.ts:200) sincepackProofBundleis Node-only — gated by the seal Worker spike. KeeppackProofBundle(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_approveMove 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
HarborClientissue, 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.