Skip to content

Signed grants and grant acceptances (D5 b) #82

Closed as not planned
Closed as not planned

Description

@Hafeok

Ruling: D5, second part (b), ruled 2 October 2026 (docs/signing-rulings-2026-10-d5-d9.md, "D5 … Second part: is the authority log itself signed?"; recorded in docs/signing-rulings-2026-10.md, "D5 to D9"). Size: M. Depends on: PR #80 merged; #70 (signing: sidecars, ssh, L011, the signed-bytes function, under from D9). Must land: before any real namespace has a second grant, and before R1.5.

Why

D5 (a) makes verify evaluate the role as of the act (A006), reading the grants in the log. Until grants and grant acceptances are signed, a forged grant file naming the genesis holder as granted_by satisfies that check. The only defence meanwhile is that grants are few and visible in review.

What to build

  • Grants signed by the grantor, who must pass the same authorize check for grant-role over the grant's scope, on the grant the act names (D9). The chain ends at the genesis grant, anchored by its external_ref and the genesis holder's self-bound key.
  • Grant acceptances signed by the holder.
  • Grant revocations signed by the revoker (added 4 October 2026, PR ledger: Session B step 1 — signed acceptances, revocations, key bindings and policy changes (#70) #89 review). A rev: revocation whose revokes is a grant is signed under ledger.revocation.v1, the payload I9: Signed acceptances and revocations #70 already defines for revocations, by its actor, on the grant it names in under. I9: Signed acceptances and revocations #70 signs only revocations of acceptances; a grant's revocation is left unsigned there, because signing it without signing the grant it ends would be the only signed record in that chain. In I9: Signed acceptances and revocations #70 ledger-core/src/signing/subject.rs subjects filters it out (revocation(…) returns None for Revocable::Grant), and the grant revoke verb (Author::revoke_grant, ledger-core/src/author/authority_ops.rs) files no sidecar.
  • Unavailability (added 5 October 2026, PR ledger: an old-style revocation is valid only as a pre-policy act (A006) #93 audit): a closed payload, a signature by its declarer (by), and a role check at verify for declare-unavailability on the basis it claims. Today only verify checks it, through authority/references.rs basis_holds, and that checks the declarer's claimed relation to the grant (a SCHEMA rule). It has no payload, no sidecar and no A006.
    • A forged one today: hand-written with the grantor named as by and basis grantor, it passes verify and makes the grant unavailable from from. The holder's acts in that window then fail A006, and a lower-ranked grant of the same role covers them instead.
  • Availability (added 5 October 2026): a closed payload and a signature by the holder (by). Its role check at verify is that by holds the unavailable grant, which references.rs checks today as a SCHEMA rule on the claimed by only.
    • A forged one today: hand-written naming the holder as by, it passes verify and ends an unavailability early, so the grant is usable again from available_at.
  • Role declarations (added 5 October 2026): a closed payload for a role file, a signature by its declarer, and a role check at verify that the declarer held grant-role over * (Act::DeclareRole). Today the declarer is checked only by the role declare verb. verify checks only authority/structure.rs role_faults (format, id, filename, duplicates) and write-once (verify/history.rs, L007). A role file names no declarer, so the payload has to add one.
    • A forged one today: a well-formed hand-written file in .decisions/roles/ passes verify, and any capability set it lists exists for a grant to name.
  • at in the grant payload. ledger.authority-grant.v1 (ledger-core/src/authority/payload.rs, grant_map) carries no at today. Add it: hashed when present, omitted when absent, required in files of the format this issue introduces. No existing digest moves.
  • A payload for the grant acceptance — accepted as proposed (2 October 2026). GrantAcceptance (ledger-core/src/authority/grant.rs) gets a closed payload under its own prefix, ledger.grant-acceptance.v1, over {grant, signs, actor, at}.
    • It carries no under. Accepting a grant is the holder's own act, so there is no authorising grant to name.
  • under on grants (D9 (a)), if I9: Signed acceptances and revocations #70 has not already added it to the grant payload. (I9: Signed acceptances and revocations #70, PR ledger: Session B step 1 — signed acceptances, revocations, key bindings and policy changes (#70) #89, adds it.)
  • The grant does not bind its role's content. Session A close-out §3 question 8 is ruled write-once and held by verify (that check lands in I9: Signed acceptances and revocations #70), so a grant's role id names fixed content. The grant payload keeps naming its role by id only.

Tests

  • A forged grant (hand-written, valid shape, unsigned, or signed by a key that is not the grantor's) fails the gate, and an acceptance made under it fails A006.
  • A grant acceptance not signed by the holder fails.
  • A grant revocation that is unsigned, or signed by a key that is not the revoker's, fails L011 where policy requires a signature.
  • An unavailability, an availability or a role file that is unsigned, or signed by a key not the declarer's, fails L011 where policy requires a signature. One whose declarer could not make it, as of the act, fails A006.
  • Every stored digest unchanged (Session A's ledger-cli/tests/digests.rs).

Constraints

  • Format document (§3.9) and migration note in the same PR. verify --export passes; regenerate exports.
  • No format change outside this list.

Activity

  1. added theissue type on Oct 2, 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

    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