Skip to content

No trust-on-first-use tier: handshake_ack carries no public key, so a host cannot pin one #130

Description

@macanderson

Problem

ADR 0016
makes the operator the trust root: a key is in
contextgraph_host::TrustStore because a person put it there. That is the
right primary tier and it is the only one that needs no organization.

It leaves a real gap. A provider a person installed but never handed a key for
serves evidence that reads as AttestationState::NoTrustedKey forever, even
though the same key has answered every query for a year. Trust-on-first-use
would record that continuity as a labelled second tier, strictly below a
configured key: pin the first key seen, and report loudly when it changes.

It is not implementable today, and the reason is a fact about the wire rather
than a judgement: a ProvenanceAttestation carries a key_id, an algorithm
and a signature, and no public key. Neither does handshake_ack
(contextgraph-types/src/capability.rs, ProviderInfo). There is nothing to
pin.

What would have to happen

  1. An additive wire field carrying a provider's public key(s) — most naturally
    on handshake_ack beside ProviderInfo, since that is where a provider
    already declares who it is. Optional, so no existing provider breaks.
    Additive within contextgraph/1 per GOVERNANCE.md.
  2. Its own ADR, because it is a normative addition and because it introduces a
    trust tier ADR 0016 deliberately did not adopt. That ADR must not let the
    pinned tier be presented as equivalent to a configured one: TOFU proves
    continuity, never identity, and an attacker present at first contact is
    trusted forever.
  3. TrustStore gains an origin on each key (configured vs pinned) and a
    pinning path. Note that pinning mutates the store during a fan-out, which
    takes &self today — either the pin happens outside the query path or the
    store needs interior mutability.
  4. AttestationState::Attested gains, or is joined by, the tier, so an audit
    can distinguish "verified against a key a person checked" from "verified
    against a key we happened to see first".

Files

  • contextgraph-host/src/trust.rsTrustStore, TrustedKey, AttestationState
  • contextgraph-host/src/host.rsHost::trust_key, query_one_isolated
  • contextgraph-types/src/capability.rsProviderInfo / handshake_ack
  • docs/adr/0016-attestation-trust-roots.md — the alternative, and why it was deferred

Definition of done

  • An ADR decides whether the pinned tier is adopted, and if so says plainly
    that it proves continuity and not identity.
  • If adopted: the wire field lands additively, with a schema update and an
    example, and SPEC.md says a receiver MUST tolerate its absence.
  • If adopted: a key's tier is visible in AttestationState, and a test
    proves a changed key under a pinned key_id is reported loudly rather
    than silently re-pinned.
  • If not adopted: ADR 0016's "tracked as follow-up" sentence is updated to
    say so, so the option is closed rather than left hanging.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Next cycle

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions