Skip to content

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

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:

  1. Governance Studio provides a project-level management surface for Gateway.
  2. Studio calls Gateway control-plane APIs directly with the current Auth Service user token.
  3. Gateway delegates Studio user token validation to Auth Service and checks Auth Service project permissions before applying project-scoped adapter actions.
  4. Gateway remains the system of record for Gateway-native state: agents, plugins, plugin snapshots, project references, membership evidence indexes, enforcement, audit, and standalone operation.
  5. Governance Service remains the system of record for Governance project data.
  6. Auth Service remains the system of record for human user authentication and project RBAC.
  7. 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.

  1. Define the ownership boundary between Governance Studio, Auth Service, Governance Service, Integrity Service, and Gateway.
  2. Define stable project identity across Studio, Auth Service, Governance Service, Integrity Service, and Gateway.
  3. Define how Gateway validates and authorizes Studio-originated project actions.
  4. Define the Gateway API surface and payload sketches required by Governance Studio.
  5. Preserve Gateway standalone/operator operation and Gateway ownership of plugins/snapshots.
  6. Make follow-on issue boundaries explicit for GAT-209 through GAT-215 and GOV-294/GOV-295.
  1. Moving Gateway agents, plugins, plugin snapshots, audit, enforcement, or membership evidence indexes into Governance Studio or Governance Service.
  2. Introducing a Governance Service publication artifact for Gateway runtime config.
  3. Introducing a Governance Service-owned Gateway resource catalog.
  4. Implementing external API mediation or non-LLM proxy routes.
  5. Implementing Viper raw HTTP proxy changes.
  6. Implementing new external agent registry adapters in this RFC.
  7. Designing a new policy DSL outside Gateway plugins and plugin snapshots.
  8. Moving Governance Studio VC issuance, storage, statement registration, or revocation status into Gateway.
  9. Implementing project-scoped snapshot persistence, ProjectMembershipV1 validation, Integrity Service membership discovery, or Studio UI in this RFC.
AreaOwnerAdapter contract
Project source of truthGovernance ServiceOwns project UUID, integer project ID, organization ID, name, archive/status fields, and project lifecycle.
Project navigation and UIGovernance StudioPresents the project-scoped Gateway area and passes the selected project context to Control Plane management APIs and Gateway runtime APIs.
Human user auth and RBACAuth ServiceOwns user identity, project roles, project permissions, Auth Service JWT inputs, and RBAC permission checks.
Governance Studio VCsIntegrity ServiceIssues, stores, signs, indexes, and revokes Governance Studio VC material, including ProjectMembershipV1 credentials.
Project referencesControl PlaneStores the minimal link needed to scope Gateway-owned state to a Governance project or standalone operator project.
AgentsGatewayOwns runtime agent identity resolution and local references; future external registries can plug in without changing this adapter boundary.
Agent project membership indexControl PlaneValidates Integrity-backed ProjectMembershipV1 references and indexes normalized evidence for project management workflows.
Plugins and plugin snapshotsGatewayRemain Gateway-owned. Project policy state is represented by project-scoped Gateway plugin snapshots.
Runtime enforcementGatewayUses Gateway-owned verification, plugins, snapshots, and audit. Governance Service does not publish runtime config.
AuditGatewayCaptures Gateway request, enforcement, snapshot, membership, and operator activity evidence.
Standalone operationGatewayContinues 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.

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-9ad3bb7f92e4
project_urn = urn:uuid:a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4

Example:

GET /v1/projects/a7e9f0d2-6e42-4f8b-9f83-9ad3bb7f92e4/plugin-snapshots/current?environment=production

