Skip to content

Keys per namespace: key close, D7, first policy; remove carried_over; the two notices (issue 6) - #160

Open
Hafeok wants to merge 2 commits into
claude/great-feynman-zcdsdd-step-05-authorityfrom
claude/great-feynman-zcdsdd-step-06-keys
Open

Hafeok wants to merge 2 commits into
claude/great-feynman-zcdsdd-step-05-authorityfrom
claude/great-feynman-zcdsdd-step-06-keys

Conversation

@Hafeok

@Hafeok Hafeok commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

Closes #149 — PRD ledger/prd/namespace-independence-prd.md §6 issue 6 (§3.3; rulings 47 and 69; AC-47 second bullet).

Stacked on #159 (issue 5). Review the last commit alone, 406b17db.

What lands

A key is bound, closed and trusted in one namespace.

  • Authority::of / as_of filter key bindings by namespace like every other record (the step 5 note on keys goes).
  • key_close::same_key compares the namespace: a close ends the key in its own namespace only (LP-6.32), and refusal's three rules — another principal's key, a closed key, a key already open — look at the binding's namespace (LP-4.37).
  • D7 (authority::filing) drops the store-wide branch: the genesis holder's first key in a namespace is that namespace's self-bound binding, once per namespace, under its own mandate (LP-4.12).
  • The signing check drops carried_over; a first policy is judged by a key trusted in its own namespace (LP-4.31); the re-acceptance deadline runs from the key's earliest close there (LP-4.13); allowed_signers ends a line at a close in its own namespace (LP-4.32).
  • Writer: init --namespace binds the configured key self-bound in every namespace it opens (carry_genesis_key and no_live_key go; --without-key stays until issue 13); identity add's self-bound test is per namespace; signing refuses a key not live in the namespace (ToSign.any_namespace goes).
  • Two repository notices, never findings (verify::key_notices, in the text and the JSON report as key_split and key_shared): a key closed in one namespace and open in another; one key bound to two principals in two namespaces (ruling 69).

The writer's close in every namespace it holds is issue 8's and is not claimed here.

Tests

ledger-cli/tests/key_across_namespaces.rs, rewritten as §3.11 says (each failing before): bound in A then B, or B then A, a close in A leaves B open — the key still signs there, A's allowed_signers line ends and B's does not, the split is a notice, the key signs nothing in A, and a third namespace opens with the same key self-bound; a rotate in A leaves B's acceptances, review items and allowed_signers byte-identical (AC-47, second bullet); the four within-namespace tests of 2026-10-06 stand; and the carried test — no agent identity and no non-interactive session produces a key close.

Rewritten, and why:

  • genesis_key.rs: init in a later namespace self-binds the holder's key there under the namespace's own mandate and signs the first policy with it; with every key closed elsewhere init binds the key again and verify notices the split; with no usable key, --without-key initialises unbound and the unbound-genesis notice names the namespace. The prompt assigned these rewrites to issue 8; they could not wait, since the cross-namespace lean they asserted is what this step removes.
  • pre_policy_binding.rs and binding_accounting.rs: the holder's first key in the second namespace is now self-bound (under hand::found's mandate); the signed case signs the first policy with the key bound before it, as LP-4.31 now requires; opening a namespace over a binding filed in it before any policy is refused again, since the holder's own self-bound binding would no longer be the first there.
  • trust.rs: init self-binds in the second namespace and a further key there is the holder's own add (was "once per store"); a hand-written self-bound binding beside init's fails, "first in the namespace". genesis_key.rs's forged-binding test reads the same wording.
  • key_close_tests.rs: three unit tests assert the namespace reading.

Protocol

LP-4.12, LP-4.13, LP-4.31, LP-4.32, LP-4.37, LP-4.38 (the later-namespace paragraph goes), LP-4.39 (ruling 101's wording), LP-6.31 (keys note dropped), LP-6.32 (loses its mark), LP-8.31 (two notices, report shape); Appendix B row; Appendix C note "Keys are per namespace". CLAUDE.md updated. No class added; no digest moves (a binding's hashed fields are unchanged and namespace was among them); no committed store in this repository binds a key.

Checks

cargo build, cargo t, cargo clippy --workspace -- -D warnings -D clippy::unwrap_used, the quality scripts, and ledger verify --export on this repository (same output as on main).

