Skip to content

feat(authority-core): an installation's consent bounds its module's operation bindings - #36

Merged
antoinetoussaint-byte merged 3 commits into
mainfrom
core/installation-binding-ceiling
Oct 27, 2026
Merged

antoinetoussaint-byte merged 3 commits into
mainfrom
core/installation-binding-ceiling

Conversation

@antoinetoussaint-byte

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

Copy link
Copy Markdown
Collaborator

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. nil keeps 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).
  • One decision point, 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.
  • A hop back to the module's own audience is exempt. It reaches nothing the parent, already addressed to that module, did not: the child is bounded by the parent and the owner's grants. A host whose consent model has no word for a self-addressed binding (accounts refuses an invoke-only binding to oneself in its own registry) still serves the hop the shared suite requires.
  • A workload issue is fenced between its decision and its mint (review finding N2). It reads the revision fence in the decision (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.
  • A redelivery that widens a binding therefore reaches an installation only once its consent does, and a narrowed consent issues no more than it allows. Capabilities issued before a narrowing already end on their next check, because the host raises the installation's revision.

Use cases → tests

TestAnInstallationsConsentBoundsItsModulesBindings (table-driven, authority/host_installs_modules_test.go):

  • no ceiling: issued as delivered;
  • a consent naming the binding as delivered: issued;
  • a consent naming no binding, or another binding: refused (Denied);
  • a widening redelivery: issued within the consent; with no ceiling it widens;
  • a person's hop through a binding to another audience the consent does not name: refused, by the consent;
  • a hop back to the module's own audience with an empty consent: issued, within the parent.

With ceiled made 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.Intersect and 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 vet and go test -count=1 ./... (GOWORK=off) pass in authority-core, conformance, the local authority, packagegate and noproxygate.

🤖 Generated with Claude Code

antoinetoussaint-byte and others added 2 commits October 27, 2026 01:28
…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>
@antoinetoussaint-byte
antoinetoussaint-byte merged commit 6e4d6c2 into main Oct 27, 2026
16 checks passed
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>
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