Agent governance

Rollout runbook for turning on the agent governance plane of a deployment with Terraform, behind two flags.

Terraform wiring for the Agent Governance plane: the MCP/Agent token-validation middleware, its telemetry/GC background workers, and agent-key authentication on the backend. Two flags gate everything and both default to false — terraform plan is a no-op until an operator opts in:

Variable Default Effect when true
enable_agent_governance false Provisions the agent-mcp-token-signing-key-<env> secret + backend-SA secretAccessor grant (via module.core), and wires AGENT_GOVERNANCE_ENABLED=true + AGENT_MCP_TOKEN_SIGNING_KEY (secret ref) onto the backend Cloud Run revision.
agent_governance_enforce false Once enable_agent_governance is on: false = monitor (record the would-be decision, always proceed); true = enforce (403 deny_tool / 429 rate_limited / over_budget before the handler runs). Ignored while enable_agent_governance is false.

This is the same additive, code-gated pattern already used for Sifflet (sifflet_base_url) and Castor (enable_castor at the module level): a disabled flag means the secret + IAM resource are never created (count = 0) and the env vars are never emitted.

Rollout sequence#

With generate_secrets = true (a first install), there is no hand step. module.core writes the key's first version itself (48 random bytes, base64) and its secret-id output waits for that version and the reader grant, so one apply creates the secret, the version and the backend revision in order: skip steps 1 and 2 below. The key is then in the Terraform state (marked sensitive): the state bucket is as secret as the key. Turn generate_secrets on only at the first install; on a deployment whose key was added by hand, the generated version would become latest and invalidate every issued jma_ token.

The steps below are the hand path, for generate_secrets = false (the default, today's deployments):

  1. Provision the (empty) secret first — two-phase apply. AGENT_MCP_TOKEN_SIGNING_KEY is a Secret Manager reference with version = "latest"; Cloud Run resolves latest at revision-deploy time. If step 1 and the backend's env wiring land in the same apply, the secret has zero versions when Cloud Run tries to deploy the new revision, and the deploy fails (a real outage risk on an already-running backend). Sequence it as two applies, same as the Sifflet/Castor rollout:
    terraform apply -target=module.core -var enable_agent_governance=true ...
    
    This creates only the empty agent-mcp-token-signing-key-<env> secret + the backend SA's secretAccessor grant — the backend Cloud Run service is untouched at this point.
  2. Populate the secret (out-of-band — with generate_secrets = false the value never enters tfvars or state):
    openssl rand -base64 48 | gcloud secrets versions add \
      agent-mcp-token-signing-key-<env> \
      --project <project-id> --data-file=-
    
  3. Full apply / redeploy. Now that a version exists, apply without -target (or let CI apply normally):
    terraform apply -var enable_agent_governance=true -var agent_governance_enforce=false ...
    
    This deploys a new backend revision with AGENT_GOVERNANCE_ENABLED=true, AGENT_GOVERNANCE_ENFORCE=false (monitor mode), and the now-resolvable AGENT_MCP_TOKEN_SIGNING_KEY secret ref.
  4. Register agents. Once the revision is live, mint per-agent jma_ tokens from the Agent Governance console (frontend Agent Governance page → Agents tab, backed by POST /api/v1/agent-governance/agents). Before this step, "Register agent" 500s with "No signing key configured for agent tokens".
  5. Flip to enforce after a monitor soak. Watch the Agent Governance dashboard / telemetry for false-positive blocks in monitor mode, then set agent_governance_enforce = true and re-apply:
    terraform apply -var enable_agent_governance=true -var agent_governance_enforce=true ...
    
    Rollback is the same apply with agent_governance_enforce = false.

What turns on#

  • Middleware (services/backend/app/main.py) — AgentGovernanceMiddleware is only added to the ASGI stack when AGENT_GOVERNANCE_ENABLED is true; off is a pure pass-through with zero overhead.
  • Workers (services/backend/app/startup/orchestrator.py) — the telemetry-drain and retention-GC background tasks (start_agent_governance_workers / stop_agent_governance_workers) start and stop with the backend process, gated on the same flag.
  • Agent-key auth (services/backend/app/api/iam_deps.py) — bearer tokens that look like agent tokens (jma_...) are only accepted and resolved to the fixed, read-only MCP_AGENT_PRINCIPAL_EMAIL principal when the flag is on; otherwise they fail normal auth like any other unrecognized token.

Not included#

This wiring does not build or deploy the hosted MCP gateway (nistor-mcp). That is a separate, additive capability (mcp_gateway_image / MCP_GATEWAY_SERVICE_ACCOUNT) blocked on a container image, CI pipeline, and Cloud Run service that do not exist yet. Until that ships, the Connect tab's remote-MCP blocks in the frontend stay placeholder, and only first-party, directly-registered agent tokens work — there is no public agent-facing endpoint.

Operator notes#

  • Run it from your deployment repository. deployments/gcp/ is the reference composition: it deploys nothing by itself; a deployment repository sources it as a module pinned to a commit or tag (//deployments/gcp?ref=<tag>, as examples/deployment-repository does) and CI applies that repository's terraform/. Run this rollout sequence there: set enable_agent_governance (and later agent_governance_enforce) in that repository's tfvars, and check its current values before assuming a fresh rollout.
  • agent_governance_enforce is new. Before this change, the backend module hardcoded AGENT_GOVERNANCE_ENFORCE=false in Terraform regardless of any variable — there was no supported way to turn enforce mode on via Terraform in either root. infra/terraform/modules/services/backend now exposes agent_governance_enforce (default false, so existing applies are unaffected); the deployment repository's terraform/ needs the same two-line variable + pass-through added before step 5 above can be applied there.
  • terraform -chdir=deployments/gcp init -backend=false and terraform test run on this root in every pipeline (job terraform-fmt-validate, tests in deployments/gcp/tests/).

Edit this page on GitLab

↑ ↓ to moveEnter to openEsc to close