Skip to content

feat(accounts): the host interfaces are decided by the shared authority core - #1257

Merged
antoinetoussaint-byte merged 15 commits into
mainfrom
accounts/adopt-authority-core
Oct 27, 2026
Merged

antoinetoussaint-byte merged 15 commits into
mainfrom
accounts/adopt-authority-core

Conversation

@antoinetoussaint-byte

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

Copy link
Copy Markdown
Contributor

What we are doing

The owner's settled design: one host-independent authority core with unit tests, and the local authority and accounts as thin wrappers around it. Before this PR, accounts implemented the host interfaces a second time, about 3,000 hand-written lines in host_interface_*.go. It was three interface revisions behind authority main and failed 13 of the shared suite's cases at main.

accounts now serves codefly.dev/authority@0.3.4, composition@0.2.0, installation@0.3.0 and session@0.2.0 through codefly-dev/authority's libraries/authority-core/go. That is the same core the local authority runs, here over accounts' Postgres. The core extensions accounts needed are in codefly-dev/authority#35, which merged at 50e76eb2; this PR pins codefly-dev/authority#36 (22b86ccb), which builds on that merge.

What changed

  • The core's store over accounts' Postgres. Files: pkg/infra/postgres_authority_core.go and migration 56.
    • The core's own records live in authority_core_* control-plane relations: modules and tombstones, installations, role grants, delegations, epochs, owner-aware revisions, and consumed nonces.
    • Identity is read from accounts' own relations. These are memberships, teams, and the organization roles a member holds, synthesized from accounts' RBAC: role assignments and live scope grants (direct or through a team), the membership's admin/owner role, and the members' contributed permissions.
    • The organization's authorization revision is accounts' own, so every RBAC change accounts records also revokes what the core issued. A person's epoch includes their principal revisions.
    • Reads run at REPEATABLE READ and read-only. Writes run SERIALIZABLE, retried, and report ErrConflict when retries run out.
  • Every handler goes through the core.
    • host_interface.go was rewritten. It is now the wire and this host's own concerns:
      • who the caller is: the verified session (signedInPerson), or a workload credential through the execution reviewer (workload);
      • the Work Context key the core signs with and Keys publishes;
      • tenancy at the perimeter (requireOrgMember, requireOrgAdmin).
    • Covered: Issue (all five owner shapes), Check with method policy and advisory, ListResources, Delegate, RevokeDelegation, GetDelegation, AttachResource, DetachResource, AuthorizeTransfer, Review, Install, GrantRole, RevokeRole, ListRoleGrants, List.
    • RegisterAdmissionKey keeps feat(accounts): the hop registers its admission key; Keys publishes it (authority 0.3.3) #1256's leased store and workload review, and now imports stamp.KeyID. The restated derivation (the labelled stopgap) is gone.
  • Delivery. ApplyManifest keeps accounts' delivery owner: verification, the inbox, the reconciler and its audit. The core then composes the applied delivery through its shared delivered package. A delivery the core would refuse is refused before anything is recorded, for example a solution declaring no installer role, which the local authority already refused. When the core is configured at start, it composes every delivery accounts has already applied, so existing cells need no redelivery.
  • Audit.
    • Each change the core journals is recorded in the core's own transaction, through Config.Journal. The events are the new saas.authority.installation_requested, installation_activated, installation_uninstalled, role_granted, role_revoked, resource_attached, resource_detached and resource_transferred, plus the existing saas.module_delegation.granted and saas.module_delegation.revoked.
    • These records are platform-scoped, with the organization named in the payload, because the control-plane transaction writes no tenant fan-out.
    • Each issue is recorded as task_started or child_session_started in the organization's transaction before it is handed out. An issue whose record cannot be written is refused.
  • A module is installed by consent, not by delivery (review blocker 2). The core is configured with HostInstallsModules (Generate bootstrap from reviewed snapshot (#33) #35): it installs no module itself. A module's installation is accounts' own installations row of kind module (an administrator's install with consent, within the module envelope), which the Postgres store answers as the core's installation, live. A native uninstall, or a ceiling that stops allowing the organization, ends it in the core at once; a capability sealed to it fails its next check and stays dead after a reinstall (new installation id). The core's one write to a module's installation, raising its revision when a redelivery narrows the module, is kept beside the row (authority_core_module_revisions, migration 56), because accounts' revision is lifecycle-owned. A workload is bound to the organization the module envelope pins it to (Workload.Organization, Generate bootstrap from reviewed snapshot (#33) #35): an issue naming another is refused. Organization creation and start no longer install every module.
  • The core issues within what each installation consented to (review finding N1). feat(authority-core): an installation's consent bounds its module's operation bindings authority#36 adds Installation.Bindings, one ceiling every issue through a binding applies. accounts' store fills it from each module installation's recorded consent, decided exactly as the native mint decides it (business/authority_core_consent.go: the registry grant, manifest within envelope, intersected with the consent by consentedGrant), with the registry fenced against the envelope revision committed in the core's transaction. An installation narrowed to no capability is not active for the core; a binding dropped from the consent or from the module's grant within the envelope issues nothing; a widening redelivery issues no more than was consented.
  • Revocation follows the consent and the envelope (review findings N2, N3). The module installation revision accounts reports to the core adds the envelope revision committed in the core's transaction, so an envelope write ends every capability decided under the ceiling it replaced, as a narrowed consent already does through accounts' own revision. Release module starter v0.0.26 #36 fences a workload issue between its decision and its mint, so a narrowing that straddles an issue is never handed out.
  • Direct narrowings (owner decision, B1). Removing a team or organization member and every other change that only reduces access applies directly, its audit record in the same transaction (business/narrowing.go). The policy log (append, receipt, commit, serving gate, reconciliation), its adapters, its tables (migration 57) and the never-wired policy-log service are deleted. Member-removal audit events now name the person removed.
  • Interface 0.3.4 and installation 0.3.0 (authority main). RegisterAdmissionKey verifies the registration proof (stamp.CheckRegistration, over the credential and accounts' audience), uses each nonce once across replicas (hop_admission_proofs) and pins one key per hop instance, the reviewed pod (hop_admission_keys.instance, migration 58). InstallationService.Current is served through the core's CurrentInstallation, authenticated by the module's workload credential.
  • Fixtures. A fixture's declared solution installations are performed in the core as host setup (installer to the owner, the role to each team), beside the product's own installation records. A declared module installation is accounts' own record alone, which the core reads.
  • Interface protos. Authority, composition and installation are copied from authority's published protos (installation gains Current, authority gains the registration proof), and module.codefly.yaml declares authority 0.3.4 and installation 0.3.0. Everything was regenerated only through codefly generate proto, the catalog go generate steps, the audit contribution update, module-compose, codefly generate contracts and codefly generate runnables, and the SDK scripts in scripts/ci/check-generated-libraries.sh. The source-read SDK needed --force once, for the new contract digest. CLI v0.1.215. A no-op regeneration came back clean before any change. OfferService, the local authority's setup read, is not served.
  • HOST_INTERFACE.md was a 1,261-line log and is now a 75-line description of what is served and where.

What moved into the core, and what was deleted here

  • Deleted, because the core now owns it:
    • host_interface_hop.go, _delegation.go, _holdings.go, _limits.go, _resources.go, _installation_owned.go, _solution_roles.go, _workload.go and _installation.go, with their unit tests. The suite and the core's tests cover the same rules.
    • business/module_delegation.go and infra/postgres_module_delegations.go.
    • The delegated-binding seal branch.
    • AdmissionKeyID.
    • The module_delegations table: migration 56 carries its rows into the core's table as they stood, and its down migration carries them back.
  • Moved into the core in Generate bootstrap from reviewed snapshot (#33) #35, so accounts does not keep a copy:
    • organization membership per session;
    • the reading of a delivered manifest and its authority document;
    • method admission and route matching;
    • the owner refusal in deliveries;
    • the administrator ending a member's delegation, the installed-module and purpose bounds on a delegation (accounts' rules until now);
    • the change journal.
  • Kept in accounts as adapters: Postgres, identity and tenancy, the execution reviewer, the delivery owner, the audit journal, the hop admission store, sessions, and the native Work Context paths the product uses (saas.accounts.v1.WorkContextService and the module capability tier). The native paths do not serve the host interfaces.

Conformance: the shared suite at #35 against accounts

50 of 50 shared cases pass (the new Current case included), all 18 of the solution-principal target pass, and so do 9 harness cases (77 in all). The suite runs with go test in pkg/adapters, over accounts' Connect listener, with modules and solutions delivered as signed carriers through ApplyManifest. Every module the harness or the suite delivers is installed at both fixture organizations by their administrators through accounts' own install path (Fixture.InstallModule, #35); nothing installs a module by hand any more, and conformance-idle simply stays installed nowhere.

Beyond the suite, the harness proves:

  • a real envelope write narrowing a binding: the capability issued before it fails its next check, the next issue fails closed until the registry is derived again, then only the narrowed ceiling is issued (fails without the revision change);
  • what an administrator consented to bounds every new issue, side by side with the native mint (the reviewer's case): narrowed to no capability, issued nothing; a binding dropped from the consent, or from the module's grant within the envelope, issued nothing; a redelivery that widens the binding, issued no more than was consented;
  • a module workload is issued nothing where it is not installed, nothing past the organization its envelope pins it to, and nothing after an administrator uninstalls it, when what it was issued fails its next check;
  • the solution-principal target's membership loss runs through RemoveTeamMember and RemoveOrgMember (production wiring, no test-only log): the removal applies at once, the leaver's earlier session is refused (not a member of this organization), and each removal is in the audit journal naming the person removed and the administrator.

The fixture's suite viewer (Bob) gains an organization-wide Record viewer role: since #35's resource-scope fix (review blocker 1), a collection grant holds only the collection, so the viewer the suite issues kind-wide needs a kind-wide role, as the local authority's viewer has.

The 13 cases that failed at authority main on #1256 all pass now:

  • the seven solution-role cases;
  • an uninstalled audience is a failed precondition;
  • installation acknowledgement gates exposure and revocation ends authority;
  • a callee refuses what does not verify (role revoke);
  • an installation acts for itself;
  • installation-owned role scopes cover only what the installation owns;
  • an installation issue verifies the installation exists (PermissionDenied, no existence oracle).
Every case (72)

Shared suite (49):

  • installation inventory is an administrator read
  • an uninstalled audience is a failed precondition
  • a person without grants cannot use an installed audience
  • a delivered solution role grants authority at its own audience
  • a solution viewer reaches a consumed module without an organization grant
  • a solution binding root carries its delivered role grants
  • a person with no solution role is refused with a reason
  • a solution role cannot exceed the delivered binding ceiling
  • a delivered solution route distinguishes requires from any checked caller
  • a signed solution role cannot exceed the served manifest ceiling
  • an installation issue verifies the installation exists
  • an installation issue authenticates the installed solution
  • an installation issue cannot name a different organization
  • Current names the person and organization for display
  • a child naming an undeclared binding is refused by name
  • a child naming the wrong binding audience is refused by name
  • a child naming no binding is refused by name
  • a child widening its owner's current binding grants is refused by name
  • a module authenticates the forwarded context against the published keys
  • parent per-kind limits survive audience changes
  • keys name one Ed25519 key and an issuer
  • a fixture person signs in and an unknown token is refused
  • every fixture person listed signs in as that person
  • a page asks who its bearer is, and a bearer naming no session is refused
  • a person is issued, checked, and refused elsewhere
  • an empty ask means everything the owner holds at the audience
  • the permission matrix holds, allow and deny
  • a callee refuses what does not verify, by name
  • every refusal reaches the caller with a reason
  • an empty workload organization with ambiguous installations is refused
  • an empty workload organization with none installations is refused
  • a workload is issued its own binding and nothing it did not declare
  • an installation acts for itself
  • an installation issue authenticates its own solution workload
  • a module tool binding carries the person's solution role
  • a child hop attenuates and names its actor and its binding
  • a delegate chain inherits the owner's authority at each audience
  • child target projection preserves explicit delegation restrictions
  • an installation owner survives successive module actors
  • installation-owned role scopes cover only what the installation owns
  • a check naming a method applies what the module's delivery declares
  • the authority answers which resources a context covers, by grant
  • a lookup through a binding carries only the binding's lookup scopes
  • a delegation issues as its grantor through its module, until revoked
  • a manifest moves only forward and a lower generation is refused by name
  • a resource limited scope passes method admission without becoming kind wide
  • installation acknowledgement gates exposure and revocation ends authority
  • every installation call stays inside the caller's organization, and an absent installation answers as a foreign one
  • only the hop registers an admission key, published apart from the Work Context keys

Solution-principal target (18):

  • install creates the solution principal and its roles; a reinstall keeps its UUID; another organization's is another principal
  • the install request's role is the agent's, and no person's; the agent is confined to the principal's data
  • without a role on the solution a person is refused, naming the reason
  • a viewer reads exactly what the principal owns, and nothing a person, an installation or another principal owns
  • a route requiring the kind admits an owner-selected scope; the module enforces the owner
  • a capability through the solution is confined to the principal's data; the person's own grants never flow in
  • every audience receiving owner-selected scopes declares owner-aware checking, solutions included
  • withdrawing owner-aware checking refuses the owner-selected capabilities issued before
  • a resource created through the solution is owned by its principal, the person its author
  • a role writes when its scopes hold write, whatever it is called; a viewer cannot
  • an administrator of the organization or the installation's installer grants, revokes and lists; anyone else, and every malformed grant, is refused by name
  • another installation's and another organization's members never reach the principal's data
  • a child never widens the owner, the actions or the ids it narrowed to
  • an existing resource joins the principal only by a transfer from the organization or its person, administered on that resource through the person's own grants, by a holder of write on the principal's data
  • revoking a person's role stops their access at once, theirs alone; their old capabilities stay dead after a re-grant
  • a team's role is its members' access; revoking it or leaving the team stops them at once; a direct grant survives the team's revoke
  • a person leaving the organization loses the solution's data at once
  • uninstalling disables the principal at once and its data stays owned; old capabilities stay dead after reinstall and re-grant

Beyond the suite (5):

  • a delegation is bounded by its grantor and ends with its revocation
  • every issue from one delegation is held to its own scope set
  • an ended session is no longer current
  • fresh approval supersedes pending attempt
  • every change of authority is audited

Fix or hack

  • Fix: everything above, each change placed where the behaviour belongs.
  • Behaviour changes, stated:
    • A solution delivered through ApplyManifest must declare an installer role (the core's contract). Role-less legacy deliveries still arrive through the HTTP delivery endpoint and are not composed in the core.
    • Org-role resource scopes follow the core (Generate bootstrap from reviewed snapshot (#33) #35's blocker-1 fix): a role scoped to one resource is issued on that id, never the whole kind; an ask beyond it is refused. A scope grant on a scope node holds that node's id, so its descendants are not carried in a capability.
    • Modules are installed by an administrator's consent: a delivery, a new organization or a restart installs none.
    • GrantRole, RevokeRole and ListRoleGrants leave the tenant decision to the core (policy tenant NONE). The core requires a session at that organization, and answers another organization's installation and an absent one alike.
    • Found and fixed in the Postgres store: Member(org, "") returned the organization's first member. It now names nobody, and Generate bootstrap from reviewed snapshot (#33) #35's storetest pins this for every host.
    • Moving core 8af4b787 to b23dc135, which the authority libraries require, makes runnable.PackageFromMethod derive a tool exposure. The source-operation tests now assert it.
  • Reused: the execution reviewer's workload review, the delivery owner and reconciler, requireOrgMember and requireOrgAdmin, the RBAC relations, bump_organization_authorization_revision, emitTx, feat(accounts): the hop registers its admission key; Keys publishes it (authority 0.3.3) #1256's admission store, and the module-delegation audit events.

Evidence

Shared suite at #36 (22b86ccb) against accounts: 50/50. Solution-principal target: 18/18. Harness extras: 9/9.

All run locally on the pushed head (see the PR's latest commit), unless noted.

  • Go, module/services/accounts/code:
    • go vet ./... and go vet -tags=pure ./... are clean.
    • go test -race -count=1 -tags=pure ./... passes.
    • go test -race -count=1 ./... passes, Postgres tests included: every package on the previous head 2db4bce2, and pkg/business, pkg/infra and pkg/adapters (the harness) again on this head.
  • New tests:
    • pkg/infra/postgres_authority_core_test.go runs the core's store contract against real Postgres. It covers the identity read from accounts' RBAC, including team-assigned roles and leaving a team.
    • pkg/business/authority_core_audit_test.go checks that every journaled change is a registered mutation and is recorded as a person or as the system.
  • Harness: 77 of 77 (50 suite, 18 target, 9 beyond the suite). pkg/business/authority_core_consent_test.go unit-tests the consent decision (table-driven, including the envelope fence).
  • Migrations: the migration reference gate against 98f56da8, including --replay on PostgreSQL 16, plus migration pairing and the RLS migration gate.
  • Contracts and generation: codefly generate contracts saas-starter --check and codefly generate runnables saas-starter --check, with CLI v0.1.215; the SDK through its own scripts/generate.mjs.
  • Repository gates:
    • authz coverage (check and no-broadening against main);
    • naming (tree and records), boundaries, generated pins, interface docs, story trace, kit version, module verify, test targets;
    • commit identity over every commit of the branch;
    • scripts/ci/release-gates.mjs check and its tests (125 pass).
  • Other modules: module/tools (go test ./..., including the database-authority inventory), the source-read SDK, qualification/module-authority, the auth-gateway and the root module.
  • CI: Host interface conformance (authority main) is now in REQUIRED_CONTEXTS and a release gate (release-gates needs it); its script reads authority's ancestry from a blobless clone, retried, never the rate-limited GitHub API.

Not done, and why

  • saas main merged (5a3c7f3b, fix(accounts): bound a module's grant by the installation scope it must fit in, not by 32 bindings #1258's binding bound) without conflict.
  • Narrowings stay on the control plane. Each runs with an explicit organisation predicate at every statement, as under the former witness. Returning the organisation-only ones (team removal, scope revocation) to the tenant transaction is open.
  • No uninstall RPC exists for a solution's host-interface installation, in either host. The interface declares none, so the core's Uninstall is reachable only from host setup. A module's is uninstalled through accounts' product surface and the core sees it at once.
  • The ruleset must add "Host interface conformance (authority main)" as a required context (a repository admin's setting); the tree's REQUIRED_CONTEXTS already names it.
  • The authority pin is Release module starter v0.0.26 #36's head (22b86ccb), which builds on Generate bootstrap from reviewed snapshot (#33) #35's merge 50e76eb2. Once Release module starter v0.0.26 #36 merges, this PR re-pins to its merge commit. Every other pseudo-version pin is reachable from its repository's main, except core 835460f16fb5, which module/frontendpayload/go.mod already pins on saas main (it exists on a core branch). accounts and auth-gateway pin frontendpayload at main's 6fd56dae.
  • A fixture solution installation that stays pending. On this head the host-interface installation of a fixture solution is made in the core with no install step wired, so it is active at once in the organization that declared it, and only there: the core's audience "<solution>" is not installed in this organization cannot come from a declared fixture installation. The product's own installation row stays pending until the solution's install step is completed by its consumer, so GrantInstallationToTeam still refuses until then; that consumer is outside this PR, and the composed platform run that found it was not repeated against this head.
  • Not run: no composed playground or cell run of the hop against this host, and no real-cluster check that a hop pod's projected token names the pod the pin uses.

🤖 Generated with Claude Code

antoinetoussaint-byte and others added 6 commits October 26, 2026 20:46
… core

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and admission cases on accounts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rved now; pin authority 8093104c

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the suite at authority main runs in CI; an empty id names no member

The shared solution-principal target (18 cases) now runs in the harness
beside the suite. Membership loss is applied to accounts' membership rows
directly, a labelled stand-in for accounts' own policy-logged membership
narrowing. A new CI job runs the suite at authority main whenever main
contains the pinned revision. Member(org, "") read as every member; it now
names nobody (codefly-dev/authority storetest pins it). Role grants, revokes
and listings leave the tenant decision to the core, which answers another
organization's installation and an absent one alike. Pins authority 8846431f.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
antoinetoussaint-byte and others added 6 commits October 26, 2026 22:49
…n the audit journal

Owner decision (2026-10-27): removing a team or organization member, and every
other change that only reduces access, applies directly, its audit record in
the same transaction. No policy-log witness is asked first; widening paths are
unchanged.

- business/narrowing.go is the one path every narrowing takes; the policy log
  (append, receipt, commit, serving gate, reconciliation) is deleted, with its
  Postgres and warehouse adapters, its interceptor gate and its refusals.
- The module envelope writer writes a narrowing like a grant: the row, the
  installations the ceiling no longer allows and the audit event commit in one
  transaction. The pure-grant classification that only chose the witness is
  deleted.
- A withdrawn solution target closes in its apply transaction.
- Migration 57 drops policy_log_commits and policy_log_cursor.
- The policy-log service, which only witnessed narrowings and was never wired,
  is deleted with its declarations, configuration and generated topology.
- Member removal audit events name the person removed (user_id).
- The solution-principal target removes team and organization members through
  RemoveTeamMember and RemoveOrgMember; the harness proves the removal is in
  the audit journal and that the leaver's earlier session is issued nothing.
- "Host interface conformance (authority main)" is a required context and a
  release gate; its script reads authority's ancestry from a blobless clone,
  retried, never the rate-limited API.
- Proto doc comments, contracts, the SDK bindings and the docs say narrowings
  apply directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dministrator's consent

Security review blocker 2: every composed module was active at every
organization in the authority core, so a module workload could be issued
its headless scopes, and a member a person capability at its audience, in an
organization that never installed it, outside its envelope tenant pin, or
after an administrator uninstalled it.

- The core is configured with HostInstallsModules (codefly-dev/authority#35):
  it installs no module at delivery, organization creation or start. A
  module's installation is accounts' own installations row of kind module
  (an administrator's install with consent, within the module envelope),
  which the Postgres store answers as the core's installation, live. A native
  uninstall or a narrowed ceiling ends it in the core at once; a capability
  sealed to it fails its next check and stays dead after a reinstall. The
  core's one write, a revision raise on a narrowing redelivery, is kept in
  authority_core_module_revisions (migration 56).
- A workload is bound to the organization the module envelope pins it to
  (Workload.Organization), read from the fenced registry.
- Re-pinned to #35 at e0b04b28, which carries the resource-scope fix
  (blocker 1) and authority main (656d23bf): the fixture's suite viewer gains
  an organization-wide role; InstallationService.Current is served through
  the core; RegisterAdmissionKey verifies the 0.3.4 registration proof, uses
  each nonce once and pins one key per hop instance (migration 58);
  module.codefly.yaml declares authority 0.3.4 and installation 0.3.0.
- The harness installs every delivered module through accounts' own install
  path (Fixture.InstallModule) and proves a workload is refused where the
  module is not installed, outside its pin and after an uninstall.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… digests

The interface protos' DIGESTS.sha256 moved after the contracts were last
generated; codefly generate contracts copies it into both exported trees.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… main

They required module/frontendpayload at 0d423cb, a commit on no branch,
so a fresh module cache could not fetch it. They now require it at
6fd56da, main's latest change to that module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A re-run replays the event payload it started with, so the naming gate kept
reading the body before its correction; a new commit carries the current one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
antoinetoussaint-byte and others added 3 commits October 27, 2026 01:41
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…allation consented to

Review finding N1: the core read a module installation's status and revision
only, so a new workload issue used the delivered binding, ignoring an
administrator's narrowing (Capabilities off, a binding dropped), the module
envelope's per-binding ceiling, and a widening redelivery not yet
re-consented. Before this PR the native mint honoured all three.

- codefly-dev/authority#36 adds Installation.Bindings, one ceiling every
  issue through a binding applies (workload, installation, hop, delegation).
- accounts' store fills it for each module installation from the consent
  recorded on it, decided as the native mint decides it
  (business/authority_core_consent.go: the registry grant, manifest within
  envelope, intersected with the consent by consentedGrant). An installation
  whose consent allows no capability, or that awaits re-consent, is not
  active for the core. The registry is fenced against the envelope revision
  committed in the core's own transaction.
- The harness proves, beside the native mint: an installation narrowed to no
  capability issues nothing, a binding dropped from the consent or from the
  module's grant within the envelope issues nothing, and a redelivery that
  widens a binding issues no more than was consented. A module's consent is
  given again through the fixture's install path at each delivery.
- Pins authority at #36's head (93bc9f58), which carries #35's merge.
- The NarrowInstallationScope comment no longer names a policy receipt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nder the ceiling it replaced

Review finding N3: an envelope narrowing a module's binding changed what the
core issued next, but a capability issued before it kept passing Check until
its TTL, because the revision the core seals to and checks never moved.

The module installation revision accounts reports to the core now adds the
envelope revision committed in the core's own transaction, beside accounts'
own revision and the core's raise, so every envelope write ends what was
issued under the ceiling it replaced; codefly-dev/authority#36's workload
fence (N2) refuses a mint that straddles the write.

The harness writes a real envelope revision narrowing a binding: the earlier
capability fails its next check, the next issue fails closed until the
registry is derived again, then issues only what the narrowed ceiling
allows. Without the revision change the case fails.

Pins authority at #36's head (22b86ccb).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@antoinetoussaint-byte
antoinetoussaint-byte added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit 3c53033 Oct 27, 2026
26 checks passed
crisfiorini-oss pushed a commit to crisfiorini-oss/module-saas-starter that referenced this pull request Oct 27, 2026
…ites nothing (codefly-dev#1259)

Every reconcile pass converges the fixture's installations, and an
installation that cannot converge yet (a solution whose install step is not
acknowledged, so its teams wait) is retried each pass. The retry resolves the
installation's solution role through EnsureDerivedRole, which upserted the
role and deleted and re-inserted every permission. roles and role_permissions
carry BEFORE triggers that move the organization's authorization revision on
every row they see, an upsert that changes nothing included, so each pass
moved the revision about nine times per role and every capability minted
before it was refused ("authorization revision superseded: minted at
revision 2473, issuer is at 2495").

EnsureDerivedRole now reads the role and its permissions and writes only
what differs: a missing role, a changed description, a permission removed or
added. Idle reconcile passes and idle fixture convergence leave the
revision where it is; a real change still moves it.

The fold of the envelope revision into module installation revisions
(codefly-dev#1257) is not the cause: reconcile passes alone left the revision unchanged.

Co-authored-by: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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