Repository navigation
feat: governed callers - MSP becomes one implementation of the caller role - #28
Merged
Merged
Conversation
… role
ADR-GKS-GOVERNED-CALLERS was accepted by the owner on 2026-09-27, with all
four open questions settled as proposed. A system that already has its own
auth and memory can now write to GKS without deploying MSP.
- Grants v2 adds a "governed" profile to GKS_CLIENT_GRANTS_PATH. Such a
grant carries clientId, a hash-only credential, the legacy knowledge
tools it may call, an exclusive list of portfolioIds, and a unique
provenanceNamespace. v1 documents still parse as read grants. Startup
fails on overlapping portfolios, a namespace claimed twice, the reserved
"msp" namespace or msp-runtime id, and pipeline tools.
- Governed calls arrive over HTTP with a Bearer credential and a
_meta.gksCallerAuth envelope (version, callerId, scopeDigest).
authorizeGovernedCallerRequest enforces the tool list, the portfolio
boundary, the scope digest and the caller's own <namespace>:proof/ refs.
- Provenance checks are caller-aware. The tool schemas widen to
^[a-z][a-z0-9-]{0,30}:proof/, which still accepts every value accepted
before. MSP stays msp:proof/, and its messages are unchanged.
- MSP remains the built-in governed caller, and GKS_MSP_CALLER now lives
in contracts. MSP is denied the portfolios a governed grant owns. Pipeline
tools stay MSP-relay only.
- Migration 0008 records the authenticated caller_id on promotions,
stage_evidence, human_resolutions and artifact_links. Existing rows read
as msp-runtime. The persistence port takes an optional callerId, and
caller_id is not returned in responses.
The C0.4 corpus replays unchanged except for TOOL-001, whose tools/list
pattern widened. The baseline is re-locked for the tool registry hash and
migration 0008. Docs amended: ADR-GKS-BOUNDARY, ADR-GKS-CLIENT-ACCESS,
GKS-PORT-CONTRACT, README, the production runbook (provisioning a governed
caller), and the call-direction rule in CLAUDE.md.
Co-Authored-By: Claude Opus 5.5 <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.
Summary
This PR implements ADR-GKS-GOVERNED-CALLERS. The owner accepted it on 2026-09-27, including all four open questions as proposed.
Why. A customer that already has its own auth and memory should not have to deploy MSP just to write to GKS. GKS doesn't need the MSP product. It needs someone to fill three roles: authenticate the user, choose the scope, and attest provenance. This PR turns MSP into the first implementation of that role, a governed caller. Any other system can be provisioned for its own portfolios.
How a governed caller works
{ "schemaVersion": "gks-client-grants/v2", "clients": [{ "profile": "governed", "clientId": "acme-backend", "credentialSha256": "…", "provenanceNamespace": "acme", "portfolioIds": ["acme-portfolio"], "allowedTools": ["gks_knowledge_promote", "gks_search", "…"] }] }Authorization: Bearer gksc_…, and each call carries_meta.gksCallerAuthwith{ version: "gks-caller-auth/v1", callerId, scopeDigest }.provenance_ref,provenanceRefandevidenceRefmust use<own namespace>:proof/…. A caller cannot write under another caller's namespace, andmspis reserved.caller_id(migration 0008).What did not change for MSP
GKS_MSP_RELAY_CREDENTIAL/gks-msp-auth/v1envelope over stdio and HTTP, API-010 payloads, and response shapes are all unchanged.msp:proof/with the same error messages.gks_scope_denied, "This portfolio is governed by another caller."). With no governed grants configured, behaviour is identical to before.^msp:proof/to^[a-z][a-z0-9-]{0,30}:proof/. Every value accepted before is still accepted.Changes
gks-contracts/client-auth.mjsgovernedprofile; v1 still parses asread);authorizeGovernedCallerRequest;governedPortfolios;GKS_MSP_CALLERgks-contracts/validation.mjs,tool-definitions.mjsmsp); schema pattern widenedgks-corepromoteCandidate,applyHumanResolutionandlinkArtifacttake acallerand passcallerIdto persistencegks-persistence,migrations/0008_caller_attribution.sqlcaller_id NOT NULL DEFAULT 'msp-runtime'on promotions, stage_evidence, human_resolutions and artifact_links. The port accepts an optionalcallerId. The value is not returned in responses.apps/gks-serverdirectClientGrantis renamedclientGrant.CLAUDE.mdEvidence
tests/contract/governed-callers.test.mjs(27 tests):msp-runtime, including a stage-evidence row created by the 0005 hook.TOOL-001, wheretools/listnow shows the widened pattern.check:baseline: caught exactly the tool registry hash and migration 0008; re-locked.MSP_REPO_ROOTpointing at the local Memory-and-Soul-Passport checkout):msp-provider-compatibilitypasses.msp-service-chainfails, but inside MSP's own client before any request reaches GKS. MSP's latest commit (fd6c24b) requires a workspace ID that this test's fixture doesn't send. This is unrelated to this PR and should be fixed separately.Follow-up
The ADR's acceptance criteria include golden corpus cases for a governed caller. The corpus runner currently speaks only stdio, so the next PR adds an HTTP mode to the runner and a governed-caller golden case.
Test plan
npm test: vitest 327 passed, 2 skipped (the MSP suites needMSP_REPO_ROOT); security 12/12; unit 9/9npm run check:c0(PASS 24 / NOT_RUN 1),check:baseline,check:corpusmsp-provider-compatibilityagainst the real MSP checkout🤖 Generated with Claude Code