Repository navigation
test: the solution-principal target, specified before it is built - #11
Merged
Merged
Conversation
An installed solution becomes a principal of its organisation with a stable UUID; its roles are permission levels on the data that principal owns; granting a role is the access; scopes select resources by owner, not by id lists; an existing resource joins only by a transfer the person administers through their own organisation grants. RunSolutionPrincipalTarget is the shared conformance target (not yet in Run). It first names every contract element the published descriptors lack, then specifies nine cases. The local authority runs it only under AUTHORITY_TARGET=solution-principal, where it fails today, naming the missing contract. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Coordinator decisions on the target: - the installation acting for itself is the solution principal: its owner id is U, not <installation>/agent; - the install request's role is the agent's role (editor's level, not installer's admin), and no person or team inherits it; - RevokeRole, by person or team, stops access at once (an old capability fails Check) and re-granting restores it; GrantRole by team is a team's access; - a scope naming an owner and ids admits only when both match, and a child never drops the ids it narrowed to. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Coordinator decisions: confinement to the principal's data; one identity (agent_principal_id deleted, the agent's owner id is principal_id, and Run's installation case reads installationPrincipal so one host passes both); fail-closed modules (hop and Claims targets, and no owner-selected scopes to an audience that does not declare owner_aware); who may grant (an organisation administrator or the solution's installer) with every malformed grant refused; a second organisation; old capabilities dead after re-grant and reinstall; transfer with a store-attested from_owner_id, only from a person or the organisation, needing the person's own admin and editor on the principal; the installer as an explicit grant. Also: GrantToTeam, GrantToTeamRequest, team_ids required absent; ListRoleGrants; ResourceGrant.resource_owner_id qualifies a match outside the oneof, asserted on a narrowed capability; team- and organisation-leaving hooks; a person, installation or organisation id refused as owner; a dedicated person with admin on one resource; independent cases; named revocation reasons; the agent through its operation binding; disabled- principal refusals; own-kind scopes owner-selected; a revoke leaves unrelated capabilities alive; transfer id rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Coordinator decisions:
- the agent is confined: eight widening asks refused "widens";
- grant authority is per organization and per installation: another
organization's administrator and A's installer acting on B are refused on
GrantRole, RevokeRole and ListRoleGrants;
- transfer admin is bound to the resource (an unadministered id is refused);
- every audience receiving owner-selected scopes declares owner_aware,
solutions included (A and B declare it; C does not and is refused);
- the target supersedes Run's conflicting cases, which now assert the target
model whenever the published contract is the target's, so one host passes
both (solution_roles [0]-[4], installation_chain, solution_tools, the
permission matrix, the callee staleness probe, installation lifecycle
exposure, applyManifest; the installing person holds no role).
Majors: transfer's previous owner allowlisted (the organization or its
person); write on the kind, not a role name ("editor" declares read only);
Check admits an owner-selected scope by kind and action and the module
enforces the owner (a route case); Python and TypeScript owner-aware targets.
Minors: grant idempotency, AllowsOwned on plain scopes and whole-kind owner
asks, a hop positive, revival after reinstall for the agent and a parent,
transfer reads roles live, own-kind confinement locally, one spelling
("organization"), owner_aware withdrawal refuses earlier capabilities, grant
edge cases (disabled installation, another organization's team, the
installer listing, direct plus team grant).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
N1: GrantRole, RevokeRole and ListRoleGrants naming the caller's own organization with another organization's installation are refused "organization" (for the administrator and the installer). N2: the Python owner target authenticates a signed context, so the decode path that projects scopes must keep the owner. Coordinator decisions in the model and cases: an uninstall clears role grants; a new grant leaves existing capabilities valid; invalidation is permanent (re-declaring owner_aware revives nothing); a module's hop into a non-owner-aware audience is refused; Go Claims.Scopes() keeps each scope's resource owner (TestScopesKeepTheirResourceOwner); Run's staleness branch revokes and restores the viewer's direct roles exactly and requires the fixture viewer to hold them only directly. 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
The owner wants solution installation to be much simpler: a solution has an identity, a UUID like a person's, and its data is owned by that identity. Access to a solution means being granted one of that solution's roles. This PR is step 1: tests first. It pins the model as shared conformance cases plus library targets in Go, Python and TypeScript, before anything is implemented. Nothing here implements it.
It has been revised for spec reviews r75, r85 and r95, following the coordinator's decisions: maximum security, one mechanism per job.
The model
The authoritative text is the doc comment on
SolutionPrincipalCase(libraries/conformance/go/solution_principal.go).owner_principal_id=principal_id;agent_principal_idis deleted).editorthat holds read only).GrantRoleandRevokeRole(by person or team, both idempotent) are the access, andListRoleGrantsanswers who holds what.GrantToTeamandteam_idsare deleted.{kind, actions, resource_owner_id: U}, taken from the solution's roles alone.owner_aware; the authority refuses the rest.owner_awarerefuses the capabilities issued before.AllowsOwnedin Go,allows_ownedin Python,allowsOwnedin TypeScript. The owner-blindallowsnever admits an owner-selected scope.from_owner_id). The authority allows the transfer only when all of these hold, each read live:The response names U and
authorized_by. The original creator stays the author, the previous owner keeps nothing through ownership, and organization grants (which name resources, not owners) are unaffected.owner_awareinvalidates at once, and permanently: only fresh capabilities work afterwards. A revoke never touches anyone else's capabilities.The cases (18, each making its own grants)
widens:holds no role)ListResources{all, owner U}, and the narrowed{id u-doc, owner U}. The same eight asks are refused withwidens.requires. The module's owner check is pinned in the library targets.confined, for read by the Reader and for admin by the Administrator. A person holding an organization grant on A's own kind still gets exactly the owner-U scope there.owner_aware. Refused withowner-aware:owner_awarerefuses earlier capabilities, and re-declaring it revives noneauthorwrites;editor(read only) is refused withwidensorganization);editor(read only) is refused withwrite on; the same capability is allowed once a write-holding role is granted, which proves the roles are read live. Allowed from the organization and from a person of it. Fourteen refusals:*id; a 129-byte idListRoleGrantsreflects it; old capabilities stay dead after a re-grantGrantRoleare all refused;Library targets:
AllowsOwnedcovers owner, owner plus ids, whole-kind with owner, and plain scopes.Claims.Scopes()keeps each scope's resource owner (TestScopesKeepTheirResourceOwner).tests/test_owner_target.py) builds its claims throughauthenticate()from a signed context, so the decode path must keep the owner. TypeScript (tests/owner-target.test.mjs) mirrors the claims target the same way.Run vs target: one host passes both
The target supersedes Run's conflicting cases. Each now asserts the earlier model while the published contract is the earlier one, and the target's model once the contract carries
Installation.principal_id. Today's local authority still passes Run unchanged. Run cases changed:conformance.goan installation acts for itselfprincipal_idconformance.gothe permission matrixconformance.gocallee stalenessconformance.goapplyManifestowner_awaresolution_roles.go[0] [1] [2] [4]owner_awaresolution_roles.go[3]holds no roleinstallation_chain.gosolution_tools.goinstallation_lifecycle.goexposureGrantRole, read throughListRoleGrantsThe installing person no longer holds the installer role. No suite code relies on that; a target host's fixture grants installer explicitly where its matrix expects it.
composition.proto:115("held by whoever installs it") is contract text the target changes. "holds nothing" stays only for module audiences (no organization grant), which is a different refusal.How it fails today
Everything is opt-in with
AUTHORITY_TARGET=solution-principal, as the coordinator decided:AuthorityManifestV1.owner_aware,AuthorizeTransferRequest.from_owner_idandResourceGrant.resource_owner_idoutside the oneof. It names 4 elements the contract still carries:GrantToTeam,GrantToTeamRequest,team_ids,agent_principal_id.WorkScopeV1field 4 as an unknown field.authenticate()("payload is noncanonical or carries a field this reader does not know").The local driver's leave-team and leave-organization hooks return a labelled "not yet" error, to be filled in step 2.
From #7: reused and deleted (step 2)
AttachResource,DetachResource,installation_owned_scopes, owned-id lists, the 32-id cap, the claim races, and org-wide revision bumps.Tests (head 433f8ef)
go vetis clean in every touched Go module. With the targets skipped, these pass:go test -race -count=1 ./...inmodules/authority/services/local/code(the full main suite),libraries/hop/goandlibraries/authority-client/go;With
AUTHORITY_TARGET=solution-principal, every target fails as described above.🤖 Generated with Claude Code