Repository navigation
feat(authority-core): an installation's consent bounds its module's operation bindings - #36
Merged
Merged
Conversation
…peration bindings domain.Installation.Bindings is what the host's consent allows of the target's operation bindings at one installation. nil keeps every binding as delivered; a non-nil map allows only the bindings it names, each within both what was delivered and what it states. Every issue through a binding applies it: a workload's, an installation acting for itself through its solution's binding, a module's hop, and an issue from a delegation. A binding the consent dropped or narrowed issues no more than the consent allows, and a redelivery that widens a binding reaches an installation only once its consent does. Found by the review of codefly-dev/module-saas-starter#1257 (N1): a host whose administrators consent to each module's bindings could not state that consent to the core, so every new issue used the delivered binding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… by its parent, not by its consent A module's hop through a binding addressed to itself reaches nothing the parent, already addressed to it, did not: the child is bounded by that parent and the owner's grants. The consent that bounds what a module reaches at other audiences therefore does not apply to it, and a host whose consent model has no word for a self-addressed binding still serves the hop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
antoinetoussaint-byte
added a commit
to codefly-dev/module-saas-starter
that referenced
this pull request
Oct 27, 2026
…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>
…and its mint Review finding N2: IssueForWorkload decided from an installation's consent in one read and sealed in another, with no check between them, so a narrowing committed in between was sealed at the new revision while the capability carried what the withdrawn consent allowed, valid for its TTL. It now reads the revision fence in the decision (fenceIn) and refuses a mint that does not hold it (f.holds), as every other issuance path does. Tests: the reviewer's interleaving (a narrowing committed right after the decision) is refused on the workload and child paths; without the fence the workload case is handed out. The consent's ceiling is now also proven on an installation acting for itself and an issue from a delegation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
antoinetoussaint-byte
added a commit
to codefly-dev/module-saas-starter
that referenced
this pull request
Oct 27, 2026
…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>
crisfiorini-oss
pushed a commit
to crisfiorini-oss/module-saas-starter
that referenced
this pull request
Oct 27, 2026
…ty core (codefly-dev#1257) * wip: accounts serves the host interfaces through the shared authority core Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip: regenerate Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip: legacy delegations into the core, gates, store contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip: product fixture installs kept beside the core's; suite isolation and admission cases on accounts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(accounts): HOST_INTERFACE.md describes the host interfaces as served now; pin authority 8093104c Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(accounts): the solution-principal target runs against accounts; 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> * feat(accounts): a narrowing of authority applies directly, recorded in 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> * fix(accounts): a module is installed at an organization only by its administrator'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 codefly-dev#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> * fix(contracts): the exported proto trees carry the recorded interface 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> * chore(source-read-sdk): regenerate for the accounts contract digest Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(go): accounts and auth-gateway pin frontendpayload at a commit on 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> * chore: run CI against the corrected pull request body 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> * fix(accounts): the authority core issues within what each module installation 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 codefly-dev#36's head (93bc9f58), which carries codefly-dev#35's merge. - The NarrowInstallationScope comment no longer names a policy receipt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(accounts): an envelope write ends every core capability decided under 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 codefly-dev#36's head (22b86ccb). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What we are doing
A host whose administrators consent to each module's operation bindings must be able to state that consent to the core, so that every issue through a binding stays within it. Before this, the core read a module installation's status and revision only, and every new issue used the binding as delivered. The review of codefly-dev/module-saas-starter#1257 found it (N1): after an administrator narrowed a module's installation to nothing, or dropped one binding from its consent, the core still issued the binding's scopes.
What changed
domain.Installation.Bindings. What the host's consent allows of the target's operation bindings at one installation.nilkeeps every binding as delivered, so the local authority and every installation the core itself writes are unchanged. A non-nil map allows only the bindings it names, each used within both what was delivered and what it states (invoke, headless and lookup scopes each intersected).ceiled, applied wherever an issue goes through a binding: a workload's issue, an installation acting for itself through its solution's binding, a module's hop (IssueChild), and an issue from a delegation.fenceIn) and refuses a mint that does not hold it (f.holds), as every other issuance path already did. A narrowing committed between the two is never handed out.Use cases → tests
TestAnInstallationsConsentBoundsItsModulesBindings(table-driven,authority/host_installs_modules_test.go):Denied);With
ceiledmade a no-op, the refusal and narrowing cases fail.TestANarrowingBetweenDecisionAndMintIsNeverHandedOut(the reviewer's interleaving, made permanent): a narrowing committed right after the decision is refused on the workload and child paths; with the fence removed, the workload case is handed out.TestTheConsentBoundsInstallationAndDelegationIssues: the ceiling refuses, by the consent, an installation acting for itself and an issue from a delegation.Fix or hack
A fix, in the core, where the binding is decided. Reused:
scope.Intersectand the existing installation lookup (installationOf). Not done: the local authority states no ceiling (it installs every module everywhere); a solution-narrowing use of it is possible later.Evidence
accounts (codefly-dev/module-saas-starter#1257) pins this branch and fills the ceiling from each module installation's consent; its harness passes the shared suite (50/50), the solution-principal target (18/18) and 9 harness cases, including the reviewer's consent case and a real envelope write that ends a live capability.
go vetandgo test -count=1 ./...(GOWORK=off) pass inauthority-core,conformance, the local authority,packagegateandnoproxygate.🤖 Generated with Claude Code