🤖 Generated with Claude Code

https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU


Generated by Claude Code

Hafeok commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

CI, standing down on one step. The check job fails at the contract-surface gate (ddd diff-contracts <base>..HEAD): 14 undischarged exposed-surface changes, all this PR's — the new verify::key_notices module (KeySplit, KeyShared, split, shared) and the two Notices fields. Every other step of check passed.

The remedy the gate names is ddd bind claude/great-feynman-zcdsdd-step-05-authority..HEAD (with --verdict), which files seam declarations under .ddd/seams/. The session's instructions forbid me to edit .ddd/, and a declaration's verdict is the author's judgment, so I am not filing them. Please run ddd bind on this branch and push, or tell me to. Recorded in the close-out.


Generated by Claude Code

@Hafeok
Hafeok force-pushed the claude/great-feynman-zcdsdd-step-05-authority branch from 4ebe365 to 73f98fb Compare October 11, 2026 06:17
@Hafeok
Hafeok force-pushed the claude/great-feynman-zcdsdd-step-06-keys branch from 406b17d to 792983a Compare October 11, 2026 06:18
Hafeok added a commit that referenced this pull request Oct 11, 2026
Issue #150 (PRD issue 7, §3.4; ruling 47). Each namespace's
`ns/<ns>/allowed_signers` is derived from that namespace's trusted
bindings alone — a line ended by a close in that namespace only, as
issue 6 made the derivation read — and a namespace that binds nothing
has no file. `signers::write` now removes a file committed in a
namespace none of whose bindings is trusted (inventory item N10) and
reports what it wrote and removed per namespace (`Synced`); `ledger
identity sync`, the identity verbs and `init --namespace` print those
lines, and `sync` says when no namespace binds a key. `[SIGNERS]`
already compared each namespace's file with its own derivation (issue
1); its protocol line now says so.

Tests (each failing before the change where the behaviour is new):
`ledger-cli/tests/signers_per_namespace.rs` — each namespace has its own
file holding its own `ledger-accept@<ns>` lines; a close in A leaves B's
file byte-identical; a file hand-written in a namespace that binds
nothing fails `[SIGNERS]` naming the namespace, and `identity sync`
removes it, prints the removal, and the store verifies again; a
namespace whose only binding is untrusted has no file after `sync`.

Protocol: LP-4.10 loses its note and gains the removal; LP-4.33 names
the namespace; Appendix B row; Appendix C note "A stale `allowed_signers`
is removed". No class added; no digest moves.

Stacked on #160 (issue 6).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
Hafeok and others added 2 commits October 11, 2026 06:22
…two notices

Issue #149 (PRD issue 6, §3.3; rulings 47 and 69; AC-47 second bullet).
A key is bound, closed and trusted in one namespace. `Authority::of` and
`as_of` now filter key bindings by namespace like every other record;
`key_close::same_key` compares the namespace, so a close ends the key in
its own namespace only and `refusal`'s three rules (another principal's,
closed, already open) look at the binding's namespace; D7 (`filing`)
drops the store-wide branch — the genesis holder's first key in a
namespace is that namespace's self-bound binding, once per namespace,
under its own mandate; the signing check drops `carried_over`, judges a
first policy by a key trusted in its own namespace, and the re-acceptance
deadline runs from the key's earliest close there; `allowed_signers` ends
a line at a close in its own namespace. The writer binds the configured
key self-bound at every `init --namespace` (`carry_genesis_key` and
`no_live_key` go; `--without-key` stays until issue 13), `identity add`'s
self-bound test is per namespace, and signing refuses a key not live in
the namespace (`ToSign.any_namespace` goes). Two repository notices,
never findings (`verify::key_notices`, in the text and JSON report): a
key closed in one namespace and open in another; one key bound to two
principals in two namespaces.

