ADR-059: Shared pure authority contracts and offline scope verification
Date: 2026-10-13 Status: Accepted (binding component; new shared admission remains SYN-231) Author: Rômulo Febasi
Context
State/receipt signatures authenticate bytes and key possession, not a company’s subject/device/scope binding. Soma already verifies real Febasi accounts and proof-enrolled device keys. Sharing one normative contract prevents issuer and verifier from assigning different meanings to signed fields.
Decision
A small pure Rust synapse-evidence package owns versioned Ed25519 bindings,
selected-key revocation snapshots, reviewed issuer roots and offline verification.
Synapse reexports its public types in model; Soma’s Rust issuer consumes an exact
Git revision. It imports no filesystem, HTTP, database or account SDK. Authentication,
issuance/key handling, scope authorization and current admission stay in adapters.
Both CLI syn state binding-verify and MCP state_binding_verify use the same core.
Signed artifacts, reviewed root snapshot/digest and expected subject/tenant/device/
resource scope are distinct inputs. Embedded keys do not choose their own roots or
scope. Core checks the actual limited-baseline workspace GUID; source hashes/images
are verified separately. source_integrity_checked:false and
shared_acceptance:unknown_without_online_event_bound_admission prevent identity
success from becoming permission to accept new shared knowledge.
Normative contract
Schemas: synapse.authority-binding/1, synapse.authority-snapshot/1,
synapse.authority-trust/1. Bytes are schema + NUL + fixed-order compact UTF-8 JSON,
explicit null signature/embedded issuer public key. Maps/strings are not normalized;
unknown fields, floats, noncanonical base64, oversized arrays and invalid namespace
identities deny. IDs are exact lowercase UUIDs; knowledge GUID is exactly 32 lowercase hexadecimal characters.
Fingerprints/key IDs are full raw-public-key SHA-256. Root-review digest is selected
independently. Canonical field order is the package’s versioned struct declaration.
Authority timestamps are integer Unix microseconds, bounded to the exact JSON integer range. This avoids rounding a concurrent revocation before issuance; existing native source/local-receipt time fields remain their documented seconds. Binding lifetime is at most one day; snapshot lifetime at most five minutes. Local clock drift/future/stale snapshots deny; offline later revocations remain unknown.
A snapshot declares exact sorted covered_key_ids; complete_key_observations
means all requested keys’ known lifecycle states were returned. It never claims all
device history when exporting a bounded subset. Missing/gap observations fail; no
silent truncation. Historical enrollment/revocation/owner-reported compromise are
separate from current observed membership and new action approval.
Trust and identity
The account issuer is the actual configured API namespace with tenant/user UUIDs, not mutable email and not an invented OIDC claim. OIDC remains an optional external adapter when implemented. Authority URI and account URI are separately selected. Issuer-key validity/revocation/compromise, exact scope and complete selected-key state are verified against pinned reviewed roots; an old signature is not current membership. Newly issued bindings are not reusable event approval grants.
The issuer authenticates accounts and locks scoped membership/device/key rows before signing/persisting. The database has a single pinned account issuer; adopting old unattested namespace metadata needs an explicit deployment acknowledgement, and a later different provider cannot alias existing UUIDs. Enrollment/rotation/revocation proofs and reported compromises are owned by Soma, not by this offline library.
No blockchain/public Sigstore is mandatory. They can transport reviewed checkpoint commitments in a later witness adapter; they establish neither content truth nor membership. Public publication/private data retention remain separate decisions.
Verification and boundaries
- Independent OpenSSL signing plus Python canonical vector; pure library adversarial issuer/tenant/subject/device/key/scope, stale/expired/partial/revoked/compromised tests.
- Core checks real governed GUID and separately selected pins; CLI/MCP output parity.
- Real local Febasi account/device proof -> Soma signed scope -> actual Synapse binary/stdio, wrong scope and historical revoke/compromise/re-enrollment cases.
- Real PG concurrent permission/device revocation waits for the signed observation transaction; a new issuance after revocation denies.
- Existing SSHSIG verification remains SYN-261, source/receipt checkpoints remain ADR-057/058. Artifact identity verification alone closes neither strict-team assurance nor SYN-231’s current event/proposal/policy-bound acceptance.
References
Alternatives checked against primary specifications
Pinned local keys establish selected key possession without authenticating an enterprise account. An OIDC adapter additionally needs ID Token issuer/audience, signature, lifetime and nonce validation; we do not label Febasi’s current API/SDK as that protocol. OpenID Connect Core defines those client checks. Scope membership and event approval remain application policy even after successful account authentication.
Sigstore provides its own artifact bundle and certificate/issuer/identity verification workflow. It is a later explicit adapter with its own roots/privacy/deployment assumptions, rather than a substitute for private project admission. See Sigstore bundle format and identity-based verification. Native private authority contracts here do not claim Sigstore conformance.