Skip to content

feat(pairing): support explicit one-time code or link pairing without invitation-file transfer #212

Closed

Description

@abruption

Motivation

Transferring a private invitation JSON between machines is a major first-use hurdle even after login and enrollment. Provide a user-friendly one-time code or link alternative without weakening endpoint identity verification or policy authorization.

Alternatives considered

  • Keep the current trusted-channel invitation-file flow as a supported fallback.
  • Login to the same account alone must not automatically authorize all devices or native targets.
  • A code/link broker must not receive endpoint private keys or agent messages. Whether a broker is required and its trust boundaries need explicit design review.

Proposed behaviour

  • The receiving device explicitly creates a short-lived pairing offer.
  • The other device enters a one-time code or opens an approved link, and both sides display sufficient peer identity information for confirmation.
  • Reuse or preserve the existing endpoint possession proofs, certificate pinning, policy checks, and one-use invitation properties.
  • Completing pairing never silently grants list/send to every session; native target access still requires the receiving policy.
  • Integrate with guided setup, while keeping standalone manual commands and invitation-file pairing available.

Acceptance criteria

  • Document a threat model and protocol proposal before implementation, including intermediary compromise, guessing, replay, link leakage, and peer substitution.
  • Offers expire, can be cancelled, are rate-limited, and are atomically single-use; concurrent redemption and interrupted confirmation have defined safe behavior.
  • Require explicit identity confirmation and preserve endpoint key ownership, pinning, and receiving-policy authorization.
  • Codes/secrets are not placed in routine logs, analytics, referrers, or issue reports; define safe handling for browser history and any link-carried material.
  • Never upload endpoint private keys, copy device state across machines, or infer authorization from account membership alone.
  • Test expiry, replay, wrong identity, guessed codes, concurrent redemption, cancellation, interrupted completion, and successful pairing.
  • Existing file-based pairing remains compatible; publish migration/version boundaries if the wire protocol changes.
  • Four-language UI/help documentation explains what pairing authorizes and how to revoke it.

Scope

v1.1.0 design/implementation candidate. This is not permission to open new public endpoints, change current signup settings, or deploy an unreviewed pairing service. Hosted availability and E2EE guarantees must not be broadened without evidence.

Activity

  1. added this to the v1.1.0 milestone on Sep 27, 2026
  2. modified the milestones: v1.1.0, v1.2.0 on Oct 6, 2026
  3. added
    priority:P2Planned reliability, acceptance or feature work; priority does not authorize implementation.
    on Oct 13, 2026
  4. modified the milestones: v1.2.0, v1.3.0 on Oct 13, 2026
  5. abruption commented on Oct 23, 2026

    @abruption
    OwnerAuthor

    Implementation proposal: receiver-bound one-time links

    This v1.3 candidate implements a long, high-entropy pairing link rather than a short numeric code. It introduces no broker, public endpoint, browser integration, OS URL handler, deployment or account-policy change. Existing invitation-file pairing remains supported.

    Protocol and identity confirmation

    The receiver explicitly creates a ten-minute offer bound to the intended client's current certificate fingerprint. Before creation, a terminal shows the receiver principal/key and intended client key; the operator must confirm that key through an independent trusted channel. A CLI-only session-peer://pair/v1#BASE64URL(JSON) envelope carries the existing invitation and intended key. Its existing 256-bit secret remains hashed in the receiver database, never stored there in plaintext.

    The intended client principal/generation is derived only from its exact active existing local key anchor, or fingerprint/0 for an unknown first-use key. Known pending, retired or revoked keys are rejected rather than treated as first use; operator text cannot manufacture a principal/generation binding.

    The client enters the link through an echo-disabled terminal, checks its own current key matches the intended key, views both endpoint identities/routes, and confirms the receiver's current fingerprint independently before any connection or trust staging. A link cannot authenticate its own claimed receiver identity. Envelope parsing is bounded, rejects duplicate/unknown fields, requires canonical IDs, finite numeric diagnostics and strict base64url, and emits fixed sanitized errors. Ordinary JSON and diagnostics never contain the link. Links are not accepted as command-line values, fetched, opened in browsers, or journaled. Terminal recording and clipboard/browser history remain user-controlled leakage risks; avoid copying links into public logs, browser address bars or issue reports.

    Existing pinned end-to-end TLS and proof of client private-key possession remain mandatory. Link clients require a pairing-offer-v1 feature response and an invitation-bound commit; older receivers cannot silently downgrade them to unbound file semantics. Rotated principals still require their existing exact active key/principal/generation anchor; a fingerprint confirmation alone cannot create that authority. Pairing never authorizes arbitrary native targets or bypasses receiving policy.

    Lifecycle and concurrency

    Offer states are open, reserved, consumed, cancelled and expired. Reservation, authenticated commit, expiry and cancellation are serialized in an immediate database transaction. Every pending peer has one invitation-owned reservation. A different invitation cannot overwrite it; an already paired peer cannot be downgraded to pending. A same-key, same-offer interrupted commit can reconcile its durable result without a new offer, but cannot undo revocation. Cold expired input is refused before networking/staging. Only a saved intent matching the offer ID, envelope digest, receiver principal/key and current client key may attempt authenticated reconciliation after expiry; it never prepares again, and the receiver may return only an already-consumed matching receipt. Cancellation removes only pending trust owned by that offer, preserving pre-existing key anchors and completed pairings. A consumed offer reports already consumed rather than revoking a peer.

    Persistent limits: five creations/minute, eight concurrent active offers, 32 prepare attempts/minute globally and eight attempts/minute per offer. Failed guesses count durably. Unknown IDs allocate no per-ID rows. Terminal receipts are retained for seven days; a 1,024-offer cap fails closed if safe pruning is unavailable. Offer metadata and its invitation are pruned together so a bound invitation cannot become an unrestricted legacy invitation.

    Threat model and limits

    A compromised intermediary can observe routing metadata, delay, replay or deny service but receives no endpoint private keys or native message plaintext. Independent receiver-fingerprint confirmation prevents peer substitution. A leaked genuine link cannot authorize a different client key. Knowledge of the intended public certificate may permit reservation/denial of service, but not TLS possession proof or native access. Rate limits are bounded resource protection, not identity evidence, and can temporarily deny legitimate attempts. Endpoint/private-key or trusted-confirmation-channel compromise is outside this protection. No automatic native message is submitted during pairing or recovery.

    Acceptance and scope

    Implement isolated offline/loopback tests for real TLS success, wrong identities and tampering, expiry/replay/guesses, cross-connection races, cancellation/commit ordering, interruption/reconciliation, rate limits, retained anchors/revocation, secret-free routine output, TTY refusal, guided setup and file-flow compatibility. Publish help/setup guidance in English, Korean, Japanese and Simplified Chinese. These tests are not a hosted or multi-machine deployment claim. No merge, tag, release, package publication or operational changes are part of this work.

  6. abruption commented on Oct 26, 2026

    @abruption
    OwnerAuthor

    v1.3.0 release closeout — 2026-10-26

    Closing as completed for the owner-approved release scope, following the owner's milestone cleanup instruction. The original proposal/checklist is retained as history; this is not a blanket assertion of every platform or broader research guarantee.

    The approved receiver-bound one-time high-entropy link and guided setup are published. Independent fingerprint confirmation, hidden TTY input, expiry/replay/cancellation, serialized reservation/commit, rate limits and interrupted reconciliation are covered; existing invitation-file pairing remains supported.

    Evidence: merged PR #331; published v1.3.0 scope, source 938630063b69bf6e0ae69d4b11e2095c30f289f4; successful publication and artifact/install verification. Static source/test-definition recheck today: tests/relay/test_pairing_offers.py; tests/relay/test_pairing_links.py; tests/relay/test_pairing_link_ui.py; tests/relay/test_pairing_offer_restore.py. Two read-only subagent audits independently reviewed the split issue groups; no new mandatory closure blocker was found. No new tests/live experiments or deployment were performed during this closeout.

    Remaining boundaries: The accepted alternative is a link, not a short numeric code. No broker/browser handler/Windows or multi-machine operational acceptance is claimed. The 1,024 client-intent cap and seven-day receiver receipt retention remain documented limits. Pairing does not grant arbitrary native permissions or prove message consumption.

    Retain the closed issue in v1.3.0 as the release record. Optional excluded expansions are not automatically promised for another release.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority:P2Planned reliability, acceptance or feature work; priority does not authorize implementation.

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions