Skip to content

Bootstrap cross-person trust with a one-time connection code, optionally PGP-signed - #191

Merged
Mearman merged 6 commits into
mainfrom
feat/connection-code-trust-bootstrap
Sep 18, 2026
Merged

Mearman merged 6 commits into
mainfrom
feat/connection-code-trust-bootstrap

Conversation

@Mearman

@Mearman Mearman commented Sep 18, 2026

Copy link
Copy Markdown
Member

Implements a single ConnectionCode artifact (nonce + expiry + deviceId + optional PGP signature) with one generation flow and one redemption flow, bootstrapping GatewayTrust without an existing mesh connection between the two devices.

  • Nonce and expiry are always checked (freshness/liveness).
  • Signature is optional PGP verification, checked only when present, never required.
  • Adds two new actions: gateway_generate_connection_code and gateway_redeem_connection_code.
  • Uses openpgp.js for signature generation/verification.
  • Redemption resolves a signer's public key either from a pasted armored block or a keys.openpgp.org keyserver lookup by fingerprint.
  • Persists a per-slot connection-code ledger (issued codes, redeemed nonces) via identity-store.ts's existing sibling-JSON-file pattern.

Work in progress; will flip to ready once complete and green.

Closes #188

Adds loadConnectionCodeLedger/saveConnectionCodeLedger to identity-store.ts, mirroring the existing loadGatewayTrust/saveGatewayTrust sibling-file pattern. Tracks two independent maps per slot: codes this slot has issued (so an outstanding code survives a restart between generation and hand-off) and nonces this slot has redeemed (so a single-use code can't be redeemed twice across a restart within its own short validity window).

Also adds openpgp as a dependency, needed by the connection-code signature verification this ledger will back (agent-comms#188).
…nd redemption

Implements the always-checked nonce/expiry half and the optional PGP-signature half of a ConnectionCode artifact (agent-comms#188): generate() mints a fresh single-use code vouching for a device-id, optionally signed with a caller-supplied armored PGP private key; redeem() validates freshness, single-use, and (only when a signature is present) verifies it against a caller-supplied public key, optionally pinned to an already-trusted fingerprint.

PGP key material is never generated or stored by this module -- signing and verification both take key material supplied per call, the same way invoking gpg directly would, rather than agent-comms managing a persisted PGP identity of its own.

Adds ConnectionCodeSchema and the gateway_generate_connection_code/gateway_redeem_connection_code CommsAction variants to types.ts, ahead of wiring them into MeshStore and CommsTool.
fetchPgpPublicKeyByFingerprint (agent-comms#188) fetches an armored public key from keys.openpgp.org's VKS API by fingerprint -- the convenience path a connection-code redeemer takes when they know the signer's fingerprint but weren't handed the armored key block directly.

The keyserver is never a trust anchor: the caller already independently trusts the fingerprint they supply, and ConnectionCodeLedger.redeem's own expectedFingerprint check is what defends against a compromised or buggy keyserver returning the wrong key, not this fetch itself.
…connection_code

MeshStore now constructs a ConnectionCodeLedger alongside GatewayTrust, sharing the same per-slot identity so both persist to the same bridge instance's own storage. generateConnectionCode mints a code vouching for this store's own peerId; redeemConnectionCode validates a candidate and, only on success, feeds its deviceId into gatewayTrust.add -- the actual point of the bootstrap.

CommsTool gains the matching MeshOnlyFeatures methods and a fetchPgpPublicKeyByFingerprintImpl dependency (defaulting to the real keys.openpgp.org lookup, injectable for tests), used by gateway_redeem_connection_code when a caller supplies a fingerprint but not the key text itself.

The two handlers live in a new connection-code-tool.ts rather than as CommsTool methods, since they take no CommsContext (unlike every other action) and keeping them out of tool.ts's own class body is what keeps that file under its existing max-lines budget.

bridge.ts's MCP_TOOL_PARAMS and buildAction gain the new actions' fields (ttlMs, privateKey, passphrase for generation; code, expiresAt, signature, publicKey, fingerprint for redemption, reusing the existing device field for the code's own embedded deviceId).
…_connection_code end to end

Exercises CommsTool.handle -> MeshStore -> ConnectionCodeLedger for both actions, mirroring gateway-trust-tool.test.ts's own pattern: an issuer store generates a code vouching for its own peerId, a separate redeemer store redeems it and ends up trusting that exact device via gatewayTrust. Covers the bare, PGP-signed, pasted-public-key, and keyserver-fingerprint-lookup paths, plus buildAction parsing and required-field validation for both actions.
Adds a Gateway trust and connection codes section covering the existing gateway_trust/gateway_untrust/gateway_list_trusted actions (previously undocumented) alongside the new gateway_generate_connection_code/gateway_redeem_connection_code pair, with usage examples for the bare, PGP-signed, pasted-public-key, and keyserver-fingerprint paths.
@Mearman
Mearman force-pushed the feat/connection-code-trust-bootstrap branch from aa7eebb to 692262c Compare September 18, 2026 08:47
@Mearman
Mearman marked this pull request as ready for review September 18, 2026 08:49
@Mearman
Mearman merged commit 60b8669 into main Sep 18, 2026
6 checks passed
@Mearman
Mearman deleted the feat/connection-code-trust-bootstrap branch September 18, 2026 08:49
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 18, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review Completed 2026-09-18T08:59:17.125369Z 692262c Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 3.16.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bootstrap cross-person trust with a one-time connection code, optionally PGP-signed

1 participant