Repository navigation
fix(authority): grant a team role on the team directory, not on membership - #56
Merged
antoinetoussaint-byte merged 8 commits intoOct 30, 2026
Merged
Conversation
…rship A host that creates a team inline for a solution-declared role has nobody in that team yet, and grants the role before activation hands anyone access. The core proved a team by finding a person who carried it, so a grant to a freshly created, empty team was refused with "<team> is not a team of <organization>". The core now asks the transaction's team directory (Tx.TeamExists) whether the team exists in the installation's organization. Every other check is unchanged: the organization boundary, the caller's authority, the active installation, the declared role and exactly one subject. Revocation still needs no directory entry, so a grant stays revocable after its team is deleted. This finishes the handoff commit b8acc41 (Tx.TeamExists, the memory store's directory, the store contract case and the local fixture's teams: list): - decide: TeamOfOrganization now documents the directory meaning. - local authority: the fixture is the local host's team directory; a team it seats a person in exists, and teams: lists the empty ones. - conformance: SolutionPrincipalCase gains a required EmptyTeam (a team of Organization nobody belongs to), granted, listed and revoked; and a grant to a team the host does not hold is refused. Hosts must supply EmptyTeam to run the suite. Tests: - authority TestARoleIsGrantedToATeamTheDirectoryHolds: an empty team receives a grant (GrantRole and GrantRoleAsHost); a populated team still works; teams of another organization (empty and populated), an unknown team and a team only a membership names are refused; a member who joins after the grant holds the role; a deleted team's grant stays revocable. - state TestTheFixtureTeamDirectoryHoldsEmptyAndSeatedTeams. - storetest "team directory includes empty teams and refuses foreign or absent ids" (from b8acc41) runs against the memory store. - conformance "a team nobody belongs to yet is granted a role, listed and revoked" against the local authority; it fails on 21ce6d9's core. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
antoinetoussaint-byte
added a commit
to codefly-dev/module-saas-starter
that referenced
this pull request
Oct 30, 2026
…mmit codefly-dev/authority#56 squash-merged as 67586be6ccb9, which orphans the branch commits accounts pinned: authority-core and conformance at 666ea1f97f80 and authority-client at 21ce6d9284ed (both diverged from authority main). Move all three to the merge commit so the pins resolve from main; delivery and resources stay at f82a98e093ef, which main holds. Co-Authored-By: Claude Fable 5.1 <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.
Problem
The SaaS installer (codefly-dev/module-saas-starter#1273) lets a person create a team inline for each solution-declared role and grants that role to the team once the installation is active, through the existing
GrantRolepath. The core proved a team existed by finding a person who belonged to it, so a grant to a freshly created, empty team was refused with"<team>" is not a team of <organization>. An inline-created team has no members yet, and it has to be grantable before activation hands anyone access.Fix
decideRoleChangeasks the transaction's team directory,Tx.TeamExists(organization, id), whether the team exists in the installation's organization. Membership no longer proves a team exists. The other checks are unchanged: the organization boundary, the caller's authority, the active installation, the declared role and exactly one subject. Revocation still needs no directory entry, so a grant can still be revoked after its team is deleted.This finishes the handoff commit b8acc41, which added
Tx.TeamExists, the memory store's directory (PutTeam/DeleteTeam), the store contract case and ateams:list in the local fixture. The commit on top:decide:TeamOfOrganizationnow documents what the directory means.teams:lists the empty ones, so existing fixtures keep working.SolutionPrincipalCasegains a requiredEmptyTeamfield (a team ofOrganizationthat nobody belongs to). The suite grants it a role, lists the grant and revokes it. A grant to a team the host does not hold (a random UUID) is refused withnot a team of.Classification: fix, made in the core, which owns the decision. A host's team directory is the source of truth for whether a team exists.
Reused
GrantRole/GrantRoleAsHostpath anddecide.ChangeRole. No new RPC, no proto or interface change, no generated code.storetest.Run) and the conformance targetRunSolutionPrincipalTarget, extended the same way their existing cases are.Tests
authority.TestARoleIsGrantedToATeamTheDirectoryHolds:GrantRoleandGrantRoleAsHost.state.TestTheFixtureTeamDirectoryHoldsEmptyAndSeatedTeams(local fixture directory, plus refusals for malformed entries).go vet ./...andgo test -race -count=1 ./...pass in all 13 Go modules, matching the CI matrix. The hop Postgres admission regressions ran withoutHOP_TEST_POSTGRES.Scope note
This branch also carries five earlier commits from
fix/standalone-module-binding(940ddd6..21ce6d9: installation setup steps, signed role declarations, the declaration wire contract). Accounts already pins them at 21ce6d9, and no open PR carries them.Adoption by accounts
Accounts must implement
Tx.TeamExistson its core transaction, reading the team table by organization and id inside the transaction (the task says this exists on its branch). Then it must:authority-core/goandconformance/goto this head inmodule/services/accounts/code/go.mod;EmptyTeam(a seeded team with no members) in itsRunSolutionPrincipalTargetcase;storetest.Identity.Teamsin its store contract adapter.🤖 Generated with Claude Code