specify selective disclosure and anchor policy
Some checks failed
CI / fmt, clippy, docs (push) Failing after 5s
CI / test (ubuntu-latest) (push) Failing after 5s
CI / iroh integration smoke tests (push) Failing after 5s
CodeQL / Analyze Rust (push) Failing after 4s
Security / RustSec cargo-audit (push) Failing after 5s
CI / test (macos-latest) (push) Has been cancelled
CI / test (windows-latest) (push) Has been cancelled

This commit is contained in:
Eric Wendland 2026-07-18 22:05:57 +02:00
commit 76eb785ee2
11 changed files with 834 additions and 387 deletions

View file

@ -1,42 +1,64 @@
# ADR 0018: Linked SSHSIGCHAIN v1
# ADR 0018: Sequence-free SSHSIGCHAIN authority protocol
## Status
Accepted for the generic core and geth keychain profile. The pre-standard
test-only static workflow is removed; it is not an alternate format or a
compatibility path.
Accepted for the generic core. The protocol is still pre-deployment and has no
legacy compatibility requirement.
## Context
The prior static JSONL keychain flow replayed records after sorting mutable
timestamps and used a downloaded allowed-signers projection to verify its own
checkpoint. That permits self-bootstrapping trust and makes revocation,
rollback, and fork semantics inadequate for a trust foundation.
The first linked draft improved on a timestamp-sorted static bundle, but still
made application profiles responsible for all key lifecycle semantics. It also
included both a sequence counter and a parent hash, fixed one application
profile for the whole chain, exposed every payload, and left rollback anchors
outside the protocol model.
That design would let applications fragment device/key behavior, made
multi-profile least privilege awkward, and offered no standard way to disclose
only selected profile data or compare independently anchored heads.
## Decision
Geth adopts SSHSIGCHAIN version 1 as its one portable signed-chain design:
SSHSIGCHAIN v1 is redesigned before deployment:
- root trust is an explicit `(chain ID, profile, namespace, root key)` tuple;
- records are linearly ordered by sequence and linked by a digest over their
signed content and SSHSIG signature;
- the initial root is only a genesis requirement, not a permanent bypass;
- profile authorization runs against the causally preceding state;
- key lifecycle is represented by causal add/revoke records, not timestamp
validity fields; and
- payloads use deterministic binary codecs with a strict round-trip check.
- the trust tuple is `(chain ID, SSHSIG namespace, root public key)`;
- parent hashes alone define order; there is no sequence number;
- link identity hashes the signed outer bytes, not the signature encoding;
- every link carries a mandatory public Authority v1 transition;
- authority owns devices, keys, proof-of-possession, causal revocation,
permission ceilings, delegation, and anchor policy;
- applications use profile-ID-scoped, salted payload commitments and cannot
mutate authority;
- disclosures are transport additions which do not change a link or its hash;
- authorized keys can sign head claims;
- distinct head attesters and backend-neutral receipts are evaluated by
separate weighted/required thresholds and required backend classes;
- cached ancestors are never replaced by older heads and incomparable verified
histories fail as forks; and
- a policy-change head is witnessed under the preceding policy before the new
policy governs descendants.
The generic core is transport-independent. Geth continues using Iroh for all
node-to-node communication; SSH remains an identity/signature integration and
not a geth transport.
The generic core defines backend interfaces and deterministic policy behavior,
not Nostr, HTTP, blockchain, or transparency-log clients. Those adapters belong
outside `geth-keychain`. Backends provide evidence and discovery, not consensus.
The generic protocol remains independent of geth transports. All geth
node-to-node communication remains Iroh-only; SSH remains a trust and signature
integration.
## Consequences
The core can be published and implemented by applications without importing
geth's resource model. Geth's keychain profile is intentionally narrow and
tested against self-bootstrap, fork, and backdated-revocation attacks.
Device and key behavior is interoperable rather than reinvented in each
profile. A verifier can validate current authority while withholding application
data, but must report affected profile state as incomplete. Proofs of possession
prevent an administrator from silently enrolling a key it does not control.
Device ceilings and delegable permission sets constrain later amplification.
The old static bundle code and commands are deleted rather than supported beside
SSHSIGCHAIN. There is no migration format because the repository has not been
deployed. Head persistence and later witness/transparency support remain
follow-up work; neither is implied by a single signed checkpoint.
The wire format is intentionally incompatible with every pre-deployment test
draft. No migration parser or version alias is retained.
Rollback protection still depends on persistent local accepted heads and the
operator's anchor policy. A Nostr relay, mutable HTTP URL, blockchain, or
transparency service has only the guarantees its receipt verifier and deployment
actually establish. Concrete publication, durable accepted-head storage, and
cross-implementation vectors remain follow-up work.