Rules:

  1. project_uuid is the stable path identifier used by Studio and Control Plane project APIs.
  2. project_urn is the canonical stored identity and is used by ProjectMembershipV1 credentials and membership evidence indexes.
  3. Control Plane project responses include both project_uuid and project_urn.
  4. Auth Service project RBAC currently uses integer project IDs, so Governance Studio-originated references also store governance_project_id.
  5. Control Plane must not treat the project reference as a full copy of Governance Service project state.
  6. Updating display metadata in Control Plane is a convenience sync, not ownership transfer.

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:

  1. source is governance_studio for Studio-created references and operator for standalone/admin-created references.
  2. project_uuid and project_urn are immutable after creation except through a Control Plane operator/admin override path.
  3. For source=governance_studio, governance_project_id and organization_id are required, immutable, and must come from Governance Service binding lookup.
  4. For source=operator, governance_project_id and organization_id may be null; Auth Service project RBAC is not required for operator/admin workflows.
  5. Project references do not define project lifecycle status; Governance Studio and Governance Service remain authoritative for project state.
  6. 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.

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:

  1. The resolved Governance Service project record is authoritative for project_uuid, governance_project_id, organization_id, display name, and lifecycle/archive decisions.
  2. Studio may submit project context in the Gateway request body, but Gateway treats those values only as hints.
  3. Gateway rejects the request when body hints conflict with the Governance Service lookup result.
  4. Governance Service lookup failure, not found, or mismatch fails closed for Studio-originated initialization.
  5. Gateway still performs Auth Service project permission checks after resolving the integer project ID.
  6. 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:

  1. Operator/admin creation uses existing Gateway operator/admin auth, not Studio bearer auth.
  2. Operator/admin creation does not require Governance Service binding lookup.
  3. Operator/admin project references use source=operator.
  4. Operator/admin references may omit governance_project_id and organization_id.
  5. Operator/admin snapshots and memberships use the same Gateway project APIs where possible, but authorization is satisfied by Gateway operator/admin auth.
  6. Studio-originated initialization must not silently fall back to operator mode.

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 = true
auth_service_base_url = "https://auth.example.com"
governance_service_base_url = "https://governance.example.com"
http_timeout = "10s"
project_snapshot_environment_fallback_enabled = true

auth_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 = true
integrity_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 = false

Validation requirements:

  1. Reject missing or malformed bearer credentials with 401 and code=missing_token or code=invalid_token.
  2. Call Auth Service POST /api/v1/auth/validate with the bearer token before project binding or RBAC authorization.
  3. Treat Auth Service 401 or valid=false responses as 401 and code=invalid_token.
  4. Treat Auth Service validation outages or malformed validation responses as fail-closed 503 responses.
  5. Keep this auth path scoped to adapter project APIs. Existing Gateway operator/system auth remains valid for standalone/admin usage.

Gateway calls Auth Service validation to verify the token and resolve current user metadata:

POST /api/v1/auth/validate
Authorization: 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 actionRequired Auth Service permission
List project referencesview_project_data
Read project referenceview_project_data
List project agents or membership evidenceview_project_data
Read current project plugin snapshotview_project_data
List project plugin snapshotsview_project_data
Validate project plugin snapshotedit_project_settings
Create or initialize project referenceedit_project_settings
Create or clone project plugin snapshotedit_project_settings
Activate project plugin snapshotedit_project_settings
Roll back project plugin snapshotedit_project_settings
Add or refresh membership evidenceedit_project_settings

Authorization rules:

  1. Control Plane calls Auth Service token validation before calling Auth Service RBAC.
  2. Existing project routes load the project reference by project_uuid before RBAC.
  3. Existing project routes use the stored governance_project_id for Auth Service project permission checks.
  4. Studio initialization routes resolve {project_uuid} through Governance Service first, then check Auth Service permission on the resolved integer project ID.
  5. If Control Plane has no project reference for a UUID, only initialization routes may proceed for Studio users.
  6. source=operator references and operator/admin workflows use Control Plane operator/admin authorization instead of Auth Service project RBAC.
  7. Auth Service outage during Studio-originated authorization fails closed.

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:

PurposeIntegrity Service APIControl Plane usage
Read content-addressed materialGET /store/v1/blob/{cid} or GET /store/v1/text/{cid}Dereference a credential or statement payload when Studio provides a CID.
Store canonical JSONPUT /store/v1/jcsUsed by Governance Studio or Integrity Service issuance flows, not by Gateway as source of truth.
Read a statement by IDGET /statements/v1/id/{id}Dereference a credential statement when Studio provides a statement ID.
Register statementsPUT /statements/v1 and PUT /statements/v1/batchUsed by issuance flows to store/index VC statements; Gateway should not register memberships.
Allocate revocation statusPOST /credentials/v1/allocateUsed before signing membership VCs; Gateway expects issued credentials to already be allocated.
Check revocation statusGET /credentials/v1/status?vcId={credential_id}Refresh Gateway’s indexed status_resolution before listing or accepting membership evidence.
Fetch status-list VCGET /credentials/v1/status-lists/{id}Optional proof/status-list verification path for follow-on VC validation helpers.
Revoke credentialPOST /credentials/v1/revoke/{credential_id}Owned by Governance Studio or operator issuance workflows, not Gateway adapter routes.
Governance VC examplesPOST /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:

  1. Gateway forwards the Studio bearer token when reading Integrity Service material for a Studio-originated request unless a service credential is explicitly configured.
  2. Gateway must not use Integrity Service token validation as a replacement for Auth Service token validation or Auth Service project RBAC.
  3. Gateway treats Integrity Service read/status failure as a structured dependency failure for membership evidence refresh, not as permission success.
  4. 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.

Follow-on implementation should add Gateway-native project routes under /v1/projects.

Common conventions:

  1. Successful item responses use { "item": ..., "correlation_id": "..." }.
  2. Successful list responses use { "items": [...], "correlation_id": "..." }.
  3. Default errors use { "code": "...", "message": "...", "correlation_id": "..." }.
  4. Validation errors may add validation_errors with field-level details.
  5. Snapshot routes require an explicit environment query or body field.
  6. Project UUID path values must be valid UUIDs.
GET /v1/projects?limit=50&source=governance_studio&status=active

Lists 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"
}

Project snapshot APIs must include environment so prod/staging/dev state is never ambiguous.

GET /v1/projects/{project_uuid}/plugin-snapshots?environment=production&limit=50

Lists 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=production

Returns 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/validate

Validates 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-snapshots

Creates 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}/activate

Activates a snapshot for (environment, project).

{
"environment": "production"
}

Response uses the current snapshot shape with resolution_scope=project.

POST /v1/projects/{project_uuid}/plugin-snapshots/rollback

Rolls 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.

GET /v1/projects/{project_uuid}/memberships?limit=50&status=valid

Lists 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}/memberships

Stores 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:

  1. At least one credential source is required: embedded credential, source_cid, statement_id, or statement_cid.
  2. credential_id and agent_did_hint are hints only. agent_did_hint is interpreted as a subject DID hint for GOV-294 compatibility; Control Plane derives authoritative values from the verified credential.
  3. When a reference is supplied, Control Plane dereferences it from Integrity Service before validation.
  4. When an embedded credential is supplied, Control Plane accepts it only when allow_embedded_credential_bootstrap=true or under operator/admin auth.
  5. Control Plane checks Integrity Service revocation status for the resolved credential_id when refresh_status=true or when cached status is stale.

Response uses the membership evidence item shape.

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=50

Lists 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:

  1. subject_did is always present and comes from verified membership evidence.
  2. 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.
  3. membership_state=active requires 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.
  4. 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:

  1. Existing environment-wide snapshots and active snapshot APIs continue to work unchanged.
  2. Project-scoped activation is keyed by environment + project_urn.
  3. Environment-default activation is keyed by environment with no project.
  4. Activating a project snapshot requires a known project reference.
  5. Activating a project snapshot with an environment that does not match the snapshot environment fails with 400 and code=invalid_environment.
  6. Only one current activation may exist for a given environment + project_urn.
  7. Project rollback candidates are derived from prior activations for the same environment + project_urn.
  8. Management API current resolution remains project-specific active snapshot first, then environment-default display fallback when project_snapshot_environment_fallback_enabled=true, then structured empty state.
  9. 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.
  10. Every project-scoped snapshot must contain exactly one enabled premade builtin.project_membership entry at project.bootstrap; validation, creation, activation, and rollback fail closed otherwise.
  11. Candidate snapshot selection is not membership authorization. Core candidate fields cannot populate authoritative project context.
  12. Only a Gateway-validated typed membership bootstrap result can establish authoritative projectctx.Context; arbitrary plugin context_writes cannot.
  13. One immutable snapshot ID/version is pinned across bootstrap, proxy pre/post, audit attribution, and request-end.
  14. Requests without any bound project candidate are explicitly projectless and pin the environment-default snapshot without running project bootstrap.

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.

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.

