Skip to content

design: a name is claimed once — the hosted binding lifecycle contract (pkgs/id hosted_binding) #5047

Open
4 of 4 issues completed

Description

@dezren39

A name is claimed once — the hosted binding lifecycle contract (pkgs/id hosted_binding)

Design doc: files/designs/code-id-hosted-binding-lifecycle.md in the keep-the-four-repos-healthy goal workspace. Audit HEAD: origin/main 8e151e89. Module: pkgs/id/src/hosted_binding.rs.

Summary

hosted_binding.rs binds a Google identity (sub) to an Iroh root key — the §3 challenge/pairing ceremony (slice 1, #3808) and the §4 delayed rebind protocol (slice 2, #3809 via #4885), both landed 2026-11-03. The module's header states its §3.4 failure table and §4 no-mutation contract in prose, but the transitions of its ephemeral state were never stated as one lifecycle, and three filings in one day are one counterexample per transition:

This audit adds a fourth surface of the same missing lifecycle: the rebind paths' shared verify_challenge_proof (:1171-1205) also consumes on expiry, and RebindError::ChallengeExpired's doc states the opposite of the ceremony side's — "it is consumed and dead." One module, two contradictory expiry semantics for the same challenge object.

The contract: every name the ceremony mints — a challenge, a pairing code, a rebind target key — is claimed exactly once, by exactly one holder, for exactly its stated lifetime, and every transition out of a live state (consumption, supersession, expiry, reservation, release) is named in the §3.4/§4 tables. Dead means the same thing on both error surfaces.

Checked and NOT stating this contract: #3809 (closed implementation slice) and #3818 (open, blocked — hosted bridge transport, gated on the #3808 contract, states no ceremony transitions). The Worker API slice (#3811) that will expose pairing over HTTP is not yet built; this contract lands before it does.

Decisions recorded

  1. Expiry creates no state, on both surfaces. Expired challenges are removed from the live map and NOT inserted into consumed_challenges, in submit_proof and in verify_challenge_proof; RebindError::ChallengeExpired's doc is amended to match §3.4 in the same change. (The docs-amended-the-other-way alternative loses: replay is a security signal, expiry is not.)
  2. A consumed challenge reports as consumed, everywhere. Every entry point checks the consumed set before the live map (the order submit_proof already uses), making create_pairing_code's ChallengeReplay reachable; UnknownChallenge means never-issued or expired away.
  3. Minting supersedes. create_pairing_code removes every existing code for the challenge first — at most one live code per challenge. Rate limits unchanged.
  4. A pending rebind reserves its target key. Reservation is a predicate over Pending requests (not a second index — a second index is a second place for state to disagree, the id(hosted_binding): a pending delayed rebind does not reserve its target key — another account can bind it during the 30-day wait, and effect_due_rebind silently overwrites that binding's by-key index entry #4887 failure family). submit_proof refuses a reserved key with IrohIdBoundElsewhere; request_delayed_rebind refuses a reserved target with NewKeyBoundElsewhere; the fast path gains the same check. Cancel/effect releases implicitly (the predicate reads only Pending).
  5. Effect re-checks; the index write refuses a foreign entry. effect_due_rebind re-checks before any mutation (NewKeyBoundElsewhere, no state change, request stays Pending), and apply_rebind itself refuses to overwrite a binding_by_iroh entry naming a different binding.

Phases (sub-issues of this design)

Open questions (recorded in the design doc, not phased)

Acceptance for this design issue

All four phases closed with evidence; the module header carries the lifecycle transition table; #4868/#4871/#4887 closed by their phases (not re-filed).

Acceptance criteria

Activity

  1. added
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    epicMeta-issue grouping multiple items (org Project Type=Epic)
    kind/designA design proposal (design-architect): multi-step, phased, decision-bearing
    on Nov 3, 2026
  2. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note: design-architect pass 45 (2026-11-03) originated this design from the standing candidate recorded at pass 44: the pkgs/id hosted_binding lifecycle trio #4868 + #4871 + #4887, audited against origin/main 8e151e8 with every defect re-verified present at the cited lines in pkgs/id/src/hosted_binding.rs. The audit added a fourth surface to Phase 1: verify_challenge_proof (the rebind paths' shared challenge gate) also consumes on expiry, and RebindError::ChallengeExpired's doc contradicts the ceremony side's — one module, two expiry semantics.

    Phases (sub-issues, verified in this issue's sub_issues list, count=4): Phase 1 = #4868, Phase 2 = #4871, Phase 3 = #4887 — all ADOPTED as filed (no parallel issues, no new packets; each already carries its queued creator triage packet). Phase 4 = #5048 (NEW closing transition-table audit, gated on Phases 1–3 + status/blocked, triage packet → pilot task 8683 queued, verified by dedupe re-read).

    Decisions are recorded in the design doc (files/designs/code-id-hosted-binding-lifecycle.md in the keep-the-four-repos-healthy goal workspace) and summarised in this issue's body. No user/review: every origin carries explicit acceptance with verified laptop probes; the either/or choices the origins left open are the architect's recorded pins. #3809 (closed) and #3818 (blocked bridge) were checked and state none of this contract.

  3. added
    priority/highImportant and time-sensitive
    effort/lLarge: multi-day or open-ended; expect staged check-ins
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Nov 3, 2026
  4. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    Intake review

    — fleet monitor intake pass (repo-fleet-monitor, 2026-11-03 12:12 CST run)

  5. 111 remaining items

  6. added
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 5, 2026
  7. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  8. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 5, 2026
  9. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  10. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  11. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  12. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  13. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    task: 9811
    lane: code-r
    state: handed_off
    note: Handed off to opencode claimant per execution-lane assignment (assigned_lane=opencode). Checkpoint handed_off recorded; parked 1800s for claimant launch. Issue #5047 re-verified open, no held/user-owned/do-not-merge/do-not-auto-edit labels.

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

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/lLarge: multi-day or open-ended; expect staged check-insepicMeta-issue grouping multiple items (org Project Type=Epic)expediteImportant - do this very soon, before anything not expeditedkind/designA design proposal (design-architect): multi-step, phased, decision-bearingmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/highImportant and time-sensitivequeue/parkedQueue task is parked (waiting on CI, time, or an event)source/design-architectFiled by the repo design architect jobstatus/parkedDeliberately paused with a recorded resume conditionwork/assignedQueue worker lifecycle state

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions