spaces: the 2026-10-22 alpha wire #18

Open scarnecchia.net
targeting k3s-scarnecchia
Open opened by and assigned to scarnecchia.net
targeting k3s-scarnecchia

Summary #

  • Moves Ziran's Spaces client to the 2026-10-22 spaces alpha (0.0.0-spaces-alpha-20261022141228) in one step: dependency pins, the client wire (credential exchange, presentation, MACs, deletion), the in-memory server double, tests, tooling, and docs.
  • The August DPoP dialect is removed (it cannot talk to an Oct-22 reference PDS); the surviving alpha/cocoon split now selects management request shapes only.

Changes #

  • Dependencies: @atproto/space, @atproto/crypto, @atproto/oauth-scopes pinned to the Oct-22 snapshot; OAuth clients intentionally stay on 2026-08-18. @atproto/lex-cbor/@atproto/lex-data vendored under patches/ — the Oct-22 ^0.0.0-spaces-alpha-… ranges are also satisfied (and preferred) by ancient broken 0.0.0 publishes with unrewritten workspace:* manifests; the links carry the real snapshot. New @noble/hashes subpath mappings for the MAC module.
  • Exchange (client.ts): unsigned body POST {space, credentialKid, delegationToken}; one fresh-mint retry on 400 InvalidDelegationToken; SpaceNotFound/SpaceDeleted/UserNotAuthorized surface by name.
  • Presentation (httpsig.ts): Authorization: Atproto-Space-Credential + audience DID + RFC 9421 signature under label atproto-space-credential with mandatory tag, covering exactly ("authorization" "atproto-space-audience"), padded base64, format: 'compact' + malleable-S verify. Vectors regenerated from the pinned upstream signer.
  • MACs (new mac.ts): #atproto_kem discovery, RFC 9180 DHKEM(P-256) encapsulation on noble curves/hashes (byte-identical to upstream, pinned by test), commit/record MAC verification, Want-Mac/Mac header pair. Client sends a fresh ephemeral key share per request, verifies responses, re-resolves the KEM key once on failure. Commits are {ver, repo, space, rev, data} (hash → data); getRepo returns a STAR archive + MAC + shared secret for caller-owned verifyRepoStarFull.
  • Deletion (provider.ts): the SpaceDeleted error name purges the space from the registry at every call site (poll, flush, invite, rename) — local tiles kept, idempotent; message-text deletion matching narrowed to not-found only; a deleted inbox resets and is recreated. classifySpaceError gains the deleted kind.
  • Fake network (fake.ts): genuinely verifies the new protocol — publishes #atproto_kem in DID docs, enforces the exchange validation order and the credential scheme/tag, requires Want-Mac on the MAC methods, computes real response MACs, emits Oct-22 commits with real CIDv1 record CIDs, answers SpaceDeleted on every space endpoint after deletion. The cocoon flag now selects management shapes only.
  • Pre-existing bugs fixed en route (found by the new gate): relay/main.ts shutdown now closes upgraded WebSocket connections (Deno.serve.shutdown() only retires the listener, which kept the test process alive forever — pre-existing, surfaced by the full-suite run); patches/undici vendors undici 8.10.0 with a guarded markAsUncloneable (undici ≥ 8.0.3 calls it unconditionally at load; this deno lacks the export — crashed e2e.test.ts at boot, also pre-existing).
  • Comments (session-fetch.ts): the header keeps the operational cause and routing rule; the incident history stays in SYNC-SPACES.md.

Validation #

  • deno install — clean (the lex-cbor/lex-data trap is handled by the vendored links).
  • deno check over main.ts, the spaces module, provider, spike, live smoke, CLI — clean.
  • deno task test:spaces — all suites green (mac vectors cross-checked against the pinned upstream primitives; httpsig vectors regenerated from the reference signer).
  • deno task test:sync — exit 0, 512 checks pass, full chain terminates.
  • Independent read-only review pass (wire conformance vs node_modules/@atproto/space/dist, scope discipline, test fidelity): verdict SHIP; its three actionable low findings fixed, the rest rebutted with evidence in the commit message. A second comment-only review pass (writing-code-comments + STE) landed one header trim.
  • Live spike + live smoke: Not run — pds.ziran.space still serves the pre-Oct-22 protocol; upgrade it first (deploy/pds/README.md has the procedure), then run the 25-check spike and the provider smoke against it.

Review notes #

  • Operator action after merge: redeploy the reference PDS on the floating pds-spaces-alpha tag (migrations 009-space-deletion-and-macs + 005-space-commit-data run automatically; a kem_key file appears beside each actor-store key — include it in backups; the KEM backfill publishes atproto_kem to PLC). Until then the app against that PDS shows exchange refusals (400 InvalidToken) — that is the old server, not a regression.
  • Cocoon interop gap: cocoon servers on the Oct-1 read shape reject the new scheme until cocoon ships Oct-22 — accepted, no fallback dialect (documented in SPACES.md/SYNC-SPACES.md).
  • Trust posture unchanged: commit/record MACs verified per response; no LtHash running-set accumulator (documented in SPACES.md).
  • patches/undici is a full vendored copy (repo's established patches/ pattern; behavior-identical on real Node ≥ 21 — the guard falls back to a no-op only where the export is missing).

This pull request has no versions submitted yet.