Integrity Service already provides the primitives needed for Governance Studio VCs:

  1. Content-addressed JSON storage through /store/v1/jcs and retrieval through /store/v1/blob/{cid} or /store/v1/text/{cid}.
  2. Statement registration and lookup through /statements/v1, /statements/v1/batch, and /statements/v1/id/{id}.
  3. Credential statement indexing by credential_subject in the credential_statements table.
  4. Revocation status allocation through /credentials/v1/allocate.
  5. Revocation status checks through /credentials/v1/status?vcId={credential_id}.
  6. Status-list VC retrieval through /credentials/v1/status-lists/{id}.
  7. 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:

  1. 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.
  2. Integrity Service allocates a revocation status entry for the credential.
  3. Integrity Service signs the allocated credential with the configured platform or trusted membership issuer DID.
  4. Integrity Service stores the signed VC or a credential statement and returns stable references such as credential_id, source_cid, statement_id, or statement_cid.
  5. Control Plane receives those references from Studio or a future Integrity Service membership discovery endpoint, verifies the VC, checks status, and indexes normalized fields.
{
"@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:

  1. credentialSubject.id is the Gateway agent DID or identity DID.
  2. credentialSubject.identity.type must be ProjectMembershipV1.
  3. credentialSubject.identity.project must equal the Control Plane project reference project_urn.
  4. credentialSubject.identity.role is normalized to a non-empty string array.
  5. issuer must match a configured trusted membership issuer or trusted platform DID.
  6. credentialStatus should include a revocation entry allocated by Integrity Service.
  7. validFrom and validUntil are evaluated by Control Plane during validation.
  8. 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:

  1. Subject DID.
  2. Project URN and normalized project UUID.
  3. Roles.
  4. Issuer DID.
  5. Credential ID.
  6. Validity window.
  7. Credential status entry metadata.
  8. Integrity Service source references under source_refs: source_cid, statement_id, statement_cid, or equivalent.
  9. Revocation status from Integrity Service under status_resolution.
  10. Stable validation status and validation errors.

Validation statuses should include valid, expired, not_yet_valid, revoked, invalid, and untrusted_issuer.

Control Plane index rules:

  1. Control Plane stores derived membership index fields and references, not the authoritative credential assignment.
  2. Control Plane may cache the credential payload for audit/debugging only if the storage policy allows it; source references remain canonical.
  3. Control Plane refreshes status_resolution from Integrity Service when listing memberships if cached status is stale. credential_status remains the normalized VC credentialStatus metadata parsed by GAT-213.
  4. Duplicate refresh of the same credential_id + project_urn + subject_did updates validation/status timestamps instead of creating a duplicate row.
  5. Revoked, expired, invalid, or untrusted credentials remain visible to authorized users with structured errors but are not treated as active membership.
  6. Standalone/operator mode may accept embedded credentials or operator-managed references, but must keep the same validation and index shape.

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:

  1. Push/reference mode: Governance Studio passes known ProjectMembershipV1 credential references to POST /v1/projects/{project_uuid}/memberships when memberships are created, refreshed, or displayed.
  2. 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.

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:

  1. Live in a dedicated project adapter API client module, separate from Governance Service clients.
  2. Use an environment-configurable Control Plane base URL for project management routes.
  3. Attach the current Auth Service bearer token.
  4. Pass raw project.uuid in project API paths.
  5. Include environment on every project snapshot request.
  6. Use typed request/response models matching this RFC.
  7. Pass Integrity Service membership credential references to Control Plane when Studio has them.
  8. Normalize project adapter errors for forbidden, not found, validation failure, and service unavailable cases.

The Gateway project area for GOV-295 must:

  1. Use the selected Governance project from existing project context.
  2. Show Control Plane project reference state and derive initialization state from Control Plane route responses and structured errors.
  3. Let authorized users initialize the project reference.
  4. Display Control Plane-indexed, Integrity-backed agent membership evidence.
  5. Display current project plugin snapshot state, including fallback state when applicable.
  6. List available snapshots and rollback candidates as read-only operational evidence.
  7. Treat snapshot validation, creation, activation, and rollback as config-as-code Gateway/plugin tooling workflows outside Studio.
  8. Handle loading, empty, forbidden, unavailable, validation error, and successful read states.

The UI must not:

  1. Store Gateway project policy state in Governance Service.
  2. Edit plugin artifacts directly as Governance Service records.
  3. Treat human project membership as agent project membership.
  4. Proxy Control Plane or Gateway APIs through Governance Service.
  5. Treat Gateway as the issuer or revocation authority for Governance Studio membership VCs.
  6. Render controls that validate, create, activate, or roll back Gateway plugin snapshots.
CaseStatusCodeExpected behavior
Missing bearer token on Studio route401missing_tokenControl Plane rejects before project lookup.
Auth Service reports token invalid401invalid_tokenControl Plane rejects before project binding or Auth Service RBAC.
Auth Service validation unavailable503auth_service_unavailableControl Plane fails closed before project binding or Auth Service RBAC.
Auth Service RBAC unavailable503auth_service_unavailableControl Plane fails closed for Studio-originated authorization.
User lacks required project permission403forbiddenControl Plane denies the action.
Project reference not found on read route404project_not_foundControl Plane returns not found or structured uninitialized state when the endpoint defines one.
Project reference not found on Studio init routevariesvariesControl Plane resolves Governance Service binding, checks RBAC, and creates only if verified.
Governance Service unavailable during Studio init503governance_service_unavailableControl Plane fails closed and does not create or update the project reference.
Governance Service binding does not match body hints409project_binding_mismatchControl Plane rejects and persists nothing.
Governance project archived409project_archivedControl Plane blocks Studio mutations based on Governance Service project state unless follow-on implementation explicitly allows.
Missing snapshot environment400invalid_environmentControl Plane requires explicit environment on project snapshot APIs.
Snapshot validation failure400snapshot_validation_failedControl Plane returns structured validation_errors.
Plugin store unavailable503plugin_store_unavailableControl Plane returns unavailable adapter state.
Integrity Service unavailable during membership read503integrity_service_unavailableControl Plane cannot refresh or dereference membership evidence and returns a structured dependency error.
Membership credential reference not found404membership_evidence_not_foundControl Plane returns not found and does not create an index row.
Membership credential project mismatch409membership_project_mismatchControl Plane rejects the credential because its ProjectMembershipV1 project does not match the route project.
Membership credential revoked409membership_revokedControl Plane indexes the revoked status but does not treat the credential as active membership.
Membership credential proof invalid400membership_invalidControl Plane returns structured validation errors.
No project or fallback snapshot exists200n/aCurrent snapshot route returns structured state=empty.
Operator/admin standalone route without Gov Servicevariesn/aExisting Control Plane operator/admin auth applies; Governance Service and Auth Service project RBAC are not needed.

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:

  1. GAT-209 project references: create, read, list, update, duplicate UUID/URN rejection, UUID validation, Studio binding verification, and operator standalone creation.
  2. 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.
  3. 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.bootstrap membership entry validation, missing environment rejection, activation environment mismatch, validation failure, activation, rollback, request pinning, and unchanged explicitly projectless environment-default behavior.
  4. 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.
  5. 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.
  6. 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.
  7. GOV-295 UI: reference initialization, Integrity-backed membership display, current snapshot display including fallback, activation, rollback, forbidden state, unavailable state, and validation error state.
  8. 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.
IssueRFC section implemented
GAT-209Project identity and reference contract in sections 5 and 7.1.
GAT-210Auth Service token validation and project authorization in section 6.
GAT-211Project-scoped plugin snapshot storage and resolution semantics in sections 7.2 and 8.
GAT-212Control Plane project snapshot management APIs and payloads in section 7.2.
GAT-213Integrity-backed ProjectMembershipV1 parsing and validation in section 9.
GAT-214Control Plane membership evidence reference ingestion, status refresh, and indexing in sections 7.3 and 9.
GOV-294Governance Studio project adapter client contract in section 10.
GOV-295Governance Studio project adapter UI contract in section 10.
GAT-215End-to-end adapter verification expectations in section 12.
  1. Current Auth Service project RBAC is integer-ID based. Control Plane should store governance_project_id on Studio-created project references rather than requiring Auth Service UUID claim changes.
  2. 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.
  3. Existing environment-scoped plugin snapshots must continue to work unchanged when project-scoped snapshots are added.
  4. Standalone deployments must be able to manage equivalent project references and snapshots without Governance Studio or Governance Service.
  5. 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.
  6. 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.
  7. 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.