Skip to content

feat: the authority core's state layer: Store, in-memory store, seal sources, roles and lifecycle - #19

Merged
antoinetoussaint-byte merged 10 commits into
mainfrom
feat/authority-service
Oct 26, 2026
Merged

antoinetoussaint-byte merged 10 commits into
mainfrom
feat/authority-service

Conversation

@antoinetoussaint-byte

@antoinetoussaint-byte antoinetoussaint-byte commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

What we are trying to do

This is step 2b of the solution-principal work (spec #11, #14). The core's decisions landed in #18. This PR adds the core's state layer: the transactional Store that both hosts implement, an in-memory store, the seal, epoch and revision sources that core's verifier reads, and the role and lifecycle operations that move them. Issuance builds on it in the next PR, which I open from main once this one merges, as split per r137 (m5). saas is untouched.

The change

libraries/authority-core/go:

  • authority/store.go: the Store (Read, Write) and Tx contract.
    • Write is all or nothing.
    • An adapter that gives up on a serialization conflict reports ErrConflict.
  • memstore: the in-memory store. Write undoes a failing transaction's writes.
  • authority/service.go: New(store, Config). The host injects the signer, the keys, the clock, the id sources and the replay store. Replay is required: nothing defaults to memory.
    • The seal sources live here.
    • A seal's revision is the installation's revision, plus the owner's grant epoch on it, plus the solution's owner-aware revision. Each term only grows and is keyed stably, so the sum never repeats. This invariant is documented and tested.
    • An installation principal's epoch is the installation's revision. It grows on every install and uninstall.
  • authority/roles.go:
    • GrantRole moves nothing.
    • RevokeRole moves the subject's grant epoch on that installation.
    • ListRoleGrants.
    • PersonChanged moves the person's epoch.
    • The installation is resolved by (organization, id), and a foreign or absent installation gets the same answer.
  • authority/lifecycle.go:
    • ApplyModule: a narrowing or an owner-aware withdrawal invalidates earlier capabilities. A module keeps its principal for life, and a principal never names a person or an installed solution's principal.
    • WithdrawModule.
    • Install: pending until its step converges. The host's own setup resumes a pending attempt.
    • Uninstall(caller, organization, id): an administrator's act. It moves its organization's authorization revision and clears the installation's grants. A foreign installation is answered as an absent one.
    • Installations.

Also in this PR:

  • CI: .github/workflows/clients.yml runs the Python and TypeScript readers.
  • hop: requires the client at main's merge commit f688c549.
  • Local service module: tidied for that bump.

r132 and r137

  • r132 m1, m2, m4, m5: fixed (x61).
  • r137 here: B2, B3, M1, M2, m2, m3, m6 and m7, recorded against r137.
  • r137 in the issuance PR: B1 (the issue/revoke race), m1 (IssueForInstallation's foreign answer) and m4 (Check verifying before it reads). That PR also carries token-level tests for B2 and M1.

Size

1,136 hand-written non-test lines (370 of them the in-memory store) and 581 test lines, under the ~2,000 budget.

Reuse and classification

Tests

  • go vet and go test -race -count=1 ./... with GOWORK=off pass in libraries/authority-core/go, libraries/hop/go, libraries/conformance/go and modules/authority/services/local/code.
  • Source-level tests (sources_test.go):
    • a reinstall never repeats the principal's epoch;
    • an uninstall moves its organization's revision and no other;
    • an uninstall is an administrator's act, and a foreign installation is answered as an absent one;
    • the seal revision never repeats across revoke, narrowing, owner-aware withdrawal, uninstall and reinstall;
    • a module's principal is its own for life;
    • the replay store is required;
    • a failed write leaves nothing;
    • a revoke moves only its subject's seal on that installation;
    • a team revoke moves its members' seals, and a leave moves the person's epoch;
    • a revoked grant stays gone across a reinstall and a role declared again.
  • I re-ran each r137 test with its fix reverted, and each failed.

🤖 Generated with Claude Code

antoinetoussaint-byte and others added 10 commits October 25, 2026 22:44
The service runs every operation as reads from a Store, a decision (package
decide), writes, and a capability minted or verified with core's
workcontext. The signer and keys are the host's (Config.Signer, Keys); the
clock and id sources are injected. Seal, epoch, binding and revision state
answer core's verifier from the store: a revoke bumps the (person,
installation) epoch, leaving a team or the organization the person epoch,
and withdrawing owner-aware checking the audience's owner-aware revision,
carried as the binding's incarnation and folded into a solution's seal.

memstore is the in-memory Store. Tests encode the conformance target over it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A parent at page holding page.entry, collection and profiles, hopping through
a binding that covers collection alone: the child's effective scopes, as issued
and as checked, are collection read/write only, and asking for profiles through
that binding is refused as a widening.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The service takes r124's decisions: RoleScopes refusing an installation with
no principal, HeldRoles per installation, Child revealing no grant (the
unused own-grants read is gone), a revoke that outlives its subject, and an
absent installation answering as a foreign one, with service tests for the
last two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… foreign answers echo the request, readers gated in CI

- m1: ChangeRole returns for a revoke right after the exactly-one-subject
  check, before the active and role-declared checks; tested in decide and in
  the service with a reinstall that reuses the id and a role retired then
  declared again.
- m2: RoleChange carries the requested id, which ForeignInstallation echoes;
  the service resolves the installation by (organization, id) before reading
  anything else, so a foreign and an absent installation do the same work and
  answer the same text.
- m4: clients.yml runs the authority client's Python and TypeScript tests,
  the other two readers of the shared claims.json.
- m5: hop requires the authority client at f688c54, main's merge commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nization's capabilities, the store is transactional; issuance moves to its own PR

- B2: an installation principal's epoch is its revision, which grows on
  every install and uninstall, so a reinstall never repeats an epoch an
  earlier incarnation's capabilities were sealed at.
- M1: Uninstall moves its organization's authorization revision, ending a
  hop through the solution sealed to another installation.
- M2: Uninstall(ctx, caller, organization, id) checks an administrator as
  Install does, and answers another organization's installation as an
  absent one, changing nothing.
- B3: modules/authority/services/local/code is tidied for hop's client bump.
- m2: memstore.Write undoes every write of a failing fn; the Store contract
  says so and names ErrConflict for an adapter that gives up retrying.
- m3: the seal revision's invariant is documented and tested across revoke,
  narrowing, owner-aware withdrawal, uninstall and reinstall; a module keeps
  its principal for life (ApplyModule refuses a rename).
- m6: ApplyModule refuses a principal naming a person or an installed
  solution's principal.
- m7: the replay store is required; nothing defaults to memory.
- Split (m5): issuance, check, transfer and delegation, with B1, m1 and m4,
  move to the next PR. This one keeps the store, the in-memory store, the
  seal sources, roles and lifecycle, with source-level revocation tests: a
  revoke moves only its subject's seal on that installation, a team revoke
  its members', a leave the person's epoch, and a revoked grant stays gone
  across reinstall and a re-declared role.

Each new test was run against its fix reverted and failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@antoinetoussaint-byte antoinetoussaint-byte changed the title feat: the authority core's service over a Store, and an in-memory store feat: the authority core's state layer: Store, in-memory store, seal sources, roles and lifecycle Oct 26, 2026
@antoinetoussaint-byte
antoinetoussaint-byte merged commit acef961 into main Oct 26, 2026
13 checks passed
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