# ADR-054: Outgoing-Only X.509 and the Three Remote Roles (Re-Owned In-Crate) ## Status Accepted (re-owns the verifier-selection rule for alkcall's surface; provenance: alknet ADR-034, not ported) ## Context alkcall's connection surface depends on a verifier-selection rule and a role taxonomy that were decided while this crate was a subdirectory of the alknet mono-repo. The deciding ADR (alknet ADR-034, "Outgoing-Only X.509 and Three Peer Roles") was never ported, so the rule's authority lives in a cross-repo link — a dependency on a document outside this crate for a constraint that binds this crate's public surface: - `ConnectionCredentials.remote_identity: None` semantics (ADR-022 §7, (ADR-022 §7; alknet ADR-034 §2/§3, provenance): `None` is a load-bearing state — the public-X.509-endpoint path, not a missing field. - Client-side `ServerCertVerifier` selection is by `PeerEntry` presence, not by key type alone (OQ-14 #2). - `PeerEntry` asymmetry: a pure-client connection to a public X.509 endpoint is not in the call-protocol peer graph on the client side (no `PeerEntry`, no `PeerId`, no `PeerRef::Specific` routing). The alknet mono-repo is being rewritten as a thin glue layer over this crate and its siblings; cross-repo ADR links will not survive that rewrite as authority. The rule must be owned here. ## Decision alkcall re-owns the rule in-crate. Three things are adopted: ### 1. Three remote roles The single label "X.509 endpoint" conflated three structurally different remote roles: - **Public X.509 endpoint** — a remote server reachable by domain name, authenticated by a CA-issued X.509 cert. The local node is a *client* of it (a third-party API, a public hub dialed over the open internet, a peer that exposes domain + X.509 instead of an Ed25519 raw key). The client authenticates to the server by bearer token (`auth_token` payload field when riding the call protocol); the server authenticates to the client by CA verification (WebPKI), not fingerprint pinning. - **Transport relay** — a connectivity-assistance node (iroh's DERP-equivalent) that forwards encrypted datagrams between peers who cannot directly connect. It is infrastructure, not an application peer: no registered operations, no peer graph participation, no `PeerEntry`. Its identity is the transport's concern, not this crate's. - **Hub / hosting node** — an application peer acting as a hub in a hub-and-spoke topology. An ordinary `PeerEntry` holding mixed fingerprints (Ed25519 raw key and/or X.509) — fingerprint pinning applies on both cert paths. ### 2. Verifier selection is by `PeerEntry` presence, not key type The client-side `ServerCertVerifier` is selected by whether the local node holds a `PeerEntry` for the remote: | Local `PeerEntry` for remote? | Remote cert | Verifier | Peer graph? | |-------------------------------|-------------|----------|-------------| | yes | Ed25519 or X.509 | fingerprint pin | in (`PeerId` resolved from client-cert fingerprint — ADR-025 §5) | | no | X.509 | `WebPkiServerVerifier` (CA) | **not in** (no `PeerId`) | | no | Ed25519 raw key | fail closed (no CA to fall back to) | not in | ### 3. `remote_identity: None` is load-bearing `Some(fingerprint)` means "pin this" (known peer). `None` means "no `PeerEntry` for this remote" — CA verification for X.509 remotes, hard failure for Ed25519 raw-key remotes. An implementation must not default `remote_identity` to a placeholder to satisfy the field, and must not treat `None` as "skip verification." ### 4. Scope of the re-own This ADR is the deciding authority for alkcall's surface (`ConnectionCredentials`, `RemoteIdentity`, the `ServerCertVerifier` selection the dial-composer implements, `PeerEntry` asymmetry in the peer graph). It does not re-own alknet ADR-034's other content (ACME issuance, cert rotation, the alktls config surface) — those remain properties of the transport/composition layer (see OQ-43, composition layer). alknet ADR-034 is demoted to provenance: historical origin of this rule, cited as such. ## Consequences Positive: - The constraint binding alkcall's public surface is decided in this crate's decision log; cross-repo link rot cannot strip the authority. - OQ-21's "deciding ADR (alknet ADR-034, not ported)" caveat is resolved — the surviving surfaces (`client-and-adapters.md` §credentials, ADR-025's `PeerEntry` asymmetry) now cite an in-crate ADR. - The alknet rewrite can proceed without a documentation dependency from this crate on the old repo's ADR numbering. Negative: - One more rule with a historical origin story in a different numbering space; the provenance note (README §References) is the single place that maps old to new. ## References - [client-and-adapters.md](../client-and-adapters.md) — the working spec carrying these semantics (§Credential sources for connections, §Constraints "Pure-client X.509 connections", `remote_identity` load-bearing rule) - [ADR-022](022-call-protocol-client-and-adapter-contract.md) §7 — transport-identity dimensions on the call protocol - [ADR-025](025-peerentry-and-identity-id-decoupling.md) — `PeerEntry` asymmetry, `PeerId` source, client-cert fingerprint → `peer_id` resolution - [ADR-012](012-connectioncredentials-decouple-dial-from-call.md) — `ConnectionCredentials` shape (the dial consumes it; verifier selection is applied there) - OQ-21 (resolved by this ADR), OQ-43 (composition-layer ownership — the transport/composition half mentioned in §4's scope carve-out) - Provenance: alknet ADR-034, "Outgoing-Only X.509 and Three Peer Roles" (`/workspace/@alkdev/alknet/docs/architecture/decisions/`), retained as history, not authority