2026-07-18 21:00:06 +02:00
|
|
|
# Geth Keychain And SSHSIGCHAIN
|
2026-05-26 18:53:20 +02:00
|
|
|
|
|
|
|
|
## Purpose
|
|
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
The geth keychain is the local identity-plane operation log for admin SSH keys,
|
|
|
|
|
users, devices, nodes, agents, and endpoint bindings. The daemon stores these
|
|
|
|
|
operations in SQLite and synchronizes authorized records over Iroh. Reducing
|
|
|
|
|
the operation log produces the current keychain view used by node management,
|
|
|
|
|
authorization, and OpenSSH `allowed_signers` projection.
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
[`sshsigchain.md`](sshsigchain.md) specifies SSHSIGCHAIN, the only portable
|
|
|
|
|
signed-chain format in this repository. It is deliberately separate from the
|
|
|
|
|
current local keychain operation log: SSHSIGCHAIN starts with an explicit
|
|
|
|
|
out-of-band trust tuple, has a strict sequence and hash link, and never uses a
|
|
|
|
|
downloaded `allowed_signers` file as a trust root. The previous test-only
|
|
|
|
|
static JSONL publication format was removed.
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
## Local keychain model
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
The durable local log contains `KeychainOp` records and their
|
|
|
|
|
`KeychainOpSignature` records. Every operation has a deterministic canonical
|
|
|
|
|
binary signing payload under the `geth.keychain.v1@geth.local` namespace.
|
|
|
|
|
JSON is an API and storage representation, never the signed payload.
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
Important operation kinds are:
|
2026-05-26 18:53:20 +02:00
|
|
|
|
|
|
|
|
- `KeychainInit`
|
2026-07-18 21:00:06 +02:00
|
|
|
- `AdminKeyAdd`, `AdminKeyRevoke`
|
2026-05-26 18:53:20 +02:00
|
|
|
- `UserAdd`, `UserRename`, `UserRevoke`
|
2026-07-18 21:00:06 +02:00
|
|
|
- `DeviceAdd`, `DeviceRevoke`, `DeviceKeyAdd`, `DeviceKeyRevoke`
|
2026-05-26 18:53:20 +02:00
|
|
|
- `NodeAdd`, `NodeRename`, `NodeRevoke`
|
|
|
|
|
- `NodeEndpointAdd`, `NodeEndpointRevoke`
|
|
|
|
|
- `AgentBind`
|
|
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
`AdminKeyAdd` carries the OpenSSH public key material used to reconstruct the
|
|
|
|
|
active `allowed_signers` view. Local operation and signature identities are
|
|
|
|
|
append-only: an existing identity with different bytes is rejected rather than
|
|
|
|
|
replaced.
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
## Local signatures
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
An operation signature contains its operation ID, signer key fingerprint,
|
|
|
|
|
signer public key, SSHSIG namespace, OpenSSH signature bytes, and creation
|
|
|
|
|
time. geth signs through OpenSSH, for example:
|
2026-05-26 18:53:20 +02:00
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
ssh-keygen -Y sign -n geth.keychain.v1@geth.local -f <signing-key> <payload>
|
|
|
|
|
```
|
|
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
`<signing-key>` may be a local private key, an OpenSSH security-key stub, or a
|
|
|
|
|
public key whose private half is available through `ssh-agent`. The daemon does
|
|
|
|
|
not store private keys or passphrases.
|
2026-05-27 00:59:52 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
Local verification accepts an operation only when a signature from an admin
|
|
|
|
|
key in the previously accepted local view verifies over the canonical payload.
|
|
|
|
|
This is useful for the local Iroh-synchronized operation log, but it is not a
|
|
|
|
|
portable SSHSIGCHAIN history and MUST NOT be presented as one.
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
## SSHSIGCHAIN keychain profile
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
Geth's SSHSIGCHAIN profile identifier is `geth.keychain.sshsigchain.v1` and its
|
|
|
|
|
SSHSIG namespace is `sshsigchain.v1`. Its canonical binary payload is a
|
|
|
|
|
versioned mirror of a keychain operation; it is separate from the
|
|
|
|
|
human-facing, internally tagged JSON API type so a decoder can prove one unique
|
|
|
|
|
payload encoding.
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
The profile requires:
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
- sequence zero to be `KeychainInit` signed by the operator-pinned root key;
|
|
|
|
|
- sequence one to be an `AdminKeyAdd` recording that root key;
|
|
|
|
|
- every following signer to be an active admin key in the causally prior
|
|
|
|
|
profile state;
|
|
|
|
|
- every keychain operation ID to occur only once in the chain;
|
|
|
|
|
- each added admin key to have canonical key material matching its declared key
|
|
|
|
|
fingerprint; and
|
|
|
|
|
- causal add/revoke records rather than validity windows or payload timestamps
|
|
|
|
|
as authorization policy.
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
The root key seeds authorization at genesis only. A causally valid revocation
|
|
|
|
|
removes it like any other admin key.
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
To inspect an SSHSIGCHAIN JSONL transport file, pin the chain ID and root public
|
|
|
|
|
key locally:
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
```sh
|
|
|
|
|
geth keychain verify-sigchain \
|
|
|
|
|
--in geth.sshsigchain.v1.jsonl \
|
|
|
|
|
--chain-id <64-hex-character-chain-id> \
|
|
|
|
|
--root-key ~/.ssh/geth-root.pub
|
2026-05-26 18:53:20 +02:00
|
|
|
```
|
|
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
This verifier is experimental while geth adds its own record creation,
|
|
|
|
|
publication, import, accepted-head persistence, and independently generated
|
|
|
|
|
wire vectors. Those later workflows must preserve the same explicit trust
|
|
|
|
|
tuple; they must not introduce a compatibility route for the removed static
|
|
|
|
|
format.
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
## Local commands
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
Bootstrap an owner node:
|
2026-05-26 18:53:20 +02:00
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
geth init \
|
|
|
|
|
--admin-key ~/.ssh/id_ed25519_sk.pub \
|
|
|
|
|
--signing-key ~/.ssh/id_ed25519_sk \
|
|
|
|
|
--node-name laptop
|
|
|
|
|
```
|
|
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
Manage the current local keychain and its OpenSSH projection:
|
2026-05-26 18:53:20 +02:00
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
geth keychain admin-add \
|
|
|
|
|
--admin-key ~/.ssh/new_admin.pub \
|
|
|
|
|
--signing-key ~/.ssh/current_admin_sk \
|
|
|
|
|
--principal admin
|
|
|
|
|
geth keychain admin-revoke <key-fingerprint> \
|
|
|
|
|
--signing-key ~/.ssh/current_admin_sk
|
2026-05-27 00:59:52 +02:00
|
|
|
geth keychain allowed-signers --out allowed_signers
|
2026-07-18 21:00:06 +02:00
|
|
|
geth keychain sign-file --in authorized_keys --out authorized_keys.sig \
|
2026-05-27 00:59:52 +02:00
|
|
|
--signing-key ~/.ssh/id_ed25519_sk
|
2026-07-18 21:00:06 +02:00
|
|
|
geth keychain verify-file --in authorized_keys --signature authorized_keys.sig
|
2026-05-27 00:59:52 +02:00
|
|
|
geth keychain explain <op-id>
|
|
|
|
|
geth keychain explain-signer <key-id>
|
2026-05-26 18:53:20 +02:00
|
|
|
geth keychain verify
|
|
|
|
|
```
|
|
|
|
|
|
2026-07-18 21:00:06 +02:00
|
|
|
The keychain does not manage host `authorized_keys` policy. It can sign a
|
|
|
|
|
snapshot supplied by the operator, allowing another system to retain its own
|
|
|
|
|
SSH login policy while usefully checking approval against an active local admin
|
|
|
|
|
key.
|