Automerge backed Markdown editor for the Atmosphere
reams docs verification client-assertion-binding.md
6.6 kB
Markdown
at main

Client-assertion-backend compatibility probe — 2026-10-04 UTC #

Question: can Reams replace its confidential token relay with the 2025 client-assertion-backend proposal, keeping browser-owned credentials and a smaller independently deployed signer?

Result #

Neither tested provider enforces the assertion's cnf.jkt binding at PAR, authorization-code exchange or refresh. Do not adopt the proposal based only on these providers accepting requests that contain cnf.

Probe PDS 0.5.1 / provider 0.18.1 PDS 0.5.19 / provider 0.20.2
PAR, matching assertion/proof key 201 accepted 201 accepted
PAR, mismatched assertion/proof key 201 accepted 201 accepted
Code exchange, matching binding 200, tokens issued 200, tokens issued
Code exchange, mismatched binding 200, tokens issued 200, tokens issued
Refresh, matching binding 200, token rotated 200, token rotated
Refresh, mismatched binding 200, tokens issued 200, tokens issued
Invalid application signature at PAR 400 invalid_client 400 invalid_client
Valid proof from wrong authorized device at code exchange 400 invalid_grant 400 invalid_grant
Valid proof from wrong authorized device at refresh 400 invalid_grant 400 invalid_grant

Ordinary DPoP grant binding is enforced, but that does not bind a client assertion issued to device A against reuse by device B. That additional check is the proposal's key requirement.

Method and provenance #

  • Probe: scripts/spikes/client-assertion-binding.test.mjs.
  • Real disposable PDS/PLC/HappyView processes; HTTPS client metadata with a fresh P-256 public signing key; actual PDS login and consent; genuine PKCE, nonce, DPoP signatures and signed client assertions. No provider responses are mocked.
  • Every code/refresh case starts a separate login; negative confirmation cases alter only cnf.jkt, retaining the genuinely authorized device and its proof. Wrong-session controls instead supply an independently valid proof/key pair.
  • The test driver holds signer/browser credentials for the probe. This is not a shipped assertion service or a browser-packaging test.
  • Fixture runner: /nix/store/09xc42j9lkqrdc5fwaq6if3mkb37dq2g-pds-0.4.5001/bin/pds, containing PDS package 0.5.1 / provider 0.18.1.
  • Second runner: the companion checkout's installed PDS package 0.5.19 / provider 0.20.2. A temporary launcher uses its real PDS.create/start API.
  • Companion harness source: bbe5834261d266a6847009d409d3a69d4abf6aa8.
  • HappyView source: 6035c557e3ff2939eb0923bb1161663106c97223, with the existing local spaces patches. HappyView starts as part of the fixture; OAuth requests and consent go directly to the PDS, not a HappyView-fabricated authority.
  • Local CA/private-network exceptions are confined to the disposable fixture. The broad scope is the existing harness's disposable-account scope, not a proposed production permission request.

The initial newer-provider run exposed an environment/driver mismatch, not an OAuth capability result: its SQLite native dependency needed rebuilding and its consent API uses did instead of older sub. Both were repaired; the final complete run passed both profiles, with 18 recorded observations and zero skips. A passing probe means controls and observations succeeded; its binding verdict is not-enforced for both profiles. A future denial-only run is marked inconclusive-denial, not automatically certified as CAB support. Ordinary-auth controls require no successful result and the expected invalid_client or invalid_grant classification.

Final run ID: 149d6c12-af20-4779-a5e0-9d3630906d15, started at 2026-10-04T03:33:08.067Z. Probe SHA-256: cf77793b88e61dc1c1bd75eb268acff5b22feda2dbaee7df9d9829dc5846dd24. Each result captures the shared run ID, proposal pin, probe/harness/lock hashes, checkout revisions/dirty flags, installed dependency lock integrities, versioned consent API profile, Node version and HappyView tracked-patch hash. The completed pair is published atomically as latest.json after successful fixture teardown; interrupted runs cannot mix profiles. Lockfile integrities identify declared dependencies, not an independent integrity audit of installed package contents.

Reproduction #

The newer installed PDS requires its native SQLite dependency:

nix develop /home/anish/usr/automerge-atproto --command sh -c \
  'export PATH="$PATH:/run/current-system/sw/bin"; npm rebuild better-sqlite3 --build-from-source'

Run that prerequisite from the companion checkout. Then, from Reams:

nix develop /home/anish/usr/automerge-atproto --command sh -c '
  export PATH="$PATH:/run/current-system/sw/bin"
  AUTOMERGE_ATPROTO_ROOT=/home/anish/usr/automerge-atproto \
  HAPPYVIEW_SOURCE=/home/anish/usr/automerge-atproto/tmp/happyview \
  RUN_CLIENT_ASSERTION_PROBE=1 \
  node --test scripts/spikes/client-assertion-binding.test.mjs
'

Redacted output is retained in:

  • .invariant-artifacts/client-assertion-binding/latest.json
  • .invariant-artifacts/client-assertion-binding/149d6c12-af20-4779-a5e0-9d3630906d15/nix-fixture.json
  • .invariant-artifacts/client-assertion-binding/149d6c12-af20-4779-a5e0-9d3630906d15/installed-pds.json

The evidence contains statuses, error codes and package versions, not private keys, passwords, tokens, codes, cookies or PAR request URIs.

Decision boundary #

This is two real local reference-provider versions, not a verdict on every production PDS or the live bsky.social entryway. Live behavior requires our public client metadata, a configured confidential client and an authorized disposable account; none was provisioned or used in this run.

For compatibility with the tested providers, keep the existing relay behind a separately deployed Reams auth service. SQLite or a separate PostgreSQL database can persist its metadata; this is independent of HappyView's Postgres choice. The smaller CAB signer remains a candidate when provider enforcement is proven.

Fresh adversary review approved the narrow negative finding. Its harness classification/control/provenance recommendations were applied before the final run; publication was additionally moved after teardown. This is a binding counterexample, not a captured-assertion replay experiment or complete conformance gate. No database backend or separate auth-service deployment was tested here.