RFC: Governance Studio Adapter Integration
Status: Draft contract for GAT-208
Last updated: 2026-07-10 (GAT-221 runtime bootstrap follow-up)
Applies to: control-plane/service, llm-gateway/service, shared/servicekit/plugins, Governance Studio adapter client work, and Integrity Service VC integration points
Related issues: GAT-209, GAT-210, GAT-211, GAT-212, GAT-213, GAT-214, GOV-294, GOV-295, GAT-215
1. Summary
Section titled “1. Summary”This RFC defines the narrow adapter contract between Governance Studio, Auth Service, Governance Service, Integrity Service, and Guardian Gateway for the R1.1 Governance Studio Adapter Integration epic.
The integration model is:
- Governance Studio provides a project-level management surface for Gateway.
- Studio calls Gateway control-plane APIs directly with the current Auth Service user token.
- Gateway delegates Studio user token validation to Auth Service and checks Auth Service project permissions before applying project-scoped adapter actions.
- Gateway remains the system of record for Gateway-native state: agents, plugins, plugin snapshots, project references, membership evidence indexes, enforcement, audit, and standalone operation.
- Governance Service remains the system of record for Governance project data.
- Auth Service remains the system of record for human user authentication and project RBAC.
- Integrity Service remains the system of record for Governance Studio verifiable credentials, credential statements, content-addressed VC material, and VC revocation status.
This RFC is intentionally contract-only. It does not implement project-scoped snapshots, ProjectMembershipV1 validation, Gateway APIs, Studio UI, Auth Service changes, or new Integrity Service membership discovery endpoints.
2. Goals
Section titled “2. Goals”- Define the ownership boundary between Governance Studio, Auth Service, Governance Service, Integrity Service, and Gateway.
- Define stable project identity across Studio, Auth Service, Governance Service, Integrity Service, and Gateway.
- Define how Gateway validates and authorizes Studio-originated project actions.
- Define the Gateway API surface and payload sketches required by Governance Studio.
- Preserve Gateway standalone/operator operation and Gateway ownership of plugins/snapshots.
- Make follow-on issue boundaries explicit for GAT-209 through GAT-215 and GOV-294/GOV-295.
3. Non-Goals
Section titled “3. Non-Goals”- Moving Gateway agents, plugins, plugin snapshots, audit, enforcement, or membership evidence indexes into Governance Studio or Governance Service.
- Introducing a Governance Service publication artifact for Gateway runtime config.
- Introducing a Governance Service-owned Gateway resource catalog.
- Implementing external API mediation or non-LLM proxy routes.
- Implementing Viper raw HTTP proxy changes.
- Implementing new external agent registry adapters in this RFC.
- Designing a new policy DSL outside Gateway plugins and plugin snapshots.
- Moving Governance Studio VC issuance, storage, statement registration, or revocation status into Gateway.
- Implementing project-scoped snapshot persistence, ProjectMembershipV1 validation, Integrity Service membership discovery, or Studio UI in this RFC.
4. System Ownership
Section titled “4. System Ownership”| Area | Owner | Adapter contract |
|---|---|---|
| Project source of truth | Governance Service | Owns project UUID, integer project ID, organization ID, name, archive/status fields, and project lifecycle. |
| Project navigation and UI | Governance Studio | Presents the project-scoped Gateway area and passes the selected project context to Control Plane management APIs and Gateway runtime APIs. |
| Human user auth and RBAC | Auth Service | Owns user identity, project roles, project permissions, Auth Service JWT inputs, and RBAC permission checks. |
| Governance Studio VCs | Integrity Service | Issues, stores, signs, indexes, and revokes Governance Studio VC material, including ProjectMembershipV1 credentials. |
| Project references | Control Plane | Stores the minimal link needed to scope Gateway-owned state to a Governance project or standalone operator project. |
| Agents | Gateway | Owns runtime agent identity resolution and local references; future external registries can plug in without changing this adapter boundary. |
| Agent project membership index | Control Plane | Validates Integrity-backed ProjectMembershipV1 references and indexes normalized evidence for project management workflows. |
| Plugins and plugin snapshots | Gateway | Remain Gateway-owned. Project policy state is represented by project-scoped Gateway plugin snapshots. |
| Runtime enforcement | Gateway | Uses Gateway-owned verification, plugins, snapshots, and audit. Governance Service does not publish runtime config. |
| Audit | Gateway | Captures Gateway request, enforcement, snapshot, membership, and operator activity evidence. |
| Standalone operation | Gateway | Continues to support operator/admin-managed Gateway state without Governance Studio, Governance Service, or Studio UI. |
Agent ownership here means Gateway owns runtime identity resolution, verification, audit linkage, and local references for Gateway workflows. It does not require Gateway to be the only possible agent catalog. Future external registries, including Visa, Cloudflare, or Governance Studio-backed registries, can be supported behind the Gateway agent boundary without changing this adapter contract.
5. Project Identity and References
Section titled “5. Project Identity and References”5.1 Identifiers
Section titled “5.1 Identifiers”Control Plane-facing adapter APIs use the raw Governance project UUID in URL paths. Project references also store the canonical project URN.
project_uuid = a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4project_urn = urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4Example:
GET /v1/projects/a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4/plugin-snapshots/current?environment=productionRules:
project_uuidis the stable path identifier used by Studio and Control Plane project APIs.project_urnis the canonical stored identity and is used by ProjectMembershipV1 credentials and membership evidence indexes.- Control Plane project responses include both
project_uuidandproject_urn. - Auth Service project RBAC currently uses integer project IDs, so Governance Studio-originated references also store
governance_project_id. - Control Plane must not treat the project reference as a full copy of Governance Service project state.
- Updating display metadata in Control Plane is a convenience sync, not ownership transfer.
5.2 Project reference shape
Section titled “5.2 Project reference shape”Control Plane project reference APIs use existing snake_case JSON conventions.
{ "project_uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "governance_project_id": 123, "organization_id": 45, "display_name": "Example Project", "source": "governance_studio", "binding_verified_at": "2026-06-23T14:22:00Z", "created_by": "alex@example.com", "created_at": "2026-06-23T14:22:00Z", "updated_by": "alex@example.com", "updated_at": "2026-06-23T14:22:00Z"}Rules:
sourceisgovernance_studiofor Studio-created references andoperatorfor standalone/admin-created references.project_uuidandproject_urnare immutable after creation except through a Control Plane operator/admin override path.- For
source=governance_studio,governance_project_idandorganization_idare required, immutable, and must come from Governance Service binding lookup. - For
source=operator,governance_project_idandorganization_idmay benull; Auth Service project RBAC is not required for operator/admin workflows. - Project references do not define project lifecycle status; Governance Studio and Governance Service remain authoritative for project state.
- Control Plane does not persist an
adapter_status. Studio derives adapter UI states such as uninitialized, forbidden, and unavailable from Control Plane route responses and structured errors.
5.3 Governance Service binding lookup
Section titled “5.3 Governance Service binding lookup”For Studio-originated initialization and metadata refresh, Control Plane must resolve {project_uuid} through Governance Service before creating or updating a project reference.
Governance Service should expose a small UUID lookup endpoint or equivalent internal service contract:
GET /api/v1/projects/by-uuid/{project_uuid}Authorization: Bearer <auth_service_user_token>Expected binding fields:
{ "id": 123, "uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "organizationId": 45, "name": "Example Project", "status": "active", "isArchived": false, "updatedAt": "2026-06-23T14:20:00Z"}Binding rules:
- The resolved Governance Service project record is authoritative for
project_uuid,governance_project_id,organization_id, display name, and lifecycle/archive decisions. - Studio may submit project context in the Gateway request body, but Gateway treats those values only as hints.
- Gateway rejects the request when body hints conflict with the Governance Service lookup result.
- Governance Service lookup failure, not found, or mismatch fails closed for Studio-originated initialization.
- Gateway still performs Auth Service project permission checks after resolving the integer project ID.
- Gateway may forward the Studio bearer token or use a configured service credential for the lookup. Either way, Auth Service RBAC remains the Gateway authorization gate.
5.4 Standalone/operator project references
Section titled “5.4 Standalone/operator project references”Standalone Gateway and operator deployments must continue to work when Governance Service is not configured.
Rules:
- Operator/admin creation uses existing Gateway operator/admin auth, not Studio bearer auth.
- Operator/admin creation does not require Governance Service binding lookup.
- Operator/admin project references use
source=operator. - Operator/admin references may omit
governance_project_idandorganization_id. - Operator/admin snapshots and memberships use the same Gateway project APIs where possible, but authorization is satisfied by Gateway operator/admin auth.
- Studio-originated initialization must not silently fall back to operator mode.
6. Auth and Authorization
Section titled “6. Auth and Authorization”6.1 Studio bearer token validation
Section titled “6.1 Studio bearer token validation”Studio calls Gateway directly with the current Auth Service user token:
Authorization: Bearer <auth_service_user_token>Gateway must not validate Studio bearer tokens locally. It must delegate token validation to Auth Service, matching the Governance Service and Integrity Service auth pattern.
Required Gateway runtime configuration for adapter auth:
[adapter_auth]enabled = trueauth_service_base_url = "https://auth.example.com"governance_service_base_url = "https://governance.example.com"http_timeout = "10s"project_snapshot_environment_fallback_enabled = trueauth_service_base_url and governance_service_base_url must be absolute http(s) URLs. HTTPS is preferred; http:// is acceptable only for trusted in-cluster or otherwise transport-protected service-to-service traffic because Gateway forwards the live Studio bearer token to these services.
Required Gateway runtime configuration for Integrity-backed membership evidence:
[adapter_integrity]enabled = trueintegrity_service_base_url = "https://integrity.example.com"http_timeout = "10s"credential_status_cache_ttl = "1m"trusted_membership_issuer_dids = ["did:key:z6MkjchhfUsD6wuxs8sK7a9Zg2cXonQ9f7V3e4YpLwT1rN5m"]allow_embedded_credential_bootstrap = falseValidation requirements:
- Reject missing or malformed bearer credentials with
401andcode=missing_tokenorcode=invalid_token. - Call Auth Service
POST /api/v1/auth/validatewith the bearer token before project binding or RBAC authorization. - Treat Auth Service
401orvalid=falseresponses as401andcode=invalid_token. - Treat Auth Service validation outages or malformed validation responses as fail-closed
503responses. - Keep this auth path scoped to adapter project APIs. Existing Gateway operator/system auth remains valid for standalone/admin usage.
6.2 Auth Service calls
Section titled “6.2 Auth Service calls”Gateway calls Auth Service validation to verify the token and resolve current user metadata:
POST /api/v1/auth/validateAuthorization: Bearer <auth_service_user_token>Gateway must call Auth Service project RBAC for Studio-originated project authorization:
GET /api/v1/rbac/check/permission/{permission}/project/{governanceProjectId}Authorization: Bearer <auth_service_user_token>Permission mapping:
| Adapter action | Required Auth Service permission |
|---|---|
| List project references | view_project_data |
| Read project reference | view_project_data |
| List project agents or membership evidence | view_project_data |
| Read current project plugin snapshot | view_project_data |
| List project plugin snapshots | view_project_data |
| Validate project plugin snapshot | edit_project_settings |
| Create or initialize project reference | edit_project_settings |
| Create or clone project plugin snapshot | edit_project_settings |
| Activate project plugin snapshot | edit_project_settings |
| Roll back project plugin snapshot | edit_project_settings |
| Add or refresh membership evidence | edit_project_settings |
Authorization rules:
- Control Plane calls Auth Service token validation before calling Auth Service RBAC.
- Existing project routes load the project reference by
project_uuidbefore RBAC. - Existing project routes use the stored
governance_project_idfor Auth Service project permission checks. - Studio initialization routes resolve
{project_uuid}through Governance Service first, then check Auth Service permission on the resolved integer project ID. - If Control Plane has no project reference for a UUID, only initialization routes may proceed for Studio users.
source=operatorreferences and operator/admin workflows use Control Plane operator/admin authorization instead of Auth Service project RBAC.- Auth Service outage during Studio-originated authorization fails closed.
6.3 Integrity Service calls
Section titled “6.3 Integrity Service calls”Control Plane does not issue or revoke Governance Studio membership credentials. For ProjectMembershipV1 evidence, Control Plane is an Integrity Service consumer.
Observed Integrity Service capabilities relevant to the adapter contract:
| Purpose | Integrity Service API | Control Plane usage |
|---|---|---|
| Read content-addressed material | GET /store/v1/blob/{cid} or GET /store/v1/text/{cid} | Dereference a credential or statement payload when Studio provides a CID. |
| Store canonical JSON | PUT /store/v1/jcs | Used by Governance Studio or Integrity Service issuance flows, not by Gateway as source of truth. |
| Read a statement by ID | GET /statements/v1/id/{id} | Dereference a credential statement when Studio provides a statement ID. |
| Register statements | PUT /statements/v1 and PUT /statements/v1/batch | Used by issuance flows to store/index VC statements; Gateway should not register memberships. |
| Allocate revocation status | POST /credentials/v1/allocate | Used before signing membership VCs; Gateway expects issued credentials to already be allocated. |
| Check revocation status | GET /credentials/v1/status?vcId={credential_id} | Refresh Gateway’s indexed status_resolution before listing or accepting membership evidence. |
| Fetch status-list VC | GET /credentials/v1/status-lists/{id} | Optional proof/status-list verification path for follow-on VC validation helpers. |
| Revoke credential | POST /credentials/v1/revoke/{credential_id} | Owned by Governance Studio or operator issuance workflows, not Gateway adapter routes. |
| Governance VC examples | POST /gov/v1/controls/* and POST /gov/v1/policy/compliance/{project_uuid} | Reference pattern for project-scoped VC issuance, status allocation, platform signing, and use. |
Integrity Service authorization rules:
- Gateway forwards the Studio bearer token when reading Integrity Service material for a Studio-originated request unless a service credential is explicitly configured.
- Gateway must not use Integrity Service token validation as a replacement for Auth Service token validation or Auth Service project RBAC.
- Gateway treats Integrity Service read/status failure as a structured dependency failure for membership evidence refresh, not as permission success.
- Gateway caches only derived membership index fields and revocation status metadata according to
credential_status_cache_ttl; it does not become the authoritative VC store.
7. Gateway Adapter API Contract
Section titled “7. Gateway Adapter API Contract”Follow-on implementation should add Gateway-native project routes under /v1/projects.
Common conventions:
- Successful item responses use
{ "item": ..., "correlation_id": "..." }. - Successful list responses use
{ "items": [...], "correlation_id": "..." }. - Default errors use
{ "code": "...", "message": "...", "correlation_id": "..." }. - Validation errors may add
validation_errorswith field-level details. - Snapshot routes require an explicit
environmentquery or body field. - Project UUID path values must be valid UUIDs.
7.1 Project reference routes
Section titled “7.1 Project reference routes”GET /v1/projects?limit=50&source=governance_studio&status=activeLists project references visible to the caller.
{ "items": [ { "project_uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "governance_project_id": 123, "organization_id": 45, "display_name": "Example Project", "source": "governance_studio", "binding_verified_at": "2026-06-23T14:22:00Z", "created_by": "alex@example.com", "created_at": "2026-06-23T14:22:00Z", "updated_by": "alex@example.com", "updated_at": "2026-06-23T14:22:00Z" } ], "correlation_id": "req_123"}GET /v1/projects/{project_uuid}Returns one project reference. A missing reference returns 404 and code=project_not_found.
PUT /v1/projects/{project_uuid}Creates or updates a project reference.
Studio-originated request:
{ "source": "governance_studio", "governance_project_id": 123, "organization_id": 45, "display_name": "Example Project"}Operator/admin standalone request:
{ "source": "operator", "display_name": "Standalone Gateway Project"}Response:
{ "item": { "project_uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "governance_project_id": 123, "organization_id": 45, "display_name": "Example Project", "source": "governance_studio", "binding_verified_at": "2026-06-23T14:22:00Z", "created_by": "alex@example.com", "created_at": "2026-06-23T14:22:00Z", "updated_by": "alex@example.com", "updated_at": "2026-06-23T14:22:00Z" }, "correlation_id": "req_123"}7.2 Project plugin snapshot routes
Section titled “7.2 Project plugin snapshot routes”Project snapshot APIs must include environment so prod/staging/dev state is never ambiguous.
GET /v1/projects/{project_uuid}/plugin-snapshots?environment=production&limit=50Lists Gateway plugin snapshots associated with the project and environment.
{ "items": [ { "snapshot_id": 987, "environment": "production", "project_uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "version": 42, "status": "active", "entry_count": 3, "created_by": "alex@example.com", "created_at": "2026-06-23T15:00:00Z", "activated_by": "alex@example.com", "activated_at": "2026-06-23T15:05:00Z" } ], "correlation_id": "req_123"}GET /v1/projects/{project_uuid}/plugin-snapshots/current?environment=productionReturns the active snapshot for (environment, project) or the environment default fallback when configured.
Project-specific response:
{ "item": { "state": "active", "resolution_scope": "project", "fallback_used": false, "environment": "production", "project_uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "active_snapshot": { "activation_id": 55, "snapshot_id": 987, "version": 42, "status": "active", "activated_by": "alex@example.com", "activated_at": "2026-06-23T15:05:00Z", "created_by": "alex@example.com", "created_at": "2026-06-23T15:00:00Z", "entries": [] }, "rollback_candidates": [ { "snapshot_id": 981, "version": 41, "status": "superseded", "activated_by": "sam@example.com", "activated_at": "2026-06-22T18:10:00Z" } ] }, "correlation_id": "req_123"}Environment-default fallback response:
{ "item": { "state": "active", "resolution_scope": "environment_default", "fallback_used": true, "environment": "production", "project_uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "active_snapshot": { "activation_id": 40, "snapshot_id": 900, "version": 17, "status": "active", "activated_by": "ops@example.com", "activated_at": "2026-06-20T10:00:00Z", "created_by": "ops@example.com", "created_at": "2026-06-20T09:55:00Z", "entries": [] }, "rollback_candidates": [] }, "correlation_id": "req_123"}Empty response when neither project nor fallback snapshot exists:
{ "item": { "state": "empty", "resolution_scope": "none", "fallback_used": false, "environment": "production", "project_uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "active_snapshot": null, "rollback_candidates": [] }, "correlation_id": "req_123"}POST /v1/projects/{project_uuid}/plugin-snapshots/validateValidates snapshot entries without creating or activating a snapshot.
{ "environment": "production", "entries": [ { "hook": "proxy.pre", "order_index": 0, "plugin_definition_id": 7, "plugin_version": "1.0.0", "plugin_mode": "enforcement", "timeout_ms": 250, "granted_capabilities": {}, "match": {}, "config_json": {}, "enabled": true } ]}{ "item": { "status": "invalid", "errors": [ { "code": "plugin_definition_not_found", "message": "plugin definition 7 was not found", "field": "entries[0].plugin_definition_id", "entry_index": 0, "plugin_id": null, "details": {} } ] }, "correlation_id": "req_123"}POST /v1/projects/{project_uuid}/plugin-snapshotsCreates a project-scoped snapshot or clones from an existing snapshot, subject to Gateway snapshot validation.
{ "environment": "production", "version": 42, "clone_from": { "snapshot_id": 900, "environment": "production", "project_uuid": null }, "entries": [], "manifest_signature": "base64url-signature", "signer_key_id": "did:key:z6Mk..."}Response uses the project snapshot summary shape from the list route.
POST /v1/projects/{project_uuid}/plugin-snapshots/{snapshot_id}/activateActivates a snapshot for (environment, project).
{ "environment": "production"}Response uses the current snapshot shape with resolution_scope=project.
POST /v1/projects/{project_uuid}/plugin-snapshots/rollbackRolls the project back to a previous project activation for the same environment. target_snapshot_id is optional; if omitted, Gateway uses the most recent valid rollback candidate.
{ "environment": "production", "target_snapshot_id": 981}Response uses the current snapshot shape with the restored snapshot active.
7.3 Project membership evidence routes
Section titled “7.3 Project membership evidence routes”GET /v1/projects/{project_uuid}/memberships?limit=50&status=validLists Control Plane-indexed ProjectMembershipV1 evidence for the project.
{ "items": [ { "subject_did": "did:key:z6MkpRdf4P2f3EaEhx3iKxJWLN8LeF3NfJ1hV4uLci3xV8j2", "gateway_agent_id": "agent:research-runner", "project_uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "credential_id": "urn:uuid:25b53f3d-b8ac-47db-8d49-1ec7b92f7416", "issuer_did": "did:key:z6MkjchhfUsD6wuxs8sK7a9Zg2cXonQ9f7V3e4YpLwT1rN5m", "roles": ["automation-agent"], "source": "integrity_service", "source_refs": { "credential_id": "urn:uuid:25b53f3d-b8ac-47db-8d49-1ec7b92f7416", "source_cid": "bafy...", "statement_id": "urn:cid:bagb...", "statement_cid": "bafy..." }, "valid_from": "2026-05-05T09:00:00Z", "valid_until": "2027-05-05T09:00:00Z", "credential_status": [ { "id": "https://integrity.example.com/credentials/v1/status-lists/3d28d30a-6f38-4af9-8b13-fb1c9bb3ee9c#42", "type": "BitstringStatusListEntry", "status_purpose": "revocation", "status_list_index": "42", "status_list_credential": "https://integrity.example.com/credentials/v1/status-lists/3d28d30a-6f38-4af9-8b13-fb1c9bb3ee9c" } ], "status_resolution": { "revoked": false, "checked_at": "2026-06-23T15:15:00Z", "metadata": { "source": "integrity_service" } }, "credential_status_checked_at": "2026-06-23T15:15:00Z", "validation_status": "valid", "validation_errors": [], "last_verified_at": "2026-06-23T15:15:00Z", "observed_at": "2026-06-23T15:15:00Z" } ], "correlation_id": "req_123"}POST /v1/projects/{project_uuid}/membershipsStores or refreshes ProjectMembershipV1 VC evidence for a project agent or identity.
{ "credential_id": "urn:uuid:25b53f3d-b8ac-47db-8d49-1ec7b92f7416", "credential": null, "source_cid": "bafy...", "statement_id": "urn:cid:bagb...", "statement_cid": "bafy...", "agent_did_hint": "did:key:z6MkpRdf4P2f3EaEhx3iKxJWLN8LeF3NfJ1hV4uLci3xV8j2", "refresh_status": true}Request rules:
- At least one credential source is required: embedded
credential,source_cid,statement_id, orstatement_cid. credential_idandagent_did_hintare hints only.agent_did_hintis interpreted as a subject DID hint for GOV-294 compatibility; Control Plane derives authoritative values from the verified credential.- When a reference is supplied, Control Plane dereferences it from Integrity Service before validation.
- When an embedded credential is supplied, Control Plane accepts it only when
allow_embedded_credential_bootstrap=trueor under operator/admin auth. - Control Plane checks Integrity Service revocation status for the resolved
credential_idwhenrefresh_status=trueor when cached status is stale.
Response uses the membership evidence item shape.
7.4 Project-scoped Gateway agent routes
Section titled “7.4 Project-scoped Gateway agent routes”Project agent APIs are derived from Control Plane-indexed ProjectMembershipV1 evidence. Studio must not call global Gateway agent routes and filter client-side.
GET /v1/projects/{project_uuid}/agents?limit=50Lists agents or subject DIDs associated with the requested project through membership evidence.
{ "items": [ { "agent_id": "agent:research-runner", "display_name": "Research Runner", "app_type": "codex", "status": "active", "subject_did": "did:key:z6MkpRdf4P2f3EaEhx3iKxJWLN8LeF3NfJ1hV4uLci3xV8j2", "primary_did": "did:key:z6MkpRdf4P2f3EaEhx3iKxJWLN8LeF3NfJ1hV4uLci3xV8j2", "did_source": "viper", "last_activity_at": "2026-06-23T17:04:00Z", "membership_state": "active", "membership_evidence": [ { "subject_did": "did:key:z6MkpRdf4P2f3EaEhx3iKxJWLN8LeF3NfJ1hV4uLci3xV8j2", "gateway_agent_id": "agent:research-runner", "project_uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "credential_id": "urn:uuid:25b53f3d-b8ac-47db-8d49-1ec7b92f7416", "roles": ["automation-agent"], "credential_status": [], "status_resolution": { "revoked": false, "checked_at": "2026-06-23T15:15:00Z" }, "validation_status": "valid", "validation_errors": [], "last_verified_at": "2026-06-23T15:15:00Z", "observed_at": "2026-06-23T15:15:00Z" } ] }, { "subject_did": "did:key:z6MkunknownLocalAgent1111111111111111111111", "last_activity_at": null, "membership_state": "active", "membership_evidence": [ { "subject_did": "did:key:z6MkunknownLocalAgent1111111111111111111111", "project_uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "credential_id": "urn:uuid:e7f78ce7-fab8-4ee0-8c5c-d4f24a7058f8", "roles": ["auditor"], "credential_status": [], "status_resolution": { "revoked": false, "checked_at": "2026-06-23T16:10:00Z" }, "validation_status": "valid", "validation_errors": [], "last_verified_at": "2026-06-23T16:10:00Z", "observed_at": "2026-06-23T16:10:00Z" } ] } ], "total_count": 2, "correlation_id": "req_123"}Rules:
subject_didis always present and comes from verified membership evidence.agent_id, display metadata, keys, users, and activity fields are present only when Gateway can join the subject DID to a local Gateway agent reference.membership_state=activerequires at least one valid membership evidence item. Revoked, expired, not-yet-valid, invalid, or untrusted evidence remains visible but must not be presented as healthy active membership.- Unknown local agents with valid subject DID evidence remain listable so Studio can explain project membership before a local agent reference exists.
GET /v1/projects/{project_uuid}/agents/{agent_id_or_subject_did}Returns one project-scoped agent detail. The detail route uses the same membership authorization as the list route and rejects wrong-project agents.
{ "item": { "agent_id": "agent:research-runner", "display_name": "Research Runner", "app_type": "codex", "status": "active", "subject_did": "did:key:z6MkpRdf4P2f3EaEhx3iKxJWLN8LeF3NfJ1hV4uLci3xV8j2", "primary_did": "did:key:z6MkpRdf4P2f3EaEhx3iKxJWLN8LeF3NfJ1hV4uLci3xV8j2", "did_source": "viper", "last_activity_at": "2026-06-23T17:04:00Z", "membership_state": "active", "membership_evidence": [ { "subject_did": "did:key:z6MkpRdf4P2f3EaEhx3iKxJWLN8LeF3NfJ1hV4uLci3xV8j2", "gateway_agent_id": "agent:research-runner", "project_uuid": "a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "credential_id": "urn:uuid:25b53f3d-b8ac-47db-8d49-1ec7b92f7416", "roles": ["automation-agent"], "credential_status": [], "status_resolution": { "revoked": false, "checked_at": "2026-06-23T15:15:00Z" }, "validation_status": "valid", "validation_errors": [], "last_verified_at": "2026-06-23T15:15:00Z", "observed_at": "2026-06-23T15:15:00Z" } ], "keys": [], "users": [], "current_snapshot": null, "logs_supported": false, "state_cids_supported": false }, "correlation_id": "req_123"}current_snapshot is read-only context for the selected environment when the caller asks Gateway to include it or when the implementation can resolve it cheaply. logs_supported and state_cids_supported are forward-compatible flags for GAT-225/GOV-329; until project-scoped log/state routes land, Studio should render those entry points as unavailable rather than calling global routes.
8. Snapshot Storage and Resolution Semantics
Section titled “8. Snapshot Storage and Resolution Semantics”Project-scoped snapshots extend existing environment-scoped snapshots without breaking them.
Required semantics:
- Existing environment-wide snapshots and active snapshot APIs continue to work unchanged.
- Project-scoped activation is keyed by
environment + project_urn. - Environment-default activation is keyed by
environmentwith no project. - Activating a project snapshot requires a known project reference.
- Activating a project snapshot with an environment that does not match the snapshot environment fails with
400andcode=invalid_environment. - Only one current activation may exist for a given
environment + project_urn. - Project rollback candidates are derived from prior activations for the same
environment + project_urn. - Management API
currentresolution remains project-specific active snapshot first, then environment-default display fallback whenproject_snapshot_environment_fallback_enabled=true, then structured empty state. - Runtime policy selection is deliberately stricter than the management display API: a bound project candidate resolves only its exact project activation with fallback disabled. Explicit or implicit project candidates never downgrade to environment-default policy.
- Every project-scoped snapshot must contain exactly one enabled premade
builtin.project_membershipentry atproject.bootstrap; validation, creation, activation, and rollback fail closed otherwise. - Candidate snapshot selection is not membership authorization. Core candidate fields cannot populate authoritative project context.
- Only a Gateway-validated typed membership bootstrap result can establish authoritative
projectctx.Context; arbitrary plugincontext_writescannot. - One immutable snapshot ID/version is pinned across bootstrap, proxy pre/post, audit attribution, and request-end.
- Requests without any bound project candidate are explicitly projectless and pin the environment-default snapshot without running project bootstrap.
8.1 Runtime membership policy ownership
Section titled “8.1 Runtime membership policy ownership”Gateway core owns project-presentation protocol safety: RFC 9421 coverage, bounded canonical header grammar, current/legacy mutual exclusion, CID/body binding, signer/credential-subject binding, deterministic project selection, ambiguity/conflict rejection, project-reference existence, protected context fields, header hygiene, and local-only hot-path access.
The selected project’s builtin.project_membership configuration owns trusted issuers, required membership, accepted status, expiration/revocation enforcement, allowed/required roles, and observe/enforce behavior. The shared servicekit/projectmembership validator remains the one ProjectMembershipV1 representation and proof-validation implementation.
9. ProjectMembershipV1 Boundary
Section titled “9. ProjectMembershipV1 Boundary”Governance Studio assigns agents or identities to projects through a ProjectMembershipV1 verifiable credential. Integrity Service is the authoritative home for this VC material. Control Plane validates, displays, and refreshes the project membership evidence without making the LLM gateway host user-facing membership APIs or become the credential issuer/canonical credential store.
9.1 Integrity Service role
Section titled “9.1 Integrity Service role”Integrity Service already provides the primitives needed for Governance Studio VCs:
- Content-addressed JSON storage through
/store/v1/jcsand retrieval through/store/v1/blob/{cid}or/store/v1/text/{cid}. - Statement registration and lookup through
/statements/v1,/statements/v1/batch, and/statements/v1/id/{id}. - Credential statement indexing by
credential_subjectin thecredential_statementstable. - Revocation status allocation through
/credentials/v1/allocate. - Revocation status checks through
/credentials/v1/status?vcId={credential_id}. - Status-list VC retrieval through
/credentials/v1/status-lists/{id}. - Platform DID signing via Auth Service-backed signing, as used by the policy compliance VC flow.
The expected ProjectMembershipV1 issuance lifecycle should follow the existing policy compliance pattern:
- Governance Studio or a membership issuance workflow builds an unsigned ProjectMembershipV1 VC whose subject DID is the Gateway agent or identity DID and whose project field is the canonical
project_urn. - Integrity Service allocates a revocation status entry for the credential.
- Integrity Service signs the allocated credential with the configured platform or trusted membership issuer DID.
- Integrity Service stores the signed VC or a credential statement and returns stable references such as
credential_id,source_cid,statement_id, orstatement_cid. - Control Plane receives those references from Studio or a future Integrity Service membership discovery endpoint, verifies the VC, checks status, and indexes normalized fields.
9.2 Expected credential profile
Section titled “9.2 Expected credential profile”{ "@context": ["https://www.w3.org/ns/credentials/v2", "https://w3id.org/security/v2"], "id": "urn:uuid:25b53f3d-b8ac-47db-8d49-1ec7b92f7416", "type": ["VerifiableCredential", "IdentityAttestation"], "credentialSubject": { "id": "did:key:z6MkpRdf4P2f3EaEhx3iKxJWLN8LeF3NfJ1hV4uLci3xV8j2", "identity": { "type": "ProjectMembershipV1", "project": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4", "role": ["automation-agent"] } }, "issuer": "did:key:z6MkjchhfUsD6wuxs8sK7a9Zg2cXonQ9f7V3e4YpLwT1rN5m", "credentialStatus": [ { "id": "https://integrity.example.com/credentials/v1/status-lists/3d28d30a-6f38-4af9-8b13-fb1c9bb3ee9c#42", "type": "BitstringStatusListEntry", "statusPurpose": "revocation", "statusListIndex": "42", "statusListCredential": "https://integrity.example.com/credentials/v1/status-lists/3d28d30a-6f38-4af9-8b13-fb1c9bb3ee9c" } ], "proof": { "type": "Ed25519Signature2018", "proofPurpose": "assertionMethod", "verificationMethod": "did:key:z6MkjchhfUsD6wuxs8sK7a9Zg2cXonQ9f7V3e4YpLwT1rN5m#z6MkjchhfUsD6wuxs8sK7a9Zg2cXonQ9f7V3e4YpLwT1rN5m", "created": "2026-05-05T09:00:00Z", "jws": "PLACEHOLDER_SIGNATURE" }, "validFrom": "2026-05-05T09:00:00Z", "validUntil": "2027-05-05T09:00:00Z"}Credential rules:
credentialSubject.idis the Gateway agent DID or identity DID.credentialSubject.identity.typemust beProjectMembershipV1.credentialSubject.identity.projectmust equal the Control Plane project referenceproject_urn.credentialSubject.identity.roleis normalized to a non-empty string array.issuermust match a configured trusted membership issuer or trusted platform DID.credentialStatusshould include a revocation entry allocated by Integrity Service.validFromandvalidUntilare evaluated by Control Plane during validation.- Control Plane must verify the VC proof and must not trust caller-supplied subject DID,
project_urn, roles, or issuer hints.
9.3 Control Plane validation and index output
Section titled “9.3 Control Plane validation and index output”Validation helper output:
- Subject DID.
- Project URN and normalized project UUID.
- Roles.
- Issuer DID.
- Credential ID.
- Validity window.
- Credential status entry metadata.
- Integrity Service source references under
source_refs:source_cid,statement_id,statement_cid, or equivalent. - Revocation status from Integrity Service under
status_resolution. - Stable validation status and validation errors.
Validation statuses should include valid, expired, not_yet_valid, revoked, invalid, and untrusted_issuer.
Control Plane index rules:
- Control Plane stores derived membership index fields and references, not the authoritative credential assignment.
- Control Plane may cache the credential payload for audit/debugging only if the storage policy allows it; source references remain canonical.
- Control Plane refreshes
status_resolutionfrom Integrity Service when listing memberships if cached status is stale.credential_statusremains the normalized VC credentialStatus metadata parsed by GAT-213. - Duplicate refresh of the same
credential_id + project_urn + subject_didupdates validation/status timestamps instead of creating a duplicate row. - Revoked, expired, invalid, or untrusted credentials remain visible to authorized users with structured errors but are not treated as active membership.
- Standalone/operator mode may accept embedded credentials or operator-managed references, but must keep the same validation and index shape.
9.4 Discovery contract
Section titled “9.4 Discovery contract”The current Integrity Service codebase provides generic statement lookup and credential indexing, but it does not expose a project-membership-specific query API. Its credential statement index is keyed by credential_subject; for ProjectMembershipV1 that subject is the agent or identity DID, not the project URN. A project-wide membership sync therefore needs either Studio-provided references or a new Integrity Service query/index over credentialSubject.identity.project.
The adapter contract should support both of these follow-on integration options:
- Push/reference mode: Governance Studio passes known ProjectMembershipV1 credential references to
POST /v1/projects/{project_uuid}/membershipswhen memberships are created, refreshed, or displayed. - Pull/discovery mode: Integrity Service adds a small query endpoint for membership VCs, for example:
GET /credentials/v1/project-memberships?project=urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4&subject=did:key:z6Mk...Expected discovery response:
{ "items": [ { "credential_id": "urn:uuid:25b53f3d-b8ac-47db-8d49-1ec7b92f7416", "source_cid": "bafy...", "statement_id": "urn:cid:bagb...", "statement_cid": "bafy...", "issuer_did": "did:key:z6MkjchhfUsD6wuxs8sK7a9Zg2cXonQ9f7V3e4YpLwT1rN5m", "subject_did": "did:key:z6MkpRdf4P2f3EaEhx3iKxJWLN8LeF3NfJ1hV4uLci3xV8j2", "project_urn": "urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4" } ]}Control Plane should standardize on the same dereference and validation path for both modes. Pull/discovery mode is preferred for complete project sync, while push/reference mode is sufficient for the first Studio project page integration.
10. Studio Adapter Contract
Section titled “10. Studio Adapter Contract”Governance Studio should add a project-level Gateway area that is a thin UI over Control Plane project APIs for management state and Gateway APIs for runtime state.
The project adapter client for GOV-294 must:
- Live in a dedicated project adapter API client module, separate from Governance Service clients.
- Use an environment-configurable Control Plane base URL for project management routes.
- Attach the current Auth Service bearer token.
- Pass raw
project.uuidin project API paths. - Include
environmenton every project snapshot request. - Use typed request/response models matching this RFC.
- Pass Integrity Service membership credential references to Control Plane when Studio has them.
- Normalize project adapter errors for forbidden, not found, validation failure, and service unavailable cases.
The Gateway project area for GOV-295 must:
- Use the selected Governance project from existing project context.
- Show Control Plane project reference state and derive initialization state from Control Plane route responses and structured errors.
- Let authorized users initialize the project reference.
- Display Control Plane-indexed, Integrity-backed agent membership evidence.
- Display current project plugin snapshot state, including fallback state when applicable.
- List available snapshots and rollback candidates as read-only operational evidence.
- Treat snapshot validation, creation, activation, and rollback as config-as-code Gateway/plugin tooling workflows outside Studio.
- Handle loading, empty, forbidden, unavailable, validation error, and successful read states.
The UI must not:
- Store Gateway project policy state in Governance Service.
- Edit plugin artifacts directly as Governance Service records.
- Treat human project membership as agent project membership.
- Proxy Control Plane or Gateway APIs through Governance Service.
- Treat Gateway as the issuer or revocation authority for Governance Studio membership VCs.
- Render controls that validate, create, activate, or roll back Gateway plugin snapshots.
11. Failure Semantics
Section titled “11. Failure Semantics”| Case | Status | Code | Expected behavior |
|---|---|---|---|
| Missing bearer token on Studio route | 401 | missing_token | Control Plane rejects before project lookup. |
| Auth Service reports token invalid | 401 | invalid_token | Control Plane rejects before project binding or Auth Service RBAC. |
| Auth Service validation unavailable | 503 | auth_service_unavailable | Control Plane fails closed before project binding or Auth Service RBAC. |
| Auth Service RBAC unavailable | 503 | auth_service_unavailable | Control Plane fails closed for Studio-originated authorization. |
| User lacks required project permission | 403 | forbidden | Control Plane denies the action. |
| Project reference not found on read route | 404 | project_not_found | Control Plane returns not found or structured uninitialized state when the endpoint defines one. |
| Project reference not found on Studio init route | varies | varies | Control Plane resolves Governance Service binding, checks RBAC, and creates only if verified. |
| Governance Service unavailable during Studio init | 503 | governance_service_unavailable | Control Plane fails closed and does not create or update the project reference. |
| Governance Service binding does not match body hints | 409 | project_binding_mismatch | Control Plane rejects and persists nothing. |
| Governance project archived | 409 | project_archived | Control Plane blocks Studio mutations based on Governance Service project state unless follow-on implementation explicitly allows. |
| Missing snapshot environment | 400 | invalid_environment | Control Plane requires explicit environment on project snapshot APIs. |
| Snapshot validation failure | 400 | snapshot_validation_failed | Control Plane returns structured validation_errors. |
| Plugin store unavailable | 503 | plugin_store_unavailable | Control Plane returns unavailable adapter state. |
| Integrity Service unavailable during membership read | 503 | integrity_service_unavailable | Control Plane cannot refresh or dereference membership evidence and returns a structured dependency error. |
| Membership credential reference not found | 404 | membership_evidence_not_found | Control Plane returns not found and does not create an index row. |
| Membership credential project mismatch | 409 | membership_project_mismatch | Control Plane rejects the credential because its ProjectMembershipV1 project does not match the route project. |
| Membership credential revoked | 409 | membership_revoked | Control Plane indexes the revoked status but does not treat the credential as active membership. |
| Membership credential proof invalid | 400 | membership_invalid | Control Plane returns structured validation errors. |
| No project or fallback snapshot exists | 200 | n/a | Current snapshot route returns structured state=empty. |
| Operator/admin standalone route without Gov Service | varies | n/a | Existing Control Plane operator/admin auth applies; Governance Service and Auth Service project RBAC are not needed. |
12. Test and Verification Expectations
Section titled “12. Test and Verification Expectations”The short-term contract fixture for GOV-294/GAT-214 lives at shared/servicekit/projectmembership/testdata/gateway_studio_adapter_contract.json. Governance Studio imports the console-local mirror at console/src/services/gateway-adapter-contract.fixture.json so frontend tooling stays inside the console source tree. shared/servicekit/projectmembership/api_contract_test.go verifies the mirror matches the shared fixture and must pass whenever the fixture or membership response contract changes.
Follow-on implementation should cover:
- GAT-209 project references: create, read, list, update, duplicate UUID/URN rejection, UUID validation, Studio binding verification, and operator standalone creation.
- GAT-210 auth: allowed user, missing token, Auth Service invalid-token response, wrong project, insufficient permission, Auth Service validation outage, Auth Service RBAC outage, and operator/admin bypass.
- GAT-211/GAT-212/GAT-221 snapshots: two projects in the same environment with different active snapshots, management-display environment fallback, runtime exact-project selection with no fallback downgrade, required
project.bootstrapmembership entry validation, missing environment rejection, activation environment mismatch, validation failure, activation, rollback, request pinning, and unchanged explicitly projectless environment-default behavior. - GAT-213/GAT-214 memberships: Integrity-backed credential reference dereference, valid ProjectMembershipV1, expired, not yet valid, revoked, wrong project, wrong subject, untrusted issuer, invalid proof, missing source reference, duplicate refresh, list by project, and list by agent DID.
- Integrity Service adapter fixtures:
/store/v1/blob/{cid},/statements/v1/id/{id},/credentials/v1/status?vcId=..., revoked status, status refresh cache, and service unavailable behavior. - GOV-294 client: base URL config, token attachment, project UUID path construction, environment query/body construction, membership reference submission, typed response handling, and normalized errors.
- GOV-295 UI: reference initialization, Integrity-backed membership display, current snapshot display including fallback, activation, rollback, forbidden state, unavailable state, and validation error state.
- GAT-215 integration: Studio project page to project reference, membership display, active snapshot read, activation, rollback, forbidden user, Integrity-backed VC status assertions, and Gateway-owned runtime state assertions.
13. Follow-On Implementation Mapping
Section titled “13. Follow-On Implementation Mapping”| Issue | RFC section implemented |
|---|---|
| GAT-209 | Project identity and reference contract in sections 5 and 7.1. |
| GAT-210 | Auth Service token validation and project authorization in section 6. |
| GAT-211 | Project-scoped plugin snapshot storage and resolution semantics in sections 7.2 and 8. |
| GAT-212 | Control Plane project snapshot management APIs and payloads in section 7.2. |
| GAT-213 | Integrity-backed ProjectMembershipV1 parsing and validation in section 9. |
| GAT-214 | Control Plane membership evidence reference ingestion, status refresh, and indexing in sections 7.3 and 9. |
| GOV-294 | Governance Studio project adapter client contract in section 10. |
| GOV-295 | Governance Studio project adapter UI contract in section 10. |
| GAT-215 | End-to-end adapter verification expectations in section 12. |
14. Open Compatibility Notes
Section titled “14. Open Compatibility Notes”- Current Auth Service project RBAC is integer-ID based. Control Plane should store
governance_project_idon Studio-created project references rather than requiring Auth Service UUID claim changes. - Current Control Plane bearer auth resolves identity but not Auth Service project permissions. Follow-on auth work should add adapter-specific Auth Service authorization helpers rather than overloading identity-only control-plane auth.
- Existing environment-scoped plugin snapshots must continue to work unchanged when project-scoped snapshots are added.
- Standalone deployments must be able to manage equivalent project references and snapshots without Governance Studio or Governance Service.
- Governance Service currently has internal project UUID resolution behavior. The adapter contract requires a stable HTTP lookup endpoint or equivalent Control Plane-accessible service contract for Studio-originated project binding verification.
- Integrity Service currently exposes generic credential status, statement, and store APIs, but not a project-membership-specific discovery API. The first adapter implementation can use Studio-pushed credential references; complete background sync needs an Integrity Service membership discovery endpoint or equivalent query contract.
- Integrity Service signs platform VCs through Auth Service-backed platform signing. Control Plane validation must trust configured issuer DIDs and verify proofs; it must not infer trust solely from successful Integrity Service dereference.