Tests (each failing before the change): `key_across_namespaces.rs`,
rewritten as §3.11 says — bound in A then B, or B then A, a close in A
leaves B open: the key still signs there, A's `allowed_signers` line ends
and B's does not, the split is a notice, the key signs nothing in A, and
a third namespace opens with the same key self-bound; a rotate in A
leaves B's acceptances, review items and `allowed_signers` byte-identical
(AC-47, second bullet); the within-namespace rules of 2026-10-06 stand
(four tests kept); and the carried test, that no agent identity and no
non-interactive session produces a key close. The writer's close in
every namespace held is issue 8's and is not claimed. `genesis_key.rs`:
`init` in a later namespace self-binds the holder's key there under the
namespace's own mandate and signs the first policy with it; with every
key closed elsewhere `init` binds the key again and `verify` notices the
split (JSON `key_split`); with no usable key, `--without-key` initialises
unbound and the unbound-genesis notice names the namespace. The prompt
assigned these rewrites to issue 8; they could not wait, since the
cross-namespace lean they asserted is what this step removes.
`key_close_tests.rs`: three tests now assert the namespace reading.
The pre-policy cases now bind the holder's first key in the second
namespace self-bound: `pre_policy_binding.rs` (`unsigned_before` is
self-bound under `hand::found`'s mandate; the signed case signs the first
policy with the key bound before it, as LP-4.31 now requires; opening a
namespace over a binding filed in it before any policy is refused again,
since the holder's own self-bound binding would no longer be the first
there — `…_and_init_is_refused_over_it`); `binding_accounting.rs`
(`self_bound_by_hand`, one namespace per first binding). `trust.rs`:
`init` self-binds in the second namespace and a further key there is the
holder's own add (renamed from "once per store"); a hand-written
self-bound binding beside `init`'s fails, "first in the namespace".
`genesis_key.rs`'s forged-binding test reads the same wording.
`key_across_namespaces.rs` keeps its four within-namespace tests.

Protocol: LP-4.12, LP-4.13, LP-4.31, LP-4.32, LP-4.37, LP-4.38, LP-4.39
(ruling 101's wording), LP-6.31 (keys note dropped), LP-6.32 (loses its
mark; the writer's close-everywhere noted for issue 8), LP-8.31 (two
notices, report shape); Appendix B row; Appendix C note "Keys are per
namespace". CLAUDE.md updated. No class added; no digest moves (a
binding's hashed fields are unchanged and `namespace` was among them).

Stacked on #159 (issue 5).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
Files the signed bindings the contract-surface gate demands for this
step's 14 exposed Rust surface changes over 3 files (13 bindings: the two
`key` fields of key_notices.rs share one): a new declaration for
verify/key_notices.rs with its verdict_knowledge written, verify/mod.rs
amended with bindings, and author/genesis_key.rs amended with its verdict
rewritten now that init binds the key in every namespace it opens.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
@Hafeok
Hafeok force-pushed the claude/great-feynman-zcdsdd-step-05-authority branch from 73f98fb to 4ee026b Compare October 11, 2026 06:22
@Hafeok
Hafeok force-pushed the claude/great-feynman-zcdsdd-step-06-keys branch from 792983a to 53becab Compare October 11, 2026 06:22
Hafeok added a commit that referenced this pull request Oct 11, 2026
Issue #150 (PRD issue 7, §3.4; ruling 47). Each namespace's
`ns/<ns>/allowed_signers` is derived from that namespace's trusted
bindings alone — a line ended by a close in that namespace only, as
issue 6 made the derivation read — and a namespace that binds nothing
has no file. `signers::write` now removes a file committed in a
namespace none of whose bindings is trusted (inventory item N10) and
reports what it wrote and removed per namespace (`Synced`); `ledger
identity sync`, the identity verbs and `init --namespace` print those
lines, and `sync` says when no namespace binds a key. `[SIGNERS]`
already compared each namespace's file with its own derivation (issue
1); its protocol line now says so.

Tests (each failing before the change where the behaviour is new):
`ledger-cli/tests/signers_per_namespace.rs` — each namespace has its own
file holding its own `ledger-accept@<ns>` lines; a close in A leaves B's
file byte-identical; a file hand-written in a namespace that binds
nothing fails `[SIGNERS]` naming the namespace, and `identity sync`
removes it, prints the removal, and the store verifies again; a
namespace whose only binding is untrusted has no file after `sync`.

Protocol: LP-4.10 loses its note and gains the removal; LP-4.33 names
the namespace; Appendix B row; Appendix C note "A stale `allowed_signers`
is removed". No class added; no digest moves.

Stacked on #160 (issue 6).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant