Skip to content

Key lifecycle across namespaces, part 1: a key is bound once per namespace; a close ends the key #104

Closed as not planned

Description

@Hafeok

Source: the principal's review of #103. Ruled 6 October; split in two. This issue is part 1. Part 2 (identity rotate|revoke across every namespace in one act, and the split-state notice) is #105, after the Varve import. The dsse ordering difference is #106.

Rulings (6 October)

  1. Binding a key that the same principal already has open in that namespace is refused at filing, and at verify it is a schema fault.
  2. A close applies to the key, not the binding. Closing any binding of a principal's key closes that key in every namespace of the store, from the close's position (D6). Every signature check considers all bindings of the matched key, not the first. The finding names the namespace it was refused in, the namespace of the close, and the close.

They are recorded in docs/signing-rulings-2026-10.md.

Part 1 scope (one PR, from a fresh branch off main)

  • Rulings 1 and 2.
  • As committed tests, all four ending closed or refused:
    • the two cross-namespace pins (bound A then B, and B then A, then closed in A);
    • the two single-namespace cases (the same key bound twice, the first or the second revoked).
  • Every allowed_signers line of a closed key gets its end date.
  • L012's list follows the same rule.
  • A test for the attack: a second binding of a live key is hand-filed, the holder revokes the first, and an acceptance signed with the key afterwards fails.
  • The format document and a migration note. No new class, no format change.

Background: what the gate did before (pinned against #103's head)

  • Bound A then B, closed in A. init C was refused with L011, because ssh_key matched A's closed binding first.
  • Bound B then A, closed in A. init C passed: B's binding was matched first, and A's close was never consulted.
  • Bound twice in one namespace, first revoked. A later accept was refused, though the second binding was live.
  • Bound twice in one namespace, second revoked. A later accept passed: the revoke did not close the key.

In every case the verdict depended on filing order, not on a rule.

Question, not ruled: a holder with no live key in a namespace where they still have bindings

With ruling 2, a holder whose key was closed has no live key in a namespace where they still have bindings, even if another key of theirs is live elsewhere. May the genesis holder then carry in a key of theirs that is live elsewhere, as for a first binding?

What happens today, from reading filing.rs and signing/check.rs (not run):

  • D7. may_file allows the genesis holder's own add when they have no open window here but one anywhere.
  • The signature check refuses it. carried_over brings in keys from other namespaces only when the holder has no binding at all in this namespace, and a closed binding counts. So the add is signed by a key that is "no key bound to them" here, and it fails L011.
  • A non-genesis principal in the same position is re-vouched by the genesis holder as a first key, which D7 already allows.

The case therefore bites the genesis holder only.

Options

  • (a) No change. The genesis holder cannot bind again in that namespace until a rotate-genesis path exists (Session A close-out §3, item 11). Everyone else is re-vouched by the genesis holder, as today. This is the safest, and it leaves the genesis holder locked out of that namespace.
  • (b) Carry in when no key here is live, genesis holder only. carried_over tests "no live key of theirs in this namespace" instead of "no binding". The reason D7 was not widened on Judge a key binding before its namespace's first policy under that policy #103 was that a key closed in one namespace and live in another could re-enter the namespace it was closed in. Ruling 2 removes that case: a carried key is live everywhere by construction, and a key closed anywhere is closed here too. D7's may_file already allows this filer.
  • (c) As (b), for every principal. Any principal's own add may be signed by their key live elsewhere when they have no live key here. This widens D7 for non-genesis principals, who today go through the genesis holder. It means fewer genesis acts, and a compromised but unclosed key elsewhere can mint bindings in every namespace where the holder's key was closed.
  • (d) The genesis holder vouches for itself with the new key (self-bound again). This is listed only to rule it out: it reopens the window init --namespace binds the genesis key; a first policy is signed when it can be; verify names a [none] namespace #96 closed, where the first self-bound binding to land for the address is the one trusted.

Activity

  1. changed the title [-]Key lifecycle across namespaces: one rotate/revoke act for every namespace a key is bound in[/-] [+]Key lifecycle across namespaces, part 1: a key is bound once per namespace; a close ends the key[/+] on Oct 6, 2026
  2. Hafeok commented on Nov 3, 2026

    @Hafeok
    CollaboratorAuthor

    Closed as not carried under D1.24`` and the definition's chapter 8:``` the Rust tool is frozen at rust-final and its tree left `main` in #166 (`53bd547`). This concerns the Rust ledger (its keys, namespaces or authority), which is Writ's authority graph now. The rewrite holds no key and no grant, and `writ import` reads `.decisions/` once, at the tag. Disposition approved on #165.


    Generated by Claude Code

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

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Effort

    None yet

    Progress

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions