Skip to content

feat(walrus): Harbor + Seal encrypted private storage (P0 #5 / #6 / #7)#29

Merged
harrymove-ctrl merged 3 commits into
mainfrom
feat/harbor-seal-storage
Jun 20, 2026
Merged

feat(walrus): Harbor + Seal encrypted private storage (P0 #5 / #6 / #7)#29
harrymove-ctrl merged 3 commits into
mainfrom
feat/harbor-seal-storage

Conversation

@harrymove-ctrl

@harrymove-ctrl harrymove-ctrl commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Private-namespace artifacts are now Seal-encrypted and stored in per-namespace Harbor (Walrus) buckets, while public namespaces keep the plaintext R2 path. Covers the P0 Harbor/Seal storage track: the encrypt/decrypt spike (#5), the HarborClient (#6), and the Worker-side namespace→bucket→Seal mapping + service-key handling (#7).

What's in here

packages/walrus — Harbor + Seal client (#5, #6)

  • HarborStorage: reserve→sign→finalize bucket lifecycle, multipart upload, status poll, download.
  • putEncrypted / getDecrypted: client-side SEAL encrypt-on-write, decrypt-on-read (@mysten/seal + @mysten/sui).
  • Bounded settlement-retry for the transient testnet upload_funding_timeout funding-settlement failure.

apps/api/src/worker.ts — Worker wiring (#7)

  • storeNamespaceImport / readArtifact / updateNamespaceArtifact route through HarborStorage when a namespace is Harbor-backed; public namespaces unchanged.
  • Version-scope the artifact write/classify path to current_version_id so a private edit never misclassifies a stale R2 row and leaks plaintext.
  • Persist-before-delete on rotation: commit the new harbor_file_id before dropping old ciphertext (no dangling pointer / silent loss).
  • Orphan-cleanup: a fresh bucket created during a failed upload is rolled back, so a failed run leaks no bucket (live-validated).

Schema / config

  • migration 0007: storage_provider + seal_identity_salt columns, per-namespace Harbor bucket / Seal policy columns, per-artifact file-pointer columns (all nullable — existing/public artifacts keep working).
  • wrangler.example.jsonc: documents HARBOR_* vars and the required secrets (HARBOR_API_KEY, HARBOR_SERVICE_PRIVATE_KEY); real values live only as wrangler secrets / gitignored .dev.vars.

Testing

  • apps/api/scripts/harbor-wiring-smoke.ts: full encrypt→upload→edit→decrypt round-trip against a FakeD1 + live Harbor (creds read from env only). FakeD1 path green.
  • Live round-trip is currently blocked on a Walrus testnet upload_funding_timeout outage (testnet-wide, not a code issue); a self-cleaning retry is armed to confirm once the network recovers.
  • bun run typecheck clean for packages/walrus and apps/api.

Notes

hien-p added 2 commits June 19, 2026 23:19
Port the proven Walrus Harbor + Seal flow (reference: benhaq/walrus-harbor-mcp)
into packages/walrus as plain async/await TypeScript (no Effect).

- HarborClient: Bearer REST surface (spaces/buckets/files) — reserve→sign→
  finalize bucket creation, multipart upload, status poll, download.
- SealCrypto: Seal encrypt/decrypt + sponsored-tx signing
  (@mysten/seal@1.1.3, pinned to match the existing @mysten/sui@2.17.0).
- HarborStorage: createPrivateBucket / putEncrypted / getDecrypted, with a
  mirror_missing_grant post-finalize retry (the reference's retry was dead
  code — the server error code was being overwritten; preserved here).
- harborConfigFromEnv() for Worker/Node env wiring.
- scripts/harbor-smoke.ts: manual live testnet round-trip (CLI only, not CI).

Verified end-to-end on Sui testnet: encrypt→upload→download→decrypt is
byte-exact; bundles for a Cloudflare Worker at ~65KB gzip with no Node
built-ins, no WASM, no eval.

Refs #5, #6
Route private-namespace artifacts through Seal encryption + Harbor storage
in the hosted Worker, mirroring how MEMWAL secrets are handled:

- storeNamespaceImport / readArtifact / updateNamespaceArtifact encrypt-on-write
  and decrypt-on-read via HarborStorage when a namespace is Harbor-backed;
  public namespaces keep the plaintext R2 path unchanged.
- Version-scope the artifact write/classify path to current_version_id so a
  private edit never misclassifies a stale R2 row and leaks plaintext.
- Persist-before-delete on rotation: commit the new harbor_file_id before
  dropping old ciphertext (no dangling pointer / silent loss).
- putEncrypted: bounded settlement-retry for transient upload_funding_timeout;
  storeNamespaceImport: orphan-cleanup rolls back a freshly-created bucket on
  upload failure so a failed run leaks no bucket (live-validated).
- migration 0007: add storage_provider + seal_identity_salt columns and the
  per-namespace Harbor bucket / Seal policy / per-artifact file pointer columns.
- wrangler.example.jsonc: document HARBOR_* vars and required secrets.
- scripts: live + FakeD1 round-trip smoke harness (reads creds from env only).
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 20, 2026

Copy link
Copy Markdown

Deploying contextmem with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0f6fe00
Status: ✅  Deploy successful!
Preview URL: https://c0e9508f.contextmem.pages.dev
Branch Preview URL: https://feat-harbor-seal-storage.contextmem.pages.dev

View logs

@harrymove-ctrl harrymove-ctrl changed the title feat(walrus): Harbor + Seal encrypted storage client (P0 #5/#6) feat(walrus): Harbor + Seal encrypted private storage (P0 #5 / #6 / #7) Jun 20, 2026
packages/walrus imports @mysten/sui across proof/bcs/resources/resolve
and harbor/{constants,seal}, but only @mysten/seal was declared in
package.json — @mysten/sui resolved via workspace hoist only. Pin it
explicitly (^2.9.1, matching apps/api) so the package is self-contained
and survives a non-hoisted install. Surfaced by the open-issue audit (#6).
@harrymove-ctrl harrymove-ctrl merged commit 4dc3c48 into main Jun 20, 2026
2 checks passed
harrymove-ctrl pushed a commit that referenced this pull request Jun 20, 2026
… bundle

worker.ts imported `harbor` via the @contextmem/walrus barrel, whose
siblings (bcs/resolve/quilt) import the @contextmem/core barrel, which
re-exports html.ts → jsdom. jsdom uses __dirname at import time, which the
Workers runtime rejects (validation error 10021), so the post-#29 deploy
failed. The harbor subtree imports no core, so add a ./harbor subpath
export and import from it — matching the existing leaf-import convention
for @contextmem/core/{chunks,facts,utils}. Drops the Worker bundle from
~11.8MB to ~3.8MB; jsdom no longer in the executable bundle.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants