Repository navigation
feat: the authority core's decisions, and readers that honour resource owners - #18
Merged
Merged
Conversation
…e owners libraries/authority-core/go is the authority's one generic core. This first layer is pure: scope (the owner-aware scope algebra and parent limits, moved from the local authority), domain (the objects both hosts share) and decide (issuance classification, confinement, the owner-aware check, the role-grant rules with the organization checked before the caller, AuthorizeTransfer, module validation). The service over a Store, and the in-memory store, follow in the next PR. Core is pinned at 8af4b787 (WorkScopeV1.resource_owner_id), mirrored in the local proto and regenerated (codefly generate proto / generate client, Go, Python, TypeScript). Every reader now refuses to read an owner-selected scope as the whole kind: the Go hop stamp, Claims.Allows (owner-blind, fails closed) with AllowsOwned and Scopes() keeping owners, Python work_context (projection, allows/allows_owned, scope_contained) and codefly_hop, and the TypeScript reader. Each language verifies a signed chain whose hop drops or changes the owner as a refusal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion per resource, owners refused locally, resolvable modules - f1: decide.RoleScopes refuses an installation with no principal and scope.Owned refuses an empty owner; an empty owner selected every owner. - f2: scope.Attenuates decides per (kind, owner, action, resource) instead of comparing rendered rows, and Intersect no longer lets an exact row hide an AnyAction row. Both are tested against a reference point-set model over 200k random cases; Intersect's union of repeated rows and Same's owner comparison are documented and tested. - f3: the local issuer refuses an owner-selected ask or delegation by name until it issues through the owner-aware core. - f4/f5: authority-core requires resources and delivery at main's pseudo-version and hop requires the authority client at 2737858, with no replace in any library. - f6: withdrawing owner-aware checking is a narrowing. - f7: decide.ForeignInstallation is the one answer for another organization's installation and for none. f8: HeldRoles counts only the installation's grants. f9: a revoke skips the subject-membership check. f10: Child says confined in the same words whatever the person's own grants hold; the unused ceiling is gone. f11: ValidateModule validates method policies and names ErrNoInstallerRole. f12: a malformed resource id is Invalid; the local service declares 0.0.1, so the generated packages carry 0.0.1 again. - The owner-selected reader rows are one Core-signed entry of the shared claims.json, run by the Go, Python and TypeScript fixture loops. - CI: #16's go.yml, verbatim, until #16 lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 26, 2026
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 trying to do
This is step 2 of the solution-principal work (spec: #11). The owner wants one generic authority core, so that the local authority and saas accounts become thin wrappers that share the same objects and decisions. That core is built test-first.
It is split by layer to stay under the ~2,000-line budget, in sequential PRs that do not stack:
Store(the transactionalStore/Txthe coordinator approved), the in-memory store, and table-driven tests encoding the conformance spec.This PR
libraries/authority-core/go, the pure layer. No store, no keys, no transport.scope: the scope algebra moved from the local authority (state.gocoverage,issuer/scope_limits.go), now owner-aware.Owned,HasOwner, and the parent limits.domain: the shared objects:OwnerAware), Installation (withPrincipalIDandAgentRole), RoleGrant, Delegation;decide: each decision takes explicit inputs.HeldRolesandRoleScopes: a role scope is owned by the installation's principal.Issuenames why an ask is refused:beyondfor an action not held,widensfor resources or an owner wider than held.Child:confinedversuswidens.OwnerAware: no owner-selected scope to an audience that does not declare owner-aware checking.ChangeRole: the organization is checked before the caller's authority (r98's ordering note); then the caller must administer the organization or hold the installer role; then the installation must be active, the role declared, exactly one subject named, and that subject of the organization.AuthorizeTransfer,ValidResourceID,ValidateModule, andNarrowed.Readers now honour owners (r109 (1) and (3)). Before this PR, every reader flattened a scope with no ids to
kind:action, so an owner-selected scope would have read as the whole kind.libraries/hop/go/hop.go): the stamp leaves owner-selected scopes out.Claims: the owner-blindAllowsnever admits an owner-selected scope; it fails closed through core's owner-awareScopeContained.AllowsOwned(kind, action, id, owner)is the module's check, andScopes()keeps owners.authority_client/work_context.py): the projection keepsresource_owner_id,allowsis owner-blind and fails closed,allows_ownedis added, andscope_containedis owner-aware.codefly_hop._flattenleaves owner-selected scopes out.work-context.ts):Scope.resourceOwnerId, an owner-blindallows,allowsOwned, an owner-awarescopeContained, and a stampflattenthat leaves owner rows out.owner_test.goin workcontext and hop,python/tests/test_owner.py,typescript/tests/owner.test.mjs):Core is pinned at 8af4b787 (core#845,
WorkScopeV1.resource_owner_id) in every Go module. The local proto mirrors field 4 (the descriptor-parity test requires it). The service and client bindings were regenerated withcodefly generate protoandcodefly generate clientin Go, Python and TypeScript, using a CLI built from main that includes cli#1169. Python generation therefore works again: the Python bindings are current.Nothing mints owner-selected scopes yet
The local authority is unchanged here and issues no owner-selected scope. When the wrapper PR turns owner minting on, the core refuses to mint one for an audience that does not declare
owner_aware(decide.OwnerAware; conformance target case 7). That covers r109 (2).Fleet consumers that must move before owner minting is turned on anywhere. Each reads scopes owner-blind today:
authority.go:307identity.go:337and:528operations.go:188executionrecorderEach needs core 8af4b787+, or this client's
AllowsOwned, and must declareowner_awareonly once it compares owners.Reuse and classification
The algebra, limits, validation and role rules are moved from the local authority's
state.goandissuer.go, not rewritten. The local copies are deleted in the wrapper PR. This is a fix at the owner.Tests
go vetis clean andgo test -race -count=1 ./...passes in all nine Go modules pluslibraries/authority-core/go(scope and decide).registry_test, is green.scope_containedowner rule, the Python hop flatten and the TypeScript flatten each fail their tests when reverted;Owned, and an epoch bump on grant.AUTHORITY_TARGET=solution-principal) until the wrapper PR.Generator outputs to note
codefly generate client(CLI main) now writes the library's own version,0.0.0, intopyproject.tomlandpackage.json(cli#1164), and adds per-package TypeScript namespace exports (src/packages/). These are generator output, not hand edits.🤖 Generated with Claude Code