Skip to content

fix(authority): run the local host on the authority core - #25

Merged
antoinetoussaint-byte merged 23 commits into
mainfrom
feat/local-authority-on-core
Oct 26, 2026
Merged

antoinetoussaint-byte merged 23 commits into
mainfrom
feat/local-authority-on-core

Conversation

@antoinetoussaint-byte

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

Copy link
Copy Markdown
Collaborator

What we are trying to do

This is the last step of the solution-principal work (spec #11, #14) for the local authority. The local authority becomes a thin host over the generic authority core (#18, #19, #20, #21; main through #24 merged), and it serves the solution-principal contract:

  • an installed solution is a principal of its organisation;
  • its roles are levels on that principal's data;
  • a role grant to a person or a team is the access.

The old model is deleted: GrantToTeam, team_ids, agent_principal_id, the <installation>/agent principal, and team-entitled scopes with no owner. The conformance target is now the contract, and it runs with every go test.

The change

The local authority over the core (modules/authority/services/local/code)

  • pkg/business/state is the host half. It holds:

    • the fixture and its sign-in tokens, which seed the core's in-memory store;
    • sessions, and membership changes (LeaveTeam and LeaveOrganization, which call the core's PersonChanged);
    • workload attestation against the launcher's anchor;
    • signed delivery admission, with the replay fence on a fixture intent's revision;
    • method admission;
    • the generated developer role;
    • the permission report and approvals.

    Modules, installations, role grants, delegations, revisions and seals are the core's. The scope algebra, issuance, seal sources, installation access checks and solution-role logic that used to live here are deleted (-2,352 Go lines).

  • pkg/business/issuer is the wire. A session bearer resolves to the core's Person and a credential to its Workload; Issue, Check, ListResources, AuthorizeTransfer and delegations call the core. A core refusal keeps its code and is classified as ErrRefused or ErrUnknown for callers that test the family.

  • plugins/installation serves Install, GrantRole, RevokeRole, ListRoleGrants and List through the core, as the signed-in caller. plugins/authority adds AuthorizeTransfer, and ListResources carries resource_owner_id.

  • Composition ingest (r132 m3): a delivery that names resource_owner_id in a scope ceiling, a binding, a role, a method requirement or what it serves is refused by name. owner_aware (AuthorityManifestV1 field 7) is read from the signed document.

  • fixture-composition/v1 keeps its shape and its meaning in the new model, so composing workspaces do not change:

    • role is the installation's own role (the agent's);
    • owner installed it and is granted the installer role;
    • each team in teams is granted role.

Contract (modules/authority/services/local/proto, interfaces/)

  • codefly.dev/installation 0.2.0:
    • GrantRole, RevokeRole and ListRoleGrants are added;
    • GrantToTeam is deleted;
    • Installation gains principal_id and roles, and reserves team_ids and agent_principal_id.
  • codefly.dev/authority 0.3.0: AuthorizeTransfer is added, and ResourceGrant.resource_owner_id (field 5).
  • codefly.dev/composition 0.2.0: AuthorityManifestV1.owner_aware (field 7).
  • r142: WorkScopeV1.resource_owner_id takes core's rule exactly (max_bytes = 512, ^[!-)+-~]*$). Core is pinned at b57347f0 in all eight Go modules.
  • Consumer ranges: edge installation@>=0.1.0 <0.3.0 (it calls List only); edge, hop and console take authority@>=0.2.0 <0.4.0.
  • Regenerated: bindings via codefly generate proto and codefly generate client (Go, Python, TypeScript).

Request and host installation authority (r152)

  • Install and Uninstall take a Person value and check current stored authority. The nil-caller convention is deleted. Trusted fixture setup/teardown uses distinct InstallAsHost and UninstallAsHost methods in the core and local state.
  • Module installations are identified from their own record (no installation principal or installer), so a withdrawn module remains protected even without a manifest. Uninstall no longer depends on a ModuleNamed lookup. Store errors encountered by lifecycle operations pass through unchanged.
  • GrantRole, RevokeRole, and ListRoleGrants return the same foreign-installation answer for present and absent installations when a member is neither administrator nor installer. The stale-session fallback is removed from role decisions.
  • Regression tests cover withdrawn modules, store outages without mutation, all three role calls, empty core callers, and anonymous/forged requests over the Codefly-generated gRPC installation client. The wire test proves no install step runs or installation is created, then verifies the same request succeeds for an authenticated administrator.
  • Child issuance preserves verified parent workspace/project and prior actor provenance; the production core regression exercises two exchanges. This does not claim cross-host qualification.

Core behaviour this PR settles, as the merged spec requires

  • A workload naming an undeclared binding is refused by name.
  • Issuing for, or granting on, a pending or uninstalled installation is FailedPrecondition naming its state. The spec's lifecycle case now expects that, matching the target's "not installed".
  • An absent installation on an installation issue is answered like a foreign one (r137 m1). The lifecycle case now expects PermissionDenied.
  • Check names a revocation ("the capability was revoked: …") wherever core reports one.
  • A transfer of a malformed id is InvalidArgument (r124 f12). The target case now expects that.
  • The local replay store is in memory, chosen explicitly (the core requires one).

Conformance (libraries/conformance/go)

  • The old-model branches are deleted: every contract-shape test (solutionPrincipal()), the GrantToTeam exposure, the team_ids checks, agent_principal_id, and Fixture.UnexposedTeam.
  • Run asserts the solution-principal model. The target's uninstall helper now names the uninstalled installation while it is disabled; before, it reinstalled it first, so the target never ran green.
  • The local driver runs RunSolutionPrincipalTarget with every go test, and the membership hooks are real (LeaveTeam, LeaveOrganization).
  • r142 vectors: core's workcontext/testdata/resource_owner_vectors.json is vendored, byte-checked against the pinned core, and run by the Go, Python and TypeScript readers.

Docs

README.md (installation interface and administration), libraries/conformance/go/README.md, and modules/authority/services/local/README.md (a host over the core, and the composition document's meaning).

Size

1,118 hand-written non-test Go lines added, 2,396 removed versus current main. The r152 follow-up adds about 100 non-test Go lines, reusing the core lifecycle and refusal logic; it introduces no new store or resolver.

Reuse and classification

  • Every decision and signature is the core's. The local authority keeps only what a host owns, and its own copies of the scope algebra, issuer and seal sources are deleted, not wrapped.
  • The fixture's composition document keeps its schema.
  • This is a fix at the owner.

Fleet consumers

I searched every local mirror (the latest 25 refs of each repo). No fleet code calls GrantToTeam or reads team_ids or agent_principal_id from codefly.dev/installation. These need to move:

  • owner-blind readers, before any host mints owner-selected scopes for them; each must declare owner_aware only once it compares owners:
    • module-annotations authority.go:307
    • module-evaluation identity.go:337/528
    • module-record-store operations.go:188
    • cli executionrecorder
  • modules receiving a solution's owner-selected scopes must declare owner_aware in their authority document. Solutions must declare it to receive their own roles' scopes.
  • cli cmd/generate/contract_surface_test.go:159 counts 3 methods on host.installation.v1.InstallationService. It needs 5 once it reads this contract.
  • Workspaces composing the authority module pin it by commit, so nothing changes until they re-pin. None of them uses fixture-composition installations, so their documents keep working:
    • robin feat/runnable-tool-exposure and authority/interface (df3ca1e);
    • core-solutions authority/wiki-on-the-authority;
    • module-directory authority/behind-the-hop;
    • platform-core authority/robin-wiki-only.
  • saas accounts serves its own saas.accounts.v1 installation service and is untouched. It adopts the core separately.

Tests

  • go vet and go test -race -count=1 ./... with GOWORK=off pass in all 10 Go modules.
  • The local registry_test runs Run and RunSolutionPrincipalTarget against the local authority over gRPC, and both are green.
  • Python: 231 passed. TypeScript: 89 passed.

Smoke check

  • Authority playground: replaced org-directory’s nonexistent 0.0.0 pin with commit c6b5cb39350a2d9fd6b17c56e34864811409fae8 from its default branch. codefly run service authority/local --load-only --headless --temporary-ports resolves the commit and loads the composed service successfully. This is a load check, not a live journey; Codefly still reports the existing document-formats composition disagreement.
  • Earlier Robin hop-path probe (not rerun for this revision): robin feat/runnable-tool-exposure chat pins the hop library at 06d0d3b, from authority's authority/vision line. Its internal/authority calls hop.Read, which is not on authority main, so it does not build against main's hop with or without this PR. With this branch's hop and client, robin chat's only build error is that one hop.Read (18 packages import internal/authority and fail to build). The other 7 packages pass.
  • What does stand in: the conformance run against the local authority. It covers sign-in, person, workload, child and delegation issuance, chains through bindings, the solution-tools case (a robin module calling a solution's operation), lifecycle and the full target.

🤖 Generated with Claude Code

antoinetoussaint-byte and others added 23 commits October 25, 2026 22:58
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r's foreign answer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ontract grants roles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…equests

Identify module installations from their durable record, preserve lifecycle store errors, and give roleless members the foreign answer across role calls. Exercise anonymous requests over the generated client and pin the playground directory to a real commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on-core

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Delete the public unsigned manifest path and require both the signed carrier and delivery workload credential before host installation work. Reuse owner-aware scope coverage for all artifact approval operations, bind identities to the installation namespace, and refuse unresolvable resource-id restrictions before storage.

Preserve the review probes as generated Connect/gRPC regressions, including independent approve/read/revoke refusal, valid delivery replay, response equivalence, and revocation races. Both blockers reproduced before the fix; all ten Go modules pass vet/race, both transport conformance suites and core's kit pass, pytest passes 231 tests, and npm test passes 89.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Accept the existing immutable org-directory pin as a playground-only dependency repair: the prior 0.0.0 reference did not resolve during the reported load smoke check. Record its independent rationale and playground-maintainer ownership without selecting another external revision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preserve the core-backed authority and regenerate the combined LocalService contract; retain the transport gate and verify generated provenance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preserve generated loopback credential forwarding alongside the core-backed local authority. Record the conflict resolutions and complete local validation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@antoinetoussaint-byte
antoinetoussaint-byte merged commit 583cb19 into main Oct 26, 2026
15 checks passed
AntoineToussaint pushed a commit that referenced this pull request Oct 26, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AntoineToussaint pushed a commit that referenced this pull request Oct 26, 2026
Run 17 encoding variants through Go stamped and bare authentication and the public hop verifier, sharing the existing Python and TypeScript fixture. Pin core PR #850 and synchronize its resource-owner vectors. Retain the saved merge of main after PR #25.

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