# Create Agent Key Source: https://docs.domesystems.ai/api/agents/create-agent-key POST /dome.agent.v1.AgentRegistry/CreateAgentKey # Delete Agent Source: https://docs.domesystems.ai/api/agents/delete-agent POST /dome.agent.v1.AgentRegistry/DeleteAgent # Get Agent Source: https://docs.domesystems.ai/api/agents/get-agent POST /dome.agent.v1.AgentRegistry/GetAgent # List Agent Keys Source: https://docs.domesystems.ai/api/agents/list-agent-keys POST /dome.agent.v1.AgentRegistry/ListAgentKeys # List Agents Source: https://docs.domesystems.ai/api/agents/list-agents POST /dome.agent.v1.AgentRegistry/ListAgents # Reactivate Agent Source: https://docs.domesystems.ai/api/agents/reactivate-agent POST /dome.agent.v1.AgentRegistry/ReactivateAgent # Register Agent Source: https://docs.domesystems.ai/api/agents/register-agent POST /dome.agent.v1.AgentRegistry/RegisterAgent # Revoke Agent Source: https://docs.domesystems.ai/api/agents/revoke-agent POST /dome.agent.v1.AgentRegistry/RevokeAgent # Revoke Agent Key Source: https://docs.domesystems.ai/api/agents/revoke-agent-key POST /dome.agent.v1.AgentRegistry/RevokeAgentKey # Rotate Agent Key Source: https://docs.domesystems.ai/api/agents/rotate-agent-key POST /dome.agent.v1.AgentRegistry/RotateAgentKey # Suspend Agent Source: https://docs.domesystems.ai/api/agents/suspend-agent POST /dome.agent.v1.AgentRegistry/SuspendAgent # Update Agent Source: https://docs.domesystems.ai/api/agents/update-agent POST /dome.agent.v1.AgentRegistry/UpdateAgent # Get Event Source: https://docs.domesystems.ai/api/audit/get-event POST /dome.audit.v1.Audit/GetEvent # Ingest Events Source: https://docs.domesystems.ai/api/audit/ingest-events POST /dome.audit.v1.Audit/IngestEvents # Query Events Source: https://docs.domesystems.ai/api/audit/query-events POST /dome.audit.v1.Audit/QueryEvents # Stream Events Source: https://docs.domesystems.ai/api/audit/stream-events POST /dome.audit.v1.Audit/StreamEvents # Delete Rules Source: https://docs.domesystems.ai/api/authorization/delete-rules POST /dome.authz.v1.Authorization/DeleteRules # Deploy Bundle Source: https://docs.domesystems.ai/api/authorization/deploy-bundle POST /dome.authz.v1.Authorization/DeployBundle # Evaluate Source: https://docs.domesystems.ai/api/authorization/evaluate POST /dome.authz.v1.Authorization/Evaluate # Explain Agent Capabilities Source: https://docs.domesystems.ai/api/authorization/explain-agent-capabilities POST /dome.authz.v1.Authorization/ExplainAgentCapabilities # Get Agent Effective Policy Source: https://docs.domesystems.ai/api/authorization/get-agent-effective-policy POST /dome.authz.v1.Authorization/GetAgentEffectivePolicy # Get Bundle Source: https://docs.domesystems.ai/api/authorization/get-bundle POST /dome.authz.v1.Authorization/GetBundle # Get Workspace Policies Source: https://docs.domesystems.ai/api/authorization/get-workspace-policies POST /dome.authz.v1.Authorization/GetAllAgentsEffectivePoliciesForWorkspace # List Bundles Source: https://docs.domesystems.ai/api/authorization/list-bundles POST /dome.authz.v1.Authorization/ListBundles # Rollback Bundle Source: https://docs.domesystems.ai/api/authorization/rollback-bundle POST /dome.authz.v1.Authorization/RollbackBundle # Validate Rules Source: https://docs.domesystems.ai/api/authorization/validate-rules POST /dome.authz.v1.Authorization/ValidateRules # Add Gateway LLM model Source: https://docs.domesystems.ai/api/gateway/add-gateway-llm-model POST /dome.gateway.v1.GatewayService/AddGatewayLLMModel # Add Gateway LLM pool Source: https://docs.domesystems.ai/api/gateway/add-gateway-llm-pool POST /dome.gateway.v1.GatewayService/AddGatewayLLMPool # Add Gateway tool Source: https://docs.domesystems.ai/api/gateway/add-gateway-tool POST /dome.gateway.v1.GatewayService/AddGatewayTool # Add Gateway tool source Source: https://docs.domesystems.ai/api/gateway/add-gateway-tool-source POST /dome.gateway.v1.GatewayService/AddGatewayToolSource # Create Gateway Source: https://docs.domesystems.ai/api/gateway/create-gateway POST /dome.gateway.v1.GatewayService/CreateGateway # Delete Gateway Source: https://docs.domesystems.ai/api/gateway/delete-gateway POST /dome.gateway.v1.GatewayService/DeleteGateway # Get Gateway Source: https://docs.domesystems.ai/api/gateway/get-gateway POST /dome.gateway.v1.GatewayService/GetGateway # List Gateways Source: https://docs.domesystems.ai/api/gateway/list-gateways POST /dome.gateway.v1.GatewayService/ListGateways # Remove Gateway LLM model Source: https://docs.domesystems.ai/api/gateway/remove-gateway-llm-model POST /dome.gateway.v1.GatewayService/RemoveGatewayLLMModel # Remove Gateway LLM pool Source: https://docs.domesystems.ai/api/gateway/remove-gateway-llm-pool POST /dome.gateway.v1.GatewayService/RemoveGatewayLLMPool # Remove Gateway tool Source: https://docs.domesystems.ai/api/gateway/remove-gateway-tool POST /dome.gateway.v1.GatewayService/RemoveGatewayTool # Remove Gateway tool source Source: https://docs.domesystems.ai/api/gateway/remove-gateway-tool-source POST /dome.gateway.v1.GatewayService/RemoveGatewayToolSource # Set Gateway all-agents grant Source: https://docs.domesystems.ai/api/gateway/set-gateway-all-agents-grant POST /dome.gateway.v1.GatewayService/SetGatewayAllAgentsGrant # Set Gateway default Source: https://docs.domesystems.ai/api/gateway/set-gateway-default POST /dome.gateway.v1.GatewayService/SetGatewayDefault # Set Gateway state Source: https://docs.domesystems.ai/api/gateway/set-gateway-state POST /dome.gateway.v1.GatewayService/SetGatewayState # Update Gateway Source: https://docs.domesystems.ai/api/gateway/update-gateway POST /dome.gateway.v1.GatewayService/UpdateGateway # Exchange Token Source: https://docs.domesystems.ai/api/identity/exchange-token POST /dome.identity.v1.Identity/ExchangeToken # API Reference Source: https://docs.domesystems.ai/api/index Connect RPC API for the Dome Platform The Dome Platform exposes a Connect RPC API organized into six services. Each service covers a distinct domain — agent registry, identity, authorization, audit, platform management, and simulation. All endpoints use `POST` with JSON request bodies. Authentication is via Bearer token from the [Identity service](/api/identity/exchange-token). Register, update, suspend, revoke agents and manage API keys. Token exchange and authentication. Deploy, evaluate, and manage Cedar rule bundles. Query, stream, and export audit events. Workspaces, backends, verification providers, and platform config. Replay candidate rules against audit history. ## Next steps Same operations show up in the CLI and MCP. Runtime calls go through Develop: * [CLI](/cli) for the equivalent command-line surfaces * [MCP](/reference/mcp) when an assistant should call the same operations * [Develop](/develop) for runtime credentials and Gateway endpoints * [Events](/reference/events) reference for audit envelope and catalog contracts # Assign Role Source: https://docs.domesystems.ai/api/management/assign-role POST /dome.mgmt.v1.Management/AssignRole # Begin LLM OAuth connect Source: https://docs.domesystems.ai/api/management/begin-llm-oauth-connect POST /dome.mgmt.v1.Management/BeginLLMOAuthConnect # Begin MCP server OAuth connect Source: https://docs.domesystems.ai/api/management/begin-mcp-server-oauth-connect POST /dome.mgmt.v1.Management/BeginMCPServerOAuthConnect # Check Tenant Permission Source: https://docs.domesystems.ai/api/management/check-tenant-permission POST /dome.mgmt.v1.Management/CheckTenantPermission # Create API Key Source: https://docs.domesystems.ai/api/management/create-api-key POST /dome.mgmt.v1.Management/CreateAPIKey # Create LLM model connection Source: https://docs.domesystems.ai/api/management/create-llm-model-connection POST /dome.mgmt.v1.Management/CreateLLMModelConnection # Create LLM pool Source: https://docs.domesystems.ai/api/management/create-llm-pool POST /dome.mgmt.v1.Management/CreateLLMPool # Create LLM pool member Source: https://docs.domesystems.ai/api/management/create-llm-pool-member POST /dome.mgmt.v1.Management/CreateLLMPoolMember # Create LLM quota Source: https://docs.domesystems.ai/api/management/create-llm-quota POST /dome.mgmt.v1.Management/CreateLLMQuota # Create MCP server connection Source: https://docs.domesystems.ai/api/management/create-mcp-server-connection POST /dome.mgmt.v1.Management/CreateMCPServerConnection # Create Tenant Source: https://docs.domesystems.ai/api/management/create-tenant POST /dome.mgmt.v1.Management/CreateTenant # Create Verification Provider Source: https://docs.domesystems.ai/api/management/create-verification-provider POST /dome.mgmt.v1.Management/CreateVerificationProvider # Create Workspace Source: https://docs.domesystems.ai/api/management/create-workspace POST /dome.mgmt.v1.Management/CreateWorkspace # Delete LLM model connection Source: https://docs.domesystems.ai/api/management/delete-llm-model-connection POST /dome.mgmt.v1.Management/DeleteLLMModelConnection # Delete LLM pool Source: https://docs.domesystems.ai/api/management/delete-llm-pool POST /dome.mgmt.v1.Management/DeleteLLMPool # Delete LLM pool member Source: https://docs.domesystems.ai/api/management/delete-llm-pool-member POST /dome.mgmt.v1.Management/DeleteLLMPoolMember # Delete LLM quota Source: https://docs.domesystems.ai/api/management/delete-llm-quota POST /dome.mgmt.v1.Management/DeleteLLMQuota # Delete MCP server connection Source: https://docs.domesystems.ai/api/management/delete-mcp-server-connection POST /dome.mgmt.v1.Management/DeleteMCPServerConnection # Delete Tenant Source: https://docs.domesystems.ai/api/management/delete-tenant POST /dome.mgmt.v1.Management/DeleteTenant # Delete Verification Provider Source: https://docs.domesystems.ai/api/management/delete-verification-provider POST /dome.mgmt.v1.Management/DeleteVerificationProvider # Delete Workspace Source: https://docs.domesystems.ai/api/management/delete-workspace POST /dome.mgmt.v1.Management/DeleteWorkspace # Disconnect LLM OAuth Source: https://docs.domesystems.ai/api/management/disconnect-llm-oauth POST /dome.mgmt.v1.Management/DisconnectLLMOAuth # Disconnect MCP server OAuth Source: https://docs.domesystems.ai/api/management/disconnect-mcp-server-oauth POST /dome.mgmt.v1.Management/DisconnectMCPServerOAuth # Get Config Source: https://docs.domesystems.ai/api/management/get-config POST /dome.mgmt.v1.Management/GetConfig # Get Grantable Permissions Source: https://docs.domesystems.ai/api/management/get-grantable-permissions POST /dome.mgmt.v1.Management/GetGrantablePermissions # Get LLM model connection Source: https://docs.domesystems.ai/api/management/get-llm-model-connection POST /dome.mgmt.v1.Management/GetLLMModelConnection # Get LLM model connection credential Source: https://docs.domesystems.ai/api/management/get-llm-model-connection-credential POST /dome.mgmt.v1.Management/GetLLMModelConnectionCredential # Get LLM pool Source: https://docs.domesystems.ai/api/management/get-llm-pool POST /dome.mgmt.v1.Management/GetLLMPool # Get LLM pool member Source: https://docs.domesystems.ai/api/management/get-llm-pool-member POST /dome.mgmt.v1.Management/GetLLMPoolMember # Get MCP server connection Source: https://docs.domesystems.ai/api/management/get-mcp-server-connection POST /dome.mgmt.v1.Management/GetMCPServerConnection # Get MCP server connection credential Source: https://docs.domesystems.ai/api/management/get-mcp-server-connection-credential POST /dome.mgmt.v1.Management/GetMCPServerConnectionCredential # Get Platform Status Source: https://docs.domesystems.ai/api/management/get-platform-status POST /dome.mgmt.v1.Management/GetPlatformStatus # Get Tenant Source: https://docs.domesystems.ai/api/management/get-tenant POST /dome.mgmt.v1.Management/GetTenant # Get User Role Source: https://docs.domesystems.ai/api/management/get-user-role POST /dome.mgmt.v1.Management/GetUserRole # Get Verification Provider Source: https://docs.domesystems.ai/api/management/get-verification-provider POST /dome.mgmt.v1.Management/GetVerificationProvider # Get Workspace Source: https://docs.domesystems.ai/api/management/get-workspace POST /dome.mgmt.v1.Management/GetWorkspace # Get Workspace Act-As Source: https://docs.domesystems.ai/api/management/get-workspace-act-as POST /dome.mgmt.v1.Management/GetWorkspaceActAs # List API Keys Source: https://docs.domesystems.ai/api/management/list-api-keys POST /dome.mgmt.v1.Management/ListAPIKeys # List Catalog Services Source: https://docs.domesystems.ai/api/management/list-catalog-services POST /dome.mgmt.v1.Management/ListCatalogServices # List Egress Connections Source: https://docs.domesystems.ai/api/management/list-egress-connections POST /dome.mgmt.v1.Management/ListEgressConnections # List Joinable Tenants Source: https://docs.domesystems.ai/api/management/list-joinable-tenants POST /dome.mgmt.v1.Management/ListJoinableTenants # List LLM model connections Source: https://docs.domesystems.ai/api/management/list-llm-model-connections POST /dome.mgmt.v1.Management/ListLLMModelConnections # List LLM pool members Source: https://docs.domesystems.ai/api/management/list-llm-pool-members POST /dome.mgmt.v1.Management/ListLLMPoolMembers # List LLM pools Source: https://docs.domesystems.ai/api/management/list-llm-pools POST /dome.mgmt.v1.Management/ListLLMPools # List LLM quotas Source: https://docs.domesystems.ai/api/management/list-llm-quotas POST /dome.mgmt.v1.Management/ListLLMQuotas # List MCP server connections Source: https://docs.domesystems.ai/api/management/list-mcp-server-connections POST /dome.mgmt.v1.Management/ListMCPServerConnections # List Tenants Source: https://docs.domesystems.ai/api/management/list-tenants POST /dome.mgmt.v1.Management/ListTenants # List Users Source: https://docs.domesystems.ai/api/management/list-users POST /dome.mgmt.v1.Management/ListUsers # List Verification Providers Source: https://docs.domesystems.ai/api/management/list-verification-providers POST /dome.mgmt.v1.Management/ListVerificationProviders # List Workspace Memberships Source: https://docs.domesystems.ai/api/management/list-workspace-memberships POST /dome.mgmt.v1.Management/ListWorkspaceMemberships # List Workspaces Source: https://docs.domesystems.ai/api/management/list-workspaces POST /dome.mgmt.v1.Management/ListWorkspaces # Probe MCP server auth Source: https://docs.domesystems.ai/api/management/probe-mcp-server-auth POST /dome.mgmt.v1.Management/ProbeMCPServerAuth # Provision Sandbox Tenant Source: https://docs.domesystems.ai/api/management/provision-sandbox-tenant POST /dome.mgmt.v1.Management/ProvisionSandboxTenant # Provision Sandbox Workspace Source: https://docs.domesystems.ai/api/management/provision-sandbox-workspace POST /dome.mgmt.v1.Management/ProvisionSandboxWorkspace # Remove Role Source: https://docs.domesystems.ai/api/management/remove-role POST /dome.mgmt.v1.Management/RemoveRole # Remove Workspace Membership Source: https://docs.domesystems.ai/api/management/remove-workspace-membership POST /dome.mgmt.v1.Management/RemoveWorkspaceMembership # Resolve Permissions Source: https://docs.domesystems.ai/api/management/resolve-permissions POST /dome.mgmt.v1.Management/ResolvePermissions # Revoke API Key Source: https://docs.domesystems.ai/api/management/revoke-api-key POST /dome.mgmt.v1.Management/RevokeAPIKey # Set default LLM pool Source: https://docs.domesystems.ai/api/management/set-default-llm-pool POST /dome.mgmt.v1.Management/SetDefaultLLMPool # Set Workspace Membership Source: https://docs.domesystems.ai/api/management/set-workspace-membership POST /dome.mgmt.v1.Management/SetWorkspaceMembership # Sync MCP server connection tools Source: https://docs.domesystems.ai/api/management/sync-mcp-server-connection-tools POST /dome.mgmt.v1.Management/SyncMCPServerConnectionTools # Update Config Source: https://docs.domesystems.ai/api/management/update-config POST /dome.mgmt.v1.Management/UpdateConfig # Update LLM model connection Source: https://docs.domesystems.ai/api/management/update-llm-model-connection POST /dome.mgmt.v1.Management/UpdateLLMModelConnection # Update LLM pool Source: https://docs.domesystems.ai/api/management/update-llm-pool POST /dome.mgmt.v1.Management/UpdateLLMPool # Update LLM pool member Source: https://docs.domesystems.ai/api/management/update-llm-pool-member POST /dome.mgmt.v1.Management/UpdateLLMPoolMember # Update LLM quota Source: https://docs.domesystems.ai/api/management/update-llm-quota POST /dome.mgmt.v1.Management/UpdateLLMQuota # Update MCP server connection Source: https://docs.domesystems.ai/api/management/update-mcp-server-connection POST /dome.mgmt.v1.Management/UpdateMCPServerConnection # Update Verification Provider Source: https://docs.domesystems.ai/api/management/update-verification-provider POST /dome.mgmt.v1.Management/UpdateVerificationProvider # Update Workspace Act-As Source: https://docs.domesystems.ai/api/management/update-workspace-act-as POST /dome.mgmt.v1.Management/UpdateWorkspaceActAs # Update Workspace Freshness Window Source: https://docs.domesystems.ai/api/management/update-workspace-freshness-window POST /dome.mgmt.v1.Management/UpdateWorkspaceFreshnessWindow # Update workspace LLM filter window Source: https://docs.domesystems.ai/api/management/update-workspace-llm-filter-window POST /dome.mgmt.v1.Management/UpdateWorkspaceLLMFilterWindow # Get Result Source: https://docs.domesystems.ai/api/simulation/get-result POST /dome.simulation.v1.Simulation/GetResult # Simulate Source: https://docs.domesystems.ai/api/simulation/simulate POST /dome.simulation.v1.Simulation/Simulate # dome agents Source: https://docs.domesystems.ai/cli/connect/agents Manage agent registrations, lifecycle, and API keys | Command | | | ------------------------------- | --------------------------------------------------- | | [Register](#register) | `dome agents register` | | [List](#list) | `dome agents list` | | [Get](#get) | `dome agents get [name-or-id]` | | [Update](#update) | `dome agents update [name-or-id]` | | [Suspend](#suspend) | `dome agents suspend [name-or-id]` | | [Reactivate](#reactivate) | `dome agents reactivate [name-or-id]` | | [Revoke](#revoke) | `dome agents revoke [name-or-id]` | | [Delete](#delete) | `dome agents delete [name-or-id]` | | [Create key](#create-key) | `dome agents create-key [name-or-id]` | | [List keys](#list-keys) | `dome agents list-keys [name-or-id]` | | [Rotate key](#rotate-key) | `dome agents rotate-key [name-or-id] [key-name]` | | [Revoke key](#revoke-key) | `dome agents revoke-key [name-or-id] [key-name]` | | [Assign-policy](#assign-policy) | `dome agents assign-policy [name-or-id] [files...]` | | [Get-policies](#get-policies) | `dome agents get-policies [name-or-id]` | *** ## Register `dome agents register` Register a new agent in the current workspace. Pass fields with per-field flags or load the full definition from a JSON/YAML file with `--file`. ```bash theme={"system"} dome agents register --name [flags] dome agents register --file ``` | Flag | Type | Required | Description | | ------------------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--file`, `-f` | string | Yes\* | Path to a JSON/YAML file with the full agent definition. Use `-` for stdin. Mutually exclusive with per-field flags | | `--name` | string | Yes\* | Agent name | | `--capabilities` | string\[] | No | Custom capability strings (repeatable). System capabilities `llm:invoke`, `mcp:call`, and `mcp:discover` are derived automatically from the selections below | | `--parent-id` | string | No | Parent agent ID | | `--metadata` | key=value | No | Agent metadata (repeatable) | | `--if-not-exists` | bool | No | Return existing agent if name is taken | | `--pool` | string\[] | No | LLM pool the agent may route to (repeatable). Emits a `permit` against `Dome::LLMModel` matched via `resource.pool == ""` in the agent's generated bundle | | `--model` | string\[] | No | LLM model connection the agent may invoke directly (repeatable, advanced — pools are preferred). Emits a `permit` against `Dome::LLMModel` | | `--tool` | string\[] | No | MCP tool the agent may call, as `/` (repeatable). Emits `mcp:call` and `mcp:discover` permits against `Dome::MCPTool` | | `--gateway` | string\[] | No | [Gateway](/connect/gateways) ID (UUID) the agent may reach (repeatable). Admits the agent at the Gateway endpoint and generates a membership-conditioned permit. | | `--actas-allowed-group` | string\[] | No | Group the agent's verified act-as identity may belong to (repeatable). Emits a `forbid` unless the verified group matches | | `--actas-allowed-email` | string\[] | No | Email the agent may act on behalf of (repeatable). Emits a `forbid` unless the verified email matches | | `--actas-allowed-subject` | string\[] | No | Subject the agent may act on behalf of (repeatable). Emits a `forbid` unless the verified subject matches | | `--actas-method` | string | No | Act-as verification: `none`, `oidc`, `hmac` | | `--actas-required` | bool | No | Require act-as header on every request | | `--actas-provider` | string | No | Workspace verification provider ID | | `--actas-oidc-url` | string | No | Inline OIDC discovery URL | | `--actas-hmac-secret` | string | No | Inline HMAC shared secret | | `--actas-config-name` | string | No | Label for agent-specific verification config | | `--pool` | string\[] | No | LLM pool name the agent may route to (repeatable) | | `--model` | string\[] | No | LLM model connection name the agent may invoke directly (advanced; repeatable) | | `--tool` | string\[] | No | MCP tool the agent may call as `/` (repeatable) | | `--actas-allowed-group` | string\[] | No | Group claim the agent's verified act-as identity may belong to (repeatable) | | `--actas-allowed-email` | string\[] | No | Email the agent may act on behalf of (repeatable) | | `--actas-allowed-subject` | string\[] | No | Subject the agent may act on behalf of (repeatable) | Populating `--pool`, `--model`, or `--tool` triggers generation of an agent-scope `kind='generated'` rule bundle and auto-derives the corresponding capabilities (`llm:invoke`, `mcp:call`, `mcp:discover`). Leaving all three `--actas-allowed-*` lists empty means "any verified identity is acceptable"; populating any one restricts the agent via a forbid-unless rule. Pool, model, tool, and `--actas-allowed-*` flags populate the agent's generated rule bundle. Update operations that change any of these fields require `rules.deploy` in addition to `agent.register`. \* Provide either `--name` (per-field mode) or `--file`. The two modes cannot be combined. ### Register from a file Use `--file` to register from a single JSON or YAML document. The file shape mirrors the `RegisterAgentRequest` proto — the same schema the SDKs and Terraform provider consume — so a typo in a field name fails fast rather than silently dropping. The workspace is always taken from the active context; a `workspace_id` in the file is ignored. ```yaml agent.yaml theme={"system"} name: claude-research capabilities: [read] allowed_pool_names: [openai-prod] allowed_tools: [github/list_repos] actas_config: method: oidc required: true oidc_discovery_url: https://idp.example.com/.well-known/openid-configuration ``` ```bash theme={"system"} dome agents register --file agent.yaml # JSON works too dome agents register -f agent.json # Read from stdin printf '{"name":"x"}' | dome agents register -f - ``` ## List `dome agents list` List agents in the current workspace. ```bash theme={"system"} dome agents list [flags] ``` | Flag | Type | Description | | ---------- | ------ | ----------------------------------------------------------------- | | `--status` | string | Filter by status: `active`, `provisioned`, `suspended`, `revoked` | | `--limit` | int | Maximum results (default: 100) | ## Get `dome agents get [name-or-id]` Get details for a specific agent. Pass `--gateway ` to select an accessible Gateway and include its complete protocol endpoints. Without the flag, the command returns agent metadata only. ```text Output theme={"system"} Name: data-pipeline-agent ID: a1b2c3d4-… Status: active Gateway ID: {{GATEWAY_ID}} Gateway URL: https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}} MCP URL: https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}}/mcp ``` | Flag | Type | Description | | ----------- | ------ | ---------------------------------------------------------------------------------------------- | | `--gateway` | string | Gateway ID used to resolve endpoints; required when the agent can access more than one Gateway | ## Update `dome agents update [name-or-id]` Update an agent's capabilities, metadata, or act-as config. | Flag | Type | Description | | ------------------------- | --------- | ------------------------------------------------------------------------------------ | | `--capabilities` | string\[] | Updated custom capabilities | | `--metadata` | key=value | Updated metadata | | `--pool` | string\[] | Replace the agent's allowed LLM pool list | | `--model` | string\[] | Replace the agent's allowed direct-model list | | `--tool` | string\[] | Replace the agent's allowed MCP tool list (`/`) | | `--gateway` | string\[] | Replace the agent's allowed [Gateway](/connect/gateways) ID list (UUIDs; repeatable) | | `--actas-allowed-group` | string\[] | Replace the agent's act-as allowed-groups list | | `--actas-allowed-email` | string\[] | Replace the agent's act-as allowed-emails list | | `--actas-allowed-subject` | string\[] | Replace the agent's act-as allowed-subjects list | | `--actas-method` | string | Act-as verification: `none`, `oidc`, `hmac` | | `--actas-required` | bool | Require act-as header | | `--actas-provider` | string | Workspace verification provider ID | | `--actas-oidc-url` | string | Inline OIDC discovery URL | | `--actas-hmac-secret` | string | Inline HMAC shared secret | | `--actas-config-name` | string | Label for agent-specific verification config | | `--pool` | string\[] | Replace the agent's allowed LLM pool list (repeatable) | | `--model` | string\[] | Replace the agent's allowed direct-model list (advanced; repeatable) | | `--tool` | string\[] | Replace the agent's allowed MCP tool list (repeatable; each `/`) | | `--actas-allowed-group` | string\[] | Replace the agent's act-as allowed-groups list (repeatable) | | `--actas-allowed-email` | string\[] | Replace the agent's act-as allowed-emails list (repeatable) | | `--actas-allowed-subject` | string\[] | Replace the agent's act-as allowed-subjects list (repeatable) | The `--pool`, `--model`, `--tool`, and `--actas-allowed-*` flags use **PUT semantics**: whatever you send IS the new state, including an empty value meaning "clear". To update only one field, get the agent first and pass the current values back. Changing any of these four-pillar fields (or `--actas-method` / `--actas-required`) regenerates the agent's `kind='generated'` rule bundle and therefore requires the `rules.deploy` permission in addition to `agent.register`. Identity-only updates (name, metadata, capabilities) pass through on `agent.register` alone. Changing any pool, model, tool, or `--actas-allowed-*` field regenerates the agent's rule bundle and requires `rules.deploy` in addition to `agent.register`. Identity-only updates (name, metadata, capabilities) require only `agent.register`. ## Suspend `dome agents suspend [name-or-id]` Suspend an agent temporarily. | Flag | Type | Description | | ----------- | ------ | ------------------------- | | `--reason` | string | Suspension reason | | `--cascade` | bool | Also suspend child agents | ## Reactivate `dome agents reactivate [name-or-id]` Reactivate a suspended agent. ## Revoke `dome agents revoke [name-or-id]` Permanently revoke an agent. Always cascades to all descendants. | Flag | Type | Description | | ---------- | ------ | ----------------- | | `--reason` | string | Revocation reason | ## Delete `dome agents delete [name-or-id]` Permanently hard-delete a revoked agent and all its data. Requires the agent to be in a revoked state. | Flag | Type | Description | | ----------- | ------ | ----------------------------- | | `--reason` | string | Deletion reason | | `--cascade` | bool | Also delete descendant agents | ## Assign-policy `dome agents assign-policy [name-or-id] [files...]` Deploy Cedar rules scoped to a specific agent. ## Get-policies `dome agents get-policies [name-or-id]` Get Cedar rules assigned to an agent. ## Agent API Keys ### Create-key `dome agents create-key [name-or-id]` Create an agent authentication key. Prints the token and the selected Gateway's complete endpoints once. | Flag | Type | Required | Description | | ----------- | ------ | -------- | ---------------------------------------------------------------------------------------------- | | `--name` | string | Yes | Key name | | `--gateway` | string | No | Gateway ID used to resolve endpoints; required when the agent can access more than one Gateway | ```text Output theme={"system"} ✓ API key "default" created for agent my-first-agent ✓ Token: dome_… Gateway ID: {{GATEWAY_ID}} Gateway URL: https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}} MCP URL: https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}}/mcp ℹ Save this token — it will not be shown again. ``` Use the URL for the protocol your application speaks. JSON output returns `gatewayId` and `gatewayEndpoints`. ### List-keys `dome agents list-keys [name-or-id]` List API keys for an agent. ### Rotate-key `dome agents rotate-key [name-or-id] [key-name]` Rotate an agent's API key — revokes the old secret and issues a new one. Use `--gateway ` to select the Gateway when the agent can access more than one. Output includes `gatewayId` and `gatewayEndpoints`. ### Revoke-key `dome agents revoke-key [name-or-id] [key-name]` Revoke an agent's API key permanently. # dome gateway Source: https://docs.domesystems.ai/cli/connect/gateway Manage Gateways, their members, agent access grants, and cost quotas `dome gateway` manages workspace-scoped groupings of callable resources — MCP tool connections, LLM pools, and direct LLM model connections — plus the agent access grants and cost caps that govern each grouping. Refer to [Gateways](/connect/gateways) for the concept model. Resource-side mirrors live under [`dome tool gateways`](/cli/connect/tools#gateways) and [`dome model pool gateways`](/cli/connect/models#pool-gateways) (each with `list`, `add`, `remove`). Attach at create time with `--gateway` on `dome tool add`, `dome model add`, and `dome model pool create`. | Command | | | ------------------------------------------- | --------------------------------------------------------- | | [List](#list) | `dome gateway list` | | [Get](#get) | `dome gateway get ` | | [Create](#create) | `dome gateway create ` | | [Update](#update) | `dome gateway update ` | | [Enable](#enable) | `dome gateway enable ` | | [Disable](#disable) | `dome gateway disable ` | | [Set default](#set-default) | `dome gateway set-default ` | | [Delete](#delete) | `dome gateway delete ` | | [Tools add](#tools-add) | `dome gateway tools add ` | | [Tools remove](#tools-remove) | `dome gateway tools remove ` | | [Tool-sources add](#tool-sources-add) | `dome gateway tool-sources add ` | | [Tool-sources remove](#tool-sources-remove) | `dome gateway tool-sources remove ` | | [Model-pools add](#model-pools-add) | `dome gateway model-pools add ` | | [Model-pools remove](#model-pools-remove) | `dome gateway model-pools remove ` | | [Models add](#models-add) | `dome gateway models add ` | | [Models remove](#models-remove) | `dome gateway models remove ` | | [Access list](#access-list) | `dome gateway access list ` | | [Access grant](#access-grant) | `dome gateway access grant ` | | [Access revoke](#access-revoke) | `dome gateway access revoke ` | | [Access grant-all](#access-grant-all) | `dome gateway access grant-all ` | | [Access revoke-all](#access-revoke-all) | `dome gateway access revoke-all ` | | [Quota list](#quota-list) | `dome gateway quota list [gateway]` | | [Quota set](#quota-set) | `dome gateway quota set --limit ` | | [Quota update](#quota-update) | `dome gateway quota update [flags]` | | [Quota remove](#quota-remove) | `dome gateway quota rm ` | *** ## List `dome gateway list` List Gateways in the active workspace: name, default marker, ID, state, and canonical endpoint path (`/gateways/`). Alias: `ls`. The endpoint column shows one path per Gateway — the single URL every client points at. The Dome SDK and Anthropic clients use it as-is; MCP clients append `/mcp`; OpenAI-compatible clients append `/v1`. Refer to [Endpoint shape](/connect/gateways#build-the-runtime-endpoint). ## Get `dome gateway get ` Show a Gateway's members — tools, tool sources (connections), LLM pools, and direct model connections — plus its canonical endpoint path (`/gateways/`), state, default marker, and grant settings. ## Create `dome gateway create ` Create a Gateway. Starts active with empty membership; attach resources with the membership commands or `--gateway` at resource create. | Flag | Type | Required | Default | Description | | --------------- | ------ | -------- | ------- | -------------------------- | | `--description` | string | No | — | Human-readable description | ```bash theme={"system"} dome gateway create prod-tools --description "Production MCP tools for the ops agent" ``` ## Update `dome gateway update ` Rename or edit the description. Toggle state with [`enable`](#enable) / [`disable`](#disable). | Flag | Type | Required | Default | Description | | --------------- | ------ | -------- | ------- | ---------------- | | `--name` | string | No | — | New Gateway name | | `--description` | string | No | — | New description | ## Enable `dome gateway enable ` Re-enable a disabled Gateway. Membership and grants are preserved. ## Disable `dome gateway disable ` Disable a Gateway. Its endpoint fails closed until re-enabled. ## Set default `dome gateway set-default ` Mark a Gateway as the workspace default — the pre-checked (but overridable) Gateway in the dashboard's tool, pool, and model create dialogs. A suggestion only; it enforces no membership and grants no access. Setting a new default clears the previous one. A disabled default is never pre-selected. ## Delete `dome gateway delete ` Delete a Gateway. Its memberships are removed, but the underlying tools, pools, and model connections are unchanged. Aliases: `rm`, `remove`. Callers pointed at this Gateway's `/gateways//…` endpoint will fail closed after deletion. Repoint them at another Gateway first. *** ## Tools add `dome gateway tools add ` Add a single MCP tool to a Gateway. `` is the observed tool's ID from [`dome tool catalog list`](/cli/connect/tools#catalog-list). To include every tool from a connection — with newly discovered tools joining automatically — use [`tool-sources add`](#tool-sources-add) instead. ## Tools remove `dome gateway tools remove ` Remove a single MCP tool. Alias: `rm`. ## Tool-sources add `dome gateway tool-sources add ` Add an MCP server connection as a tool source. The Gateway includes every tool observed on the connection; newly discovered tools propagate automatically. ## Tool-sources remove `dome gateway tool-sources remove ` Remove the tool source. Tools that joined only through this source stop being included. Alias: `rm`. ## Model-pools add `dome gateway model-pools add ` Add an LLM pool. The Gateway exposes the pool's models at its `/gateways//v1` endpoint. ## Model-pools remove `dome gateway model-pools remove ` Alias: `rm`. ## Models add `dome gateway models add ` Add a direct (un-pooled) LLM model connection. The Gateway exposes the model without going through a pool — useful when a single provider serves one purpose. ## Models remove `dome gateway models remove ` Alias: `rm`. *** ## Access list `dome gateway access list ` List agents and whether each is currently granted access to the Gateway. Revoked agents are excluded — they cannot exchange their API key for a JWT, so they can never reach a gateway. Suspended agents remain in the list because suspension is reversible. ```bash theme={"system"} dome gateway access list prod-tools --json ``` ## Access grant `dome gateway access grant ` Grant one agent access. Requires the agent-access permission set (`agent.register` + `rules.deploy`). ## Access revoke `dome gateway access revoke ` Revoke one agent's access. ## Access grant-all `dome gateway access grant-all ` Grant every current and future workspace agent access to this Gateway. Requires `rules.deploy` + `gateway.manage`. ## Access revoke-all `dome gateway access revoke-all ` Clear the all-agents grant. Per-agent grants are unaffected. *** ## Quota list `dome gateway quota list [gateway-name-or-id]` List Gateway cost quotas (USD spend caps on a whole Gateway's aggregate LLM spend). Optionally filter to one Gateway. Alias: `ls`. ## Quota set `dome gateway quota set --limit ` Create a USD spend cap that covers every LLM call routed through the Gateway's member pools and direct connections. Once window spend exceeds the cap, calls to the Gateway return HTTP `429`. | Flag | Type | Required | Default | Description | | ------------ | ------ | -------- | --------- | ---------------------------------------------- | | `--limit` | string | Yes | — | Spend cap in US dollars, e.g. `500` or `12.50` | | `--window` | string | No | `monthly` | Reset window: `daily` or `monthly` | | `--name` | string | No | — | Human-readable quota label | | `--disabled` | bool | No | `false` | Create in a disabled state | ```bash theme={"system"} dome gateway quota set prod-tools --limit 500 --window monthly --name "prod-monthly" ``` ## Quota update `dome gateway quota update [flags]` Update a Gateway quota's name, limit, window, or enabled state. The Gateway subject is immutable; create a new quota to cap a different Gateway. | Flag | Type | Description | | ----------- | ------ | -------------------------------------- | | `--name` | string | New label | | `--limit` | string | New spend cap in US dollars | | `--window` | string | New reset window: `daily` or `monthly` | | `--enabled` | bool | Enable or disable enforcement | ## Quota remove `dome gateway quota rm ` Remove a Gateway cost quota. Aliases: `remove`, `delete`. Refuses IDs of non-Gateway quotas — use [`dome model quota rm`](/cli/connect/models#quota-remove) for those. # dome model Source: https://docs.domesystems.ai/cli/connect/models Manage LLM model connections and routing pools `dome model` registers LLM upstreams the gateway proxies — Anthropic, OpenAI, Azure OpenAI, Google, Bedrock, hosted OpenAI-compatible vendors (Groq, Mistral, Together, Fireworks, DeepSeek, xAI, Perplexity, Cohere, OpenRouter, Cerebras, NVIDIA, DeepInfra, SambaNova, AI21, Databricks), and self-hosted endpoints — and groups them into routing pools that fan out across providers with priority, weighting, and failover. | Command | | | ----------------------------------------- | ------------------------------------------------------------- | | [List](#list) | `dome model list [workspace-id-or-slug]` | | [Get](#get) | `dome model get ` | | [Add](#add) | `dome model add ` | | [Update](#update) | `dome model update ` | | [Remove](#remove) | `dome model rm ` | | [OAuth connect](#oauth-connect) | `dome model oauth-connect ` | | [OAuth disconnect](#oauth-disconnect) | `dome model oauth-disconnect ` | | [Pool create](#pool-create) | `dome model pool create ` | | [Pool list](#pool-list) | `dome model pool list [workspace-id-or-slug]` | | [Pool get](#pool-get) | `dome model pool get ` | | [Pool update](#pool-update) | `dome model pool update ` | | [Pool set default](#pool-set-default) | `dome model pool set-default ` | | [Pool move](#pool-move) | `dome model pool move --before \| --after ` | | [Pool remove](#pool-remove) | `dome model pool rm ` | | [Pool gateways](#pool-gateways) | `dome model pool gateways list\|add\|remove [gateway]` | | [Pool member add](#pool-member-add) | `dome model pool member add ` | | [Pool member list](#pool-member-list) | `dome model pool member list ` | | [Pool member update](#pool-member-update) | `dome model pool member update ` | | [Pool member remove](#pool-member-remove) | `dome model pool member rm ` | | [Quota list](#quota-list) | `dome model quota list` | | [Quota set](#quota-set) | `dome model quota set --subject … --limit ` | | [Quota remove](#quota-remove) | `dome model quota rm ` | *** ## List `dome model list [workspace-id-or-slug]` List LLM models in the workspace. Aliases: `ls`. ## Get `dome model get ` Show details for one model, including provider config and Cedar attributes. ## Add `dome model add ` Register an LLM model upstream. The shared API key, when supplied, is stored under the provider's managed auth header — `Authorization: Bearer …` for OpenAI, Bedrock, and OpenAI-compatible hosted vendors; `x-api-key` for Anthropic; `api-key` for Azure OpenAI; `x-goog-api-key` for Google. When `--endpoint` is omitted, the CLI prefills the vendor's default base URL from the provider registry. | Flag | Type | Required | Default | Description | | ------------------------ | ------------------- | -------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `--provider` | string | Yes | — | `openai`, `anthropic`, `google`, `bedrock`, `azure_openai`, `mistral`, `groq`, `together`, `fireworks`, `deepseek`, `xai`, `perplexity`, `cohere`, `openrouter`, `cerebras`, `nvidia`, `deepinfra`, `sambanova`, `ai21`, `databricks`, `openai_compatible`, `custom`. Refer to [Supported providers](/reference/resources/models#providers) reference. | | `--model` | string | No | — | Model id (e.g. `gpt-4o-mini`, `claude-3-5-sonnet-20241022`) | | `--endpoint` | string | No | registry default | Provider endpoint URL. Required for `azure_openai`, `bedrock`, `databricks`, `openai_compatible`, and `custom`. | | `--auth-method` | string | No | `api-key` | `none`, `api-key`, `oauth` | | `--credential-type` | string | No | `shared` | `none`, `shared`, `per-user` | | `--api-key` | string | No | — | API key (use with `--auth-method=api-key --credential-type=shared`) | | `--provider-config` | JSON | No | — | Full `provider_config` as JSON; merges with `--model`/`--endpoint` | | `--attributes` | JSON | No | — | Cedar attributes as JSON object | | `--filter-window-bytes` | uint32 | No | `0` | Outbound filter window in bytes for this connection. `0` inherits the workspace floor; the engine takes the max of workspace, connection, and per-request. | | `--filter-window-tokens` | uint32 | No | `0` | Outbound filter window in tokens for this connection. `0` inherits the workspace floor. | | `--gateway` | string (repeatable) | No | — | Attach directly to the named [Gateway](/connect/gateways) (name or ID). Without it the model is unreachable until attached. | ```bash title="Anthropic shared API key" theme={"system"} dome model add claude-sonnet \ --provider anthropic \ --model claude-3-5-sonnet-20241022 \ --api-key "$ANTHROPIC_API_KEY" ``` ```bash title="Groq (endpoint prefilled from registry)" theme={"system"} dome model add fast-groq \ --provider groq \ --model llama-3.3-70b-versatile \ --api-key "$GROQ_API_KEY" ``` ```bash title="Azure OpenAI" theme={"system"} dome model add gpt-4o \ --provider azure_openai \ --model gpt-4o \ --endpoint https://my-resource.openai.azure.com \ --api-key "$AZURE_OPENAI_KEY" \ --provider-config '{"api_version":"2024-08-01-preview","deployment":"gpt-4o-prod"}' ``` ## Update `dome model update ` Update a model. Only provided flags change. Provider is immutable post-create — switch by adding a new connection. | Flag | Type | Description | | ------------------------ | ------ | ---------------------------------------------------------------------------------------- | | `--name` | string | New connection name | | `--model` | string | New model id | | `--endpoint` | string | New endpoint URL | | `--auth-method` | string | New auth method | | `--credential-type` | string | New credential scope | | `--api-key` | string | Replace the shared API key | | `--provider-config` | JSON | Replace `provider_config` wholesale | | `--attributes` | JSON | Replace Cedar attributes (pass `'{}'` to clear) | | `--filter-window-bytes` | uint32 | Per-connection outbound filter window in bytes. `0` clears back to the workspace floor. | | `--filter-window-tokens` | uint32 | Per-connection outbound filter window in tokens. `0` clears back to the workspace floor. | ## Remove `dome model rm ` Remove an LLM model. Aliases: `remove`, `delete`. ## OAuth connect `dome model oauth-connect ` Begin the shared-OAuth admin consent flow for a model configured with `--auth-method=oauth --credential-type=shared`: Anthropic OAuth, Azure AAD/Entra service principal, Google Workload Identity for Vertex. Prints a one-shot URL valid for \~10 minutes. Open it in a browser to complete consent — Dome stores the tokens on the callback. Per-user OAuth models do not use this command; consent is gateway-triggered on each end user's first call. ## OAuth disconnect `dome model oauth-disconnect ` Revoke shared OAuth credentials. The OAuth client configuration is preserved, so a subsequent `oauth-connect` reuses the same client. Idempotent. *** ## Pool create `dome model pool create ` Create a logical pool that fans out across one or more model connections. | Flag | Type | Default | Description | | -------------------- | ------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--description` | string | — | Pool description | | `--match-when` | JSON | — | Predicate selecting this pool by request shape (e.g. `'{"prompt_tokens":{"gt":100000}}'`). Refer to [match\_when predicates](/reference/resources/model-pools#pool-resolution) reference. | | `--default` | bool | `false` | Mark as workspace default (or use [`set-default`](#pool-set-default) later) | | `--cache-ttl-secs` | int32 | `0` | Exact-match cache TTL (`0` disables) | | `--cache-scope` | string | `workspace` | `workspace` or `caller` | | `--routing-strategy` | string | `priority_weighted` | `priority_weighted`, `round_robin`, `least_loaded`. Refer to [Routing strategies](/reference/resources/model-pools#routing-strategies) reference. | | `--strategy-scope` | string | `workspace` | Scope of stateful routing counters: `workspace` or `caller`. Ignored by `priority_weighted`. | | `--failover-max` | string | `all` | Failover ceiling. `all` walks every member; integer `N` gives primary + N retries (`0` disables failover). | | `--gateway` | string (repeatable) | — | Attach to the named [Gateway](/connect/gateways) (name or ID). Without it the pool is unreachable until attached. | ```bash title="Pool with two providers and weighted distribution" theme={"system"} dome model pool create default --failover-max 2 dome model pool member add default claude-sonnet --priority 0 --weight 3 dome model pool member add default gpt-4o --priority 0 --weight 1 dome model pool member add default claude-haiku --priority 1 dome model pool set-default default ``` ## Pool list `dome model pool list [workspace-id-or-slug]` List pools. Aliases: `ls`. ## Pool get `dome model pool get ` Show pool config and its members. ## Pool update `dome model pool update ` Update a pool. Only provided flags change. Pass `--match-when '{}'` to clear the match predicate. | Flag | Type | Description | | -------------------- | ------ | ------------------------------------------------------------------------------- | | `--name` | string | Rename the pool | | `--description` | string | New description | | `--match-when` | JSON | Replace match predicate (pass `'{}'` to clear) | | `--cache-ttl-secs` | int32 | New cache TTL (`0` disables) | | `--cache-scope` | string | New cache scope | | `--routing-strategy` | string | `priority_weighted`, `round_robin`, or `least_loaded` | | `--strategy-scope` | string | `workspace` or `caller` | | `--failover-max` | string | `all` (walk every member) or non-negative integer `N`. Omit to leave unchanged. | ## Pool set default `dome model pool set-default ` Mark this pool as the workspace default. Replaces any prior default. ## Pool move `dome model pool move --before ` or `--after ` Reposition a pool in its workspace's `match_when` evaluation order. Pools with non-empty predicates evaluate in ascending `sort_key`; the first match wins. Refer to [Pool order](/reference/resources/model-pools#pool-resolution) reference. | Flag | Type | Description | | ---------- | ------ | ------------------------------------------------------------ | | `--before` | string | Anchor pool name; the moved pool lands immediately before it | | `--after` | string | Anchor pool name; the moved pool lands immediately after it | Exactly one of `--before` or `--after` is required. ## Pool remove `dome model pool rm ` Remove a pool. Aliases: `remove`, `delete`. ## Pool gateways `dome model pool gateways [gateway-name-or-id]` Manage the pool's membership in [Gateways](/connect/gateways) from the pool side. Mirrors [`dome gateway model-pools`](/cli/connect/gateway#model-pools-add). * `list ` — Gateways this pool belongs to. Alias: `ls`. * `add ` — expose the pool's models at the Gateway's `/gateways//v1` endpoint. * `remove ` — detach. Alias: `rm`. *** ## Pool member add `dome model pool member add ` Add a model to a pool. Failover walks ascending priority; within a bucket, requests distribute by weight. | Flag | Type | Default | Description | | ------------ | ----- | ------- | -------------------------------------------------- | | `--priority` | int32 | `0` | Priority (`0` = primary; failover walks ascending) | | `--weight` | int32 | `1` | Weight within the priority bucket | | `--enabled` | bool | `true` | Whether the member is included in routing | ## Pool member list `dome model pool member list ` List members of a pool with priority, weight, and enabled state. Aliases: `ls`. ## Pool member update `dome model pool member update ` Update a member's priority, weight, or enabled flag. | Flag | Type | Description | | ------------ | ----- | ---------------- | | `--priority` | int32 | New priority | | `--weight` | int32 | New weight | | `--enabled` | bool | New enabled flag | ## Pool member remove `dome model pool member rm ` Remove a member from a pool. Aliases: `remove`, `delete`. *** ## Quota list `dome model quota list` List LLM cost quotas in the workspace with their subject, USD limit, window, and enabled state. Aliases: `ls`. Refer to [Cost quotas](/govern/quotas#set-a-quota) for what each subject covers. ## Quota set `dome model quota set --subject --limit ` Create a USD spend cap. The `--subject` flag decides what the cap covers; the gateway enforces it in-memory and either spills routing (per-model budgets) or rejects with 429 (total caps). | Flag | Type | Required | Default | Description | | -------------- | ------ | ----------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `--subject` | string | Yes | — | `workspace`, `agent`, `act-as`, `pool`, `model`, or `gateway` | | `--limit` | string | Yes | — | Cap in US dollars (e.g. `500` or `12.50`) | | `--window` | string | No | `monthly` | Reset window: `daily` or `monthly` | | `--name` | string | No | — | Optional human-readable label | | `--model` | string | Conditional | — | Model connection name (`--subject model`) | | `--pool` | string | Conditional | — | Pool name (`--subject pool`, or to scope a model quota to one pool) | | `--agent` | string | Conditional | — | Agent id (`--subject agent`) | | `--act-as` | string | Conditional | — | Verified end-user OIDC subject (`--subject act-as`) | | `--gateway` | string | Conditional | — | Gateway name or id (`--subject gateway`). [`dome gateway quota set`](/cli/connect/gateway#quota-set) is the Gateway-first spelling of the same cap. | | `--per-caller` | bool | No | `false` | Apply the limit independently to each agent and verified end user (`--subject model` only) | | `--disabled` | bool | No | `false` | Create disabled; no enforcement until enabled | ```bash title="Workspace-wide monthly cap" theme={"system"} dome model quota set --subject workspace --limit 5000 --window monthly ``` ```bash title="Pool spill-over budget for one model" theme={"system"} # Caps claude-sonnet spend inside the production pool at $1000/month. # When exhausted, the pool spills to the next member. dome model quota set \ --subject model --model claude-sonnet --pool production \ --limit 1000 --window monthly ``` ```bash title="Per-caller daily cap" theme={"system"} dome model quota set \ --subject model --model gpt-4o --per-caller \ --limit 25 --window daily ``` ## Quota remove `dome model quota rm ` Remove a quota by id (from `dome model quota list`). Aliases: `remove`, `delete`. # dome tool Source: https://docs.domesystems.ai/cli/connect/tools Manage MCP server connections the gateway proxies for agents `dome tool` registers upstream MCP servers (the "tools" an agent reaches through Dome) and manages their auth, outbound headers, and field classifications. To list the capabilities each agent currently sees, use [`dome agents list`](/cli/connect/agents#list) instead. | Command | | | --------------------------------------- | ------------------------------------------------------------- | | [List](#list) | `dome tool list [workspace-id-or-slug]` | | [Get](#get) | `dome tool get [workspace-id-or-slug]` | | [Add](#add) | `dome tool add [workspace-id-or-slug]` | | [Update](#update) | `dome tool update [workspace-id-or-slug]` | | [Remove](#remove) | `dome tool remove [workspace-id-or-slug]` | | [OAuth connect](#oauth-connect) | `dome tool oauth-connect [workspace-id-or-slug]` | | [OAuth disconnect](#oauth-disconnect) | `dome tool oauth-disconnect [workspace-id-or-slug]` | | [Gateways](#gateways) | `dome tool gateways list\|add\|remove [gateway]` | | [Header list](#header-list) | `dome tool header list ` | | [Header add](#header-add) | `dome tool header add ` | | [Header remove](#header-remove) | `dome tool header remove ` | | [Catalog list](#catalog-list) | `dome tool catalog list [workspace-id-or-slug]` | | [Catalog sync](#catalog-sync) | `dome tool catalog sync [workspace-id-or-slug]` | | [Catalog block](#catalog-block) | `dome tool catalog block ` | | [Catalog deprecate](#catalog-deprecate) | `dome tool catalog deprecate ` | | [Catalog restore](#catalog-restore) | `dome tool catalog restore ` | *** ## List `dome tool list [workspace-id-or-slug]` List MCP server connections. Aliases: `ls`. ## Get `dome tool get [workspace-id-or-slug]` Show details for one tool connection. ## Add `dome tool add [workspace-id-or-slug]` Register a new MCP server. | Flag | Type | Required | Default | Description | | ----------------------------- | ----------------------------- | ---------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--name` | string | Yes | — | Tool name (unique within workspace) | | `--url` | string | Streamable | — | URL (required for `streamable-http`) | | `--command` | string | Stdio | — | Command to exec (required for `stdio`) | | `--arg` | string (repeatable) | No | — | Command argument for `stdio` | | `--protocol` | string | No | `streamable-http` | `streamable-http`, `stdio` | | `--header-literal` | `Name=Value` (repeatable) | No | — | Literal outbound header | | `--header-secret` | `Name=SecretKey` (repeatable) | No | — | Outbound header sourced from a stored secret | | `--header-actas` | `Name` (repeatable) | No | — | Header that reinjects the verified `X-Dome-Act-As` | | `--authorization` | string | No | — | `Authorization` HTTP header value. REQUIRED with `--auth-method=api-key --credential-type=shared`. The server auto-injects the managed `Authorization` header — do not declare it via `--header-secret`. | | `--secret-value` | `KEY=VALUE` (repeatable) | No | — | Advanced: per-tool credential bundle entry for custom secret-source headers. The `Authorization` key is reserved — use `--authorization` instead. | | `--auth-method` | string | No | `none` | `none`, `api-key`, `oauth` | | `--credential-type` | string | No | — | `shared`, `per-user` | | `--oauth-authorize-url` | string | No | — | OAuth: authorize endpoint | | `--oauth-token-url` | string | No | — | OAuth: token endpoint | | `--oauth-revoke-url` | string | No | — | OAuth: revocation endpoint | | `--oauth-registration-url` | string | No | — | OAuth: RFC 7591 registration endpoint (for `client-origin=dcr`) | | `--oauth-client-id` | string | No | — | OAuth: client ID (required when `client-origin=manual`) | | `--oauth-client-secret` | string | No | — | OAuth: client secret | | `--oauth-default-scope` | string (repeatable) | No | — | OAuth: default scope | | `--oauth-client-origin` | string | No | — | OAuth: `dcr` or `manual` | | `--oauth-token-endpoint-auth` | string | No | `auto` | OAuth: `auto`, `client_secret_basic`, `client_secret_post`. `auto` discovers the server's preferred method via RFC 8414 metadata. | | `--field-classification` | `key=value` | No | — | Field sensitivity labels (e.g. `email=PII,phone=PII`) | | `--write-tools` | string | No | — | Comma-separated list of tool names classified as write operations | | `--gateway` | string (repeatable) | No | — | Attach as a tool source to the named [Gateway](/connect/gateways) (name or ID). Without it the connection is unreachable until attached. | When `--auth-method=oauth --credential-type=shared`, `add` automatically prints the URL to start the admin consent flow. Open it in a browser to complete consent. If the flow can't start automatically, run [`oauth-connect`](#oauth-connect) afterwards. ```bash title="Shared OAuth tool (manual client)" theme={"system"} dome tool add \ --name notion \ --url https://mcp.notion.com/mcp \ --auth-method oauth \ --credential-type shared \ --oauth-client-origin manual \ --oauth-authorize-url https://api.notion.com/v1/oauth/authorize \ --oauth-token-url https://api.notion.com/v1/oauth/token \ --oauth-client-id "$NOTION_CLIENT_ID" \ --oauth-client-secret "$NOTION_CLIENT_SECRET" \ --oauth-default-scope read_content \ --oauth-default-scope update_content ``` ```bash title="Per-user OAuth tool (dynamic client registration)" theme={"system"} dome tool add \ --name jira \ --url https://mcp.atlassian.com/v1/sse \ --auth-method oauth \ --credential-type per-user \ --oauth-client-origin dcr \ --oauth-authorize-url https://auth.atlassian.com/authorize \ --oauth-token-url https://auth.atlassian.com/oauth/token \ --oauth-registration-url https://auth.atlassian.com/oauth/clients ``` ## Update `dome tool update [workspace-id-or-slug]` Update an MCP server connection. Only provided flags change. Switching `--auth-method` between `api-key` and `oauth` clears the previously stored credential bundle. | Flag | Type | Description | | ------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------- | | `--url` | string | New URL | | `--command` | string | New command (stdio) | | `--arg` | string (repeatable) | Replace command args | | `--protocol` | string | `streamable-http`, `stdio` | | `--header-literal` | `Name=Value` (repeatable) | Replace literal-header list | | `--header-secret` | `Name=SecretKey` (repeatable) | Replace secret-header list | | `--header-actas` | `Name` (repeatable) | Replace act-as header list | | `--authorization` | string | New `Authorization` value. Use to rotate an `api-key`+`shared` tool's token. | | `--secret-value` | `KEY=VALUE` (repeatable) | Advanced: replace per-tool credential bundle | | `--auth-method` | string | `none`, `api-key`, `oauth` | | `--credential-type` | string | `shared`, `per-user` | | `--oauth-*` | various | OAuth client config overrides (same shape as `add`). Empty `--oauth-client-secret` keeps the existing secret. | | `--field-classification` | `key=value` | Add or update field classifications | | `--remove-field-classification` | string\[] | Remove field classifications by name | | `--write-tools` | string | Comma-separated write-tools list (replaces current) | ## Remove `dome tool remove [workspace-id-or-slug]` Remove an MCP server connection. ## Gateways `dome tool gateways [gateway-name-or-id]` Manage the connection's tool-source membership in [Gateways](/connect/gateways) from the connection side. Mirrors [`dome gateway tool-sources`](/cli/connect/gateway#tool-sources-add). * `list ` — Gateways this connection is a tool source of. Alias: `ls`. * `add ` — attach as a tool source; every observed tool joins, and newly discovered tools propagate automatically. * `remove ` — detach. Alias: `rm`. ```bash theme={"system"} dome tool gateways add notion prod-tools ``` ## OAuth connect `dome tool oauth-connect [workspace-id-or-slug]` Begin the shared-OAuth admin consent flow. Prints a one-shot URL valid for \~10 minutes. Open it in a browser to complete consent at the upstream's authorization server. After consent, Dome stores the tokens. Only valid for `--auth-method=oauth --credential-type=shared` tools. Per-user OAuth tools trigger consent automatically on each end user's first call. ```bash theme={"system"} dome tool oauth-connect notion ``` ## OAuth disconnect `dome tool oauth-disconnect [workspace-id-or-slug]` Revoke shared OAuth credentials. The OAuth client configuration is preserved, so a subsequent `oauth-connect` reuses the same client. Idempotent. ```bash theme={"system"} dome tool oauth-disconnect notion ``` ## Header list `dome tool header list [workspace-id-or-slug]` List the outbound headers configured on a tool. ## Header add `dome tool header add [workspace-id-or-slug]` Append outbound headers to a tool without replacing the existing list. Combine `--header-literal`, `--header-secret`, and `--header-actas` flags (refer to [`add`](#add) for shapes). ## Header remove `dome tool header remove [workspace-id-or-slug]` Remove outbound headers by name from a tool. ## Catalog list `dome tool catalog list [workspace-id-or-slug]` List the persistent MCP tool catalog for a connection — the tools the gateway has observed on the upstream MCP server. The catalog survives gateway restarts and stays queryable without a live data plane. | Flag | | | ---------------- | -------------------------------------------------------------------------------------------------------------------- | | `--with-schema` | Include input schema in the output (truncated). | | `--seen-since` | Only include tools seen within this window (e.g. `30d`, `24h`). Pass `0` for all-time. Defaults to `720h` (30 days). | | `--show-blocked` | Include blocked tools (hidden by default). | ```bash theme={"system"} dome tool catalog list notion --seen-since 7d ``` ## Catalog sync `dome tool catalog sync [workspace-id-or-slug]` Sync the persistent MCP tool catalog for a `per-user` connection by dispatching exactly one upstream `tools/list` against the calling admin's own per-user credential. Shared connections already pre-warm via the gateway's startup discovery and are rejected client-side. Attach your per-user credential first through the normal magic-link flow (refer to [`oauth-connect`](#oauth-connect) or the per-user provisioning flow in the dashboard), then run this command. Sync works both before any agent traffic (pre-warm) and after (refresh). The result is recorded through the shared catalog observation path and is visible to `dome tool catalog list` immediately afterwards. | Flag | | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--act-as-sub` | Workspace-verified act-as subject under which the admin attached the per-user credential. Defaults to the calling admin's email. Required when the workspace's act-as verifier does not use email as Sub (e.g. OIDC/HMAC verifiers issuing opaque subs, or `local-auth` which sets Sub to a user UUID). | ```bash theme={"system"} dome tool catalog sync notion ``` Prints four counters: tools observed, inserted, updated schema, and updated last-seen. Requires the `gateway.manage` permission. Returns `FailedPrecondition` when no per-user credential exists at the resolved sub-hash — the error message names both remedies (attach the credential, or pass `--act-as-sub`). ## Catalog block `dome tool catalog block ` Hide a discovered MCP tool from agents. The Gateway filters the tool out of `tools/list` and denies any `tools/call` against it. A block takes precedence over ordinary connection-level access. Blocks persist across re-observation. | Flag | Type | Required | Description | | ----------- | ------ | -------- | --------------------------------------------------------------------------------------------- | | `--message` | string | No | Operator note shown alongside the tool in dashboards and CLI (e.g. `"dangerous mass-delete"`) | Requires the `gateway.manage` permission. Blocked-call attempts emit a `tool.blocked_call_denied` audit event. ```bash theme={"system"} dome tool catalog block notion delete_page --message "dangerous mass-delete; use archive_page" ``` ## Catalog deprecate `dome tool catalog deprecate ` Mark a tool as deprecated. The tool stays callable and visible to agents; the dashboard renders a warning chip and operators see the status in `dome tool catalog list`. Use when an upstream replaces a tool and you want to soft-signal callers without breaking them. | Flag | Type | Required | Description | | ----------- | ------ | -------- | ------------------------------------------- | | `--message` | string | No | Operator note (e.g. `"use create_page_v2"`) | Requires the `gateway.manage` permission. ```bash theme={"system"} dome tool catalog deprecate notion create_page --message "use create_page_v2" ``` ## Catalog restore `dome tool catalog restore ` Return a tool to `active`, clearing any prior block or deprecation. Requires the `gateway.manage` permission. ```bash theme={"system"} dome tool catalog restore notion delete_page ``` `dome tool catalog list ` shows the current `STATUS` column for every observed tool. Blocked tools are hidden by default — pass `--show-blocked` to include them. # dome quickstart Source: https://docs.domesystems.ai/cli/developer/quickstart-cmd Scaffold a new Dome-enabled project ## `dome quickstart [name]` Creates a new project directory with Dome SDK integration, starter rules, and configuration. ```bash theme={"system"} dome quickstart my-agent-project ``` | Flag | Type | Default | Description | | ------------------- | ------ | ------- | ---------------------------------- | | `--language` | string | `go` | Project language (supported: `go`) | | `--non-interactive` | bool | false | Skip confirmation prompts | ### Generated Files | File | Description | | -------------- | ------------------------------------- | | `main.go` | Entry point with Dome SDK integration | | `go.mod` | Go module definition | | `dome.yaml` | Dome configuration | | `.env.example` | Environment variable template | | `Dockerfile` | Container build definition | | `rules.cedar` | Starter authorization rules | # dome sandbox Source: https://docs.domesystems.ai/cli/developer/sandbox Provision sandbox environments for development and testing ## Provision `dome sandbox provision` Provision a sandbox environment. ```bash theme={"system"} # Create a sandbox workspace in the current tenant dome sandbox provision --scope=workspace # Create an isolated sandbox tenant with workspace and API key dome sandbox provision --scope=tenant ``` | Flag | Type | Required | Description | | ------------------ | ------ | -------- | --------------------------------------------------------- | | `--scope` | string | Yes | Scope: `tenant` or `workspace` | | `--tenant-name` | string | No | Sandbox tenant name suffix (auto-generated if omitted) | | `--workspace-name` | string | No | Sandbox workspace name suffix (auto-generated if omitted) | ### Scope Options * **`workspace`** — creates a sandbox workspace in the current tenant. Requires tenant admin, operator, or security role. * **`tenant`** — creates an isolated sandbox tenant with a workspace and API key. Requires org admin or owner role. # CLI Overview Source: https://docs.domesystems.ai/cli/index Install, authenticate, and navigate the Dome CLI The `dome` CLI is the primary command-line interface for the Dome Platform. ## Installation ```bash title="macOS (Homebrew)" theme={"system"} brew trust dome-systems/tap brew install dome-systems/tap/dome ``` Homebrew 6 requires the `brew trust` line before it loads a third-party tap's formulae. Refer to [Install](/install) for the full walkthrough, including a direct-download alternative. ## Global Flags All commands support these flags: | Flag | Type | Description | | ------------ | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--context` | string | Use a specific context (overrides active context) | | `--server` | string | Dome Platform server URL | | `--activity` | string | Stable activity ID this run asserts. Every Connect RPC carries `X-Dome-Activity-Id` so audit events from the run share one [activity chain](/operate/audit#list-activity-chains). Overrides `DOME_ACTIVITY_ID`. | | `--json` | bool | Output as JSON | | `--quiet` | bool | Minimal output (IDs only) | | `--no-color` | bool | Disable colored output | ## Environment Variables | Variable | Description | | ------------------ | --------------------------------------------------------------- | | `DOME_API_KEY` | Sets the active context's token. Useful for CI and scripts. | | `DOME_ACTIVITY_ID` | Default activity ID for the process. `--activity` overrides it. | ## Command Groups ### Connect | Command | Description | | --------------------------------- | ----------------------------------------------------- | | [`agents`](/cli/connect/agents) | Manage agent registrations, lifecycle, and API keys | | [`gateway`](/cli/connect/gateway) | Manage Gateways, membership, agent access, and quotas | | [`tool`](/cli/connect/tools) | Manage MCP server connections the gateway proxies | | [`model`](/cli/connect/models) | Manage LLM model connections and routing pools | ### Secure | Command | Description | | ---------------------------- | ------------------------------------------------------------ | | [`rules`](/cli/secure/rules) | Deploy, validate, simulate, and rollback authorization rules | ### Operate | Command | Description | | ----------------------------------- | ------------------------------------------------------------------------ | | [`audit`](/cli/operate/audit) | Query, stream, and export audit events | | [`webhooks`](/cli/operate/webhooks) | Manage webhook subscriptions, delivery history, and the provider catalog | | [`usage`](/cli/operate/usage) | Show LLM token usage and estimated cost for the active workspace | ### Settings | Command | Description | | ---------------------------------------------- | -------------------------------------------------------------- | | [`tenants`](/cli/settings/tenants) | Create tenants under your active organization | | [`workspace`](/cli/settings/workspace) | Manage workspaces, members, act-as, and verification providers | | [`platform-keys`](/cli/settings/platform-keys) | Manage platform API keys | | [`config`](/cli/settings/config) | Platform configuration | ### Developer | Command | Description | | --------------------------------------------- | ----------------------------------- | | [`quickstart`](/cli/developer/quickstart-cmd) | Scaffold a new Dome-enabled project | | [`sandbox`](/cli/developer/sandbox) | Provision sandbox environments | ### Session | Command | Description | | --------------------------------- | --------------------------------------------------- | | [`auth`](/cli/session/auth) | Authentication management | | [`context`](/cli/session/context) | Manage CLI contexts (org/tenant/workspace sessions) | ### Standalone | Command | Description | | ------------------------------------ | ------------------------ | | [`mcp`](/cli/standalone/mcp) | MCP server for AI agents | | [`version`](/cli/standalone/version) | Show CLI version | ## Next steps Authenticate first, then scaffold — or drive the same jobs from MCP: * [Install](/install) to authenticate and select a workspace * [Quickstart](/quickstart) to scaffold and verify governed traffic * [MCP](/reference/mcp) when you prefer tool-based access from an assistant * [API](/api) for the management and data-plane HTTP surfaces # dome audit Source: https://docs.domesystems.ai/cli/operate/audit Query, stream, get, export audit events, and list activity chains | Command | | | ----------------- | -------------------------------- | | [Query](#query) | `dome audit query` | | [Stream](#stream) | `dome audit stream` | | [Get](#get) | `dome audit get [event-id]` | | [Export](#export) | `dome audit export` | | [Chains](#chains) | `dome audit chains` | | [Chain](#chain) | `dome audit chain [activity-id]` | All commands scope the read to the active workspace (from `dome config`), so the server checks the workspace-level `audit.view` permission. Switch workspaces with `dome workspace use ` before running the command. Tenant- or org-scoped API keys with no workspace selected fall back to the tenant-level `audit.view`. *** ## Query `dome audit query` Query audit events with optional filters. | Flag | Type | Description | | ------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `--types` | string\[] | Filter by event types | | `--classes` | string\[] | `governing`, `state_change`, `security`, `audit_access` | | `--results` | string\[] | `attempted`, `succeeded`, `denied`, `failed`, `filtered`, `noop`, `permitted` | | `--agent-id` | string | Filter by agent ID | | `--agent-ids` | string\[] | Filter by agent IDs | | `--primary-resource-id` | string | Filter by primary resource ID | | `--primary-resource-kind` | string | Filter by primary resource kind | | `--trace-id` | string | Filter by trace ID — events for one request | | `--activity-id` | string | Filter by activity chain ID. Refer to [Activity chains](/reference/events#correlation) reference. | | `--operation-id` | string | Filter by operation ID | | `--parent-event-id` | string | Filter by parent event ID | | `--actor-kind` | string | Filter by actor kind | | `--actor-id` | string | Filter by actor ID | | `--producer-service` | string | Filter by producer service | | `--producer-gateway-id` | string | Filter by producer Gateway ID | | `--request-surface` | string | Filter by request surface | | `--caller-surface` | string | Filter by caller application surface | | `--payload-filter` | string\[] | `event.type:field` or `event.type:field=value` (repeatable). Refer to [Payload filters](/reference/events#payload-filters) reference. | | `--since` | string | Start time (RFC3339) | | `--until` | string | End time (RFC3339) | | `--limit` | int32 | Maximum results (default: 50) | | `--page-token` | string | Cursor from a previous query | | `--compact` | bool | Bounded investigation rows; use `audit get` for full payload | ## Stream `dome audit stream` Stream audit events in real-time via SSE. Accepts the same filters as [`query`](#query) except `--limit` and `--compact`. | Flag | Type | Description | | ------------------------- | --------- | ------------------------------------ | | `--types` | string\[] | Filter by event types | | `--classes` | string\[] | Filter by event classes | | `--results` | string\[] | Filter by event results | | `--agent-id` | string | Filter by agent ID | | `--agent-ids` | string\[] | Filter by agent IDs | | `--primary-resource-id` | string | Filter by primary resource ID | | `--primary-resource-kind` | string | Filter by primary resource kind | | `--trace-id` | string | Filter by trace ID | | `--activity-id` | string | Follow one activity chain live | | `--operation-id` | string | Filter by operation ID | | `--parent-event-id` | string | Filter by parent event ID | | `--actor-kind` | string | Filter by actor kind | | `--actor-id` | string | Filter by actor ID | | `--producer-service` | string | Filter by producer service | | `--producer-gateway-id` | string | Filter by producer Gateway ID | | `--request-surface` | string | Filter by request surface | | `--caller-surface` | string | Filter by caller application surface | | `--payload-filter` | string\[] | Payload filter (repeatable) | | `--since` | string | Start time (RFC3339) | | `--until` | string | End time (RFC3339) | | `--page-token` | string | Cursor from a previous stream | ## Get `dome audit get [event-id]` Get a single audit event by ID. Enum values — event class, result, actor kind, surface — render as short tokens (`governing`, `denied`, `gateway_mcp`) in both the table and `--format json` output, and the filter flags accept those same tokens. Refer to [Enum rendering](/reference/events#enum-rendering) reference. ## Export `dome audit export` Export audit events in JSON-lines, JSON, OCSF, or CEF format. Pick the format your downstream pipeline expects: | Format | Output | | ----------------- | ------------------------------------------------------------------ | | `jsonl` (default) | One JSON object per line (NDJSON). | | `json` | Single pretty-printed JSON array. | | `ocsf` | OCSF v1.1.0 JSON array for Splunk, Sentinel, or Chronicle. | | `cef` | Common Event Format lines for ArcSight, QRadar, or generic syslog. | Accepts the same filters as [`query`](#query), plus: | Flag | Type | Description | | ---------- | ------ | -------------------------------------------------------- | | `--limit` | int32 | Maximum results (default: 1000) | | `--format` | string | `jsonl`, `json`, `csv`, `ocsf`, `cef` (default: `jsonl`) | ```bash theme={"system"} # Export the last 24 hours as OCSF for a SIEM dome audit export \ --since 2026-05-26T00:00:00Z \ --format ocsf > audit.ocsf.json ``` ## Chains `dome audit chains` List activity chains in the active workspace. A chain is the set of audit events sharing one `activity_id` — a session, an LLM turn, a conversation thread, or a scripted run — computed at read time by grouping on `activity_id` within the workspace. A chain is listed when **any** of its events matches the filter. Per-chain counts always reflect the full chain. Drill into one chain's events with [`dome audit chain `](#chain). Refer to [Activity chains](/reference/events#correlation) reference for the model and trust labels. | Flag | Type | Description | | -------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------- | | `--types` | string\[] | Chain contains an event of these types | | `--classes` | string\[] | Chain contains an event of these classes (`governing`, `state_change`, `security`, `audit_access`) | | `--results` | string\[] | Chain contains an event with these results (`attempted`, `succeeded`, `denied`, `failed`, `filtered`, `noop`, `permitted`) | | `--agent-id` | string | Chain contains an event for this agent | | `--agent-ids` | string\[] | Chain contains an event for these agents | | `--actor-kind` | string | Chain contains an event with this actor kind | | `--actor-id` | string | Chain contains an event with this actor ID | | `--producer-service` | string | Chain contains an event from this producer service | | `--request-surface` | string | Chain contains an event from this request surface | | `--since` | string | Start time (RFC3339) | | `--until` | string | End time (RFC3339) | | `--limit` | int32 | Maximum chains (default: 50) | | `--page-token` | string | Cursor from a previous chains query | Per-event identity filters (`--trace-id`, `--operation-id`, `--parent-event-id`) and payload filters identify a single event, not a chain, and are not offered here. ```bash theme={"system"} # Recent chains in the active workspace dome audit chains --limit 20 # Chains that contain a denied governing event in the last day dome audit chains \ --classes governing \ --results denied \ --since 2026-06-05T00:00:00Z ``` Each row reports the chain's `activity_id`, event count, last activity timestamp, outcome breakdown, trust label (`server-owned`, `caller-asserted`, or mixed — refer to [Activity trust](/reference/events#correlation) reference), and distinct event-type count. ## Chain `dome audit chain [activity-id]` Show one activity chain's events in order. A drill-in into `audit query` filtered by `activity_id`. Accepts the same per-event filters as `audit query` to narrow the chain's events further. | Flag | Type | Description | | ------------------ | --------- | ----------------------------------------------------------------------------- | | `--types` | string\[] | Filter by event types | | `--classes` | string\[] | Filter by event classes | | `--results` | string\[] | Filter by event results | | `--agent-id` | string | Filter by agent ID | | `--actor-kind` | string | Filter by actor kind | | `--actor-id` | string | Filter by actor ID | | `--payload-filter` | string\[] | Payload filter as `event.type:field` or `event.type:field=value` (repeatable) | | `--since` | string | Start time (RFC3339) | | `--until` | string | End time (RFC3339) | | `--limit` | int32 | Maximum events (default: 200) | | `--page-token` | string | Cursor from a previous query | ```bash theme={"system"} # Every event in one chain dome audit chain case-12345 # Only the denied events in that chain dome audit chain case-12345 --results denied ``` ## Emit events into a chain Set `--activity` (or `DOME_ACTIVITY_ID`) on the root command to assert a stable activity ID. Every Connect RPC the run makes carries `X-Dome-Activity-Id`, so the audit events all share one `activity_id` and form one chain. ```bash theme={"system"} # One run, many commands, one chain. export DOME_ACTIVITY_ID="case-12345" dome agents register --name worker dome rules deploy ./rules dome audit query --limit 5 # Inspect the chain you just emitted. dome audit chains --since 2026-06-05T00:00:00Z dome audit chain case-12345 ``` `--activity` overrides `DOME_ACTIVITY_ID`. The ID is opaque to Dome — pick whatever maps to your unit of work. The header is first-party only and is never forwarded to third-party LLM or MCP backends. The `--activity` emit flag (root command) and the `--activity-id` filter flag (audit reads) are distinct: one stamps events, the other selects them. # dome usage Source: https://docs.domesystems.ai/cli/operate/usage Show LLM token usage and estimated cost for the active workspace Print LLM token consumption and a per-class cost estimate for the active workspace. Use it to spot-check spend, build budget alerts in scripts, or confirm token attribution matches what the dashboard and `dome_usage` MCP tool show. The estimated cost is a blended cross-model rate intended for trend-watching, not a billable figure. Refer to [Token Usage and Cost](/operate/observe#token-usage-and-cost) for the full token-class breakdown and pricing model. ## Usage `dome usage` Reads `GET /api/v1/metrics/summary` for the active workspace over the last 24 hours and renders the totals. | Flag | Type | Description | | -------- | ---- | ---------------------------- | | `--json` | bool | Output as JSON for scripting | **Permission:** workspace `audit.view` **Prerequisites:** active workspace selected (`dome context use ` or `dome workspace use `). ```bash theme={"system"} dome usage ``` Output (table): ``` LLM token usage (last 24h) Input tokens 412000 Cache read tokens 1820000 Cache write tokens 88000 Output tokens 64000 Reasoning tokens 12000 Total tokens 2396000 Estimated cost $3.4521 (per-class estimate, not billable) ``` Output (`--json`): ```json theme={"system"} { "llm_input_tokens": 412000, "llm_cache_read_tokens": 1820000, "llm_cache_write_tokens": 88000, "llm_output_tokens": 64000, "llm_reasoning_tokens": 12000, "llm_total_tokens": 2396000, "estimated_cost_usd": 3.4521 } ``` Pipe into `jq` to build budget guards: ```bash theme={"system"} # Fail if estimated 24h spend crosses $10 cost=$(dome usage --json | jq '.estimated_cost_usd') awk "BEGIN { exit !($cost > 10) }" && echo "over budget" >&2 ``` For per-agent, per-key, per-identity, or per-model attribution, see the [REST query surface](/operate/observe#token-usage-and-cost) — the dashboard and the [`dome_usage`](/reference/mcp/audit#audit-usage) MCP tool consume the same endpoints. # dome webhooks Source: https://docs.domesystems.ai/cli/operate/webhooks Manage webhook subscriptions, delivery history, and the provider catalog | Command | | | ----------------------------------------------------------- | -------------------------------------------------------- | | [Subscriptions list](#subscriptions-list) | `dome webhooks subscriptions list` | | [Subscriptions get](#subscriptions-get) | `dome webhooks subscriptions get ` | | [Subscriptions create](#subscriptions-create) | `dome webhooks subscriptions create …` | | [Subscriptions update](#subscriptions-update) | `dome webhooks subscriptions update ` | | [Subscriptions disable](#subscriptions-disable) | `dome webhooks subscriptions disable ` | | [Subscriptions rotate-secret](#subscriptions-rotate-secret) | `dome webhooks subscriptions rotate-secret ` | | [Subscriptions test](#subscriptions-test) | `dome webhooks subscriptions test ` | | [Subscriptions delete](#subscriptions-delete) | `dome webhooks subscriptions delete ` | | [Deliveries list](#deliveries-list) | `dome webhooks deliveries list` | | [Deliveries get](#deliveries-get) | `dome webhooks deliveries get ` | | [Deliveries replay](#deliveries-replay) | `dome webhooks deliveries replay ` | | [Deliveries replay-failed](#deliveries-replay-failed) | `dome webhooks deliveries replay-failed --since ` | | [Deliveries cancel](#deliveries-cancel) | `dome webhooks deliveries cancel ` | | [Providers list](#providers-list) | `dome webhooks providers list` | | [Providers get](#providers-get) | `dome webhooks providers get ` | `dome webhooks` also answers to `dome events`. Group aliases: `subscriptions` = `sub`, `deliveries` = `del`, every `list` = `ls`, `delete` = `rm`. All commands scope to the active workspace (from `dome context`). ### Permissions | Permission | Commands | | ----------------- | ---------------------------------------------------------------------------------- | | `eventing.view` | All `list` and `get` commands | | `eventing.manage` | Subscription create, update, disable, rotate-secret, test, delete; delivery cancel | | `eventing.replay` | `deliveries replay`, `deliveries replay-failed` | Workspace `admin` and `operator` roles hold all three. `security`, `developer`, and `viewer` hold `eventing.view`. *** ## Subscriptions list `dome webhooks subscriptions list` List every subscription in the active workspace with its delivery endpoint, event types, and active state. ## Subscriptions get `dome webhooks subscriptions get ` Show a subscription's delivery endpoint, provider, event types, filters, and active state. Name matching is case-insensitive. ## Subscriptions create `dome webhooks subscriptions create (--url | --provider ) --event-type ` Create a subscription and its delivery destination atomically. A failed request leaves neither record behind. ``` Subscription "alerts" created (id: …) Signing secret (shown once, key key_…): whsec_… ``` The signing secret prints **once** — capture it before the terminal closes. It is only recoverable via `rotate-secret`. Providers that authenticate with a credential in the body (PagerDuty) send unsigned deliveries and return no secret. | Flag | Type | Description | | ------------------ | --------- | ------------------------------------------------------------------------------------------- | | `--url` | string | Custom HTTPS endpoint URL. Exactly one of `--url` or `--provider`. | | `--provider` | string | Provider key from `providers list`. | | `--setting` | string\[] | Provider setting `key=value` (repeatable). Non-secret. | | `--credential` | string\[] | Provider credential `key=value` (repeatable). Stored write-only. | | `--event-type` | string\[] | Event type to subscribe to (repeatable, required). | | `--filter` | string\[] | Filter predicate (repeatable). Refer to [filter grammar](#filter-grammar). | | `--content-format` | string | Custom URL only: `object` (default) or `array` (one-element JSON array). | | `--header-literal` | string\[] | Custom header `Name=Value` (repeatable). Custom URL only. | | `--header-secret` | string\[] | Custom header `Name=Value` stored write-only, masked on read (repeatable). Custom URL only. | ### Filter grammar | Form | Meaning | | ---------------- | ------------------------------------------------------------------------------------------------------------------------- | | `key=value` | Exact match. Repeat the same key for an OR set. | | `key~=glob` | Glob pattern. `*` any run, `?` one char, `\` escapes. Matches the full value, case-sensitive. | | `tool=conn/tool` | Sugar. Splits at the first `/` into `connection_name` + `tool_name` filters; each side globs when it contains `*` or `?`. | Filter keys: `agent_name`, `connection_name` (resolved from IDs at delivery time), `agent_id`, `tool_name`, plus each event type's own filterable payload fields — browse them in the dashboard's **Event catalog** tab. An event delivers only if it satisfies every filter that applies to its type. ## Subscriptions update `dome webhooks subscriptions update ` Update a subscription or its delivery destination. Unpassed flags leave the field untouched. | Flag | Type | Description | | ------------------- | --------- | ----------------------------------------------------------------------------- | | `--name` | string | New display name. | | `--event-type` | string\[] | Replace the full event-type set (repeatable). | | `--filter` | string\[] | Replace the full filter set (repeatable). Excludes `--clear-filters`. | | `--clear-filters` | bool | Remove every filter. | | `--active` | bool | `--active=false` pauses fanout; `--active` resumes it. No backfill on resume. | | `--url` | string | Replace the destination URL. | | `--endpoint-active` | bool | Enable or disable the destination itself. | | `--content-format` | string | `object` or `array`. Custom URL only. | | `--header-literal` | string\[] | Upsert a literal header `Name=Value`. | | `--header-secret` | string\[] | `Name=Value` rotates the secret; bare `Name` keeps the stored value. | | `--remove-header` | string\[] | Remove a header by name. | | `--clear-headers` | bool | Remove all custom headers. | | `--setting` | string\[] | Replace the full provider settings set `key=value`. | | `--credential` | string\[] | Merge a write-only provider credential `key=value`. | ## Subscriptions disable `dome webhooks subscriptions disable ` Stop new fanout. Delivery history and pending retries remain queryable. ## Subscriptions rotate-secret `dome webhooks subscriptions rotate-secret ` Rotate the destination's HMAC signing secret. Prints the new secret **once**: ``` New signing secret (shown once, key key_…): whsec_… ``` During the grace window the `webhook-signature` header carries a signature per active key, so a consumer verifying with either secret keeps working. ## Subscriptions test `dome webhooks subscriptions test ` Send a fixed `eventing.test` event through the subscription's destination. Verifies signing, headers, and the destination's own routing end-to-end. Prints the queued delivery ID — inspect it with `deliveries get`. ## Subscriptions delete `dome webhooks subscriptions delete ` Delete the subscription and cancel every pending delivery. Delivery history stays queryable. *** ## Deliveries list `dome webhooks deliveries list` Inspect deliveries by status, event type, subscription, or time window. Prints last attempt, status, event type, delivery ID, attempt count, and last error per row. | Flag | Type | Description | | ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------- | | `--subscription-id` | string | Narrow to one subscription ID. | | `--destination-id` | string | Narrow to one destination ID. | | `--event-type` | string | Narrow to one event type. | | `--status` | string | One of `created`, `enqueued`, `delivering`, `retry_scheduled`, `succeeded`, `failed`, `canceled`, `expired`, `dlq`. | | `--event-id` | string | Every delivery for one source event. | | `--start` | string | RFC3339 start time. | | `--end` | string | RFC3339 end time. | | `--limit` | int | Page size. | | `--page-token` | string | Cursor from a previous listing. | ## Deliveries get `dome webhooks deliveries get ` Show a delivery with its status, timing, last HTTP status, and per-attempt history. | Flag | Type | Description | | -------- | ---- | ----------------------------------------------------------------------------------------------------------- | | `--body` | bool | Include the delivered webhook body while retained — up to 30 days, or 24 hours after a successful delivery. | ## Deliveries replay `dome webhooks deliveries replay ` Replay one delivery. Creates a **new** delivery from the retained body — fresh `webhook-id`, same `Dome-Event-Id` — and sends another request to the destination. Consumers that dedupe on `Dome-Event-Id` ignore the overlap. ## Deliveries replay-failed `dome webhooks deliveries replay-failed --since ` Bulk-replay terminally-failed deliveries in a time window — the outage-recovery counterpart to `replay`. Safe to re-run: a delivery with a live or succeeded replay is not matched again, and canceled deliveries are never recovered. | Flag | Type | Description | | ---------------- | --------- | --------------------------------------------------------------------------------------------- | | `--since` | string | Duration back from now (`24h`, `7d`) or RFC3339 timestamp. Required, within the last 30 days. | | `--until` | string | RFC3339 upper bound. Defaults to now. | | `--subscription` | string | Narrow to one subscription name or ID. | | `--status` | string\[] | Statuses to recover — subset of `failed`, `expired`, `dlq`. Default all three. | | `--max` | int | Deliveries per run, up to 500. Repeat the command to continue. | Prints `matched · replayed · skipped (body expired) · failed`. ## Deliveries cancel `dome webhooks deliveries cancel ` Cancel a delivery's pending retries. No further request is sent to the destination. *** ## Providers list `dome webhooks providers list` List every off-the-shelf provider with its setting and credential fields, recommended event bundle, and authentication style — Standard Webhooks signature or credential-in-body. ## Providers get `dome webhooks providers get ` Show a provider's setup instructions, documentation link, and `--setting` / `--credential` fields. Run before `subscriptions create --provider`. # dome guards Source: https://docs.domesystems.ai/cli/secure/guards Author content-inspection Filters and assign them to model and MCP server connections `dome guards` manages Guards — content-inspection definitions that redact, omit, or block governed traffic. A Filter is a workspace-scoped, versioned Guard bound to a connection's ordered chain per direction. Text Filters attach to model connections; JSON Filters attach to MCP server (tool) connections. | Command | | | ------------------------------------------------------- | ------------------------------------------------------------ | | [Filters list](#filters-list) | `dome guards filters list` | | [Filters get](#filters-get) | `dome guards filters get ` | | [Filters versions](#filters-versions) | `dome guards filters versions ` | | [Filters create](#filters-create) | `dome guards filters create ` | | [Filters update](#filters-update) | `dome guards filters update ` | | [Filters rollback](#filters-rollback) | `dome guards filters rollback --to-version ` | | [Filters delete](#filters-delete) | `dome guards filters delete ` | | [Model guards filters list](#model-guards-filters-list) | `dome model guards filters list ` | | [Model guards filters set](#model-guards-filters-set) | `dome model guards filters set ` | | [Tool guards filters list](#tool-guards-filters-list) | `dome tool guards filters list ` | | [Tool guards filters set](#tool-guards-filters-set) | `dome tool guards filters set ` | Editing a Filter deploys a new active version. Assigned connections pick it up on the next gateway sync — history is preserved and can be rolled back to. ## Filter kinds A Filter's kind is structural and immutable at create time. | Kind | Assignable to | Component | Actions | | ------ | ----------------------------- | -------------------------------------------------------------------------- | ------------------------- | | `text` | Model connections | Ordered text-content matchers (substring, SSN, credit card, phone, digits) | `redact`, `block` | | `json` | MCP server (tool) connections | Ordered JSON field-actions keyed by field name | `redact`, `omit`, `block` | Both kinds enforce **fail-closed**: a Filter whose stored config fails to decode blocks that `(connection, direction)` until the config is fixed. The gateway never relays unfiltered traffic. *** ## Filters list `dome guards filters list` List the workspace's Filters, showing each one's active version and a compact config summary (e.g. `redact[substring:"foo", ssn]; block[substring:"bar"]` or `omit[ssn, dob]; redact[email]`). ## Filters get `dome guards filters get ` Show a single Filter's active version and config. | Arg | Type | Required | Description | | ------------ | ------ | -------- | -------------------------------------- | | `name-or-id` | string | Yes | Filter name (workspace-scoped) or UUID | ## Filters versions `dome guards filters versions ` List the Filter's version history (newest first). Rollback-produced versions cite the source version id. ## Filters create `dome guards filters create ` Create a Filter — its first active version. ```bash theme={"system"} # Text Filter: redact SSNs and a substring, then block on another substring dome guards filters create pii-redact \ --description "Redact PII in model responses" \ --redact-ssn \ --redact-substring "internal-only" \ --block-substring "TOP-SECRET" # JSON Filter: omit and redact fields in tool arguments/results (via --config-from) dome guards filters create tool-scrub --config-from ./tool-scrub.json ``` | Flag | Type | Required | Default | Description | | -------------------- | ------------------- | ------------------------------- | ------- | ----------------------------------------------------------------------------------------------------- | | `--description` | string | No | — | One-line description | | `--redact-substring` | string (repeatable) | Text only | — | Redact matches of this substring | | `--block-substring` | string (repeatable) | Text only | — | Block the response on a substring match | | `--redact-ssn` | bool | Text only | `false` | Redact US SSN patterns | | `--block-ssn` | bool | Text only | `false` | Block the response on an SSN match | | `--config-from` | path or `-` | For JSON, or full-fidelity text | — | Read a protojson `dome.guards.v1.FilterConfig` from a file or stdin. Overrides the convenience flags. | The convenience flags build at most two components — a `redact` component and a `block` component, redact ordered first — and only produce text Filters. Use `--config-from` to author a JSON Filter or a multi-component text chain. ### JSON Filter config example ```json title="tool-scrub.json" theme={"system"} { "json": { "components": [ { "field_actions": [ { "matcher": { "key": "ssn" }, "action": "FILTER_ACTION_OMIT" }, { "matcher": { "key": "dob" }, "action": "FILTER_ACTION_OMIT" }, { "matcher": { "key": "email" }, "action": "FILTER_ACTION_REDACT" }, { "matcher": { "key": "card" }, "action": "FILTER_ACTION_BLOCK" } ] } ] } } ``` `BLOCK` withholds the whole message. `OMIT` removes the key and value. `REDACT` rewrites the value with a redaction sentinel. When multiple actions target the same key, `BLOCK` > `OMIT` > `REDACT`. ## Filters update `dome guards filters update ` Edit metadata in place, or deploy a new active version by passing any config flag. The old version is preserved in history and assigned connections follow the active version on the next gateway sync. | Flag | Type | Description | | --------------------------------------------------------------------------------------------- | ------ | -------------------------------------------- | | `--name` | string | New name for the logical Filter | | `--description` | string | New description | | `--redact-substring` / `--block-substring` / `--redact-ssn` / `--block-ssn` / `--config-from` | — | Any config flag deploys a new active version | Pass at least one of name, description, or a config flag. ## Filters rollback `dome guards filters rollback --to-version ` Roll a Filter back to an earlier version. The target version's config is copied forward into a new active version — history is never mutated. | Flag | Type | Required | Description | | -------------- | ----- | -------- | ------------------------------ | | `--to-version` | int32 | Yes | Version number to roll back to | ## Filters delete `dome guards filters delete ` Hard-delete a Filter and its connection assignments. Corrupt Filters can still be addressed by UUID even when their config fails to render. *** ## Model guards filters list `dome model guards filters list ` List the text Filters assigned to a model connection, in chain order, grouped by direction. | Arg | Type | Required | Description | | ------------ | ------ | -------- | --------------------- | | `connection` | string | Yes | Model connection name | ## Model guards filters set `dome model guards filters set ` Replace the entire ordered Filter chain for one direction on a model connection. Only `text`-kind Filters may be assigned. ```bash theme={"system"} # Order matters — filters run in the given order dome model guards filters set claude-prod \ --direction response \ --filters pii-redact,secrets-block # Clear a chain dome model guards filters set claude-prod --direction request --filters "" ``` | Flag | Type | Required | Description | | ------------- | -------------------- | -------- | ------------------------------------------------------------------------------ | | `--direction` | string | Yes | `response` (completions the agent receives) or `request` (the outbound prompt) | | `--filters` | comma-separated list | Yes | Ordered Filter names or UUIDs; empty clears the chain | `request` runs before dispatch to the upstream provider — a match can redact the prompt or block the call. `response` runs on the streamed completion; multi-chunk patterns are inspected inside the [LLM outbound filter window](/govern/guards#configure-the-streaming-window). *** ## Tool guards filters list `dome tool guards filters list ` List the JSON Filters assigned to an MCP server (tool) connection, in chain order, grouped by direction. | Arg | Type | Required | Description | | ------------ | ------ | -------- | -------------------------- | | `connection` | string | Yes | MCP server connection name | ## Tool guards filters set `dome tool guards filters set ` Replace the entire ordered Filter chain for one direction on a tool connection. Only `json`-kind Filters may be assigned. ```bash theme={"system"} # Scrub PII from tool arguments before they leave, and again from results dome tool guards filters set github-mcp \ --direction request \ --filters tool-scrub dome tool guards filters set github-mcp \ --direction response \ --filters tool-scrub ``` | Flag | Type | Required | Description | | ------------- | -------------------- | -------- | -------------------------------------------------------------------------------------------- | | `--direction` | string | Yes | `response` (tool result returned to the agent) or `request` (tool arguments before dispatch) | | `--filters` | comma-separated list | Yes | Ordered Filter names or UUIDs; empty clears the chain | `request` filters run over tool arguments **before** the call reaches the upstream MCP server — matched fields are redacted or omitted, or the whole call is blocked. `response` filters run over the tool result before the agent sees it. # dome rules Source: https://docs.domesystems.ai/cli/secure/rules Deploy, validate, simulate, and rollback authorization rules | Command | | | --------------------- | --------------------------------- | | [Apply](#apply) | `dome rules apply [files...]` | | [List](#list) | `dome rules list` | | [Show](#show) | `dome rules show` | | [Validate](#validate) | `dome rules validate [files...]` | | [Rollback](#rollback) | `dome rules rollback ` | | [Generate](#generate) | `dome rules generate` | | [Simulate](#simulate) | `dome rules simulate` | *** ## Apply `dome rules apply [files...]` Deploy a rule bundle from one or more Cedar files. ```bash theme={"system"} dome rules apply rules.cedar [flags] ``` | Flag | Type | Description | | --------------- | ------ | -------------------------------------------------------------------------------------------------- | | `--agent` | string | Agent name or ID — deploy rules scoped to this agent | | `--name` | string | Friendly name for the rule bundle | | `--court-scope` | string | Directional [Court-review marker](/govern/judges): `none` (default), `reduce`, `expand`, or `both` | After a successful deploy, `apply` prints advisory `warning:` lines to stderr for any `Dome::MCPTool` reference that does not match a tool in the target workspace's persisted MCP tool catalog (typo, rename, or a tool not yet observed). Warnings never block the deploy. ## List `dome rules list` List rule bundle history. | Flag | Type | Description | | --------- | ------ | -------------------------------------------------- | | `--agent` | string | Agent name or ID — list rules scoped to this agent | | `--limit` | int | Maximum results (default: 20) | ## Show `dome rules show` Show effective rules across the full scope hierarchy. | Flag | Type | Description | | --------- | ------ | -------------------------------------------------- | | `--agent` | string | Agent name or ID — show rules scoped to this agent | ## Validate `dome rules validate [files...]` Validate Cedar rule files without deploying. Returns errors and prints catalog warnings (unknown tools or connections) to stderr. Pass `--agent` to run catalog checks against an agent's scope — without it, only syntax and semantic validation runs. ```bash theme={"system"} dome rules validate rules.cedar [flags] ``` | Flag | Type | Description | | --------- | ------ | ---------------------------------------------------------- | | `--agent` | string | Agent name or ID — validate rules against this agent scope | ## Rollback `dome rules rollback ` Rollback to a previous rule bundle. The bundle UUID (from `dome rules list`) encodes the scope — no extra flags needed. ## Generate `dome rules generate` Generate starter Cedar authorization rules. | Flag | Type | Description | | -------------- | ------ | ---------------------------------------- | | `--from-tools` | bool | Generate rules based on registered tools | | `--output` | string | Write rules to file (default: stdout) | ## Simulate `dome rules simulate` Simulate an authorization decision against active rules. | Flag | Type | Required | Description | | ----------------- | --------- | -------- | ------------------------------------------------------------ | | `--agent` | string | Yes | Agent ID or name | | `--action` | string | Yes | Action to evaluate (e.g., `mcp:call`) | | `--resource` | string | Yes | Resource identifier | | `--resource-type` | string | No | `mcp_tool`, `llm_model`, or `resource` (default: `mcp_tool`) | | `--eval-context` | key=value | No | Evaluation context key-value pairs (repeatable) | | `--actas-sub` | string | No | Act-as subject identifier | | `--actas-email` | string | No | Act-as email | | `--actas-roles` | string | No | Act-as roles (comma-separated) | | `--actas-groups` | string | No | Act-as groups (comma-separated) | # dome auth Source: https://docs.domesystems.ai/cli/session/auth Authentication management | Command | | | ----------------- | ------------------ | | [Login](#login) | `dome auth login` | | [Logout](#logout) | `dome auth logout` | | [Status](#status) | `dome auth status` | *** ## Login `dome auth login` Authenticate with the Dome Platform. Opens your default browser for authentication. After login, an identity session is created and all available org/tenant contexts are synced. ```bash theme={"system"} dome auth login ``` | Flag | Type | Description | | --------- | ------ | -------------------------------------- | | `--token` | string | API token for CI/headless environments | ## Logout `dome auth logout` Sign out and remove stored credentials. ## Status `dome auth status` Show authentication status including active context, server, org, tenant, and role. # dome context Source: https://docs.domesystems.ai/cli/session/context Manage CLI contexts (org/tenant/workspace sessions) Contexts represent authenticated sessions scoped to a specific org, tenant, and workspace. | Command | | | ------------------- | ------------------------------------------- | | [List](#list) | `dome context list` | | [Current](#current) | `dome context current` | | [Use](#use) | `dome context use [name]` | | [Sync](#sync) | `dome context sync` | | [Rename](#rename) | `dome context rename ` | | [Delete](#delete) | `dome context delete ` | | [Alias](#alias) | `dome context alias ` | *** ## List `dome context list` List all contexts in org/tenant/workspace hierarchy. Aliases: `ls`. ## Current `dome context current` Show the active context name. ```text theme={"system"} prod/acme/backend ``` With `--format json`, the response includes the context name, level, server, and available organization, tenant, and workspace names. Use `dome gateway list` or `dome gateway get` for complete Gateway URLs. ## Use `dome context use [name]` Switch the active context by name, alias, or shorthand. ```bash theme={"system"} # By exact name dome context use "prod/acme/backend" # By alias dome context use backend # By shorthand (workspace name) dome context use backend # By shorthand (tenant/workspace) dome context use acme/backend # By shorthand (org/tenant/workspace) dome context use prod/acme/backend ``` If the shorthand is ambiguous, an error lists the matching contexts. | Flag | Type | Description | | --------------------- | ---- | ------------------------------ | | `--interactive`, `-i` | bool | Interactively select a context | ## Sync `dome context sync` Refresh contexts from server memberships. Fetches all org/tenant memberships and creates or updates local contexts with scoped session tokens. | Flag | Type | Description | | ---------- | ------ | ---------------------------------------------------------- | | `--server` | string | Sync only this server (default: all authenticated servers) | ## Rename `dome context rename ` Rename a context. ## Delete `dome context delete ` Delete a context. ## Alias `dome context alias ` Set or remove an alias for a context. Pass two args (context-name and alias) to set an alias. With `--remove`, pass a single arg (the alias to remove). | Flag | Type | Description | | ---------- | ---- | -------------------------------------------------------- | | `--remove` | bool | Remove the alias (pass the alias as the single argument) | # dome config Source: https://docs.domesystems.ai/cli/settings/config Platform configuration ## Get `dome config get [key]` Get a configuration value by key. ```bash theme={"system"} dome config get audit.retention_days dome config get registry.staleness_window ``` # dome platform-keys Source: https://docs.domesystems.ai/cli/settings/platform-keys Manage platform API keys for users and CI Platform API keys provide programmatic access for users and CI pipelines. For agent authentication keys, use `dome agents create-key`. | Command | | | --------------------------- | ---------------------------------------- | | [List](#list) | `dome platform-keys list` | | [Create](#create) | `dome platform-keys create` | | [Revoke](#revoke) | `dome platform-keys revoke [name-or-id]` | | [Permissions](#permissions) | `dome platform-keys permissions` | *** ## List `dome platform-keys list` List platform API keys for the current workspace. ## Create `dome platform-keys create` Create a new platform API key. | Flag | Type | Required | Description | | --------------- | --------- | ----------- | ---------------------------------------------------------------------------------------------------------------- | | `--name` | string | Yes | Key name | | `--permissions` | string\[] | Conditional | Permissions to grant. Required in non-interactive mode; in an interactive terminal a picker is shown if omitted. | | `--expires-at` | string | No | Expiration time (RFC3339) | The interactive picker fetches the grantable permissions for your role at the current scope. Use [`dome platform-keys permissions`](#permissions) to discover the same list non-interactively. ## Revoke `dome platform-keys revoke [name-or-id]` Revoke a platform API key by name or ID. ## Permissions `dome platform-keys permissions` List the permissions you can grant to a new platform API key at the current context's scope. The output is exactly what `dome platform-keys create --permissions` will accept for your role; the server re-validates on submit. Use this for CI scripts that need to know valid `--permissions` values, or to confirm what `create` would offer in its interactive picker. The scope follows your active context: a workspace context returns workspace permissions, a tenant context adds tenant permissions, and an org context adds org permissions on top. ```bash theme={"system"} dome platform-keys permissions ``` ```bash theme={"system"} dome platform-keys permissions --format json ``` Workspace admins do not see tenant-only permissions such as `agent.delete` or `agentkey.manage` in this list. Switch to a tenant or org context (or have a tenant admin run the command) to grant them. # dome tenants Source: https://docs.domesystems.ai/cli/settings/tenants List, inspect, and create tenants under your active organization Manage tenants in your organization. List, get, and create are gated on the org-level `tenant.manage` permission (held by org `admin` or `owner`); `get` additionally admits any caller whose active session is scoped to the requested tenant. ## List `dome tenants list` List tenants under your active organization. | Flag | Type | Required | Description | | ---------- | ------ | -------- | ------------------------------------------------------ | | `--org-id` | string | No | Target org UUID. Defaults to the active context's org. | **Permission:** Org-level `tenant.manage` (held by org `owner` or `admin`). ```bash title="List tenants in the active org" theme={"system"} dome tenants list ``` ```bash title="Target a specific org" theme={"system"} dome tenants list --org-id 7d2c… ``` Output is a table of `ID`, `NAME`, `PLAN`, and `CREATED`. Pass `--format json` for the raw response. ## Get `dome tenants get ` Get a single tenant by id. The caller is admitted if either (a) their active session is scoped to the requested tenant, or (b) they hold org `admin` or `owner` on the requested tenant's org. | Argument / Flag | Type | Required | Description | | --------------- | ------ | -------- | ------------------------------------------------------ | | `` | string | Yes | Tenant UUID | | `--org-id` | string | No | Target org UUID. Defaults to the active context's org. | **Permission:** Tenant-scoped session for the requested tenant **or** org-level `tenant.manage` on the requested tenant's org. ```bash title="Get a tenant in the active org" theme={"system"} dome tenants get 5b1e… ``` ```bash title="Get a tenant from a specific org" theme={"system"} dome tenants get 5b1e… --org-id 7d2c… ``` Output is a key/value table including ID, name, plan, org id, default gateway, and created timestamp. Pass `--format json` for the raw response. ## Create `dome tenants create` Create a new tenant under your active organization. The caller becomes the initial tenant admin. | Flag | Type | Required | Description | | ---------- | ------ | -------- | ----------------------------------------------------------------------- | | `--name` | string | Yes | Tenant display name | | `--plan` | string | No | Tenant plan: `essential` (default), `managed`, `enterprise`, `complete` | | `--org-id` | string | No | Target org UUID. Defaults to the active context's org. | **Permission:** Org-level `tenant.manage` on the org named in `--org-id` (held by org `owner` or `admin`). The permission check runs against the request's target org, not your active context — a caller authorized on org X cannot create a tenant in org Y by overriding `--org-id`. ```bash title="Create a tenant under the active org" theme={"system"} dome tenants create --name acme-prod --plan managed ``` ```bash title="Target a specific org" theme={"system"} dome tenants create --name acme-staging --org-id 7d2c… ``` `dome tenants create` works directly after `dome auth login` — no prior `dome use` is required. As long as `--org-id` (or the default active org) names an org you hold `tenant.manage` on, the request succeeds. Creating a new organization is operator-only. Contact Dome to provision a new org. # dome workspace Source: https://docs.domesystems.ai/cli/settings/workspace Manage workspaces, members, act-as enforcement, policy freshness, and verification providers ## Workspace Lifecycle ### List `dome workspace list` List workspaces in the active tenant. Aliases: `ls`. ### Create `dome workspace create` Create a new workspace. | Flag | Type | Required | Description | | -------- | ------ | -------- | ---------------------- | | `--name` | string | Yes | Workspace display name | ### Get `dome workspace get ` Get workspace details. The output includes the configured `Freshness Window` — how long the gateway may serve cached policy for the workspace before failing closed. ### Delete `dome workspace delete ` Delete a workspace permanently. ## Members ### Members list `dome workspace members list [workspace-id-or-slug]` List workspace memberships. ### Members set `dome workspace members set [workspace-id-or-slug]` Set a user's role in a workspace. | Flag | Type | Required | Description | | -------- | ------ | -------- | ----------------------------------------------- | | `--user` | string | Yes | User email | | `--role` | string | Yes | Role: `admin`, `operator`, `security`, `viewer` | ### Members remove `dome workspace members remove [workspace-id-or-slug]` Remove a user's workspace membership. If the user has active API keys, an interactive picker prompts you to select which to revoke; pass `--revoke-all-keys` or `--revoke-keys` to skip the prompt. | Flag | Type | Required | Description | | ------------------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------- | | `--user` | string | Yes | User email | | `--revoke-keys` | string\[] | No | Comma-separated API key IDs to revoke (skips interactive prompt). Mutually exclusive with `--revoke-all-keys`. | | `--revoke-all-keys` | bool | No | Revoke all API keys created by the user (skips interactive prompt). Mutually exclusive with `--revoke-keys`. | ## Act-As Enforcement ### Actas get `dome workspace actas get [workspace-id-or-slug]` Get the act-as enforcement policy. ### Actas update `dome workspace actas update [workspace-id-or-slug]` Update the act-as enforcement policy. | Flag | Type | Description | | ------------------- | --------- | --------------------------------------------------- | | `--required` | bool | Require act-as header on all requests | | `--allowed-methods` | string\[] | Allowed verification methods (e.g., `oidc`, `hmac`) | ## Policy Freshness Window Each workspace has a configurable freshness window that bounds how long the gateway may serve cached authorization policy for that workspace before failing closed. Default is 90 seconds; allowed range is 30–86400 seconds. Enforcement is per-workspace — one workspace failing closed does not affect others. ### `dome workspace set-freshness-window [workspace-id-or-slug]` Set the freshness window (in seconds) for a workspace. | Argument | Type | Required | Description | | ------------------------ | ------ | -------- | --------------------------------------------------- | | `` | uint | Yes | Freshness window in seconds (30–86400) | | `[workspace-id-or-slug]` | string | No | Target workspace (defaults to the active workspace) | Once the window elapses, the gateway returns `access_denied` for every request in that workspace and emits a `authorization.staleness_rejected` audit event until the workspace's policy is fresh again. ## Verification Providers ### Verification-providers list `dome workspace verification-providers list [workspace-id-or-slug]` List verification providers. ### Verification-providers create `dome workspace verification-providers create [workspace-id-or-slug]` Create a verification provider. | Flag | Type | Required | Description | | ---------------------- | ------ | ----------- | ----------------------------------------------- | | `--name` | string | Yes | Provider name | | `--method` | string | Yes | Verification method: `oidc` or `hmac` | | `--oidc-discovery-url` | string | Conditional | OIDC issuer discovery URL (required for `oidc`) | | `--hmac-secret` | string | Conditional | HMAC shared secret (required for `hmac`) | ### Verification-providers delete `dome workspace verification-providers delete [workspace-id-or-slug]` Delete a verification provider. # dome mcp Source: https://docs.domesystems.ai/cli/standalone/mcp MCP server for AI agents ## Serve `dome mcp serve` Start an MCP server on stdio. Exposes Dome Platform operations as tools for MCP clients like Claude. Authentication is read from the active CLI context. ### Setup ```bash theme={"system"} dome auth login dome mcp serve ``` ### Claude Integration ```bash theme={"system"} claude mcp add dome -- dome mcp serve ``` Or add manually to your MCP config: ```json theme={"system"} { "mcpServers": { "dome": { "command": "dome", "args": ["mcp", "serve"] } } } ``` See the [MCP Server Reference](/reference/mcp) for the complete tool list organized by group. # dome version Source: https://docs.domesystems.ai/cli/standalone/version Show CLI version information ## Version `dome version` Show the dome CLI version, commit, build time, Go version, and OS/architecture. ```bash theme={"system"} dome version ``` | Flag | Type | Description | | --------- | ---- | ------------------------- | | `--check` | bool | Check for a newer version | # Agent lifecycle Source: https://docs.domesystems.ai/concepts/agents/agent-lifecycle The four states an agent moves through, what triggers each transition, and what cascades ## High-level overview The lifecycle is a directed graph with four states, two reversible transitions, and one terminal state. Provisioned and active agents can send traffic through the gateway. Suspended and revoked agents are rejected. Revocation is permanent. Suspension is reversible. Moving an agent through its lifecycle looks like this: 1. An operator registers an agent. 2. The agent stays provisioned until its first gateway request, then becomes active automatically. 3. During an incident the operator suspends the agent (optionally cascading to active children). 4. After the window, reactivate restores traffic for that agent alone. 5. If the agent is decommissioned, revoke permanently disables it and every descendant. Lifecycle state gates whether the agent identity can send traffic. That gate is independent of rotating keys or rewriting Rules. Agent lifecycle states: provisioned, active, suspended, revoked ### States | State | Description | Can receive traffic? | | --------------- | --------------------------------------------------- | -------------------- | | **provisioned** | Created but never used. Waiting for first request. | Yes | | **active** | Operational. Making requests and receiving traffic. | Yes | | **suspended** | Temporarily disabled by an operator. | No | | **revoked** | Permanently decommissioned. Terminal. | No | ### Transitions #### Provisioned to Active Happens **automatically** on first traffic. When a provisioned agent sends its first request through the gateway, the platform records the timestamp (`LastSeenAt`) and transitions the agent to active. No operator action required. #### Active to Suspended Triggered by an operator via `dome agents suspend`. Suspension is reversible. Use it for maintenance windows, incident response, or temporary access revocation. With `--cascade`, the platform also suspends all active child agents in the hierarchy. Without it, only the target agent is suspended. #### Suspended to Active Triggered by an operator via `dome agents reactivate`. The agent immediately resumes accepting traffic. #### Any State to Revoked Triggered by an operator via `dome agents revoke`. Revocation is **permanent** and **always cascades**. Every descendant in the hierarchy is revoked depth-first, regardless of their current state. There is no undo. ### Cascade semantics Agents form hierarchies through the `parent_id` field (max depth: 10). Cascade behavior differs between suspend and revoke: | Operation | Cascade behavior | Reversible? | | ----------- | --------------------------------------------------------- | ----------- | | **Suspend** | Optional (`--cascade`). Suspends active descendants only. | Yes | | **Revoke** | Always. Revokes all descendants depth-first, any state. | No | The parent must belong to the same tenant as the child. Cross-tenant hierarchies are not permitted. Hierarchy grants no access on its own. Each agent reaches only what you allow it. Refer to [Agents](/concepts/agents#lifecycle-and-hierarchy) concept and [Agents](/connect/agents#hierarchy). ### Heartbeat and staleness The platform tracks `LastSeenAt` for every agent. It is updated on each request through the gateway. This timestamp serves two purposes: * **Automatic activation.** Transitions provisioned agents to active on first traffic. * **Staleness detection.** Operators can identify agents that have stopped communicating, though there is no automatic suspension on staleness. ## Next steps With the agent lifecycle model in mind, continue with: * [Agents](/concepts/agents) concept for how agents are identified and authenticated * [Agent Identity](/concepts/agents/identity) concept for credentials and act-as claims * [Agents](/connect/agents) to suspend, reactivate, revoke, and manage hierarchy * [Rules](/concepts/controls/rules) concept when authorization should also reference agent state # Agent identity Source: https://docs.domesystems.ai/concepts/agents/identity How agents authenticate: API keys, token exchange, session tokens, and act-as claims ## High-level overview Dome authenticates agents in layers. An API key is the long-lived secret. The Identity service exchanges it for a short-lived session token. Every API call, MCP invocation, and gateway request carries that token. Services verify it locally. When the agent acts for a person, an act-as claim on `X-Dome-Act-As` can carry verified end-user identity beside the agent. Issuing credentials and authenticating looks like this: 1. An operator registers an agent and creates an API key. 2. The runtime calls `ExchangeToken` with that key, receives a JWT, and sends the JWT on each request. 3. Dome confirms the agent is active, reads claims from the token, and proceeds to Gateway and Rule checks. 4. If the call also carries a verified act-as header, Rules can test `principal.act_as` and audit records the end user next to the agent. Every request authenticates the agent with a self-contained session token. End-user claims are optional verified context beside that agent. Identity flow: API Key → Token Exchange → Session Token → Authenticated Request The gateway uses separate credentials for outbound backend calls. Follow the complete request path in [Architecture](/concepts/architecture). The CLI handles token exchange automatically. Run `dome auth login` and the CLI manages key exchange, token refresh, and header injection. ### How authentication works 1. **API key issuance.** Register an agent and [create an API key](/connect/agents#create-key). The key is returned once. Store it immediately. 2. **Token exchange.** The agent sends the API key to the Identity service (`ExchangeToken` with `grant_type: api_key`). The service validates the key, confirms the agent is active, and returns a short-lived JWT. Tune the lifetime per-workspace via [`identity.token_ttl`](/manage/settings#token-lifetime) (default `10m`, range `1m`–`24h`). 3. **Session token.** The JWT carries the agent's identity for all subsequent requests. There is no round-trip to the Identity service on every call. The token is self-contained and verified locally. 4. **Authenticated requests.** Every API call, MCP invocation, and gateway request carries the session token. Services extract identity without additional lookups. ### Session token The session token is a JWT (HMAC-SHA256) carrying: | Claim | Description | | -------------- | -------------------------------------------------- | | `sub` | Agent UUID | | `jti` | Unique token ID | | `iss` | `dome` | | `aud` | `["dome"]` | | `iat` / `exp` | Issued-at and expiration timestamps | | `agent_id` | Agent UUID | | `tenant_id` | Tenant UUID. Scopes all operations to this tenant. | | `workspace_id` | Workspace UUID (when workspace context is set) | | `capabilities` | Agent capability strings | | `api_key_id` | The specific API key that minted this token | The token is self-contained: any service can verify it locally using the signing key without calling the Identity service. Expiration is enforced on every request. ### Act-as identity Act-as identity identifies the end user represented by a [delegated agent](/concepts/identity-patterns#delegated-identity). Attach that user's claims with the `X-Dome-Act-As` header. Configure providers and the workspace floor on [Delegated agents](/connect/agents/delegated). #### What it carries Verified claims land on `principal.act_as` (`sub`, `email`, `roles`, `groups`, and custom `claims`). Claim list for setup: [Delegated agents](/connect/agents/delegated#identity-claims). Attribute types for policy: [Rules](/reference/controls/rules#principal-attributes). #### How it's verified Act-as claims are untrusted by default. Verification (`oidc`, `hmac`, `bound`, or `none` for development only) ensures the agent is not fabricating user identities. Method catalog: [Delegated agents](/connect/agents/delegated#verification-methods). Configure verification at the [workspace level](/connect/agents/delegated#require-verified-identity) as a baseline, or per-agent for tighter control. Workspace enforcement is a floor. Agents can add stricter verification but cannot weaken it. #### Act-as in authorization rules Once verified, act-as claims are available in Cedar rules: ```cedar title="rules.cedar" theme={"system"} // Allow tool access only when the end-user has the analyst role permit( principal, action == Dome::Action::"mcp:call", resource ) when { principal.act_as.roles.contains("analyst") }; ``` Full `principal.act_as` attribute notes are on the [Rules](/reference/controls/rules) reference. ## Next steps With that authentication and act-as model in mind, continue with: * [Agents](/concepts/agents) concept for how agents are identified and authenticated * [Identity Patterns](/concepts/identity-patterns) concept for standing vs delegated design * [Agents](/connect/agents) to create keys and manage agents * [Delegated agents](/connect/agents/delegated) for act-as providers, methods, claims, and enforcement * [Architecture](/concepts/architecture) concept for where identity sits on the request path # Agents Source: https://docs.domesystems.ai/concepts/agents/index Why Dome uses agent identity as the principal for auth, authorization, and audit ## High-level overview Registering an agent creates a durable identity in a workspace. That identity is separate from credentials and from access. An API key or short-lived token lets the runtime authenticate as the agent. Allowed resources, Gateway access grants, and Rules decide what requests Dome permits. You can rotate a key without changing permissions, or change permissions without issuing a new key. Registering an agent and calling through Dome looks like this: 1. An operator registers `data-pipeline`, allows a Gateway and the tools behind it, and creates an API key. 2. The runtime exchanges the key for a session token and calls through the Gateway. 3. Dome authenticates the agent, checks the access grant and resource membership, evaluates Rules (and Guards and Quotas on the content path), and records the outcome against that agent identity. 4. Suspending the agent later stops traffic for that identity without deleting the record. One agent identity covers authentication, authorization, and audit. Credentials and access stay separate. ### Identity is not permission An agent credential identifies the caller. It does not contain the agent's permissions and is not bound to a specific Gateway. The same agent credentials can use multiple Gateways when the agent has an access grant to each one. Three separate checks must allow a request: | Check | Asks | | ---------------- | --------------------------------------------------- | | **Access grant** | May this agent use the Gateway? | | **Membership** | Is this resource in the Gateway? | | **Rule** | May this agent perform this action on the resource? | Allowed resources on the agent record are shorthand for creating Rules in the agent's rule bundle. Changing them needs `rules.deploy` in addition to permission to edit the agent. When the shorthand cannot express a condition, author Rules directly. Refer to [Rules](/concepts/controls/rules) concept and [Agents](/connect/agents#allowed-resources). ### Credentials Agents authenticate with an API key or a short-lived agent token minted from a key. Keys are issued once and cannot be retrieved again. Rotate or revoke a key to cut off that credential. Session tokens carry the agent UUID, tenant and workspace context, capabilities, and the API key id that minted them. Details are on the [Agent Identity](/concepts/agents/identity) concept. ### Capabilities Capabilities are permission strings declared on an agent and carried in its token, such as `mcp:call` or `llm:invoke`. They describe classes of action the agent may attempt. Cedar Rules then authorize each specific request. Dome derives many capabilities from allowed resources. You set a capability yourself when a Rule tests a string Dome would not derive. ### Delegated identity An agent can act for a person rather than only for itself (**delegated identity**). When it presents an act-as claim, Dome verifies the claim before authorizing the request, and the audit trail records the end user alongside the agent. Workspace and per-agent settings control whether a claim is required and which verified identities the agent may present. Refer to [Delegated agents](/connect/agents/delegated) and [Identity Patterns](/concepts/identity-patterns) concept. ### Lifecycle and hierarchy Every agent has a lifecycle state: provisioned, active, suspended, or revoked. Provisioned and active agents can send traffic. Suspended and revoked agents are rejected. Revocation is permanent and always cascades to descendants. An agent can have a parent in the same tenant (max depth 10). Hierarchy grants no access on its own. Refer to [Agent Lifecycle](/concepts/agents/agent-lifecycle) concept. ## Next steps With the agent identity model in mind, continue with: * [Architecture](/concepts/architecture#agents) concept for where agents sit among components and controls * [Agent Identity](/concepts/agents/identity) concept for API keys, tokens, and act-as claims * [Agent Lifecycle](/concepts/agents/agent-lifecycle) concept for states and transitions * [Identity Patterns](/concepts/identity-patterns) concept for standing vs delegated design * [Gateways](/concepts/gateways) concept for reachability after identity * [Rules](/concepts/controls/rules) concept for what an agent may do once it reaches a resource * [Agents](/connect/agents) to register, issue keys, and manage agents # Authorization model Source: https://docs.domesystems.ai/concepts/architecture/authorization-model How Cedar rules evaluate permit and forbid against the assembled effective policy ## How a request is decided Each request maps to one principal (`Dome::Agent`), one action (`Dome::Action`), and one resource (`Dome::MCPTool` or `Dome::LLMModel`). Conditions may also read request `context` and, when present, verified act-as claims on the principal. ```cedar title="examples.cedar" theme={"system"} permit( principal == Dome::Agent::"data-pipeline", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"database-query" ); forbid( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"production-deploy" ) unless { principal.capabilities.contains("production:deploy") }; ``` ### Evaluation semantics 1. The evaluator checks every rule in the effective policy against the request. 2. If **any** `forbid` matches, the request is **denied**, regardless of any matching `permit`. 3. If **no** `forbid` matches and **at least one** `permit` matches, the request is **allowed**. 4. If **no rule matches at all**, the request is **denied** (default-deny / fail-closed). `forbid` always wins. A `forbid` at organization scope cannot be overridden by a `permit` at agent scope. Design rules accordingly: broad restrictions at higher scopes, specific allowances at lower scopes. ## Effective policy The effective policy is the merged set of all active rule bundles across the [scope hierarchy](/concepts/platform/scopes). Assembly works as follows: 1. Fetch the active bundle at each scope: organization, tenant, workspace, agent. 2. Merge Cedar files with scope-prefixed filenames (`org/rules.cedar`, `workspace/rules.cedar`) to prevent name collisions. 3. Compute a composite content hash from the merged file list. 4. Load the merged policy into the evaluator as a single PolicySet. The gateway polls for content-hash changes on a sync interval (default: 10 seconds). When a bundle is deployed or rolled back, the hash changes and the gateway picks up the new policy on the next sync. ### Per-workspace vs per-agent evaluation | Deployment | Evaluator | Use case | | --------------------------- | ------------------------------------------------------------------------------ | -------------------------------- | | **Hosted gateway** | `WorkspaceRuleEvaluator` maintains separate policy sets per assigned workspace | Multi-tenant shared gateway | | **SDK / dedicated runtime** | `RuleEvaluator` uses a single workspace policy set | Dedicated per-service deployment | Both evaluators enforce the same Cedar semantics. The hosted gateway adds per-workspace freshness checks. If a workspace's policy has not synced within its configured freshness window, requests are denied with a `authorization.staleness_rejected` audit event. ## Fail-closed behavior The authorization model is fail-closed at every level: * **No matching rule:** denied. When [Intelligent Authorization](/concepts/intelligent-authorization) convenes on live traffic, a `no_match` in a `live`-mode workspace can be overridden by a Court `permit`. Until then `no_match` always resolves to the default-deny. * **Evaluator error:** denied. * **Stale policy:** denied (hosted gateway only, per-workspace freshness window). * **No policy loaded:** denied. * **Court unavailable:** denied with `reason: "court_unavailable"` when fewer than two judges return a usable vote. Cedar itself has no permissive or audit-only mode. Use [simulation](/govern/rules/simulate) to test rule changes before deploying. Intelligent Authorization `audit` mode is a separate construct. It records Court rulings without enforcing them so you can validate the panel before flipping to `live`. ## Next steps With that Cedar evaluation model in mind, continue with: * [Rules](/concepts/controls/rules) concept for bundles, scopes, and effective Rules * [Rules](/reference/controls/rules) reference for entity, action, and attribute catalogs * [Authorize Access](/govern/rules) to write, validate, simulate, and apply Rules * [Permissions](/concepts/platform/permissions) concept for platform RBAC (separate from Cedar) * [Scopes](/concepts/platform/scopes) concept for the hierarchy that feeds effective policy # Data plane Source: https://docs.domesystems.ai/concepts/architecture/dataplane Why Dome puts a fail-closed runtime between agents and their tools and models ## High-level overview The data plane is the process that enforces traffic to Gateway surfaces. A Gateway is the named access surface you configure (membership and grants). Two **ingresses** share one engine, credential store, and audit pipeline. Both require a Gateway prefix on the wire: * [Tools gateway](/concepts/gateways/tools-gateway) concept — MCP and related tool traffic at `/gateways//mcp` * [LLM gateway](/concepts/gateways/llm-gateway) concept — OpenAI- and Anthropic-shaped model traffic at `/gateways//v1/...` A request through the data plane looks like this: 1. An agent sends a request to `/gateways//...` with a Dome credential. 2. The data plane resolves the Gateway, authenticates the agent, and checks admission. 3. It evaluates Rules (and Quotas on metered calls) and applies request Guards. 4. It calls the backend with Dome-injected credentials, applies response Guards, and writes an audit event. The agent never receives the backend secrets. The data plane is fail-closed. Stale policy, missing rules, unknown Gateways, and evaluation errors deny the request rather than forwarding ungoverned traffic. ### Why the data plane exists Without a shared enforcement runtime, each agent integration would need its own way to authn, authz, inject credentials, filter content, and audit. The data plane centralizes that: * **One hot path** for tools and models, so Rules, Guards, Quotas, and audit mean the same thing on both protocols * **Server-side credentials** so agents authenticate to Dome, not to every upstream * **Fail closed** when policy or Gateway config cannot be trusted * **Deploy the same engine** as hosted, dedicated, or in-process SDK without changing rules ### How a request moves Each call walks the same stages on the hot path. Audit records the outcome at the end: Data plane pipeline: Agent → Ingress → Evaluate → Egress → Filter → Backend, with Audit below | Stage | What happens | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Ingress** | Resolve the Gateway from the URL, validate the agent credential, check Gateway admission, verify act-as when present, apply kill switches and rate limits | | **Evaluate** | Load the workspace's effective Cedar policy and decide allow or deny. Stale policy denies. [Quotas](/concepts/controls/quotas) can stop metered calls here | | **Egress** | Resolve the backend or model, inject stored credentials, forward over the configured protocol | | **Filter** | Apply [Guards](/concepts/controls/guards) and related content controls before the agent sees the response | | **Audit** | Emit an event for allowed, denied, filtered, and errored outcomes into the shared trail | Protocol details for MCP live on the [Tools gateway](/concepts/gateways/tools-gateway) concept. Model ingress behavior lives on the [LLM gateway](/concepts/gateways/llm-gateway) concept. Connections and routing live on the [Models](/concepts/resources/models) concept and [Pools](/concepts/resources/model-pools) concept. How Cedar decides a call is on the [Rules](/concepts/controls/rules) concept and [Authorization Model](/concepts/architecture/authorization-model) concept. ### Credentials stay server-side Backends declare an auth method (`none`, `api-key`, `oauth`) and a credential type: * **Shared** uses one credential for the connection. Every allowed caller reuses it. * **Per-user** gives each verified end user their own credential. First use can prompt provisioning (magic link / OAuth consent) Dome stores runtime secrets and injects them on egress. Agents present Dome credentials only. Configure connections on [Tools](/connect/resources/tools) and [Models](/connect/resources/models). ### Policy sync and fail-closed The hosted data plane polls the control plane for Gateway membership, grants, Rules, and related config (about every 10 seconds by default). Each workspace has a **freshness window**. If policy has not synced inside that window, requests deny with staleness rather than using an untrusted cache. Widen the window when rule changes are rare and brief sync gaps are acceptable. Narrow it when policy changes must bite quickly. ### Deployment shapes The same engine runs wherever your infrastructure demands. Behavior matches. Only where the control plane and data plane run changes: | Control plane | Data plane | Best for | | ------------- | ------------------------------------------- | ---------------------------------------- | | Dome-hosted | SDK in your code | Minimal footprint, in-process evaluation | | Dome-hosted | Hosted data plane (Dome-managed) | Fastest setup, fully managed | | Dome-hosted | Sidecar or self-hosted data plane (you run) | Control over the data path | | Self-hosted | Self-hosted | Full isolation, air-gapped environments | All combinations run the same Cedar evaluation engine and produce the same audit events. ## Next steps With the data-plane hot-path model in mind, continue with: * [Architecture](/concepts/architecture) concept for where the data plane sits on the request path * [Gateways](/concepts/gateways) concept for the access surfaces the data plane serves * [Tools gateway](/concepts/gateways/tools-gateway) concept for MCP ingress * [LLM gateway](/concepts/gateways/llm-gateway) concept for model ingress * [Rules](/concepts/controls/rules) concept for authorization after admission * [Guards](/concepts/controls/guards) concept for content inspection on the path * [Audit events](/concepts/audit) concept for the evidence trail * [Develop](/develop#errors-and-denials) for denial and credential wire shapes # Architecture Source: https://docs.domesystems.ai/concepts/architecture/index How Dome Platform components govern agent access to tools and models Dome governs how AI agents access tools and models. The following components decide what happens to each request. For a high-level platform overview, start with [What is Dome?](/what-is-dome). ## High-level overview Dome architecture separates into three kinds of concept: the **components** you define, the **controls** you apply to them, and the **audit** that records what happened. The **control plane** is where you configure those components and controls and sync that configuration. The **[data plane](/concepts/architecture/dataplane)** is where requests are enforced on the hot path. **Components** are what you define in the control plane: * **Agents** identify the applications and automated processes making requests. * **Resources** are the tools, model connections, and model pools exposed through Dome. * **Gateways** group resources behind named endpoints and control which agents can reach them. **Controls** are what you apply to components to govern traffic: * **Rules** authorize what an agent may do after reaching a Gateway. * **Guards** inspect request and response content before it reaches its destination. * **Quotas** cap what an agent can spend on model calls over a window. **[Audit events](/concepts/audit)** record every request's outcome across control-plane changes and data-plane decisions, so you can review or investigate it later. Within a workspace, these concepts have the following relationships: An agent reaches tools, model pools, and model connections through a Gateway while Rules, Guards, and Quotas govern traffic An agent reaches a Gateway through an access grant, then reaches the tools, model pools, and model connections behind it through membership. On every call that passes through the data plane, Rules, Guards, and Quotas govern the traffic, and Audit logs the outcome. The access grant, membership, and rule are three separate checks, and all three must allow the request: | Check | Asks | | ---------------- | --------------------------------------------------- | | **Access grant** | May this agent use the Gateway? | | **Membership** | Is this resource in the Gateway? | | **Rule** | May this agent perform this action on the resource? | ### Agents An **agent** is an identity in Dome. It represents the application, assistant, or automated process making a request. Dome does not run the agent. An agent authenticates each request with an API key or a short-lived agent token. The credential identifies the agent, but it does not contain the agent's permissions and is not bound to a specific Gateway. Each agent can have one or more API keys. Because identity is independent from the endpoint, you can use the same agent credentials with multiple Gateways, as long as the agent has access to each Gateway. Refer to [Agents](/concepts/agents) concept for how identity works across Gateways. ### Resources A **resource** is something an agent can call through Dome. Dome supports three resource types: 1. **Tools** are MCP servers, local processes over stdio, or catalog-backed tool integrations. 2. **Model connections** point to LLM providers such as Anthropic, OpenAI, Google, or a self-hosted runtime. 3. **Pools** group model connections and add routing, weighting, and failover. Refer to [Resources](/concepts/resources) concept for how backends are modeled, and [Tools](/concepts/resources/tools) concept, [Models](/concepts/resources/models) concept, and [Pools](/concepts/resources/model-pools) concept for each type. ### Gateways A **Gateway** is a named logical access surface in Dome. Each Gateway packages a selected set of resources behind an endpoint and defines which agents may use it. A Gateway is not a separately deployed data-plane process. A Gateway can contain many resources, and the same resource can belong to more than one Gateway. A registered resource that belongs to no Gateway is **Unreachable**. A Gateway exposes both tools and models through different protocol endpoints: * Tools use `/gateways//mcp` * Models and model pools use `/gateways//v1/...` For a model call, the request can target a direct model connection or a model pool. Configured routing can also select a matching or default pool. A direct model connection must be a member of the Gateway. When a pool is selected, Dome chooses one of its model connections. Tools and models do not have to share a Gateway. You can use different Gateways for different jobs, as long as each Gateway grants that agent access and Rules permit each request. An **access grant** gives an agent permission to use a Gateway. A Gateway can also grant access to every agent in its workspace. ### Rules An access grant and membership get the request to the resource. A **rule** then controls what the agent may do once it's there, deciding tool calls and model calls the same way. Cedar, the default rule engine, evaluates the agent, requested action, resource, and request context, and denies the request by default when no rule matches. Dome combines rules from organization, tenant, workspace, and agent scopes. Rules can permit access to one tool while forbidding another, even when both belong to the same Gateway. Dome fails closed if it can't verify current policy, rather than falling back to a stale or default configuration. ### Guards **Guards** inspect content in both directions. Request Guards inspect prompts or tool arguments before Dome calls the resource. Response Guards inspect model output or tool results before content returns to the agent. Guards use ordered, connection-specific Filters. A Filter can redact, omit, or block matching content. Dome fails closed when an assigned Filter cannot be evaluated, rather than forwarding uninspected traffic. Refer to [Guards](/concepts/controls/guards) concept for the model and [Configure Guards](/govern/guards) for configuration. ### Quotas **Quotas** cap spend on model calls. Each Quota sets a budget over a window at a chosen scope: workspace, agent, Gateway, pool, model, or verified end user. Dome checks applicable Quotas before it authorizes the call. An exhausted total cap rejects the request. An exhausted per-model budget can spill to another member of the pool. Refer to [Quotas](/concepts/controls/quotas) concept for the model and [Set Usage Limits](/govern/quotas) for configuration. ## How requests move through Dome Every request passes through Dome before it reaches a tool or model. Dome identifies the agent, enforces access and authorization, applies request Guards, calls the resource with protected backend credentials, and applies response Guards before returning content. The agent runtime sends an API key or agent token to a Gateway endpoint. Dome validates the credential, identifies the agent, and checks the active Gateway, access grant, and resource membership. If the request carries an act-as claim for an end user, Dome verifies it before exposing it to Rules or backends. For a metered call, Dome checks applicable spend Quotas. An exhausted limit stops the request here. Dome evaluates the agent, action, resource, and request context. A denied request stops here. Dome inspects configured prompts or tool arguments before they reach the resource. A Guard can transform or block the request. For an allowed request, Dome retrieves the backend credentials and calls the tool or model provider. The agent never receives those credentials. Dome inspects configured model output or tool results before disclosure, then returns the governed response. Dome writes an audit event for the permitted, denied, failed, or filtered result. Tool discovery follows the same core model. Tools blocked by Rules are omitted from the catalog. Other access or operational failures can return an empty catalog or deny the request. If a model produces a tool call, the model does not invoke the tool itself. The agent runtime receives the tool request and makes a second call through the Gateway's MCP endpoint. The model call and tool call are separate requests governed with the same agent identity. ## Workspaces and isolation An **organization** is a company's top-level account. It contains one or more **tenants**, hard isolation boundaries usually drawn around a business unit or product line, such as a `Commerce` tenant kept separate from a `Support` tenant. Each tenant holds **workspaces** that scope day-to-day work, such as separate production and staging workspaces for the same team. Agents, resources, Gateways, Rules, Guards, Quotas, and audit events all live inside a workspace. Dome enforces tenant and workspace boundaries on every configuration and runtime request, so nothing scoped to one workspace leaks into another. ## Next steps With the component and request-path model in mind, continue with: * [Scopes](/concepts/platform/scopes) concept for organization, tenant, and workspace isolation * [Agents](/concepts/agents) concept, [Resources](/concepts/resources) concept, and [Gateways](/concepts/gateways) concept for the components you define * [Rules](/concepts/controls/rules) concept, [Guards](/concepts/controls/guards) concept, and [Quotas](/concepts/controls/quotas) concept for the controls on each request * [Audit events](/concepts/audit) concept for the evidence trail * [Data plane](/concepts/architecture/dataplane) concept for fail-closed runtime placement # Audit events Source: https://docs.domesystems.ai/concepts/audit/index Why Dome keeps one evidence trail across configuration and runtime events ## High-level overview Dome keeps one audit system for configuration and runtime. Both write the same envelope into one workspace-scoped trail, with one query surface. Governance records both kinds of activity. The table below names each source, where its traffic arrives, and which actions it records. | Source | What it is | What it audits | | ----------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | **Configuration** | API, CLI, MCP, SDK, and dashboard calls that create or change settings | Agent lifecycle, rule deploys, grants, credentials, workspace settings, identity and security actions | | **Runtime** | The [data plane](/concepts/architecture/dataplane) path that authorizes tool and model traffic | Admission, rule decisions, quotas, Guards, tool and LLM call lifecycles | Joining a configuration change to a runtime decision looks like this: 1. An operator deploys a rule bundle. That write is a configuration state change. 2. Minutes later an agent calls a tool that the new rule denies. That denial is a runtime governing decision. 3. Both land as typed events in the same trail, so an investigator can move from "who changed policy?" to "what did that policy do at runtime?" without switching systems. Query, stream, and export, including `dome audit export` and scheduled forwarders, read that shared trail. Those surfaces do not choose a source. Filters narrow within the unified corpus. A few rules hold across the whole trail: * Configuration and runtime producers write the same envelope into one record. * If Dome cannot prove a governed mutation was recorded, it denies the action. * Use `activity_id` to follow a story across requests or planes, and `trace_id` to join one request. * Who queried or exported the trail is itself recorded in the trail. * Gateway decisions often leave `request_surface` unset. Join through correlation when you need client origin. ### How surfaces consume the trail Product surfaces do not invent separate evidence stores. They consume the same activity in different shapes: | Surface | Contract | Job | | ------------------------------------- | ---------------------------------------- | ------------------------------------------------------------ | | **Audit v1** | Full forensic envelope + typed `payload` | Durable evidence: query, stream, export, ingest | | **Webhooks** (`dome.webhook.v1`) | Slim public envelope + `data` | Real-time automation through signed HTTP delivery | | **OpenTelemetry / product telemetry** | Separate models | Observability and product analytics, not compliance evidence | [Audit events](/operate/audit) and [Stream Live Events](/operate/observe) both read **Audit v1**: the same workspace trail, same envelope, same type catalog, and same filter vocabulary. Audit queries and reconstructs. Streaming shows the present. [Subscribe to Events](/operate/webhooks) delivers a **public projection** of overlapping activity. Refer to [Webhooks](/concepts/audit/webhooks) concept for the delivery model. Many type strings match (for example `agent.suspended`), but the wire shape, field names, PII rules, and catalog size differ. Some names diverge (webhook `llm.output.filtered` vs audit `llm.model_result.filtered`). Prefer the webhook name on the wire for webhook consumers. ### What an event records Every event uses the same envelope. The envelope answers the forensic questions without opening the typed payload. * **Who:** `actor` and optional `identity_chain` (including verified act-as end users) * **Where:** `scope` (organization, tenant, workspace, agent) and `request_surface` when the call had a direct network origin * **What:** `event_type`, `primary_resource`, and related resources * **Outcome:** `result` (`attempted`, `succeeded`, `denied`, `failed`, `filtered`, …) * **When:** `occurred_at` at the producer, `accepted_at` when the audit pipeline accepted the event * **How to join:** `correlation` (`trace_id`, `activity_id`), plus `operation_id` / `parent_event_id` for multi-phase actions The payload holds event-specific detail such as the matched rule, tool name, token usage, or deny reason. Envelope fields stay filterable across classes, so operators do not learn a different schema per producer. Refer to [Events](/reference/events#envelope) reference for field-level contracts, enum forms, and filter syntax. ### Event classes Events group into four classes. Class is a lens for investigation, not a separate store. The table below lists each class and the kind of action it covers. | Class | Role | | -------------- | ---------------------------------------------------------------------------------------------- | | `governing` | Authorization and runtime decisions: allows, denies, filters, quotas | | `state_change` | Configuration mutations: agents, rules, resources, Gateways, integrations | | `security` | Authentication front door: login, session, impersonation, token exchange, credential rejection | | `audit_access` | Reads of the trail itself: query, get, stream, and export | Reading audit is audited. `audit_access` events are first-class evidence. Evidence of who inspected or exported the trail is part of the same record. Not every identity lifecycle signal is an audit event. `identity.session.created`, `identity.session.switched`, and `identity.session.rotated` live in operational telemetry (slog / OTEL), not Audit v1. Session rotation is a background \~5-minute token refresh, not a governed decision. `identity.session.terminated` remains on the trail. ### Why some decisions carry a policy snapshot A governing decision that applied a loaded Rule bundle stamps the policy snapshot into the payload: which bundle versions contributed, which rule matched, and the content-hash of the effective policy at decision time. That lets an investigator answer "which policy denied this?" and detect drift against today's deployed Rules. Events with no loaded bundle omit those fields. Refer to [Events](/reference/events#decision-provenance) reference for the stamped fields and which event types carry them. ### Correlation across sources Three identifiers nest so a single human activity can span many HTTP hops and both configuration and runtime. Narrower scopes sit inside wider ones. | Scope | Field | Typical use | | -------------------------------------- | -------------- | ------------------------------------------------- | | One action phase pair | `operation_id` | `attempted` → `succeeded` / `denied` | | One HTTP request cascade | `trace_id` | Everything stamped for one inbound request | | One session, LLM turn, or scripted run | `activity_id` | End-to-end chain across configuration and runtime | `trace_id` answers what happened in one request. `activity_id` answers what happened in the whole activity. An operator run that deploys rules and then exercises the gateway can share one activity chain. Configuration state changes and runtime tool calls group together when they carry the same `activity_id`. Chains are computed at read time inside a workspace. There is no stored chain object, only events that share an id. Trust labels (`server_owned` vs `caller_asserted`) describe how that id was set. Those labels do not split the chain. ### Why attribution differs by source Configuration events carry `request_surface`: surface, source IP, user agent, and HTTP method. A person or automation called Dome directly, so those fields support pivots by IP or client during an incident. Runtime authorization decisions often leave `request_surface` unset. The initiator of a rule evaluation is the gateway hop, not a dashboard click, and the hop's IP would not identify the originating client. Those events pivot by `actor`, `activity_id`, or `trace_id`, then join back to an originating configuration or application event when IP attribution is required. Join through correlation when client origin is required. Gateway events stamp the Gateway that scoped the request. A filter on `producer_gateway_id` answers "what happened on this Gateway?" rather than "who called the API?" ### Durability and completeness Audit is evidence, not best-effort telemetry. When durability cannot be proven, governed mutations fail closed rather than silently dropping the record. Dome denies the action rather than silently losing evidence. State-changing configuration paths co-commit audit with the business mutation when the design requires it. If audit cannot be accepted, the mutation fails. Security-sensitive admits (login, token issuance, impersonation start) follow the same posture. Forced terminations write after the fact with retry. Refusing to kill a revoked session because the audit store is down would be the worse failure. Reads and exports expose completeness metadata so a page of events is never silently mistaken for the full matching set. Interactive views may warn and still show readable rows. Strict export paths refuse to claim success on a gapped corpus. Retention and sink details are operational concerns. Conceptually, S3 (or the configured archive) is the store of record. Query indexes and live streams are projections of that trail. ## Next steps With the one-trail audit model in mind, continue with: * [Events](/reference/events) reference for Audit v1 and webhook envelopes, catalogs, and filters * [Audit events](/operate/audit) to query, filter, and reconstruct activity chains * [Stream Live Events](/operate/observe) to stream live Audit v1 events * [Subscribe to Events](/operate/webhooks) to deliver signed webhook projections * [Export Data](/manage/export) to ship retained history downstream # Webhooks Source: https://docs.domesystems.ai/concepts/audit/webhooks Why Dome delivers a signed public projection of events to your endpoints and providers ## High-level overview Dome does not ship a separate event bus for integrations. The same underlying activity that lands on Audit v1 can also fan out as a **public projection** under `dome.webhook.v1`. Many type strings match Audit v1 (for example `agent.suspended`). The wire shape, field names, PII rules, and catalog size differ. Some names diverge (webhook `llm.output.filtered` versus audit `llm.model_result.filtered`). Prefer the webhook name on the wire for webhook consumers. A webhook delivery looks like this: 1. An operator suspends an agent. That write is a configuration state change on the audit trail. 2. If a subscription matches `agent.suspended`, Dome also renders a public body and POSTs it to each matching destination. 3. Downstream chat, SIEM, or workflow tools react without reading the forensic envelope. ### Subscription and destination A **subscription** matches event types and optional filters. A **destination** is where deliveries go (custom URL or provider). Creating a subscription can provision the destination in the same call. Multiple subscriptions can share one destination so several event selections hit the same sink without duplicating credentials. Secrets (signing keys, provider credentials, secret header values, capability URLs) are write-only. They are set at create time, rotatable later, and never readable back. Reads mask every secret. ### What every destination guarantees Three guarantees hold across custom URLs and providers. * **At-least-once delivery.** Duplicates are possible. Dedupe on the `Dome-Event-Id` header. That ID is stable across retries and replays. * **Public fields only.** Payloads carry each event's versioned public contract. They never include PII, credentials, or raw tool payloads. * **Durable failure handling.** Failed deliveries retry with backoff for up to 72 hours, keep attempt history, and stay replayable for 30 days. Custom HTTPS destinations follow the [Standard Webhooks](https://www.standardwebhooks.com/) signing model so any off-the-shelf verifier works. Providers that authenticate with a credential in the body (for example PagerDuty) receive no `webhook-signature` header. The credential is the authentication. ### Providers as rendered projections A destination can target an off-the-shelf provider by picking it and pasting credentials. Dome renders the wire format that provider expects (Datadog array wrap, GitHub `repository_dispatch`, Slack message, and so on). The rendered body is deterministic. Retries and replays reuse the same bytes. Discover required settings and credentials with `dome webhooks providers list` and `dome webhooks providers get `. Eleven providers authenticate via a paste-in capability URL. Dome stores the URL write-only and displays only its host. | Category | Examples | | -------------------------- | ------------------------------------------------------------------------------------- | | Observability and SIEM | Datadog, Splunk HEC, Elasticsearch, New Relic, Dynatrace, Google SecOps, Grafana Loki | | Automation and workflow | GitHub, Jira Automation, Zapier, Tines, n8n, ServiceNow, Azure Event Grid | | Incident response and chat | PagerDuty, Slack, Microsoft Teams, Discord, incident.io | ### Why retries and circuit breaking exist Transient failures should not drop a security or lifecycle signal. Dome retries on a backoff schedule (1m, 5m, 15m, 1h, 4h, then every 6h with jitter) for up to 72 hours. | Response | Handling | | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | `2xx` | Delivered | | `408`, `409`, `425`, `429`, any `5xx`, network timeouts | Retried until the 72-hour window closes | | Any other `4xx`, any `3xx` (redirects are not followed), TLS failures | Terminal `failed`. The destination rejected the request, so retrying does not help | A delivery that exhausts the window lands in `expired`. Both `failed` and `expired` stay recoverable via replay while the body is retained. Each destination also has a circuit breaker. Twenty failures within 10 minutes pause deliveries to that destination for 5 minutes while other destinations keep flowing. Paused deliveries retry. They are not failed. That protects a healthy sink from a stuck peer without discarding the queue. ### Operating limits Dome-managed constraints for destinations and deliveries: * **Custom endpoints** are HTTPS-only. Private, loopback, and cloud-metadata addresses are rejected. * **Custom headers**: up to 5 per destination; names up to 128 bytes, values up to 2048 bytes. `webhook-*` and `Dome-*` names are reserved. Secret header values are write-only and masked on every read. * **Body cap**: a rendered body over 128 KiB lands terminal (`body_too_large`) for that delivery. Sibling destinations still deliver. * **Delivery policy** is not configurable: 5 sends per second per destination, 5 in-flight, 10-second response timeout. * **Retention**: delivery bodies are retained 30 days, trimmed to 24 hours after a successful delivery. Attempt history stays queryable. * **Quotas**: subscriptions and destinations per workspace are plan-dependent. Over-quota creates fail with a resource-exhausted error. * **Deleting a subscription** stops routing and cancels pending deliveries immediately. History stays queryable. ### How this differs from Audit and Export | Path | Job | | ---------------------------------------------------------------- | ------------------------------------------------- | | [Audit events](/concepts/audit) / [Audit events](/operate/audit) | Durable forensic trail. Query and reconstruct. | | [Stream Live Events](/operate/observe) | Live Audit v1 stream inside Dome. | | **Webhooks** | Real-time signed delivery of a public projection. | | [Export Data](/manage/export) | Batch and scheduled ship of retained history. | Use webhooks when something must react now. Use audit or export when you need the full evidence record or a SIEM archive. ## Next steps With the webhook delivery model in mind, continue with: * [Audit events](/concepts/audit) concept for the trail and how surfaces consume it * [Events](/reference/events#webhooks) reference for the webhook envelope, headers, and catalogs * [Subscribe to Events](/operate/webhooks) to create, verify, inspect, and replay * [Export Data](/manage/export) for batch delivery of retained history # Guards Source: https://docs.domesystems.ai/concepts/controls/guards Why Dome inspects request and response content on tool and model connections ## High-level overview Guards attach to a specific model or tool **connection** and to one **direction** (`request` or `response`). They run on the content path after the call is authorized. When an assigned Guard cannot be evaluated, the gateway blocks that connection and direction. It never relays the traffic without inspection. **Filters** are a Guard type. A Filter is a versioned, deterministic text or JSON inspector. You assign Filters in an ordered chain on a connection and direction. Assigning a Filter and seeing it run looks like this: 1. An operator creates a text Filter that redacts SSNs and assigns it to the response direction on a model connection. 2. On the next gateway config sync, that connection runs the Filter over streamed completions. 3. Matching spans are redacted before the agent sees them. 4. If an assigned Filter later cannot be decoded, the gateway blocks that direction until the operator rolls back or clears the chain. Guards inspect content at the connection and fail closed when inspection cannot run. ### Directions Each Guard assignment binds to one direction on a connection. | Direction | Model connection | Tool connection | | ---------- | ----------------------------------------------- | ------------------------------------------------ | | `request` | Prompt before dispatch to the model provider | Tool arguments before dispatch to the MCP server | | `response` | Streamed completion before it reaches the agent | Tool result before it reaches the agent | Request Guards can transform content or block a call. Response Guards run before any inspected content reaches the caller. ### Filters A Filter is workspace-scoped and versioned. Assigned connections pick up the active version on the next gateway sync. Previous versions remain available for rollback. Filter kind is immutable and decides which connections the Filter can attach to. | Kind | Connection | Role | | ------ | ---------- | ---------------------------------------------------------- | | `text` | Model | Substring and pattern inspection on prompts or completions | | `json` | Tool | Field-path inspection on tool arguments or results | Each `(connection, direction)` slot holds one ordered Filter chain. Setting a chain replaces the complete list for that slot. Model connections accept only `text` Filters. Tool connections accept only `json` Filters. Narrow transforms ahead of broad blocking matchers keep audit attribution predictable when multiple matchers could fire. A matching `BLOCK` short-circuits the chain. Kinds, actions, and precedence are on the [Guards](/reference/controls/guards) reference. ### Streaming model responses LLM chat responses arrive as fragmented SSE chunks. The gateway buffers each connection's response into a sliding window and runs response Filters over the decoded text so multi-chunk patterns are visible. The effective window is `max(workspace floor, connection override, per-request override)`. A layer set to `0` drops out of the max. Window bounds and where each layer is set are on the [Guards](/reference/controls/guards#streaming-window) reference. How to set them is on [Guards](/govern/guards#configure-the-streaming-window). ## Next steps With the Guards content-inspection model in mind, continue with: * [Architecture](/concepts/architecture#guards) concept for where Guards sit in the request path * [Rules](/concepts/controls/rules) concept for allow and deny before content inspection * [Guards](/reference/controls/guards) reference for Filter kinds, actions, path syntax, and streaming layers * [Configure Guards](/govern/guards) to create Filters, assign chains, and tune streaming windows # Quotas Source: https://docs.domesystems.ai/concepts/controls/quotas Why Dome caps governed LLM spend over a window at scoped subjects ## High-level overview A Quota is a budget attached to one **subject** inside a workspace: the workspace itself, one agent, one verified end user, one Gateway, one pool, or one model. The Quota names a USD limit and a window (`daily` or `monthly`). Dome prices each completed model call at per-model token rates, adds that cost to every matching subject's spend for the window, and checks those budgets before the next call proceeds. Setting a Quota and exhausting it looks like this: 1. An operator sets a monthly workspace Quota of \$5,000. 2. Agents keep calling models through a Gateway. 3. Each completed call adds priced spend to the workspace counter for the current UTC month. 4. While spend stays under \$5,000, calls continue. The first call that would push the counter over the cap receives HTTP `429`. 5. The same call can also hit an agent Quota, a Gateway Quota, and a model Quota. Every matching budget must still have room. Pick who owns the budget and how often it resets. Dome refuses or reroutes when the money is spent. ### When a Quota change takes effect The **limit** you set (subject, amount, window, enabled) is workspace config. Dome loads that config on a poll interval (default about 10 seconds), the same tick that picks up Rule and Guard config. A new or updated Quota may not shape enforcement until the next successful sync. After that sync, the new limit is live. **Spend** moves when priced calls complete. Edit the Quota form and the counters do not jump. Raise a limit and spend already recorded in the window stays. Disable a Quota and that budget stops being checked. Other Quotas that still apply keep their own counters. ### Every matching Quota must pass Subjects carve different slices of the same traffic. A single model call can touch several Quotas at once: the workspace total, the calling agent, a verified Act-As user, the Gateway in the URL, the pool that routed the call, and the model that served it. Dome allows the call only when every applicable enabled Quota still has remaining budget. An agent Quota with room left cannot override an exhausted workspace Quota. Subject types and what each rejects are on the [Quotas](/reference/controls/quotas#subjects) reference. ### Windows reset spend, not the Quota A window is the calendar period the counter covers. `daily` resets at UTC midnight. `monthly` resets at the start of each UTC calendar month. The default on create is `monthly`. You can keep both a daily and a monthly Quota on the same subject. They are separate budgets. When the window rolls over, spend for that window starts at zero again. The Quota itself stays until you update or remove it. Details are on the [Quotas](/reference/controls/quotas#windows) reference. ### Exhaustion: reject or spill inside a pool Most Quotas reject affected calls with HTTP `429` when the budget is gone. Model Quotas inside a pool can behave differently. The resolver skips the exhausted member. If another eligible pool member remains, the caller never sees the exhaustion. If every member is unavailable or exhausted, the gateway returns HTTP `429` with `subject_type: all_members`. Direct calls to an exhausted model (no pool spillover path) return HTTP `429` with `subject_type: model`. Quota enforcement is a barrier applied before the pool's routing strategy runs, so spillover composes with `priority_weighted`, `round_robin`, and `least_loaded` alike. Refer to [Pools](/concepts/resources/model-pools) concept and [model Quotas](/reference/controls/quotas#model-quotas) reference. ### What counts toward a Gateway Quota A Gateway Quota follows the gateway in the request path, not a fixed membership snapshot. Usage accrues when a call is routed through that Gateway, whichever pool or model member serves it. Membership can change during the window. Spend still lands on the same Gateway Quota. Passthrough LLM calls are still checked against Quotas, but they do not add priced spend. The opaque body carries no token usage to meter. ### What you see in audit Completed model calls emit `llm.model_call.completed` with token counts. Exhaustion emits `access.denied` with deny detail `QUOTA_EXCEEDED`. Create, update, and delete emit their own lifecycle events. Event names are on the [Quotas](/reference/controls/quotas#observability) reference. ## Next steps With the Quotas spend-cap model in mind, continue with: * [Architecture](/concepts/architecture#quotas) concept for where Quotas sit in the request path * [Rules](/concepts/controls/rules) concept for allow and deny before spend metering * [Guards](/concepts/controls/guards) concept for content inspection after authorization * [Quotas](/reference/controls/quotas) reference for subjects, windows, and event names * [Set Usage Limits](/govern/quotas) to create, update, list, and remove Quotas # Rules Source: https://docs.domesystems.ai/concepts/controls/rules Why Dome authorizes agent tool and model calls with scoped Cedar Rule bundles ## High-level overview Dome stores Rules in **bundles** at organization, tenant, workspace, and agent scope. Before forwarding a request, Dome combines every active bundle that covers the authenticated agent and evaluates the request against that set. Dome allows the request only when at least one `permit` matches and no `forbid` matches, regardless of which scope each Rule came from. Dome also denies any request it cannot evaluate because a Rule errors or its Rule data is missing or stale. Applying a Rule and hitting it at runtime looks like this: 1. An operator applies a workspace bundle that permits `mcp:call` on `github/list_issues` for a named agent. 2. Minutes later that agent calls the tool through its Gateway. 3. Dome loads the agent's effective Rules, finds a matching `permit` and no matching `forbid`, and forwards the call. 4. A later org-scoped `forbid` on the same tool would deny the call even if the workspace `permit` still exists. ### What a Rule states A Rule states an effect (`permit` or `forbid`) and the requests that effect applies to. Inside the parentheses it names a principal, an action, and a resource. Optional `when` / `unless` conditions narrow the match further. ```cedar title="starter.cedar" theme={"system"} permit( principal, action == Dome::Action::"mcp:discover", resource ); ``` That Rule lets every agent in scope list available tools. It does not let them call a tool. Constraint forms, actions, and attributes are on the [Rules](/reference/controls/rules) reference. ### Bundles, not lone Rules Dome never stores or deploys a single Rule on its own. You organize Rules into one or more `.cedar` files and apply them together as a **Rule bundle** at one scope. Each scope holds one active user-authored bundle. Applying a new bundle replaces the active one. An apply must include every file that should remain active. Earlier versions stay in history so you can roll back. Dome also writes its own **generated bundles** alongside yours at the same scope. Agent allowed resources, Act-As settings, blocked tools, and other managed configuration produce them. Change those Rules through the feature that created them, not by applying or rolling back their bundle directly. ### Where Rules apply A Rule's constraints decide which requests it matches. The scope of the bundle holding it decides which agents it can affect at all. | Scope | Agents affected | | ------------ | ------------------------------------------ | | Organization | Agents in every tenant in the organization | | Tenant | Agents in every workspace in the tenant | | Workspace | Every agent in the workspace | | Agent | One agent | Scope decides which Rules take part in a decision, not which Rule wins. Dome evaluates every covering bundle as one set. A narrower scope carries no extra weight. A `permit` from any scope can allow a request. A `forbid` from any scope denies it. An agent-scoped `permit` cannot reopen access that an organization-scoped `forbid` closes. ### Effective Rules The **effective Rules** for an agent are the merged set of every active bundle that covers that agent, including generated bundles. The assembled set includes flat Cedar files for the evaluator, a content hash for change detection, and the list of contributing bundles. The gateway caches effective Rules and polls for content-hash changes (default sync interval: 10 seconds). Rule changes propagate without restarting the gateway. There is a brief delay between apply and enforcement. Assembly steps, freshness windows, and fail-closed behavior when Rules are missing or stale are on the [Authorization Model](/concepts/architecture/authorization-model#effective-policy) concept. ## Next steps With that Rules authorization model in mind, continue with: * [Authorize Access](/govern/rules) * [Authorization Model](/concepts/architecture/authorization-model) concept * [Rules](/reference/controls/rules) reference * [Gateways](/concepts/gateways) concept — reachability before Rules run # Gateways Source: https://docs.domesystems.ai/concepts/gateways/index Why Dome uses named Gateways to package which resources agents can reach Every Gateway has a complete `/gateways/` URL plus protocol-specific MCP, OpenAI, and Anthropic endpoints. ## High-level overview A Gateway is a workspace entity that packages membership and grants behind one URL for both protocols. The same Gateway URL serves tool traffic at `/gateways//mcp` and model traffic at `/gateways//v1/...`. The [Tools gateway](/concepts/gateways/tools-gateway) and [LLM gateway](/concepts/gateways/llm-gateway) are ingresses on the [data plane](/concepts/architecture/dataplane) that require that Gateway prefix — they are protocol paths, not kinds of Gateway. Each Gateway carries two relationships: * **Membership** lists which tools, tool sources, pools, and model connections belong to this Gateway * **Access grant** lists which agents may call this Gateway A resource in no Gateway is valid but **Unreachable**. An agent without a grant is rejected at the edge before Rules run. Membership and grants package *reachability*. [Rules](/concepts/controls/rules), [Guards](/concepts/controls/guards), and [Quotas](/concepts/controls/quotas) still decide what happens after admission. Standing up and calling through a Gateway looks like this: 1. An operator creates a Gateway (or uses the Default gateway), adds the tools and models that surface should expose, and grants an agent access. 2. The agent points its client at `/gateways/`. 3. Tool calls and model calls use the same Gateway id with different path suffixes. 4. Dome admits the agent, checks membership, evaluates Rules, then serves the call through the Tools or LLM ingress. A Gateway is the unit of curated reachability for both tools and models. ### Why Gateways exist Without Gateways, every callable resource in the workspace competes for the same front door, or operators encode reachability only in Cedar. Gateways give you a named bundle you can grant as one unit: * Attach related tools and models once, then grant the Gateway to each agent that should see that bundle * Keep credentials and Rules separate. Rotating a key does not change membership. Rewriting Rules does not redefine which endpoint an agent calls. * Fail closed without a Gateway prefix: every caller must choose a Gateway (`/gateways//...`) ### When to use one Gateway vs many **One Gateway is enough** when a workspace has a single shared set of tools and models and every active agent should reach that set. New workspaces start with a **Default gateway** that is empty until you attach members and grants. Free and Pro plans allow one Gateway per workspace. That Default gateway is usually the one you keep. **Create another Gateway** when you need a second curated front in the same workspace, for example: * A coding agent may reach repo and CI tools. A support agent may reach ticket and CRM tools. Same workspace, different memberships and grants. * Production models stay on one Gateway. Eval or shadow pools stay on another so agents cannot mix them by accident. * Human-operated MCP clients and automated agents should see different tool catalogs The same resource can belong to more than one Gateway. An agent can hold grants to more than one Gateway and pick the URL that matches the job. Prefer another Gateway over duplicating connections when the split is “who may reach which set,” not “which upstream credential to store.” ### How a call is authorized Every `/gateways//...` request must pass three checks. The final decision is `addressable AND admitted AND Cedar-allowed`. 1. **Addressable:** The path names an active Gateway in the caller's workspace. 2. **Admitted:** The agent has a grant to that Gateway (discovery such as `tools/list` and `GET /v1/models` is exempt and stays membership- and Cedar-filtered). 3. **Cedar:** Workspace Rules permit the action on the resource. Forbid wins. Admission never overrides a deny. So: Gateway decides *whether this agent may use this front door and whether this resource is on it*. Rules decide *whether this action is allowed*. ### Default gateway Every workspace gets one auto-provisioned **Default gateway**. It is real, editable, and deletable. * **New workspaces** start with an empty Default gateway and no grants. Attach members and grants explicitly. * **Migrated workspaces** may already have resources attached, but agents still need explicit per-agent or all-agents grants. ## Next steps With that Gateway reachability model in mind, continue with: * [Gateways](/connect/gateways) to create Gateways, manage membership and grants, and build client URLs * [Architecture](/concepts/architecture#gateways) concept for where Gateways sit among components * [Data plane](/concepts/architecture/dataplane) concept for the runtime that serves Gateway URLs * [Tools gateway](/concepts/gateways/tools-gateway) concept for the MCP ingress under a Gateway * [LLM gateway](/concepts/gateways/llm-gateway) concept for the model ingress under a Gateway * [Agents](/concepts/agents) concept for identity before reachability * [Rules](/concepts/controls/rules) concept for what an agent may do after admission # LLM gateway Source: https://docs.domesystems.ai/concepts/gateways/llm-gateway Route agent LLM calls through Dome to enforce authorization, redact content, and audit every prompt and completion ## High-level overview Every model call targets a [Gateway](/concepts/gateways). Bare `/v1/...` fails closed with a `"select a gateway"` 400. The Gateway, not the LLM ingress, is what you create and grant in Connect. A model call through the LLM gateway looks like this: 1. An operator adds a model connection (and often a pool), puts it on a Gateway, and grants an agent access. 2. The agent points an OpenAI or Anthropic client at `https:///gateways//v1` with a Dome agent token. 3. Each call runs admission, pool resolution, Rules, request Guards, provider dispatch with injected credentials, response Guards, and audit. The agent never sees the provider credential. The LLM gateway never exposes provider credentials to the agent. ### Endpoints The LLM gateway exposes provider-native shapes so existing SDKs work without modification. Use the Gateway's returned OpenAI or Anthropic base URL as-is. | Method | Path (relative to `/gateways/`) | Shape | | ------ | ----------------------------------- | ------------------------------- | | `POST` | `/v1/chat/completions` | OpenAI chat | | `POST` | `/v1/messages` | Anthropic messages | | `POST` | `/v1/embeddings` | OpenAI embeddings | | `POST` | `/v1/moderations` | OpenAI moderations | | `POST` | `/v1/responses` | OpenAI responses | | `POST` | `/v1/passthrough/:name` | Opaque per-provider passthrough | | `GET` | `/v1/models` | Cedar-filtered model listing | Streaming and non-streaming are both supported. SSE chunks are filtered in flight. For embeddings, `encoding_format` (`float` or `base64`) controls the response wire shape only. Refer to [Develop](/develop#route-traffic). Passthrough limits for `custom` connections are on the [Models](/reference/resources/models#custom-passthrough) reference. ### Pipeline Model calls share these stages, with pool resolution and failover specific to this ingress: 1. **Ingress:** Resolve the Gateway from the path, validate the bearer credential, extract agent identity, check kill switches, and translate the native request into Dome's canonical shape. 2. **Pool resolution:** Resolve the request's `model` value to a pool or direct connection. Order and `match_when` are on the [Pools](/reference/resources/model-pools#pool-resolution) reference. 3. **Authorize:** Evaluate Cedar against the upstream that will be dispatched. Stale policy fails closed. Refer to [Authorization](#authorization). 4. **Dispatch:** Call the provider through the connection's adapter. Dome injects credentials at request time. Providers and credential pairings are on the [Models](/reference/resources/models) reference. 5. **Filter:** Apply [Guards](/concepts/controls/guards) over streamed and non-streamed content before the agent sees it. 6. **Failover:** On a transient upstream error, try further eligible pool members up to the failover limit. Each candidate is re-authorized first. Limits and streaming rules are on the [Pools](/reference/resources/model-pools#failover) reference. 7. **Audit:** Emit `llm.*` events with token counts, matched rule, pool, and final upstream. Event types are on the [Events](/reference/events) reference. Shared credential models and deployment topology live on the [Data plane](/concepts/architecture/dataplane) concept. Denial and credential wire shapes live on [Develop](/develop#errors-and-denials). ### Authorization LLM calls authorize against the same Cedar policy as tool calls, with resource type `Dome::LLMModel`. Policy evaluates the upstream that is **actually dispatched**: the primary member up front, and each failover candidate immediately before its own attempt. A `forbid` on a model or connection attribute holds for every upstream the call could reach, not just the primary. Use `resource.name` for the caller-facing alias, `resource.resolved_model` for the upstream that will run, and `resource.pool` when the request was routed through a pool. Injected attributes and evaluation notes are on the [Rules](/reference/controls/rules#llm-injected-attributes) reference. Pool grants and listing behavior for `GET /v1/models` follow the same rules. Refer to [Rules](/concepts/controls/rules) concept and [Authorize Access](/govern/rules). **Non-streaming.** A deny on the primary is terminal. A policy-refused failover candidate is skipped (with `access.denied`) rather than treated as a successful route-around. **Streaming.** Failover applies only before the first response byte reaches the caller. After that, a later upstream failure terminates the stream. Verified [act-as identity](/concepts/agents/identity#act-as-identity) concept is forwarded into evaluation on every LLM endpoint. Configure verification on [Delegated agents](/connect/agents/delegated). Runtime errors and denial shapes are on [Develop](/develop#errors-and-denials). ## Next steps With the LLM gateway model in mind, continue with: * [Gateways](/concepts/gateways) concept for membership, grants, and URL shape * [Tools gateway](/concepts/gateways/tools-gateway) concept for the sibling MCP ingress * [Data plane](/concepts/architecture/dataplane) concept for the shared enforcement pipeline * [Models](/concepts/resources/models) concept and [Pools](/concepts/resources/model-pools) concept for connections and routing * [Models](/reference/resources/models) reference and [Pools](/reference/resources/model-pools) reference for providers, credentials, and routing catalogs * [Rules](/reference/controls/rules#llm-injected-attributes) reference for LLM resource attributes * [Models](/connect/resources/models) and [Pools](/connect/resources/models/pools) to configure connections * [Develop](/develop) to point SDKs at a Gateway and handle denials # Tools gateway Source: https://docs.domesystems.ai/concepts/gateways/tools-gateway Route agent MCP and tool calls through Dome to enforce authorization, inject credentials, and audit every invocation ## High-level overview Every tool call targets a [Gateway](/concepts/gateways). Bare `/mcp` fails closed with a `"select a gateway"` 400. A tool call through the Tools gateway looks like this: 1. An operator adds an MCP server connection, puts it (or its tools) on a Gateway, and grants an agent access. 2. The agent points an MCP client at `https:///gateways//mcp` with a Dome agent token. 3. Discovery (`tools/list`) returns membership- and Cedar-filtered tools. 4. Each `tools/call` runs admission, Rules, request Guards, egress with injected credentials, response Guards, and audit. The Tools gateway never exposes backend credentials to the agent. ### Endpoints | Path (relative to `/gateways/`) | Role | | ----------------------------------- | ----------------------------------------------- | | `/mcp` | MCP Streamable HTTP session and JSON-RPC frames | Point MCP clients at the Gateway's returned `mcp_url`. Do not omit the `/gateways/` segment. ### Pipeline Tool calls share these stages: 1. **Ingress:** Resolve the Gateway from the path, validate the bearer credential, extract agent identity, check Gateway admission (discovery exempt), verify act-as when present, apply kill switches and rate limits. 2. **Evaluate:** Load the workspace Cedar policy and authorize the tool action. Stale policy fails closed. 3. **Egress:** Resolve the backend from the tool catalog, inject stored credentials, forward over MCP Streamable HTTP, stdio, or REST catalog. 4. **Filter:** Apply response Guards and field-level redaction before the agent sees the result. 5. **Audit:** Emit `mcp.tool_call.*`, filtering, discovery, and denial events stamped with the Gateway id. Details of credential models, denial wire shapes, and deployment topology live on the [Data plane](/concepts/architecture/dataplane) concept. ### Discovery vs invoke `tools/list` stays membership- and Cedar-filtered and does not require a Gateway access grant at the edge. `tools/call` requires addressability, admission, and Cedar. A tool that is not a member of the addressed Gateway does not appear for that caller. ### Egress protocols | Protocol | Use case | | ----------------------- | -------------------------------------------------------- | | **MCP Streamable HTTP** | Primary protocol for MCP-native backends. | | **Stdio** | Local backends spawned by the gateway runtime. | | **REST Catalog** | HTTP tool discovery and invocation for non-MCP backends. | ## Next steps With the Tools gateway model in mind, continue with: * [Gateways](/concepts/gateways) concept for membership, grants, and URL shape * [Data plane](/concepts/architecture/dataplane) concept for the shared pipeline, credentials, and readiness * [LLM gateway](/concepts/gateways/llm-gateway) concept for the sibling model ingress * [Tools](/concepts/resources/tools) concept for credentials, catalogs, and reachability * [Tools](/connect/resources/tools) to register MCP and tool connections * [Gateways](/connect/gateways) to attach tools to a Gateway # Identity patterns Source: https://docs.domesystems.ai/concepts/identity-patterns Choose standing or delegated identity per agent ## Two identities Dome supports two identity models: * **Standing identity** means the agent is the principal: it acts as itself and authorizes on its own grants. * **Delegated identity** means the agent still authenticates as itself, but every governed call also carries a verified human, and authorization reads that human's roles. The diagram and table below compare how each model reaches a tool or model through the Gateway. Standing identity authorizes on the agent. Delegated identity carries an act-as assertion and authorizes on the human | | Standing identity | Delegated identity | | ----------------------- | ------------------------- | ---------------------------------------------------------------- | | **The agent** | Acts as itself | Acts for a human ([Delegated agents](/connect/agents/delegated)) | | **Authorization reads** | The agent's own grants | The human's roles (`principal.act_as`) | | **Credential** | Agent Bearer JWT | Agent Bearer JWT + `X-Dome-Act-As` | | **Audit answers** | "What can this agent do?" | "What did this agent do, and for whom?" | | **Choose when** | The agent does a job | The agent acts on someone's behalf | The choice is independent of how the agent attaches to a Gateway. Standing and delegated agents use the same Registry, Cedar, Gateway, and audit primitives. ### Standing identity Give an agent standing identity when it does a job. A revenue analyzer reading customer data needs the same permissions regardless of which analyst triggers it. The agent is itself, acts as itself, and its permissions are its own: a governed service account. ### Delegated identity Give an agent delegated identity when it acts for a person. The agent still authenticates as itself, but every governed call carries an `X-Dome-Act-As` assertion naming the human, [verified](/connect/agents/delegated#require-verified-identity) against the workspace's OIDC providers. Authorization then evaluates against that human's context. This unlocks something standing identity cannot express: the same agent, calling the same tool on the same record, allowed for one person and denied for another. Authorization reads the human on the token, so your rules can encode conflicts of interest or need-to-know: ```cedar title="information-barrier.cedar" theme={"system"} // A person screened from a deal cannot open its records, whichever agent acts for them. forbid( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource ) when { principal has act_as && principal.act_as.roles.contains("barrier_screened") && resource.name like "*open_deal*" }; ``` The audit trail then names the human. That is often the reason the workflow could be automated at all. If the agent does a job, give it standing identity. If it acts for someone, have it borrow theirs. A fat standing identity on an acting-for-someone agent cannot be reconstructed into who it was really for. ## Common examples These two shapes show how standing and delegated identity usually look when you run many agents on one platform. ### Self-service agent platform A self-service agent platform is a common example of **standing identity**. Any team declares an agent (instructions plus a tool list), and your platform builds, governs, and runs it. Governance is automatic and invisible to the author. Standing identity fits because the agent does a job, not a person's job. Permissions come from the declared tools, not from who triggered the run. Behind the manifest, your platform [registers the agent](/connect/agents#register-agent), compiles those tools into a least-privilege [rule bundle](/govern/rules), admits it to a [Gateway](/concepts/gateways), and runs it. The author never writes a policy or holds a credential. Each agent is a service account whose permissions are exactly its declared tools. Audit answers "What can this agent do?" ### Purpose-built agent service A purpose-built agent service is a common example of **delegated identity**. Engineers build one high-stakes workflow so domain rules are exact, and the agent acts for a specific person. Delegated identity fits because authorization must follow the human, not a shared service account. A generic least-privilege grant from a manifest cannot express conflicts of interest or need-to-know. Authored policy reads the human's roles on `principal.act_as` instead: * **Finance:** an adviser agent that may act for one client's book but is walled off from a deal it is screened from. * **Healthcare:** a case agent held to minimum-necessary access: the records of patients in its care team, decided by the clinician it acts for. * **Multi-tenant SaaS:** a support agent scoped to one customer's data boundary, where the tenant is a property of the human on the token, not a parameter the model can change. Because identity is delegated, the audit trail names the human. Audit answers "What did this agent do, and for whom?" ## Next steps With standing vs delegated identity in mind, continue with: * [Agent Identity](/concepts/agents/identity) concept for how agents authenticate and carry act-as context * [Delegated agents](/connect/agents/delegated) to configure verification so agents can act for a verified person * [Authorization Model](/concepts/architecture/authorization-model) concept for Cedar entities, permit and forbid semantics, and rule evaluation * [Rules](/govern/rules) to author, simulate, and deploy Cedar rule bundles # Core concepts Source: https://docs.domesystems.ai/concepts/index How Dome governs agents, resources, and requests **Core concepts** explain what each Dome piece is, how the pieces fit together, and when to choose one design over another. If you have not picked a role yet, start with [Personas](/personas) or [What is Dome?](/what-is-dome). ## How the pieces fit Dome separates **components** you define (agents, resources, Gateways), **controls** you apply to traffic (Rules, Guards, Quotas), and **audit** that records what happened. Agents reach tools and models through a Gateway. Dome holds the upstream credentials. On each call, the controls decide what is allowed, what content may pass, and how much spend is left. Read the concepts in this order when you want them in dependency order, from the system map down to the evidence trail. 1. **[Architecture](/concepts/architecture)** covers components, controls, audit, and the request path. 2. **[Scopes](/concepts/platform/scopes)** explains organization → tenant → workspace isolation and where configuration attaches. 3. **[Identity Patterns](/concepts/identity-patterns)** compares standing vs delegated identity before you register agents. 4. **[Agents](/concepts/agents)**, **[Resources](/concepts/resources)**, and **[Gateways](/concepts/gateways)** are the three components you define so a workload can call anything. 5. **[Rules](/concepts/controls/rules)**, **[Guards](/concepts/controls/guards)**, and **[Quotas](/concepts/controls/quotas)** are the three controls that govern traffic after admission. 6. **[Audit events](/concepts/audit)** is the evidence trail across both planes. Add [Authorization Model](/concepts/architecture/authorization-model) or [Data plane](/concepts/architecture/dataplane) when you need Cedar semantics or runtime placement. Add [Permissions](/concepts/platform/permissions) when you need platform RBAC vs agent authorization. ## Architecture The system map and request path. * **[Architecture](/concepts/architecture)** explains how agents, resources, Gateways, controls, and audit relate. * **[Authorization Model](/concepts/architecture/authorization-model)** covers Cedar permit/forbid, effective policy, and fail-closed evaluation. * **[Data plane](/concepts/architecture/dataplane)** is the hot-path runtime that enforces configuration synced from Dome. ## Identity Choose how an agent presents itself before you wire credentials. * **[Identity Patterns](/concepts/identity-patterns)** compares standing identity vs delegated identity (act-as) at design time. * **[Agent Identity](/concepts/agents/identity)** covers API keys, session tokens, and verified end-user claims on the wire. ## Components Things you define so a workload can reach tools and models. * **[Agents](/concepts/agents)** are the principals for auth, authorization, and audit. Lifecycle states live on [Agent Lifecycle](/concepts/agents/agent-lifecycle). * **[Resources](/concepts/resources)** include [tools](/concepts/resources/tools), [models](/concepts/resources/models), and [pools](/concepts/resources/model-pools). Dome stores upstream credentials. Agents never hold them. * **[Gateways](/concepts/gateways)** package membership and agent grants behind one URL. [Tools gateway](/concepts/gateways/tools-gateway) and [LLM gateway](/concepts/gateways/llm-gateway) are the MCP and model ingresses on that URL, not separate Gateway types. ## Controls Things you apply after a call is admitted. * **[Rules](/concepts/controls/rules)** are scoped Cedar bundles that authorize what an agent may do. * **[Guards](/concepts/controls/guards)** inspect content on request and response paths. * **[Quotas](/concepts/controls/quotas)** cap spend over a window, including pool spillover. ## Platform Boundaries for tenancy and who may administer Dome. * **[Scopes](/concepts/platform/scopes)** define the organization, tenant, and workspace hierarchy. * **[Permissions](/concepts/platform/permissions)** cover platform RBAC for people and API clients. This layer is separate from Cedar for agents. ## Audit Evidence after configuration and runtime decisions. * **[Audit events](/concepts/audit)** is one trail across configuration changes and runtime decisions. * **[Webhooks](/concepts/audit/webhooks)** deliver signed copies of selected events to your endpoints. ## Next steps With that map of Dome's pieces in mind, continue with: * [Connect](/connect) to register agents, attach resources, and grant Gateway access. * [Govern](/govern) to write Rules, assign Guards, and set Quotas. * [Operate](/operate) to investigate audit events, stream live events, and subscribe to webhooks. * [Develop](/develop) to send credentials, Gateway URLs, and Act-As claims from the application. # Permissions Source: https://docs.domesystems.ai/concepts/platform/permissions How platform RBAC differs from runtime agent authorization ## Two authorization layers Dome separates who can administer the platform from what agents may do at runtime. The layers look different on purpose: | Layer | Principal | Governs | Decision point | | ----------------------- | ---------------------------------------------- | ----------------------------------------------------- | -------------------- | | **Platform RBAC** | User, service account, or scoped API key | CLI commands, API endpoints, and MCP management tools | Dome API / CLI / MCP | | **Agent authorization** | Registered agent with optional act-as identity | Tool calls and model invocations | Gateway request | Granting a user `operator` does not authorize an agent to call a tool. A Cedar `permit` does not grant that user platform administration. Both layers fail closed and record audit events. Apply each layer independently. ## How roles cascade RBAC roles are assigned at workspace, tenant, or organization scope. Higher-scope roles cascade their equivalent workspace permissions into every workspace below them. Refer to [Scopes](/concepts/platform/scopes) concept for the hierarchy. Workspace roles cover day-to-day work in one workspace (`admin`, `operator`, `security`, `developer`, `viewer`, plus reserved `impersonator`). Tenant roles add tenant-wide operations and can cascade a workspace role into every workspace in the tenant. Organization roles are the broadest standing grants and cascade into every tenant and workspace in the org. Use tenant or org roles when someone needs the same access everywhere below that scope. Use a workspace role when access should stay local. Exact permission IDs and role matrices are on the [Permissions](/reference/permissions) reference. ### Cross-domain workspace invitees When a user is invited to a workspace and their email domain does not match any of the org's verified domains, Dome auto-provisions tenant and org memberships with role `unprivileged` rather than `member`. That keeps cross-domain collaborators scoped to the workspaces they were explicitly invited to. * The invitee receives no org or tenant permissions, only the workspace role chosen by the inviter. * The invitee is excluded from auto-enrollment into open tenants on first login. * The invitee does not block the first-user-becomes-owner / first-user-becomes-admin auto-promotion logic. * If the org later verifies the invitee's domain, existing `unprivileged` memberships are promoted to `member` automatically. Previously assigned higher roles are preserved. * If the invitee's last workspace membership in a tenant is removed (or the workspace itself is deleted), the orphaned `unprivileged` tenant and org memberships are cleaned up automatically. Higher operator-granted roles survive workspace removal. If the org has no verified domains yet, all workspace invitees default to `member` so onboarding stays workable. ## Agent authorization is separate Agent requests are authorized with Cedar on each governed call. Platform RBAC never substitutes for a Cedar `permit`. Refer to [Authorization Model](/concepts/architecture/authorization-model) concept for entities, permit/forbid semantics, and effective policy. Write and deploy rules on [Authorize Access](/govern/rules). ## How RBAC maps to surface visibility RBAC roles filter what you see across surfaces. * **CLI** hides commands your role cannot use in the `--help` menu. Invoking one you lack permission for returns an authorization error. * **API** checks permissions before executing endpoints. Unauthorized calls return 403. * **MCP** filters tools by role. Tools outside your permission set are hidden entirely. After selecting a workspace context, only tools matching your role's permissions are available. See the [MCP Server Reference](/reference/mcp) for the tool list with permission requirements. ## Next steps With that platform RBAC model in mind, continue with: * [Permissions](/reference/permissions) reference for role and permission catalogs * [Manage Access](/manage/access) to assign members and create platform keys * [Scopes](/concepts/platform/scopes) concept for the hierarchy * [Authorization Model](/concepts/architecture/authorization-model) concept for Cedar * [Authorize Access](/govern/rules) to write and deploy rules # Scope hierarchy Source: https://docs.domesystems.ai/concepts/platform/scopes How organization, tenant, workspace, and agent scopes nest, inherit, and isolate ## The four levels Every governed object sits in exactly one of these levels. They nest top-down from the company account to an individual agent: Scope hierarchy: Organization → Tenant → Workspace → Agent | Level | Purpose | Example | | ---------------- | -------------------------------------------------------- | --------------------- | | **Organization** | Administrative and ownership boundary containing tenants | `acme-corp` | | **Tenant** | Hard isolation boundary for data and resources | `commerce` | | **Workspace** | Operational scope for agents, rules, backends, and audit | `ml-platform-prod` | | **Agent** | Individual AI agent identity in one workspace | `data-pipeline-agent` | Every rule bundle, agent record, backend, and audit event belongs to exactly one scope. There is no floating agent or rule bundle. A few rules hold everywhere in the tree: * Tenants are hard isolation boundaries. Agents and data in one tenant cannot reach another. * Workspaces separate day-to-day work inside a tenant (for example production vs staging). * A `forbid` at any scope overrides matching `permit`s below it. * Being a member of a workspace lets you administer it. It does not mean you own that workspace's resources. A typical scope hierarchy looks like this: 1. Acme Corp is the organization. 2. It holds a `Commerce` tenant and a `Support` tenant so those product lines never share agents, backends, or audit. 3. Inside `Commerce`, `ml-platform-prod` and `ml-platform-staging` are separate workspaces so a staging rule deploy cannot change production enforcement. 4. Each workspace holds its own agents. ### Organization An **organization** is the company's top-level account. It covers billing, ownership, and the container for tenants. It answers who owns the Dome estate, not which agents may call which tools. Baselines that must apply everywhere in the company belong here or at tenant scope. Day-to-day agents, backends, and audit do not live at organization scope. If you try to run operations from the org layer, you lose the isolation and operational boundaries the levels below exist to provide. ### Tenant A **tenant** is a hard isolation boundary, usually drawn around a business unit or product line. For example, keep `commerce` separate from `cloud-services`. Agents in one tenant cannot access resources in another. Choose a tenant boundary when a leak across that line is unacceptable. If two units that must not share data sit in one tenant, application checks and database row-level security treat them as one world. Audit, agents, backends, and rule bundles mix. Splitting later means migrating identities and history, not flipping a flag. ### Workspace A **workspace** is the operational scope inside a tenant and the primary unit of day-to-day work. Agents, rules, backends, Gateways, Guards, Quotas, and audit events live here. Use separate workspaces when blast radius should differ: production vs staging, or two teams that share a tenant but not backends and audit. If prod and staging share one workspace, they share rule deploys, reachable backends, and one audit trail. A staging experiment and a production deny investigation compete in the same record. Users can belong to multiple workspaces inside a tenant. Workspace membership controls who may administer that workspace. It does not move resource ownership across workspace lines. ### Agent An **agent** is an individual AI identity inside exactly one workspace. Agent-scoped rule bundles tighten what that one agent may do. Agent scope is the wrong place for a company-wide security baseline. A `forbid` that exists only on today's agents does not apply to the agent you register tomorrow. Broad restrictions belong higher. Agent scope narrows further. ## Policy inheritance Deploy authorization bundles at any hierarchy level. At evaluation time, Dome pulls the active bundles that cover the request and merges them into one effective policy: Rule merge: Org, Tenant, Workspace, Agent rules merge into Effective Policy → Evaluator 1. Fetch the active bundle from each applicable scope. 2. Prefix Cedar filenames by scope to prevent collisions. 3. Merge the files and compute a composite content hash. 4. Load the effective policy into the evaluator. The gateway polls for content-hash changes. New policy normally reaches enforcement within one sync interval (default: 10 seconds) without restarting the gateway. The effective policy hash changes when any contributing bundle changes. The gateway detects and re-syncs automatically. ### Forbid wins Cedar combines `permit` and `forbid` across every contributing scope: * A `forbid` at any scope overrides every matching `permit`. * Lower scopes can narrow access but cannot override a higher-scope `forbid`. * An org- or tenant-level `forbid` cannot be reopened by a workspace- or agent-level `permit`. * Agent-level rules can restrict access further but cannot expand access granted only by removing a higher-scope deny. Place broad security baselines high and specific allowances near the agent. If the baseline lives only at agent scope, every new agent starts without it until someone copies the forbid. If a wide `permit` lives at organization scope with no matching `forbid`, every workspace inherits that width. ## Isolation boundaries Tenants separate data at the application and database layers. Row-level security rejects missing or incorrect tenant context by returning no rows silently, rather than raising an error, so a wrong or missing tenant scope fails closed as empty results. Cross-tenant access requires an explicit system bypass, used only when no tenant identity can be set on the connection (for example, the scheduled-job runner that processes jobs across all tenants). Workspaces separate teams, environments, agents, rules, backends, and audit inside a tenant. The same RLS model applies: wrong or missing workspace scope returns zero rows. Cross-workspace access uses the same explicit system bypass only when no workspace identity can be set on the connection. Assign users to multiple workspaces without weakening tenant isolation. RLS is the backstop. Even misconfigured application code cannot leak data across scopes when the database context is set correctly. ## Roles and scopes Assign platform RBAC roles at organization, tenant, or workspace scope. Higher-scope roles cascade permissions into lower scopes. That layer decides which people and API clients may administer the platform. Agent authorization rules use the same hierarchy but govern runtime requests: what a registered agent may do to a tool or model. Confusing the two layers produces the wrong fix. Granting a user `operator` does not authorize an agent to call a tool. A Cedar `permit` does not grant that user platform administration. Refer to [Permissions](/concepts/platform/permissions) concept before assigning roles or writing Cedar. ## Next steps With that scope hierarchy in mind, continue with: * [Permissions](/concepts/platform/permissions) concept for platform RBAC versus runtime agent authorization * [Architecture](/concepts/architecture) concept for where scopes sit among components, controls, and audit * [Rules](/govern/rules) to author and deploy Cedar bundles at each scope * [Authorization Model](/concepts/architecture/authorization-model) concept for permit, forbid, and effective policy assembly # Resources Source: https://docs.domesystems.ai/concepts/resources/index Why Dome models tools, model connections, and model pools as callable resources ## High-level overview A resource is a workspace record for something callable. Dome stores how to reach it, how to authenticate outbound, and how it participates in Gateways and Rules. The agent never holds the upstream credential. Membership on a [Gateway](/concepts/gateways) makes the resource addressable. [Rules](/concepts/controls/rules) decide whether a specific call is allowed. Dome supports three resource types: | Type | What it represents | Typical call path | | -------------------- | ------------------------------------------------------------ | -------------------------------------------------- | | **Tool** | An MCP server (or related tool backend) | Gateway `/gateways//mcp` | | **Model connection** | One LLM provider endpoint and credential | Gateway `/gateways//v1/...` by connection name | | **Model pool** | A named group of model connections with routing and failover | Gateway `/gateways//v1/...` by pool name | Attaching resources and calling them looks like this: 1. An operator adds a Notion MCP tool and an Anthropic model connection, puts both on a Gateway, and grants an agent access. 2. The agent discovers tools over MCP and names a model or pool on chat requests. 3. Dome authenticates the agent, checks Gateway membership and Rules, injects credentials on egress, and audits the outcome against that resource. Resources are the callable side of Connect. Gateways package which ones an agent can reach. ### Reachability vs authorization A resource can exist without being callable. Until it is a member of a Gateway an agent may use, it stays **Unreachable**. Membership and access grants package reachability. Rules, Guards, and Quotas still decide what happens after the call is admitted. Refer to [Gateways](/concepts/gateways) concept. ### Tools vs models Tools act on the world through MCP (and related protocols). Models produce completions through the LLM ingress. Both are resources, but they use different Gateway path suffixes, credential shapes, and Guard kinds. Pools apply only to model traffic. ### Direct models vs pools Agents can name a model connection directly or name a pool. Prefer a pool when the client should stay stable while you swap providers, weight traffic, or fail over. Prefer a direct connection when one endpoint should always serve that name. Details are on the [Models](/concepts/resources/models) concept and [Pools](/concepts/resources/model-pools) concept. ## Next steps With that resources model in mind, continue with: * [Architecture](/concepts/architecture#resources) concept for where resources sit among components * [Tools](/concepts/resources/tools) concept for MCP connections, credentials, and catalogs * [Models](/concepts/resources/models) concept for provider connections and attributes * [Pools](/concepts/resources/model-pools) concept for routing, failover, and resolution * [Gateways](/concepts/gateways) concept for membership and reachability * [Tools](/connect/resources/tools), [Models](/connect/resources/models), and [Pools](/connect/resources/models/pools) to configure them # Pools Source: https://docs.domesystems.ai/concepts/resources/model-pools Why Dome groups model connections behind a pool name for routing and failover ## High-level overview A pool contains one or more model connections as **members**. When an agent puts the pool name in the request's `model` field, Dome orders eligible members by the routing strategy, sends the request to the primary, and may try further members on failure up to the failover limit. For an agent to use a pool: 1. Connect the models that can serve the traffic. 2. Create the pool and add members. 3. Configure routing and failover. 4. Attach the pool to a [Gateway](/concepts/gateways). 5. Allow that Gateway on the agent. 6. Verify with the agent's key. Routing through a pool looks like this: 1. An operator creates `production` with priority-weighted routing, adds `claude-prod` at priority 0 and `gpt-backup` at priority 1, attaches the pool to `prod-llms`, and grants an agent access. 2. The agent calls with `model: "production"`. 3. Dome resolves the pool, authorizes the primary, and dispatches. 4. On a transient provider failure, Dome may authorize and try the backup. 5. Audit records which member served the call. A pool is the stable name clients and Rules use. Members and strategy stay editable underneath. ### Members Each member is one model connection. With priority-weighted routing, priority places the member in a failover tier and weight shares traffic inside the tier. Round-robin and least-loaded ignore priority and weight. A model can belong to more than one pool. Removing a member leaves the connection available elsewhere. The management interfaces store an `enabled` value, but the current gateway routes to members regardless of that value. To stop sending traffic to a member, remove it from the pool. ### Routing strategies The strategy chooses the primary and orders failover candidates: `priority_weighted`, `round_robin`, or `least_loaded`. Strategy scope (`workspace` or `caller`) controls whether round-robin and least-loaded state is shared. Catalog is on the [Pools](/reference/resources/model-pools#routing-strategies) reference. ### Failover Failover limits how many additional members Dome may try after the primary fails. Dome authorizes the primary first, then re-authorizes each candidate before trying it. Streaming can fail over only before the first response byte reaches the caller. Limits and behavior are on the [Pools](/reference/resources/model-pools#failover) reference. ### Pool resolution When a request reaches a Gateway, Dome resolves the `model` value in a fixed order: exact pool name, exact connection name, upstream model id match, first matching `match_when` pool, then the workspace default pool. Exact names beat conditional routes. The `match_when` dialect is on the [Pools](/reference/resources/model-pools#pool-resolution) reference. Routing does not replace authorization. Rules still evaluate the pool and selected model. ### Response caching Pool records store cache TTL and scope fields intended for identical-request reuse. The gateway does not read these settings during model dispatch. Setting `cache_ttl_secs` or `cache_scope` does not cache responses. ## Next steps With that model-pool routing model in mind, continue with: * [Resources](/concepts/resources) concept for how pools sit among resource types * [Models](/concepts/resources/models) concept for the connections pools select * [LLM gateway](/concepts/gateways/llm-gateway) concept for dispatch and failover on the wire * [Quotas](/concepts/controls/quotas) concept for spend caps and pool spillover * [Pools](/reference/resources/model-pools) reference for strategies, failover, and `match_when` * [Pools](/connect/resources/models/pools) to create pools, manage members, and attach Gateways # Models Source: https://docs.domesystems.ai/concepts/resources/models Why Dome connects LLM providers as model connections agents call without holding provider credentials ## High-level overview A model connection represents one upstream model. The connection stores provider, model identifier, credentials, trusted attributes, and an optional filter window. An agent selects it by putting the connection name in the request's `model` field, or by naming a pool that includes it. For an agent to use a model: 1. Choose a provider and model identifier. 2. Add the model with its upstream credentials. 3. Attach it directly to a Gateway, or add it to a [pool](/concepts/resources/model-pools) attached to one. 4. Allow that Gateway on the agent. 5. Verify the route with the agent's key. Adding a model and calling it looks like this: 1. An operator adds `claude-prod` for Anthropic with a shared API key, attaches it to `prod-llms`, and grants an agent access. 2. The agent calls `/gateways//v1/chat/completions` with `model: "claude-prod"`. 3. Dome admits the Gateway, resolves the connection, evaluates Rules, injects the Anthropic key, filters the stream, and audits the call. The agent never sees the provider key. A model connection is one provider endpoint. Prefer a [pool](/concepts/resources/model-pools) when the client name should stay stable across failover or traffic shifts. ### Providers The provider determines how Dome translates requests and where it places the upstream credential. You cannot change the provider after create. Native providers, hosted OpenAI-compatible providers, and bring-your-own endpoints (`openai_compatible`, `custom`) are listed on the [Models](/reference/resources/models#providers) reference. ### Credentials Agent API keys authenticate inbound requests to Dome. Model credentials authenticate Dome's outbound requests to the provider. Authentication method and credential type must form a valid pair. Per-user credentials select the provider credential from the verified act-as identity and may return a `provision_url` when the user has not connected yet. Pairings and workload-identity notes are on the [Models](/reference/resources/models#credentials) reference. ### Attributes Attributes are trusted metadata on the connection for [Rules](/concepts/controls/rules) concept to evaluate, such as `region=eu` or `data_class=restricted`. They do not change how Dome calls the provider. Derived fields such as `resolved_model` and `pool` are set server-side and cannot be spoofed through attributes. Refer to [LLM gateway](/concepts/gateways/llm-gateway#authorization) concept. ### Filter window A model can raise how much streamed output Dome buffers before applying outbound Guards. The effective window is the max of workspace floor, connection override, and per-request override. A model can increase coverage but cannot reduce the workspace minimum. Bounds are on the [Models](/reference/resources/models#filter-window) reference and [Guards](/reference/controls/guards#streaming-window) reference. ### Direct attach vs pool Attach a model directly when that name should always route to one connection. Add it to a pool when you need weighted distribution, failover, or a stable alias while you change members. A model can belong to more than one pool and more than one Gateway. ## Next steps With the model connection model in mind, continue with: * [Resources](/concepts/resources) concept for how models sit among resource types * [Pools](/concepts/resources/model-pools) concept for routing and failover * [LLM gateway](/concepts/gateways/llm-gateway) concept for the model ingress under a Gateway * [Gateways](/concepts/gateways) concept for membership and reachability * [Guards](/concepts/controls/guards) concept for text Filters on prompts and completions * [Models](/reference/resources/models) reference for providers, credentials, and filter windows * [Models](/connect/resources/models) to add models, rotate credentials, and attach Gateways # Tools Source: https://docs.domesystems.ai/concepts/resources/tools Why Dome attaches MCP servers as tools agents call without holding upstream credentials ## High-level overview When you add a tool, Dome stores its endpoint, protocol, credentials, and egress headers. After it connects, every operation the server exposes appears in that tool's **discovered catalog**. You can block or deprecate one operation without removing the whole tool. For an agent to use a tool: 1. Add the tool for the upstream MCP server. 2. Configure upstream authentication when the server requires it. 3. Attach the tool to a [Gateway](/concepts/gateways). 4. Allow that Gateway on the agent. 5. Confirm discovery with the agent's key. Adding a tool and calling it looks like this: 1. An operator adds a Streamable HTTP tool for Notion, sets shared API-key auth, attaches it to `prod-tools`, and grants an agent access. 2. The agent lists tools at `/gateways//mcp`. 3. Each `tools/call` runs admission, Rules, request Guards, egress with injected credentials, response Guards, and audit. The agent never sees the Notion credential. A tool is a callable MCP connection. Gateway membership makes it reachable. Rules still decide each call. ### Upstream authentication Agent API keys authenticate inbound requests to Dome. They never pass through as upstream credentials. Dome stores upstream secrets and injects them only on the outbound call. Two settings control which credential the gateway sends: the **authentication method** (how the upstream verifies the request) and the **credential type** (whose credential is sent: shared or per-user). The pairings are fixed. Catalog of methods, types, and defaults is on the [Tools](/reference/resources/tools#upstream-authentication) reference. With per-user credentials, each request must carry a verified act-as identity. When the end user has no credential yet, Dome returns `401` with a short-lived `provision_url`. Refer to [Errors and denials](/develop#errors-and-denials). ### Egress headers Egress headers are values the gateway adds on the outbound request. A header can be a literal, a stored secret, or the verified act-as identity. Only Streamable HTTP tools can carry them. An act-as header requires OIDC or HMAC verification and is incompatible with OAuth auth on the same tool. Details are on the [Tools](/reference/resources/tools#egress-headers) reference. ### Discovered tools After the gateway connects, every operation the server exposes appears in the tool's catalog. Status (`active`, `deprecated`, `blocked`) lets you change one operation without removing the tool. Blocking denies the operation even when a rule would permit it. Status behavior is on the [Tools](/reference/resources/tools#discovered-tools) reference. ### Protocols Dome supports Streamable HTTP for remote MCP servers and stdio for local processes. OAuth and egress headers require Streamable HTTP. Protocol notes are on the [Tools](/reference/resources/tools#protocols) reference and the [Tools gateway](/concepts/gateways/tools-gateway#egress-protocols) concept. ## Next steps With the tool connection model in mind, continue with: * [Resources](/concepts/resources) concept for how tools sit among resource types * [Tools gateway](/concepts/gateways/tools-gateway) concept for the MCP ingress under a Gateway * [Gateways](/concepts/gateways) concept for membership and reachability * [Guards](/concepts/controls/guards) concept for JSON Filters on tool arguments and results * [Tools](/reference/resources/tools) reference for auth, headers, catalog status, and protocols * [Tools](/connect/resources/tools) to add tools, manage catalogs, and attach Gateways # Agents Source: https://docs.domesystems.ai/connect/agents Register agents, issue credentials, allow resources, and manage lifecycle Refer to [Agents](/concepts/agents) concept for how agent identity works. ## Overview Dome separates an agent's identity from its credentials and access. Registering an agent creates a durable identity for the application or process, but does not grant access. An API key lets the runtime authenticate as that identity. Allowed resources, Gateway membership, and [Rules](/govern/rules) determine what requests Dome permits. Because credentials and access are separate, you can rotate a key without changing permissions or change permissions without issuing a new key. The typical workflow is: 1. [Register the agent](#register-agent) to create the identity. 2. [Get the agent](#get-agent) to confirm the record and note the workspace gateway endpoint. 3. Allow the [resources](#allowed-resources) it needs. 4. [Create an API key](#create-key) for the runtime. 5. Point the client at Dome. Refer to [Develop](/develop#authenticate). When access must depend on the person the agent acts for, configure [delegated identity](#delegated-identity). When one agent spawns others, set a [parent](#hierarchy) at register time. ### Allowed resources An agent reaches only the resources you allow it: model pools, individual models, MCP tools, and Gateways. When an agent allows a Gateway, Dome lets it use that endpoint. Allowing a resource is shorthand for creating a rule. Each entry generates a rule in the agent's rule bundle which the gateway enforces on the next request without a separate deploy. Because it creates rules, allowing a resource needs `rules.deploy` in addition to the permission to edit the agent, so someone with the developer role can register an agent and change its metadata but cannot change which resources it reaches. Dome derives the agent's **capabilities** (the strings its rules can test) from the same selections: | You allow | Capabilities Dome derives | | ------------------- | ---------------------------------------- | | Model pool or model | `llm:invoke` | | Tool | `mcp:call`, `mcp:discover` | | Gateway | `llm:invoke`, `mcp:call`, `mcp:discover` | You set a capability yourself only when one of your rules tests a string Dome would not derive, such as `read-data`. When the shorthand cannot express a condition, such as a time window, a PII field, or an act-as claim, create [Rules](/govern/rules) directly. A generated bundle adds to workspace defaults rather than replacing them. You can allow resources when you [register an agent](#register-agent) or [change them later](#change-allowed-resources). ### Delegated identity An agent can act for a person rather than only for itself. When it presents an act-as claim, Dome verifies the claim before authorizing the request, and the audit trail records the end user alongside the agent. On the agent you choose the verification method (`oidc`, `hmac`, `bound`, or `none`), whether a claim is mandatory on every request, which provider to use, and optional allow-lists by group, email, or subject. An agent with no restrictions accepts any verified identity. Like allowed resources, these settings generate rules, so changing them needs `rules.deploy`. Workspace providers, the workspace floor, and claim-aware Rules are on [Delegated agents](/connect/agents/delegated). You can configure per-agent settings when you [register](#register-agent) or [update](#change-end-user-identity) the agent. ### Hierarchy An agent can have a parent. The parent and its descendants form a hierarchy, which helps when one agent spawns or manages others and you want to act on them together. A hierarchy grants no access on its own, and each agent reaches only what you allow it. A hierarchy can be at most 10 levels deep, and a parent and child must belong to the same tenant. You can set an agent's parent when you [register](#register-agent) it. An update cannot change it afterward. How lifecycle operations reach descendants is under [Manage lifecycle](#manage-lifecycle). ## Requirements Before you begin: * Install and authenticate the Dome CLI, then select a workspace context * Create or select a [Gateway](/connect/gateways), attach the required resources, and grant the agent access ### Permissions Agent registry, lifecycle, and key operations require platform permissions. Each operation states the permission it requires. For exhaustive flags, tool parameters, and endpoint contracts, refer to the [CLI](/cli/connect/agents), [MCP](/reference/mcp/agents), and [API](/api/agents/register-agent) references. | Default roles | Permission | Grants | | -------------------------------------------- | ----------------- | --------------------------------------------------------------------- | | All workspace roles | `agent.view` | Get and list agents and keys | | `admin`, `operator`, `developer` | `agent.register` | Register and update agents | | `admin`, `operator`, `security`, `developer` | `agent.suspend` | Suspend and reactivate agents | | `admin`, `operator`, `developer` | `agent.revoke` | Revoke agents | | `admin`, `operator` | `agent.delete` | Delete agents | | `admin`, `operator`, `developer` | `agentkey.manage` | Create, rotate, or revoke agent keys | | `admin`, `operator`, `security` | `rules.deploy` | Change allowed resources or end-user identity (with `agent.register`) | | All workspace roles | `rules.view` | View generated rule bundles | ## Register agent Register an agent to create the record that credentials, allowed resources, and lifecycle operations attach to. You can set [allowed resources](#allowed-resources), [end-user identity](#delegated-identity), and a [parent](#hierarchy) in the same call. Requires `agent.register`, plus `rules.deploy` if you set allowed resources or end-user identity in the same call. ```bash theme={"system"} dome agents register \ --name "data-pipeline-agent" ``` Add `--parent-id` to place the agent in a hierarchy, `--metadata key=value` for your own labels, and `--if-not-exists` to return the existing agent instead of failing when the name is taken. Reference: [`dome agents register`](/cli/connect/agents#register) Tool: `dome_agents_register` ```json theme={"system"} { "name": "data-pipeline-agent" } ``` Reference: [`dome_agents_register`](/reference/mcp/agents#agents-register) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/RegisterAgent HTTP/1.1 Content-Type: application/json { "name": "data-pipeline-agent", "workspace_id": "{{WORKSPACE_ID}}" } ``` Reference: [`RegisterAgent`](/api/agents/register-agent) ```text title="Register an agent" theme={"system"} Register an agent named "data-pipeline-agent". ``` ```json Response theme={"system"} { "id": "a1b2c3d4-...", "name": "data-pipeline-agent", "status": "provisioned" } ``` A new agent starts in `provisioned` and becomes `active` on its first gateway request. Refer to [Manage lifecycle](#manage-lifecycle) for the full state model. ### Register from a file Register an agent from a YAML or JSON file when a reviewable definition is clearer than a long flag list, such as in CI. The file shape mirrors `RegisterAgentRequest`, the same schema the SDKs and Terraform provider consume, so a misspelled field fails fast instead of dropping silently. The workspace always comes from the active context. A `workspace_id` in the file is ignored. ```yaml agent.yaml theme={"system"} name: data-pipeline-agent capabilities: [read] allowed_pool_names: [openai-prod] allowed_tools: [github/list_repos] ``` ```bash theme={"system"} dome agents register --file agent.yaml ``` Reference: [`dome agents register --file`](/cli/connect/agents#register-from-a-file) ## Update agent Update an agent to change its metadata or capabilities. Only the fields you send change, and list fields replace rather than merge. Requires `agent.register`. ```bash theme={"system"} dome agents update data-pipeline-agent \ --metadata environment=production ``` Reference: [`dome agents update`](/cli/connect/agents#update) Tool: `dome_agents_update` ```json theme={"system"} { "agent_id": "data-pipeline-agent", "metadata": { "environment": "production" } } ``` Reference: [`dome_agents_update`](/reference/mcp/agents#agents-update) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/UpdateAgent Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "capabilities": ["read-data", "write-reports"] } ``` Reference: [`UpdateAgent`](/api/agents/update-agent) ```text title="Update an agent" theme={"system"} Update agent "data-pipeline-agent" to add metadata environment=production. ``` ### Change allowed resources You can change which pools, models, tools, and Gateways the agent may reach. Refer to [Allowed resources](#allowed-resources) for what these grants mean. When you allow models, a pool is usually the better choice than an individual model, because a pool can [fail over and load-balance](/connect/resources/models/pools). Requires `agent.register` and `rules.deploy`, because every change regenerates the agent's rule bundle. ```bash theme={"system"} dome agents update data-pipeline-agent \ --pool "gpt-4-prod" \ --tool "github/list-issues" \ --tool "github/create-comment" \ --gateway "{{GATEWAY_ID}}" ``` Tool names take the form `/`. When a pool is not appropriate, `--model` allows a specific model connection, and `--capabilities` sets custom capability strings. Reference: [`dome agents update`](/cli/connect/agents#update) Tool: `dome_agents_update` ```json theme={"system"} { "agent_id": "data-pipeline-agent", "allowed_pool_names": ["gpt-4-prod"], "allowed_tools": ["github/list-issues", "github/create-comment"] } ``` Reference: [`dome_agents_update`](/reference/mcp/agents#agents-update) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/UpdateAgent Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "allowed_pool_names": ["gpt-4-prod"], "allowed_direct_model_names": [], "allowed_tools": ["github/list-issues", "github/create-comment"] } ``` Reference: [`UpdateAgent`](/api/agents/update-agent) ```text title="Change allowed resources" theme={"system"} Restrict agent "data-pipeline-agent" to pool "gpt-4-prod" and tools github/list-issues and github/create-comment. ``` When you change one of these fields, the value you send replaces the existing list rather than merging into it, so a partial list drops whatever it omits and an empty value clears the list. In the dashboard, the agent detail page hides the edit controls on Capabilities and Delegation when the caller lacks `rules.deploy`, and the edit dialog shows a banner explaining the limit. ### Change end-user identity You can also change how Dome verifies the end user an agent acts for, and which identities it may present. Refer to [Delegated identity](#delegated-identity) and [Delegated agents](/connect/agents/delegated) for providers and the workspace floor. Requires `agent.register` and `rules.deploy`. | Setting | Flag | | ------------------------------- | --------------------------------------------------------------------------- | | Verification method | `--actas-method` (`none`, `oidc`, `hmac`, or `bound`) | | Mandatory claim | `--actas-required` | | Workspace provider | `--actas-provider` | | Inline OIDC or HMAC credentials | `--actas-oidc-url`, `--actas-hmac-secret` | | Acceptable identities | `--actas-allowed-group`, `--actas-allowed-email`, `--actas-allowed-subject` | ```bash theme={"system"} dome agents update support-bot \ --actas-method oidc \ --actas-required \ --actas-allowed-group "support-engineers" ``` Reference: [`dome agents update`](/cli/connect/agents#update) Tool: `dome_agents_update` ```json theme={"system"} { "agent_id": "support-bot", "actas_method": "oidc", "actas_required": true, "actas_allowed_groups": ["support-engineers"] } ``` Reference: [`dome_agents_update`](/reference/mcp/agents#agents-update) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/UpdateAgent Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "actas_method": "oidc", "actas_required": true, "actas_allowed_groups": ["support-engineers"] } ``` Reference: [`UpdateAgent`](/api/agents/update-agent) ```text title="Require verified end-user identity" theme={"system"} Require OIDC-verified act-as identity on agent "support-bot" and allow only the support-engineers group. ``` Populating an acceptable-identity list denies any request whose verified claim is not on the list. Leaving all three empty accepts any verified identity, and workspace-level act-as enforcement still applies. ## Get agent Retrieve an agent by name or UUID. Before you configure a runtime, you should retrieve an agent's information to confirm its status and the endpoint its traffic should reach. Requires `agent.view`. ```bash theme={"system"} dome agents get data-pipeline-agent ``` Reference: [`dome agents get`](/cli/connect/agents#get) Tool: `dome_agents_get` ```json theme={"system"} { "agent_id": "{{AGENT_ID}}", "gateway_id": "{{GATEWAY_ID}}" } ``` Reference: [`dome_agents_get`](/reference/mcp/agents#agents-get) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/GetAgent Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}" } ``` Reference: [`GetAgent`](/api/agents/get-agent) ```text title="Get an agent" theme={"system"} Get "data-pipeline-agent" for Gateway "{{GATEWAY_ID}}" and confirm its status and connection endpoints. ``` When `gateway_id` is supplied, the response includes the agent record and that accessible Gateway's complete protocol endpoints. Omit it for metadata only. ```json Response theme={"system"} { "agent": { "id": "a1b2c3d4-…", "name": "data-pipeline-agent", "status": "active" }, "gateway_id": "{{GATEWAY_ID}}", "gateway_endpoints": { "gateway_id": "{{GATEWAY_ID}}", "gateway_url": "https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}}", "mcp_url": "https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}}/mcp", "openai_base_url": "https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}}/v1", "anthropic_base_url": "https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}}" } } ``` The dashboard shows the same pair on the agent detail page's Identity card, each with a copy button. ## List agents Retrieve a list of agents in a workspace. You can narrow the results by filtering on status, such as to find every suspended agent. Requires `agent.view`. ```bash theme={"system"} dome agents list --status active ``` A list returns 100 agents unless `--limit` says otherwise. Reference: [`dome agents list`](/cli/connect/agents#list) Tool: `dome_agents_list` ```json theme={"system"} { "status": "active", "limit": 25 } ``` `offset` pages through results beyond the limit. Reference: [`dome_agents_list`](/reference/mcp/agents#agents-list) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/ListAgents Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "status": "active" } ``` Reference: [`ListAgents`](/api/agents/list-agents) ```text title="List agents" theme={"system"} List the active agents in this workspace. ``` ## Manage lifecycle An agent moves through four states. Provisioned and active agents can send traffic. Suspended and revoked agents are denied. | State | Traffic | How it leaves this state | | ------------- | ------- | ------------------------------------------------------------------------------ | | `provisioned` | Allowed | Becomes `active` automatically on the first gateway request | | `active` | Allowed | An operator [suspends](#suspend) or [revokes](#revoke) it | | `suspended` | Denied | An operator [reactivates](#reactivate) or [revokes](#revoke) it | | `revoked` | Denied | Terminal. You can [delete](#delete) the record, but you cannot undo revocation | When agents are in a [hierarchy](#hierarchy), lifecycle operations reach descendants differently: | Operation | Reach | | ---------- | ----------------------------------------------------------------------------------- | | Suspend | The agent alone, or the agent plus its active descendants when you pass `--cascade` | | Reactivate | The agent alone. Undoing a cascaded suspension takes one call per agent | | Revoke | The agent and every descendant, always, depth-first, any state | | Delete | The agent alone, or the agent plus its descendants when you request a cascade | Parent and child must already share a tenant. Hierarchy does not grant access. Full transition detail is on the [Agent Lifecycle](/concepts/agents/agent-lifecycle) concept. ### Suspend Suspend an agent to deny its requests until you reactivate it. Suspension is reversible. With cascade, it also suspends active descendants. Requires `agent.suspend`. ```bash theme={"system"} dome agents suspend data-pipeline-agent --reason "maintenance window" ``` Reference: [`dome agents suspend`](/cli/connect/agents#suspend) Tool: `dome_agents_suspend` ```json theme={"system"} { "agent_id": "data-pipeline-agent", "reason": "maintenance window", "cascade": false } ``` Reference: [`dome_agents_suspend`](/reference/mcp/agents#agents-suspend) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/SuspendAgent Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "reason": "maintenance window" } ``` Reference: [`SuspendAgent`](/api/agents/suspend-agent) ```text title="Suspend an agent" theme={"system"} Suspend agent "data-pipeline-agent" for a maintenance window. ``` ### Reactivate Reactivate a suspended agent to restore its traffic. Reactivation applies only to an agent in the `suspended` state, and it covers one agent, so a cascaded suspension takes one reactivation per agent. Requires `agent.suspend`. ```bash theme={"system"} dome agents reactivate data-pipeline-agent ``` Reference: [`dome agents reactivate`](/cli/connect/agents#reactivate) Tool: `dome_agents_reactivate` ```json theme={"system"} { "agent_id": "data-pipeline-agent" } ``` Reference: [`dome_agents_reactivate`](/reference/mcp/agents#agents-reactivate) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/ReactivateAgent Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}" } ``` Reference: [`ReactivateAgent`](/api/agents/reactivate-agent) ```text title="Reactivate an agent" theme={"system"} Reactivate agent "data-pipeline-agent". ``` ### Revoke Revoke an agent to decommission it permanently. Its record and audit history remain readable. Requires `agent.revoke`. Revocation cannot be reversed and always cascades to every descendant agent. ```bash theme={"system"} dome agents revoke data-pipeline-agent --reason "decommissioned" ``` Reference: [`dome agents revoke`](/cli/connect/agents#revoke) Tool: `dome_agents_revoke` ```json theme={"system"} { "agent_id": "data-pipeline-agent", "reason": "decommissioned" } ``` Reference: [`dome_agents_revoke`](/reference/mcp/agents#agents-revoke) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/RevokeAgent Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "reason": "decommissioned" } ``` Reference: [`RevokeAgent`](/api/agents/revoke-agent) ### Delete Delete a revoked agent to remove it and all of its data. Deletion works only on a revoked agent and rejects an agent in any other state. Requires `agent.delete`. Deletion removes the agent record and its data irreversibly, including its keys. Revoke instead when you need the record retained. ```bash theme={"system"} dome agents delete data-pipeline-agent --reason "offboarded" ``` Deletion covers one agent, and it fails when that agent still has descendants. Adding `--cascade` deletes the agent and its descendants in one call, deepest first, and every descendant must already be revoked. ```bash theme={"system"} dome agents delete data-pipeline-agent --cascade ``` Reference: [`dome agents delete`](/cli/connect/agents#delete) Tool: `dome_agents_delete` ```json theme={"system"} { "agent_id": "data-pipeline-agent", "reason": "offboarded", "cascade": false } ``` Reference: [`dome_agents_delete`](/reference/mcp/agents#agents-delete) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/DeleteAgent Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "reason": "offboarded" } ``` Reference: [`DeleteAgent`](/api/agents/delete-agent) ## Manage API keys An API key is the credential the agent runtime presents to Dome. An agent can hold several keys, so you can rotate a credential or keep one key per environment. Keys scope to the agent's tenant and workspace. Requires `agentkey.manage` to create, rotate, or revoke a key, and `agent.view` to list them. The token is returned once, when you create or rotate a key. Store it immediately, because it cannot be retrieved again. ### Create key Create a key to receive the one-time `token`, selected `gateway_id`, and complete `gateway_endpoints`. Supply a Gateway when the agent can access more than one. ```bash theme={"system"} dome agents create-key data-pipeline-agent --name "default" --gateway "{{GATEWAY_ID}}" ``` Reference: [`dome agents create-key`](/cli/connect/agents#create-key) Tool: `dome_agents_create_key` ```json theme={"system"} { "agent": "data-pipeline-agent", "name": "default", "gateway_id": "{{GATEWAY_ID}}" } ``` Reference: [`dome_agents_create_key`](/reference/mcp/agents#agents-create-key) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/CreateAgentKey Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "name": "default", "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}" } ``` Reference: [`CreateAgentKey`](/api/agents/create-agent-key) ```text title="Create an API key" theme={"system"} Create an API key named "default" for agent "data-pipeline-agent" using Gateway "{{GATEWAY_ID}}". ``` ```json Response theme={"system"} { "token": "dome_…", "key": { "id": "k_…", "name": "default", "createdAt": "…" }, "gateway_id": "{{GATEWAY_ID}}", "gateway_endpoints": { "gateway_id": "{{GATEWAY_ID}}", "gateway_url": "https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}}", "mcp_url": "https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}}/mcp", "openai_base_url": "https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}}/v1", "anthropic_base_url": "https://gateway.example.dome.dev/gateways/{{GATEWAY_ID}}" } } ``` A separate key for each runtime or environment lets you revoke one without disrupting the others. ### List keys Retrieve an agent's active keys with their names and creation times. A list never returns tokens, because Dome returns a token only when you create or rotate a key. ```bash theme={"system"} dome agents list-keys data-pipeline-agent ``` Reference: [`dome agents list-keys`](/cli/connect/agents#list-keys) Tool: `dome_agents_list_keys` ```json theme={"system"} { "agent": "data-pipeline-agent" } ``` Reference: [`dome_agents_list_keys`](/reference/mcp/agents#agents-list-keys) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/ListAgentKeys Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}" } ``` Reference: [`ListAgentKeys`](/api/agents/list-agent-keys) ```text title="List an agent's API keys" theme={"system"} List the API keys for agent "data-pipeline-agent". ``` ### Rotate key Rotate a key to revoke the old secret and issue a new one under the same name. Dome returns the new token and the gateway endpoint. The old secret stops authenticating as soon as you rotate, so deploy the new token promptly. ```bash theme={"system"} dome agents rotate-key data-pipeline-agent production ``` Reference: [`dome agents rotate-key`](/cli/connect/agents#rotate-key) Tool: `dome_agents_rotate_key` ```json theme={"system"} { "agent": "data-pipeline-agent", "key_name": "production" } ``` Reference: [`dome_agents_rotate_key`](/reference/mcp/agents#agents-rotate-key) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/RotateAgentKey Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "key_name": "production" } ``` Reference: [`RotateAgentKey`](/api/agents/rotate-agent-key) ```text title="Rotate an agent key" theme={"system"} Rotate the "production" API key for "data-pipeline-agent". ``` ### Revoke key Revoke a key to retire it without affecting the agent's other keys. A revoked key stops authenticating immediately, so confirm the runtime holds a working key before you revoke this one. ```bash theme={"system"} dome agents revoke-key data-pipeline-agent production ``` Reference: [`dome agents revoke-key`](/cli/connect/agents#revoke-key) Tool: `dome_agents_revoke_key` ```json theme={"system"} { "agent": "data-pipeline-agent", "key_name": "production" } ``` Reference: [`dome_agents_revoke_key`](/reference/mcp/agents#agents-revoke-key) ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/RevokeAgentKey Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "key_name": "production" } ``` Reference: [`RevokeAgentKey`](/api/agents/revoke-agent-key) ```text title="Revoke an agent key" theme={"system"} Revoke the "production" API key for agent "data-pipeline-agent". ``` ## Manage agent-scoped rules Agent-scope rules apply to one agent, for conditions [allowed resources](#allowed-resources) cannot express. They evaluate in addition to organization, tenant, and workspace rules, and a deny at the workspace scope overrides an allow at the agent scope. Refer to [Authorize Access](/govern/rules) for creating rules, inherited scopes, and deployment. ### Assign rules Assign rule files to the agent scope, where the gateway evaluates them alongside the rules the agent inherits from higher scopes. Requires `rules.deploy`. ```bash theme={"system"} dome agents assign-policy data-pipeline-agent agent-policy.cedar ``` Reference: [`dome agents assign-policy`](/cli/connect/agents#assign-policy) Tool: `dome_agents_assign_policy` ```json theme={"system"} { "agent_id": "data-pipeline-agent", "files": [ { "name": "agent-policy.cedar", "content": "permit(principal, action == Dome::Action::\"mcp:call\", resource == Dome::MCPTool::\"github/list-issues\");" } ] } ``` Reference: [`dome_agents_assign_policy`](/reference/mcp/rules#agents-assign-policy) ```text title="Assign agent-scoped rules" theme={"system"} Assign the rules in agent-policy.cedar to agent "data-pipeline-agent". ``` ### View rules Retrieve the rules assigned at the agent scope. The result excludes the organization, tenant, and workspace rules the agent also inherits. Requires `rules.view`. ```bash theme={"system"} dome agents get-policies data-pipeline-agent ``` Reference: [`dome agents get-policies`](/cli/connect/agents#get-policies) Tool: `dome_agents_get_policies` ```json theme={"system"} { "agent_id": "data-pipeline-agent" } ``` Reference: [`dome_agents_get_policies`](/reference/mcp/rules#agents-get-policies) ```text title="View agent-scoped rules" theme={"system"} Show the rules currently assigned to agent "data-pipeline-agent". ``` ## Next steps * [Delegated agents](/connect/agents/delegated) when access must depend on the person * [Tools](/connect/resources/tools) and [Models](/connect/resources/models) an agent can call * [Gateways](/connect/gateways) for membership and grants * [Develop](/develop#authenticate) to authenticate and route runtime traffic * [Agent Lifecycle](/concepts/agents/agent-lifecycle) concept for states and cascades # Delegated agents Source: https://docs.domesystems.ai/connect/agents/delegated Configure agents that act for a verified person so Rules, Quotas, and audit can authorize on that human ## Overview Your application sends the identity on each request as `X-Dome-Act-As`. Dome verifies it, then Rules and audit can read `principal.act_as`. The agent still authenticates as itself. Act-As never replaces agent authentication. For a request to authorize as a person, three things have to be true: 1. **Dome knows how to verify the person.** You point Dome at an IdP (OIDC) or a shared HMAC secret, either as a workspace verification provider on this page or inline on the agent. You can also set a workspace floor that requires every agent to present verified Act-As and limits which methods are allowed. 2. **The agent is configured to act for people.** On the agent record you set the Act-As method, whether a person is required, which provider to use, and optional allow-lists. Do that when you [register](/connect/agents#register-agent) or [update](/connect/agents#change-end-user-identity) the agent. 3. **Your application sends who the person is.** Each request includes `X-Dome-Act-As`. Without it, Rules have no end user to evaluate. Refer to [Pass identity for delegated agents](/develop#pass-identity-for-delegated-agents). Unverified headers never become Cedar claims. The agent credential still identifies the workload. Act-as never replaces agent authentication. A verified end user cannot expand access beyond a higher-scope `forbid`. The typical workflow is: 1. [Create a verification provider](#create-a-verification-provider): [OIDC](#oidc) or [HMAC](#hmac), or inline OIDC/HMAC on the agent. 2. Configure Act-As on the agent when you [register](/connect/agents#register-agent) or [update](/connect/agents#change-end-user-identity) it. 3. Optionally [require verified identity](#require-verified-identity) for every agent in the workspace. 4. Write [claim-aware Rules](#claim-aware-rules) that read `principal.act_as`. Claim names are on the [Rules](/reference/controls/rules#principal-attributes) reference. ### Verification methods Each agent chooses how Dome verifies the Act-As envelope. Your application must send a matching `X-Dome-Act-As` value on MCP, OpenAI, and Anthropic requests. | Method | How Dome verifies | What your application sends in `X-Dome-Act-As` | | ------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------- | | `oidc` | Verify an end-user JWT through the provider's OpenID Connect discovery metadata | Raw end-user JWT | | `hmac` | Verify a signed identity header with a shared secret | Signed, timestamped act-as envelope | | `none` | Act-As headers are not processed (standing identity only) | Canonical JSON encoded with standard base64 (development only) | | `bound` | Server-bound enrollment identity with no client-supplied Act-As | No header. The platform supplies the enrolled user | Prefer OIDC or bound identity for production traffic. Agents can share a workspace verification provider or use inline OIDC/HMAC credentials on the agent record. ### Identity claims Verified identity is exposed to Cedar as `principal.act_as`: | Claim | Description | | -------- | ---------------------------------- | | `sub` | Stable end-user subject identifier | | `email` | End-user email | | `roles` | End-user roles | | `groups` | End-user groups | | `claims` | Additional customer-defined claims | Attribute types for policy are also on the [Rules](/reference/controls/rules#principal-attributes) reference. ### Workspace floor The workspace can require Act-As on every request and restrict which verification methods are allowed. That floor applies to every agent in the workspace. Agent settings can be stricter than the floor, but cannot weaken it. ### Where verified identity is consumed Verified identity can also: * Scope [Quotas](/govern/quotas) to an Act-As subject * Forward to upstream tools when a connection uses an Act-As egress header. Refer to [Tools](/connect/resources/tools#change-egress-headers) and [Tools](/reference/resources/tools#egress-headers) reference * Drive per-user credentials on the [Tools](/reference/resources/tools#upstream-authentication) reference and [Models](/reference/resources/models#credentials) reference * Drive pool `match_when` conditions on `principal.act_as.*`. Refer to [Pools](/reference/resources/model-pools#pool-resolution) reference ## Requirements Before you begin, authenticate to Dome and select a workspace. ### Permissions Workspace Act-As settings and shared verification providers use `config.manage`. Per-agent Act-As configuration uses agent register permissions. Refer to [Agents](/connect/agents#change-end-user-identity). | Default roles | Permission | Grants | | -------------------------------- | ---------------- | -------------------------------------------------------------------------------------- | | `admin`, `operator` | `config.manage` | Create, list, or delete verification providers and update workspace Act-As enforcement | | `admin`, `operator`, `developer` | `agent.register` | Configure Act-As on an agent | | `admin`, `operator`, `security` | `rules.deploy` | Required with `agent.register` when setting Act-As allow-lists | ## Create a verification provider Create a workspace-scoped verification provider that delegated agents can share. Assign the returned provider ID when registering or updating an agent with `--actas-provider` / `actas_provider_id`. Agents can instead use inline `--actas-oidc-url` or `--actas-hmac-secret` without a shared provider. The method you pick here must match what your application puts in `X-Dome-Act-As`. Refer to [Verification methods](#verification-methods). How to send the header is on [Pass identity for delegated agents](/develop#pass-identity-for-delegated-agents). Requires `config.manage`. ### OIDC Use OIDC when end users sign in through an IdP and your application sends their JWT as `X-Dome-Act-As`. ```bash theme={"system"} dome workspace verification-providers create \ --name workforce-oidc \ --method oidc \ --oidc-discovery-url https://auth.example.com/.well-known/openid-configuration ``` Reference: [`dome workspace verification-providers create`](/cli/settings/workspace#verification-providers-create) Tool: `dome_verification_providers_create` ```json theme={"system"} { "name": "workforce-oidc", "method": "oidc", "oidc_discovery_url": "https://auth.example.com/.well-known/openid-configuration" } ``` Reference: [`dome_verification_providers_create`](/reference/mcp/verification#verification-providers-create) ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateVerificationProvider Content-Type: application/json { "workspace_id": "", "name": "workforce-oidc", "method": "oidc", "oidc_discovery_url": "https://auth.example.com/.well-known/openid-configuration" } ``` Reference: [`CreateVerificationProvider`](/api/management/create-verification-provider) ```text title="Create an OIDC verification provider" theme={"system"} Create an OIDC verification provider named "workforce-oidc" using https://auth.example.com/.well-known/openid-configuration. ``` ### HMAC Use HMAC when a trusted service constructs and signs the Act-As envelope with a shared secret. ```bash theme={"system"} dome workspace verification-providers create \ --name internal-signer \ --method hmac \ --hmac-secret ``` Reference: [`dome workspace verification-providers create`](/cli/settings/workspace#verification-providers-create) Tool: `dome_verification_providers_create` ```json theme={"system"} { "name": "internal-signer", "method": "hmac", "hmac_secret": "" } ``` Reference: [`dome_verification_providers_create`](/reference/mcp/verification#verification-providers-create) ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateVerificationProvider Content-Type: application/json { "workspace_id": "", "name": "internal-signer", "method": "hmac", "hmac_secret": "" } ``` Reference: [`CreateVerificationProvider`](/api/management/create-verification-provider) ```text title="Create an HMAC verification provider" theme={"system"} Create an HMAC verification provider named "internal-signer" with my shared secret. ``` ## List verification providers List the verification providers in the workspace. Requires `config.manage`. ```bash theme={"system"} dome workspace verification-providers list ``` Reference: [`dome workspace verification-providers list`](/cli/settings/workspace#verification-providers-list) Tool: `dome_verification_providers_list` ```json theme={"system"} { "name": "dome_verification_providers_list", "arguments": {} } ``` Reference: [`dome_verification_providers_list`](/reference/mcp/verification#verification-providers-list) ```http theme={"system"} POST /dome.mgmt.v1.Management/ListVerificationProviders Content-Type: application/json { "workspace_id": "" } ``` Reference: [`ListVerificationProviders`](/api/management/list-verification-providers) ## Delete a verification provider Delete a verification provider when no agent should use it. Requires `config.manage`. Deleting a provider breaks verification for agents that reference it. Move those agents to another provider or remove their provider assignment first. ```bash theme={"system"} dome workspace verification-providers delete ``` Reference: [`dome workspace verification-providers delete`](/cli/settings/workspace#verification-providers-delete) Tool: `dome_verification_providers_delete` ```json theme={"system"} { "provider_id": "" } ``` Reference: [`dome_verification_providers_delete`](/reference/mcp/verification#verification-providers-delete) ```http theme={"system"} POST /dome.mgmt.v1.Management/DeleteVerificationProvider Content-Type: application/json { "workspace_id": "", "provider_id": "" } ``` Reference: [`DeleteVerificationProvider`](/api/management/delete-verification-provider) ## Require verified identity Update the workspace Act-As floor so every agent must present a verified end-user identity (every agent in the workspace behaves as a delegated agent at the edge), and optionally restrict which methods are allowed. When the workspace requires verified Act-As: * Every request must include a valid identity envelope. * The verification method must be allowed by the workspace. * An agent configured with `actas_method=none` is rejected. * Invalid, expired, malformed, or unverifiable identity fails closed before authorization. Agent settings can be stricter than the workspace floor, but cannot weaken it. An agent can require Act-As when the workspace does not, or narrow which verified identities it may present by group, email, or subject. Set per-agent options on [Agents](/connect/agents#change-end-user-identity). Requires `config.manage`. ```bash theme={"system"} dome workspace actas update --required ``` ```bash theme={"system"} dome workspace actas update --allowed-methods oidc,hmac ``` Reference: [`dome workspace actas update`](/cli/settings/workspace#actas-update) Tool: `dome_workspace_actas_update` ```json theme={"system"} { "actas_required": true, "actas_allowed_methods": ["oidc", "hmac"] } ``` Reference: [`dome_workspace_actas_update`](/reference/mcp/workspaces#workspace-actas-update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateWorkspaceActAs Content-Type: application/json { "workspace_id": "", "actas_required": true, "actas_allowed_methods": ["oidc", "hmac"] } ``` Reference: [`UpdateWorkspaceActAs`](/api/management/update-workspace-act-as) ```text title="Require verified identity" theme={"system"} Require verified Act-As on every request in this workspace and allow only oidc and hmac. ``` Read the current floor with `dome workspace actas get`, `dome_workspace_actas_get`, or [`GetWorkspaceActAs`](/api/management/get-workspace-act-as). ## Claim-aware Rules Rules can require Act-As and test verified claims. When the workspace permits requests without Act-As, a Rule that reads claims should test `principal has act_as` first. ```cedar title="end-user-access.cedar" theme={"system"} permit( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"customer-records" ) when { principal has act_as && principal.act_as.roles.contains("support") }; forbid( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"production-deploy" ) unless { principal has act_as && principal.act_as.roles.contains("admin") }; ``` Refer to [Authorize Access](/govern/rules) for the Rule lifecycle, [Rules](/reference/controls/rules#principal-attributes) reference for claim names, and the [authorization model](/concepts/architecture/authorization-model) concept for evaluation. ## Next steps * [Configure Act-As on an agent](/connect/agents#change-end-user-identity) for method, required flag, provider, and allow-lists * [Pass identity for delegated agents](/develop#pass-identity-for-delegated-agents) from your application * [Identity Patterns](/concepts/identity-patterns) concept for when to choose delegated vs standing identity * [Agent Identity](/concepts/agents/identity#act-as-identity) concept for tokens and act-as on the request path * [Simulate Rules](/govern/rules/simulate#simulate-with-agent-act-as) with representative Act-As claims before deploy # Gateways Source: https://docs.domesystems.ai/connect/gateways Group tools and models behind Gateways, grant agent access, and configure scoped runtime endpoints ## Overview A Gateway carries two related controls: * **Resource membership** makes a tool, pool, or model addressable through the Gateway's runtime endpoint. * **Agent access** admits the agent at the edge and generates rules that permit all current and future resource members. Other workspace and custom rules still apply. Those rules can deny a resource member after the agent passes Gateway admission. The typical workflow is: 1. [Create a Gateway](#create-gateway) or reuse the [Default gateway](#default-gateway). 2. [Add the resource as a member](#manage-resource-memberships). 3. [Grant the agent access](#manage-agent-access) to the Gateway. 4. Point the client at the [Gateway's endpoint](#build-the-runtime-endpoint). 5. [Verify reachability](#verify-reachability) with that agent's API key. ### Resource membership Membership determines which resources a Gateway exposes: | Kind | What it exposes | | --------------- | ----------------------------------------------------------------- | | Individual tool | One selected tool from a connection | | Tool source | Every tool on an MCP connection, including tools discovered later | | Pool | A pool behind its stable name | | Direct model | One model connection by name | A resource with no Gateway membership stays valid. The resource shows as **Unreachable** because no runtime endpoint addresses it. Removing a membership leaves the underlying resource intact. Gateway membership is available in Rules through `resource.gateways.contains("")`. Refer to the [Rules reference](/reference/controls/rules#resource-attributes). You can change what a Gateway exposes when you [add or remove members](#manage-resource-memberships). ### Agent access A **per-agent** grant adds the Gateway to that agent's allowed resources (`agent.register` and `rules.deploy`). A **workspace-wide** grant covers every current and future agent in the workspace (`rules.deploy` and `gateway.manage`). `gateway.manage` alone cannot grant runtime access. Revoking a workspace-wide grant leaves per-agent grants in place. Without either grant, invocation fails at edge admission (`access.denied` / `permission_denied` with `DENY_DETAIL_CODE_GATEWAY_ADMISSION`). Discovery such as `tools/list` and `GET /v1/models` is exempt from admission and stays membership- and Cedar-filtered. With a grant, other rules can still deny individual resource members. Generated permit rules are shown read-only in the dashboard. You can change who reaches a Gateway when you [grant or revoke access](#manage-agent-access). ### Default gateway Every new workspace starts with an active **Default** Gateway that has no resource memberships or agent grants. Migrated workspaces may already have resources attached, but callers still need an explicit per-agent or all-agents grant. The default marker only pre-selects a Gateway in dashboard dialogs that create tools, pools, and models. The marker adds no resources, grants no access, and overrides no rules. Free and Pro workspaces support one Gateway, and the auto-created Default counts toward that limit. Team, Enterprise, and self-managed workspaces support additional Gateways. Exceeding the plan cap returns HTTP `429` / `RESOURCE_EXHAUSTED`. You can move the marker when you [set the Default gateway](#set-default-gateway). ### Runtime endpoints Each Gateway has its own path under the workspace gateway host. Callers select a Gateway in the request URL. The base URL depends on the client protocol. A disabled Gateway rejects requests but keeps its memberships and grants. Refer to [Build the runtime endpoint](#build-the-runtime-endpoint) for client-specific URL paths. ### Cost quotas A Gateway cost quota caps aggregate LLM spend for member pools and direct model connections. Exceeding the cap blocks further LLM calls through that Gateway until the active window resets. Filter audit by `producer_gateway_id`. Metrics dashboards do not break down by Gateway. You can cap spend when you [set a cost quota](#set-a-cost-quota). Window and scope semantics are on the [Quotas](/reference/controls/quotas) reference. ## Requirements Before you begin: * Authenticate to Dome and select a workspace * Collect the resource and agent identifiers the operation needs * Have an active agent key with access to the selected Gateway when you verify runtime reachability ### Permissions Gateway membership, grants, and related operations require platform permissions. Each operation states its required permission inline. | Default roles | Permission | Grants | | -------------------------------- | ---------------- | -------------------------------------------------- | | All workspace roles | `gateway.view` | List Gateways and inspect membership | | `admin`, `operator` | `gateway.manage` | Create, update, and delete Gateways and membership | | All workspace roles | `agent.view` | List agents when managing grants | | `admin`, `operator`, `developer` | `agent.register` | Per-agent Gateway grants (with `rules.deploy`) | | `admin`, `operator`, `security` | `rules.deploy` | Required for Gateway access-grant changes | ## Create Gateway Create a separate Gateway when callers need a resource set or endpoint boundary that the Default gateway should not share. A new Gateway starts active with empty membership and no grants. Requires `gateway.manage`. ```bash theme={"system"} dome gateway create prod-resources --description "Production tools and models" ``` Reference: [`dome gateway create`](/cli/connect/gateway#create) Tool: `dome_gateway_create` ```json theme={"system"} { "name": "prod-resources", "description": "Production tools and models" } ``` Reference: [`dome_gateway_create`](/reference/mcp/gateway#dome_gateway_create) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/CreateGateway Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "name": "prod-resources", "description": "Production tools and models" } ``` Reference: [`CreateGateway`](/api/gateway/create-gateway) ```text title="Create a Gateway" theme={"system"} Create a Gateway named "prod-resources" for production tools and models. ``` Free and Pro workspaces support only one Gateway. Reuse, rename, or delete the Default gateway before creating another one. After creation, [retrieve the Gateway](#get-gateway) to confirm its ID, active state, empty membership, and grant settings. ## Update Gateway Update a Gateway to change its name or description. Requires `gateway.manage`. ```bash theme={"system"} dome gateway update prod-resources --description "Production tools, pools, and models" ``` Omitted values remain unchanged. Reference: [`dome gateway update`](/cli/connect/gateway#update) Tool: `dome_gateway_update` ```json theme={"system"} { "name": "prod-resources", "new_name": "production-resources", "description": "Production tools, pools, and models" } ``` Omitted values remain unchanged. Reference: [`dome_gateway_update`](/reference/mcp/gateway#dome_gateway_update) An empty `name` keeps the current name. The API always writes `description`, so include the current description when it must remain unchanged. ```http theme={"system"} POST /dome.gateway.v1.GatewayService/UpdateGateway Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "name": "production-resources", "description": "Production tools, pools, and models" } ``` Reference: [`UpdateGateway`](/api/gateway/update-gateway) ```text title="Update a Gateway" theme={"system"} Rename "prod-resources" to "production-resources" and update its description to "Production tools, pools, and models". ``` After the update, [retrieve the Gateway](#get-gateway) to confirm its name and description. ## Get Gateway Retrieve one Gateway to inspect its state, default marker, workspace-wide grant, and resource memberships. Requires `gateway.view`. ```bash theme={"system"} dome gateway get prod-resources ``` Reference: [`dome gateway get`](/cli/connect/gateway#get) Tool: `dome_gateway_get` ```json theme={"system"} { "name": "prod-resources" } ``` Reference: [`dome_gateway_get`](/reference/mcp/gateway#dome_gateway_get) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/GetGateway Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}" } ``` Reference: [`GetGateway`](/api/gateway/get-gateway) ```text title="Get a Gateway" theme={"system"} Get "prod-resources" and show its state, grants, and resource memberships. ``` ## List Gateways List Gateways to retrieve their names, IDs, states, default markers, and canonical endpoint paths. Requires `gateway.view`. ```bash theme={"system"} dome gateway list ``` Reference: [`dome gateway list`](/cli/connect/gateway#list) Tool: `dome_gateway_list` ```json theme={"system"} {} ``` Reference: [`dome_gateway_list`](/reference/mcp/gateway#dome_gateway_list) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/ListGateways Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}" } ``` Reference: [`ListGateways`](/api/gateway/list-gateways) ```text title="List Gateways" theme={"system"} List the Gateways in the active workspace with their IDs, states, and endpoint paths. ``` ## Set Default gateway Set a Gateway as the workspace default to pre-select it in dashboard dialogs that create tools, pools, and models. Setting a new default clears the previous marker. A disabled default is not pre-selected. Requires `gateway.manage`. ```bash theme={"system"} dome gateway set-default prod-resources ``` Reference: [`dome gateway set-default`](/cli/connect/gateway#set-default) Tool: `dome_gateway_set_default` ```json theme={"system"} { "name": "prod-resources" } ``` Reference: [`dome_gateway_set_default`](/reference/mcp/gateway#dome_gateway_set_default) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/SetGatewayDefault Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}" } ``` Reference: [`SetGatewayDefault`](/api/gateway/set-gateway-default) ```text title="Set the default Gateway" theme={"system"} Set "prod-resources" as the workspace's Default gateway. ``` After setting the default, [retrieve the Gateway](#get-gateway) and confirm that it is marked as the workspace default. ## Manage lifecycle Manage a Gateway's availability without changing resource memberships or agent grants. Delete a Gateway only when callers no longer need that endpoint. ### Enable Gateway Enable a disabled Gateway to restore its endpoint. Disabling a Gateway preserves existing memberships and grants. Requires `gateway.manage`. ```bash theme={"system"} dome gateway enable prod-resources ``` Reference: [`dome gateway enable`](/cli/connect/gateway#enable) Tool: `dome_gateway_set_state` ```json theme={"system"} { "name": "prod-resources", "state": "active" } ``` Reference: [`dome_gateway_set_state`](/reference/mcp/gateway#dome_gateway_set_state) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/SetGatewayState Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "state": "active" } ``` Reference: [`SetGatewayState`](/api/gateway/set-gateway-state) ```text title="Enable a Gateway" theme={"system"} Enable the "prod-resources" Gateway. ``` ### Disable Gateway Disable a Gateway to block its endpoint without removing memberships or grants. Requests stay rejected until you enable the Gateway again. Requires `gateway.manage`. ```bash theme={"system"} dome gateway disable prod-resources ``` Reference: [`dome gateway disable`](/cli/connect/gateway#disable) Tool: `dome_gateway_set_state` ```json theme={"system"} { "name": "prod-resources", "state": "disabled" } ``` Reference: [`dome_gateway_set_state`](/reference/mcp/gateway#dome_gateway_set_state) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/SetGatewayState Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "state": "disabled" } ``` Reference: [`SetGatewayState`](/api/gateway/set-gateway-state) ```text title="Disable a Gateway" theme={"system"} Disable "prod-resources" while preserving its memberships and grants. ``` After enabling or disabling a Gateway, [retrieve the Gateway](#get-gateway) to confirm the resulting state. ### Delete Gateway Delete a Gateway only after moving callers to another endpoint. Deletion removes the Gateway and its resource memberships. The `/gateways/{{GATEWAY_ID}}/...` endpoint starts rejecting requests. Deletion does not remove the underlying tools, connections, pools, or models. Requires `gateway.manage`. Deletion is permanent. Per-agent grants for the deleted ID no longer admit callers. Callers must use another Gateway ID. ```bash theme={"system"} dome gateway delete prod-resources ``` Reference: [`dome gateway delete`](/cli/connect/gateway#delete) Tool: `dome_gateway_delete` ```json theme={"system"} { "name": "prod-resources" } ``` Reference: [`dome_gateway_delete`](/reference/mcp/gateway#dome_gateway_delete) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/DeleteGateway Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}" } ``` Reference: [`DeleteGateway`](/api/gateway/delete-gateway) ```text title="Delete a Gateway" theme={"system"} Delete "prod-resources" without deleting its underlying resources. ``` After deletion, [list the Gateways](#list-gateways) to confirm the deleted Gateway no longer appears. ## Manage resource memberships Add or remove members so resources are addressable through a Gateway. Membership kinds are in [Overview](#resource-membership). Membership changes do not modify or delete the underlying resources. ### Individual tools Add or remove one tool without exposing the rest of its connection. ##### Add individual tool Add one tool without exposing the other tools from its connection. Requires `gateway.manage`. ```bash theme={"system"} dome gateway tools add prod-resources {{TOOL_ID}} ``` Reference: [`dome gateway tools add`](/cli/connect/gateway#tools-add) Tool: `dome_gateway_tool_add` ```json theme={"system"} { "gateway": "prod-resources", "connection": "notion", "tool": "create_page" } ``` Reference: [`dome_gateway_tool_add`](/reference/mcp/gateway#dome_gateway_tool_add) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/AddGatewayTool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "tool_id": "{{TOOL_ID}}" } ``` Reference: [`AddGatewayTool`](/api/gateway/add-gateway-tool) ```text title="Add an individual tool" theme={"system"} Add the "create_page" tool from the "notion" connection to "prod-resources". ``` ##### Remove individual tool Remove an individual membership to stop exposing the tool on its own. The tool stays addressable when a tool-source membership also covers that tool. Requires `gateway.manage`. ```bash theme={"system"} dome gateway tools remove prod-resources {{TOOL_ID}} ``` Reference: [`dome gateway tools remove`](/cli/connect/gateway#tools-remove) Tool: `dome_gateway_tool_remove` ```json theme={"system"} { "gateway": "prod-resources", "connection": "notion", "tool": "create_page" } ``` Reference: [`dome_gateway_tool_remove`](/reference/mcp/gateway#dome_gateway_tool_remove) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/RemoveGatewayTool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "tool_id": "{{TOOL_ID}}" } ``` Reference: [`RemoveGatewayTool`](/api/gateway/remove-gateway-tool) ```text title="Remove an individual tool" theme={"system"} Remove the "create_page" tool from "prod-resources" without deleting the tool. ``` ### Tool source Add or remove an entire MCP connection. Newly discovered tools on that connection join the Gateway automatically. ##### Add tool source Add a tool source to include every tool currently on that connection. Requires `gateway.manage`. ```bash theme={"system"} dome gateway tool-sources add prod-resources notion ``` Reference: [`dome gateway tool-sources add`](/cli/connect/gateway#tool-sources-add) Tool: `dome_gateway_tool_source_add` ```json theme={"system"} { "gateway": "prod-resources", "connection": "notion" } ``` Reference: [`dome_gateway_tool_source_add`](/reference/mcp/gateway#dome_gateway_tool_source_add) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/AddGatewayToolSource Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "connection_id": "{{CONNECTION_ID}}" } ``` Reference: [`AddGatewayToolSource`](/api/gateway/add-gateway-tool-source) ```text title="Add a tool source" theme={"system"} Add the entire "notion" tool source to "prod-resources", including newly discovered tools. ``` ##### Remove tool source Remove a tool-source membership to stop automatically including tools from that connection. Individually added tools remain in the Gateway. Requires `gateway.manage`. ```bash theme={"system"} dome gateway tool-sources remove prod-resources notion ``` Reference: [`dome gateway tool-sources remove`](/cli/connect/gateway#tool-sources-remove) Tool: `dome_gateway_tool_source_remove` ```json theme={"system"} { "gateway": "prod-resources", "connection": "notion" } ``` Reference: [`dome_gateway_tool_source_remove`](/reference/mcp/gateway#dome_gateway_tool_source_remove) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/RemoveGatewayToolSource Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "connection_id": "{{CONNECTION_ID}}" } ``` Reference: [`RemoveGatewayToolSource`](/api/gateway/remove-gateway-tool-source) ```text title="Remove a tool source" theme={"system"} Remove the "notion" tool source from "prod-resources" without deleting the connection. ``` ### Pool Add or remove a pool so agents can call it by its stable name through the Gateway. Changes to the pool's members do not require a membership update. ##### Add pool Add a pool to expose its routed models through the Gateway. Requires `gateway.manage`. ```bash theme={"system"} dome gateway model-pools add prod-resources production ``` Reference: [`dome gateway model-pools add`](/cli/connect/gateway#model-pools-add) Tool: `dome_gateway_model_pool_add` ```json theme={"system"} { "gateway": "prod-resources", "pool": "production" } ``` Reference: [`dome_gateway_model_pool_add`](/reference/mcp/gateway#dome_gateway_model_pool_add) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/AddGatewayLLMPool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "llm_pool_id": "{{POOL_ID}}" } ``` Reference: [`AddGatewayLLMPool`](/api/gateway/add-gateway-llm-pool) ```text title="Add a model pool" theme={"system"} Add the "production" model pool to "prod-resources". ``` ##### Remove pool Remove a pool to stop exposing that pool through the Gateway. The pool and its members remain configured. Requires `gateway.manage`. ```bash theme={"system"} dome gateway model-pools remove prod-resources production ``` Reference: [`dome gateway model-pools remove`](/cli/connect/gateway#model-pools-remove) Tool: `dome_gateway_model_pool_remove` ```json theme={"system"} { "gateway": "prod-resources", "pool": "production" } ``` Reference: [`dome_gateway_model_pool_remove`](/reference/mcp/gateway#dome_gateway_model_pool_remove) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/RemoveGatewayLLMPool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "llm_pool_id": "{{POOL_ID}}" } ``` Reference: [`RemoveGatewayLLMPool`](/api/gateway/remove-gateway-llm-pool) ```text title="Remove a model pool" theme={"system"} Remove the "production" model pool from "prod-resources" without deleting the pool. ``` ### Direct model Add or remove one model connection without routing through a pool. ##### Add direct model Add a direct model connection to make that model name addressable through the Gateway. Requires `gateway.manage`. ```bash theme={"system"} dome gateway models add prod-resources claude-prod ``` Reference: [`dome gateway models add`](/cli/connect/gateway#models-add) Tool: `dome_gateway_model_add` ```json theme={"system"} { "gateway": "prod-resources", "model": "claude-prod" } ``` Reference: [`dome_gateway_model_add`](/reference/mcp/gateway#dome_gateway_model_add) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/AddGatewayLLMModel Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "llm_model_connection_id": "{{MODEL_CONNECTION_ID}}" } ``` Reference: [`AddGatewayLLMModel`](/api/gateway/add-gateway-llm-model) ```text title="Add a direct model" theme={"system"} Add "claude-prod" directly to "prod-resources". ``` ##### Remove direct model Remove a direct model connection to stop exposing it through the Gateway without deleting the connection. Requires `gateway.manage`. ```bash theme={"system"} dome gateway models remove prod-resources claude-prod ``` Reference: [`dome gateway models remove`](/cli/connect/gateway#models-remove) Tool: `dome_gateway_model_remove` ```json theme={"system"} { "gateway": "prod-resources", "model": "claude-prod" } ``` Reference: [`dome_gateway_model_remove`](/reference/mcp/gateway#dome_gateway_model_remove) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/RemoveGatewayLLMModel Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "llm_model_connection_id": "{{MODEL_CONNECTION_ID}}" } ``` Reference: [`RemoveGatewayLLMModel`](/api/gateway/remove-gateway-llm-model) ```text title="Remove a direct model" theme={"system"} Remove "claude-prod" from "prod-resources" without deleting the model connection. ``` After any membership change, [retrieve the Gateway](#get-gateway) to confirm its individual tool, tool source, model pool, and direct model memberships. ## Manage agent access Grant or revoke who is admitted at the Gateway edge. Per-agent and workspace-wide grants are in [Overview](#agent-access). Other Rules can still deny individual members after admission. ### List agent access List agents and show whether each agent has a per-agent grant to the Gateway. Revoked agents are excluded. Suspended agents remain listed so you can inspect their grants before reactivation. Requires `agent.view`. ```bash theme={"system"} dome gateway access list prod-resources ``` Reference: [`dome gateway access list`](/cli/connect/gateway#access-list) Tool: `dome_gateway_access_list` ```json theme={"system"} { "gateway": "prod-resources" } ``` Reference: [`dome_gateway_access_list`](/reference/mcp/gateway#dome_gateway_access_list) The API returns every agent in the workspace. Exclude revoked agents, then check whether each remaining agent's `allowed_gateway_ids` contains the target Gateway ID. ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/ListAgents Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}" } ``` Reference: [`ListAgents`](/api/agents/list-agents) ```text title="List agent access" theme={"system"} List agents and show whether each one has a per-agent grant to "prod-resources". ``` ### Grant one agent Grant one agent access while preserving its existing Gateway grants. Dome generates and manages the permit rules. You cannot edit those rules directly. Requires `agent.register` and `rules.deploy`. ```bash theme={"system"} dome gateway access grant prod-resources data-pipeline-agent ``` Reference: [`dome gateway access grant`](/cli/connect/gateway#access-grant) Tool: `dome_gateway_agent_grant` ```json theme={"system"} { "gateway": "prod-resources", "agent_id": "{{AGENT_ID}}" } ``` Reference: [`dome_gateway_agent_grant`](/reference/mcp/gateway#dome_gateway_agent_grant) The `allowed_gateway_ids` field replaces the agent's full list. Include every existing Gateway ID that the agent should keep, plus the new ID. ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/UpdateAgent Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "allowed_gateway_ids": [ "{{EXISTING_GATEWAY_ID}}", "{{GATEWAY_ID}}" ], "update_mask": "allowed_gateway_ids" } ``` Reference: [`UpdateAgent`](/api/agents/update-agent) ```text title="Grant one agent" theme={"system"} Grant "data-pipeline-agent" access to "prod-resources" and preserve its existing Gateway grants. ``` ### Revoke one agent Revoke one agent's access to the Gateway while preserving its other Gateway grants. Requests from that agent to the Gateway endpoint fail at edge admission unless a workspace-wide grant also applies. Requires `agent.register` and `rules.deploy`. ```bash theme={"system"} dome gateway access revoke prod-resources data-pipeline-agent ``` Reference: [`dome gateway access revoke`](/cli/connect/gateway#access-revoke) Tool: `dome_gateway_agent_revoke` ```json theme={"system"} { "gateway": "prod-resources", "agent_id": "{{AGENT_ID}}" } ``` Reference: [`dome_gateway_agent_revoke`](/reference/mcp/gateway#dome_gateway_agent_revoke) The `allowed_gateway_ids` field replaces the agent's full list. Omit the revoked ID and preserve every other ID the agent should keep. ```http theme={"system"} POST /dome.agent.v1.AgentRegistry/UpdateAgent Content-Type: application/json { "agent_id": "{{AGENT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "allowed_gateway_ids": [ "{{OTHER_GATEWAY_ID}}" ], "update_mask": "allowed_gateway_ids" } ``` Reference: [`UpdateAgent`](/api/agents/update-agent) ```text title="Revoke one agent" theme={"system"} Revoke "data-pipeline-agent" access to "prod-resources" and preserve its other Gateway grants. ``` ### Grant every workspace agent Grant every current and future workspace agent access when all agents need the same endpoint. Other rules can still deny individual resources after admission. Requires `gateway.manage` and `rules.deploy`. ```bash theme={"system"} dome gateway access grant-all prod-resources ``` Reference: [`dome gateway access grant-all`](/cli/connect/gateway#access-grant-all) Tool: `dome_gateway_all_agents_grant` ```json theme={"system"} { "gateway": "prod-resources" } ``` Reference: [`dome_gateway_all_agents_grant`](/reference/mcp/gateway#dome_gateway_all_agents_grant) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/SetGatewayAllAgentsGrant Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "granted": true } ``` Reference: [`SetGatewayAllAgentsGrant`](/api/gateway/set-gateway-all-agents-grant) ```text title="Grant every workspace agent" theme={"system"} Grant every current and future workspace agent access to "prod-resources". ``` ### Revoke every workspace agent Revoke the workspace-wide grant to stop automatically admitting every agent. Existing per-agent grants remain in place. Requires `gateway.manage` and `rules.deploy`. ```bash theme={"system"} dome gateway access revoke-all prod-resources ``` Reference: [`dome gateway access revoke-all`](/cli/connect/gateway#access-revoke-all) Tool: `dome_gateway_all_agents_revoke` ```json theme={"system"} { "gateway": "prod-resources" } ``` Reference: [`dome_gateway_all_agents_revoke`](/reference/mcp/gateway#dome_gateway_all_agents_revoke) ```http theme={"system"} POST /dome.gateway.v1.GatewayService/SetGatewayAllAgentsGrant Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "gateway_id": "{{GATEWAY_ID}}", "granted": false } ``` Reference: [`SetGatewayAllAgentsGrant`](/api/gateway/set-gateway-all-agents-grant) ```text title="Revoke every workspace agent" theme={"system"} Revoke the workspace-wide grant from "prod-resources" without changing per-agent grants. ``` After any access change, [list agent access](#list-agent-access) to confirm the resulting per-agent grants. For a workspace-wide grant, [retrieve the Gateway](#get-gateway) and check its workspace-wide grant setting. ## Manage cost quotas Cap aggregate LLM spend for member pools and direct model connections on this Gateway. Refer to [Overview](#cost-quotas). Window and scope semantics are on the [Quotas](/reference/controls/quotas) reference. ### List cost quotas List Gateway cost quotas in the workspace or filter the result to one Gateway. Requires `gateway.view`. ```bash theme={"system"} dome gateway quota list prod-resources ``` Reference: [`dome gateway quota list`](/cli/connect/gateway#quota-list) Tool: `dome_gateway_quota_list` ```json theme={"system"} { "gateway": "prod-resources" } ``` Reference: [`dome_gateway_quota_list`](/reference/mcp/gateway#dome_gateway_quota_list) The API returns every LLM quota in the workspace. Filter the response to records with `subject_type` equal to `gateway` and the desired `subject_id`. ```http theme={"system"} POST /dome.mgmt.v1.Management/ListLLMQuotas Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}" } ``` Reference: [`ListLLMQuotas`](/api/management/list-llm-quotas) ```text title="List cost quotas" theme={"system"} List the cost quotas for the "prod-resources" Gateway. ``` ### Set a cost quota Set a daily or monthly USD spend cap for LLM calls through a Gateway. Once spend exceeds the cap, LLM calls addressed to that Gateway return HTTP `429`. Requires `gateway.manage`. ```bash theme={"system"} dome gateway quota set prod-resources --limit 500 --window monthly --name "prod-monthly" ``` Reference: [`dome gateway quota set`](/cli/connect/gateway#quota-set) Tool: `dome_gateway_quota_set` ```json theme={"system"} { "gateway": "prod-resources", "limit_usd": 500, "window": "monthly", "name": "prod-monthly" } ``` Reference: [`dome_gateway_quota_set`](/reference/mcp/gateway#dome_gateway_quota_set) The API expresses the limit in millionths of a US dollar. For example, `$500` is `500000000`. ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateLLMQuota Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "subject_type": "gateway", "subject_id": "{{GATEWAY_ID}}", "limit_micros": "500000000", "window": "monthly", "name": "prod-monthly", "enabled": true } ``` Reference: [`CreateLLMQuota`](/api/management/create-llm-quota) ```text title="Set a cost quota" theme={"system"} Create a monthly $500 cost quota named "prod-monthly" for "prod-resources". ``` ### Remove a cost quota Remove a Gateway cost quota to stop enforcing that cap. Requires `gateway.manage`. Removing a quota takes away an active spending control. Confirm the quota ID with the list operation first. ```bash theme={"system"} dome gateway quota rm {{QUOTA_ID}} ``` The command rejects quota IDs that belong to another subject type. Reference: [`dome gateway quota rm`](/cli/connect/gateway#quota-remove) Tool: `dome_gateway_quota_remove` ```json theme={"system"} { "quota_id": "{{QUOTA_ID}}" } ``` The tool rejects quota IDs that belong to another subject type. Reference: [`dome_gateway_quota_remove`](/reference/mcp/gateway#dome_gateway_quota_remove) Confirm that the quota has `subject_type: gateway` before deleting it. The generic API endpoint does not add the CLI and MCP subject-type guard. ```http theme={"system"} POST /dome.mgmt.v1.Management/DeleteLLMQuota Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "quota_id": "{{QUOTA_ID}}" } ``` Reference: [`DeleteLLMQuota`](/api/management/delete-llm-quota) ```text title="Remove a cost quota" theme={"system"} Confirm that "{{QUOTA_ID}}" is a Gateway quota, then remove it. ``` After any quota change, [list cost quotas](#list-cost-quotas) to confirm the active caps and reset windows. ## Build the runtime endpoint Build the runtime endpoint from the workspace gateway host and the Gateway ID. Retrieve the ID with [List Gateways](#list-gateways). Requires `gateway.view` to retrieve the ID. Runtime calls require an active agent key and a grant to the Gateway. Use the ID in the canonical prefix: ```text theme={"system"} https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}} ``` Choose the base URL that matches the client. | Client | Base URL | Client appends | | --------------------- | ------------------------------------------------------ | ------------------------------------- | | Dome SDK | `https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}}` | nothing, used as-is | | Anthropic SDK | `https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}}` | `/v1/messages` (SDK-managed) | | OpenAI-compatible SDK | `https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}}/v1` | `/chat/completions`, `/embeddings`, … | | MCP client | `https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}}/mcp` | protocol frames | Do not add `/v1` to the Anthropic base URL because the Anthropic SDK appends `/v1/messages`. Bare `/mcp` and `/v1` requests return HTTP `400`. Every caller must select a Gateway in the URL. Run `dome gateway get --json` to retrieve the complete `gateway_url`, `mcp_url`, `openai_base_url`, and `anthropic_base_url`. SDK agent-key creation and token exchange return the same authoritative endpoint set. Refer to [Python SDK Gateways](/sdks/python/reference#gateways). ## Verify reachability Verify discovery before invoking a tool or model. Discovery returns only members of the selected Gateway that the agent's grant and applicable rules permit. Requires an active agent key, access to the Gateway, and rules that allow discovery. List MCP tools through the scoped endpoint: ```bash theme={"system"} curl -X POST "https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}}/mcp" \ -H "Authorization: Bearer {{DOME_AGENT_KEY}}" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` List model pools and direct model connections through the OpenAI-compatible endpoint: ```bash theme={"system"} curl "https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}}/v1/models" \ -H "Authorization: Bearer {{DOME_AGENT_KEY}}" ``` Configure runtime credentials and SDK clients in [Develop](/develop). ## Troubleshoot reachability Use the observed response to locate the failed layer. | Symptom | Likely cause | Resolution | | ---------------------------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | | HTTP `400` asks for a Gateway | The client used bare `/mcp` or `/v1` | Add `/gateways/{{GATEWAY_ID}}` before the protocol path | | Gateway is unavailable | The ID is unknown, disabled, or not yet synchronized | Verify the ID, enable the Gateway, and wait for gateway synchronization to complete | | Invocation is denied before rules evaluate | The agent lacks access to the Gateway | Add a per-agent or workspace-wide grant | | Discovery returns no resources | The agent lacks access, the Gateway has no matching members, or rules deny discovery | Check agent access, membership, and rules | | Tool is missing from `tools/list` | The tool lacks membership, is blocked, or rules deny discovery | Check individual and tool-source membership, tool status, and rules | | Pool or model is missing from `/v1/models` | The resource lacks membership or rules deny discovery | Check pool or direct-model membership and model rules | | Resource shows **Unreachable** | The resource belongs to no Gateway | Add the resource or its containing tool source or pool | | LLM call returns HTTP `429` | A Gateway or other LLM cost quota is exhausted | List active quotas and check their reset windows | | Gateway creation is rejected at the plan limit | The workspace already has the maximum number of Gateways | Reuse, rename, or delete the Default gateway, or change plans | | Anthropic request returns HTTP `404` | The client base URL includes an extra `/v1` | Use `https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}}` | After changing memberships or grants, wait for gateway synchronization before testing again. If discovery succeeds but invocation fails, inspect the resource rules and the agent's lifecycle state. ## Next steps * [Gateways](/concepts/gateways) concept for how membership and grants work * [Register agents](/connect/agents) to create agent identities and keys * [Tools](/connect/resources/tools) to configure tool connections and catalog state * [Pools](/connect/resources/models/pools) to configure pools and routing # Connect Source: https://docs.domesystems.ai/connect/index Agents, resources, and Gateways that give workloads governed access to tools and models **Connect** defines the entities an agent needs to reach tools and models: agent identity, resources, and Gateways. Dome stores upstream credentials on those resources. Agents authenticate to Dome, not to every backend. Refer to [Core Concepts](/concepts) for how the pieces fit together and [Architecture](/concepts/architecture) concept for the request path. ## How Connect defines entities The following entities are what you configure in Dome so workloads can call tools and models: * **Agents** identify the applications and automated processes making requests. Credentials authenticate inbound traffic. * **Delegated agents** act for a verified person. Dome verifies act-as before authorization so Rules, Quotas, and egress can use trusted claims. Choose standing vs delegated on the [Identity Patterns](/concepts/identity-patterns) concept. * **Resources** are what agents call through Dome: * **Tools** are MCP servers and related backends. * **Models** are LLM provider connections. * **Pools** group model connections behind one name with routing and failover. * **Gateways** package resource membership and agent grants behind one runtime URL. An agent authenticates to Dome and reaches members only through a Gateway that admits it. It never holds the upstream credentials stored on those resources. Rules, Guards, and Quotas still decide what the call may do, what content may pass, and how much spend is allowed after admission. The typical workflow is: 1. [Register an agent](/connect/agents), allow the resources it needs, and issue credentials. 2. When access must depend on the person the agent acts for, configure [delegated agents](/connect/agents/delegated). 3. Add [tools](/connect/resources/tools), [models](/connect/resources/models), and [pools](/connect/resources/models/pools) when you want a stable name, routing, or failover. 4. Create a [Gateway](/connect/gateways), attach those resources as members, and grant the agent access. 5. Point the [runtime](/develop) at the agent credential, Gateway endpoint, and any Act-As envelope. After setup, you can rotate credentials, update connections and memberships, or manage lifecycle without rebuilding the rest of the configuration. ## Next steps * [Govern](/govern) to authorize actions, set usage limits, and inspect content * [Operate](/operate) to investigate audit events, stream live events, and subscribe to events * [Manage](/manage) for platform access, environments, integrations, and data exports * [Develop](/develop) to send runtime credentials, Gateway endpoints, and Act-As envelopes from the application # Models Source: https://docs.domesystems.ai/connect/resources/models Connect LLM providers, secure upstream credentials, and publish governed model endpoints ## Overview A model connection represents one upstream model in Dome. The connection stores the configuration and credentials Dome needs to call that model. An agent selects the connection by putting its name in the request's `model` field. A [pool](/connect/resources/models/pools) groups multiple models behind one name. Dome selects one member for each request. Prefer a pool when you need routing, traffic distribution, or failover. Refer to [Pools](/concepts/resources/model-pools) concept. The typical workflow is: 1. [Choose a provider](#providers) and model identifier. 2. [Add the model](#add-model) with its upstream credentials. 3. Attach the model directly to a [Gateway](#attach-to-a-gateway) or add it to a [model pool](/connect/resources/models/pools). 4. Allow the Gateway on the [agent](/connect/agents#allowed-resources). 5. [Verify the route](#verify-model) with that agent's API key. ### Providers The provider determines how Dome translates requests and where Dome places the upstream credential. You cannot change the provider after adding the model. Provider IDs fall into three groups. * **Native providers.** Dome translates between the Gateway request and the provider's native wire format: `openai`, `anthropic`, `google`, `azure_openai`, and `bedrock`. * **Hosted OpenAI-compatible providers.** Dome sends requests through the provider's OpenAI-compatible API (for example `mistral`, `groq`, `together`, `openrouter`, `databricks`). * **Bring your own endpoint.** Use `openai_compatible` for an OpenAI-shaped endpoint such as vLLM or Ollama. Use `custom` when Dome should forward provider-specific requests without translating them. Dome supplies a base URL for hosted providers that have a default. For Google, Azure OpenAI, Bedrock, Databricks, `openai_compatible`, and `custom`, you must provide an endpoint or the provider-specific details needed to locate the model. The full provider ID list is on the [Models](/reference/resources/models#providers) reference. ### Credentials Agent API keys authenticate inbound requests to Dome. Model credentials authenticate Dome's outbound requests to the provider, and agents never receive them. | Authentication method | Credential type | What Dome sends | | --------------------- | --------------- | ------------------------------------------------------- | | `none` | `none` | No provider credential | | `api-key` | `shared` | One workspace-managed API key for every call | | `api-key` | `per-user` | An API key linked to the verified end user | | `oauth` | `shared` | One administrator-authorized OAuth grant for every call | | `oauth` | `per-user` | An OAuth grant linked to the verified end user | When the provider requires no authentication, set both the authentication method and credential type to `none`. When the provider uses an API key or OAuth, the credential type must be `shared` or `per-user`. If you omit both settings, Dome uses `api-key` with `shared`. With per-user credentials, Dome uses the verified act-as identity on each request to select the provider credential. If that end user has not connected a credential, Dome returns HTTP `401` with a short-lived `provision_url`. The end user opens the URL to enter an API key or complete OAuth consent, then retries the request. Refer to [Errors and denials](/develop#errors-and-denials) for runtime handling. Google models with shared credentials use a service account. On a self-hosted gateway, Google and Bedrock models can instead use the gateway's cloud-native identity. A Dome-hosted gateway cannot assume a role or workload identity from your cloud account. ### Attributes Attributes add trusted metadata to a model for [rules](/govern/rules) to evaluate. For example, a rule can allow models with `region=eu` for a European workload or deny models whose `data_class` does not match the request. Attributes do not change how Dome calls the provider. You can set attributes when you [add a model](#add-model) or [change them later](#change-attributes). ### Filter window A model can change how much streamed output Dome buffers before applying outbound Guards. Dome uses the largest window configured on the workspace, model, or request. A model can therefore increase inspection coverage but cannot reduce the workspace minimum. A value of `0` uses the workspace setting. A model can buffer at most 1 MiB or 4,096 tokens. You can set a filter window when you [add a model](#add-model) or [change it later](#change-filter-window). ## Requirements Before you begin: * Authenticate to Dome and select a workspace * Have the provider, model identifier, and provider-specific configuration * Have a provider credential unless the model uses no authentication or workload identity * Have a [Gateway](/connect/gateways) ready when you want the model reachable ### Permissions Model connection operations require platform permissions. Each operation states its required permission inline. | Default roles | Permission | Grants | | ------------------- | ---------------- | ------------------------------ | | All workspace roles | `gateway.view` | List and inspect models | | `admin`, `operator` | `gateway.manage` | Add, update, and remove models | ## Add model Add a model for one provider endpoint. You can attach the model directly to a Gateway during this operation. If you do not attach it, agents cannot call the model until you attach it later. Requires `gateway.manage`. ```bash theme={"system"} dome model add claude-prod \ --provider anthropic \ --model claude-sonnet-4-6 \ --api-key "$ANTHROPIC_API_KEY" \ --gateway prod-llms ``` Add `--endpoint` or `--provider-config` when the provider requires more configuration. Reference: [`dome model add`](/cli/connect/models#add) Tool: `dome_model_add` ```json theme={"system"} { "name": "claude-prod", "provider": "anthropic", "provider_config": { "model": "claude-sonnet-4-6" }, "api_key": "{{ANTHROPIC_API_KEY}}", "gateways": ["prod-llms"] } ``` Reference: [`dome_model_add`](/reference/mcp/models#dome_model_add) ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateLLMModelConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "name": "claude-prod", "provider": "anthropic", "provider_config": { "model": "claude-sonnet-4-6" }, "auth_method": "api-key", "credential_type": "shared", "secret_values": { "x-api-key": "{{ANTHROPIC_API_KEY}}" } } ``` The endpoint returns the model's UUID. The other management endpoints use this UUID to identify the model. Attach the model to a Gateway in a separate operation. Reference: [`CreateLLMModelConnection`](/api/management/create-llm-model-connection) ```text title="Connect an LLM model" theme={"system"} Add an Anthropic model named "claude-prod" for claude-sonnet-4-6 using my API key. Attach it to the "prod-llms" Gateway. ``` Agents cannot call a model until you attach it directly to a Gateway or add it to a pool attached to one. ## Update model Update a model to change its name, model identifier, endpoint, authentication, attributes, or filter window. You cannot change the provider after creating the model. To switch providers, add a new model and replace the old connection in any pools or Gateways. Requires `gateway.manage`. ```bash theme={"system"} dome model update claude-prod \ --model claude-sonnet-4-6 ``` Only the flags you pass change. `--provider-config` replaces the full provider configuration instead of merging with it. Reference: [`dome model update`](/cli/connect/models#update) Tool: `dome_model_update` ```json theme={"system"} { "name": "claude-prod", "provider_config": { "model": "claude-sonnet-4-6" } } ``` Only the parameters you send change. `provider_config` and `attributes` each replace the existing object. Reference: [`dome_model_update`](/reference/mcp/models#dome_model_update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateLLMModelConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}", "provider_config": { "model": "claude-sonnet-4-6" } } ``` Omitted fields keep their stored values. When present, `provider_config` replaces the full object. To replace or clear attributes, headers, credentials, or the filter window, send the new value and set its corresponding `*_provided` field to `true`. Reference: [`UpdateLLMModelConnection`](/api/management/update-llm-model-connection) ```text title="Update a model" theme={"system"} Update the "claude-prod" model to use claude-sonnet-4-6. Keep its provider, credentials, attributes, and Gateway membership unchanged. ``` ### Change credentials You can change the authentication method, credential type, or shared API key. Switching authentication methods deletes the previously stored provider credential. ```bash theme={"system"} dome model update claude-prod \ --auth-method api-key \ --credential-type per-user ``` The current CLI can change the authentication method or credential type, but it does not replace an existing shared API key. Use MCP or the API to rotate that key. Reference: [`dome model update`](/cli/connect/models#update) Tool: `dome_model_update` ```json theme={"system"} { "name": "claude-prod", "auth_method": "api-key", "credential_type": "shared", "api_key": "{{ANTHROPIC_API_KEY}}" } ``` Reference: [`dome_model_update`](/reference/mcp/models#dome_model_update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateLLMModelConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}", "auth_method": "api-key", "credential_type": "shared", "secret_values": { "x-api-key": "{{ANTHROPIC_API_KEY}}" }, "secret_values_provided": true } ``` Reference: [`UpdateLLMModelConnection`](/api/management/update-llm-model-connection) ```text title="Rotate a model credential" theme={"system"} Replace the shared API key on "claude-prod" with my new Anthropic API key. ``` ### Change attributes You can replace the trusted attributes that rules evaluate for this model. Send an empty object to clear them. ```bash theme={"system"} dome model update claude-prod \ --attributes '{"region":"eu","data_class":"restricted"}' ``` Reference: [`dome model update`](/cli/connect/models#update) Tool: `dome_model_update` ```json theme={"system"} { "name": "claude-prod", "attributes": { "region": "eu", "data_class": "restricted" } } ``` Reference: [`dome_model_update`](/reference/mcp/models#dome_model_update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateLLMModelConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}", "attributes": { "region": "eu", "data_class": "restricted" }, "attributes_provided": true } ``` Reference: [`UpdateLLMModelConnection`](/api/management/update-llm-model-connection) ```text title="Change model attributes" theme={"system"} Replace the attributes on "claude-prod" with region "eu" and data class "restricted". ``` ### Change filter window You can change how much streamed output Dome buffers for outbound Guard inspection. A model can buffer at most 1 MiB or 4,096 tokens. Set both values to `0` to use the workspace minimum. ```bash theme={"system"} dome model update claude-prod \ --filter-window-bytes 65536 \ --filter-window-tokens 1024 ``` Reference: [`dome model update`](/cli/connect/models#update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateLLMModelConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}", "filter_window_bytes": 65536, "filter_window_tokens": 1024, "filter_window_provided": true } ``` Reference: [`UpdateLLMModelConnection`](/api/management/update-llm-model-connection) ## Get model Retrieve a model to inspect its provider configuration, authentication, attributes, filter window, and Gateway membership. Use this information before an update or when troubleshooting routing. Requires `gateway.view`. ```bash theme={"system"} dome model get claude-prod ``` Reference: [`dome model get`](/cli/connect/models#get) Tool: `dome_model_get` ```json theme={"system"} { "name": "claude-prod" } ``` Reference: [`dome_model_get`](/reference/mcp/models#dome_model_get) ```http theme={"system"} POST /dome.mgmt.v1.Management/GetLLMModelConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}" } ``` The management API addresses a model by UUID. If you know only its name, call `ListLLMModelConnections` to find the corresponding ID. Reference: [`GetLLMModelConnection`](/api/management/get-llm-model-connection) ```text title="Get a model" theme={"system"} Get the "claude-prod" model and confirm its provider, model identifier, authentication method, and Gateway membership. ``` ## List models Retrieve the models in a workspace with their providers and configurations. Use the list to find a model to inspect, update, add to a pool, or remove. Requires `gateway.view`. ```bash theme={"system"} dome model list ``` Reference: [`dome model list`](/cli/connect/models#list) Tool: `dome_model_list` ```json theme={"system"} {} ``` Reference: [`dome_model_list`](/reference/mcp/models#dome_model_list) ```http theme={"system"} POST /dome.mgmt.v1.Management/ListLLMModelConnections Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}" } ``` Reference: [`ListLLMModelConnections`](/api/management/list-llm-model-connections) ```text title="List models" theme={"system"} List the models in this workspace with their providers and model identifiers. ``` ## Remove model Remove a model to delete its connection and provider credentials from the workspace. Requires `gateway.manage`. Removing a model also removes it from model pools and Gateways. Before deleting a model that serves traffic, replace it in those routes or move the traffic elsewhere. ```bash theme={"system"} dome model rm claude-prod ``` Reference: [`dome model rm`](/cli/connect/models#remove) Tool: `dome_model_remove` ```json theme={"system"} { "name": "claude-prod" } ``` Reference: [`dome_model_remove`](/reference/mcp/models#dome_model_remove) ```http theme={"system"} POST /dome.mgmt.v1.Management/DeleteLLMModelConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}" } ``` Reference: [`DeleteLLMModelConnection`](/api/management/delete-llm-model-connection) ```text title="Remove a model" theme={"system"} Remove the "claude-prod" model after confirming no pools or Gateways still depend on it. ``` ## Attach to a Gateway Attach a model directly to a Gateway when its name should always route to that one connection. The attachment makes the model reachable at the Gateway endpoint but does not authorize an agent to call it. The agent must allow the Gateway, and rules must permit the request. When a route needs failover or weighted distribution across multiple models, add the model to a [model pool](/connect/resources/models/pools) and attach the pool instead. Requires `gateway.manage`. ```bash theme={"system"} dome gateway models add prod-llms claude-prod ``` Reference: [`dome gateway models add`](/cli/connect/gateway#models-add) Tool: `dome_gateway_model_add` ```json theme={"system"} { "gateway": "prod-llms", "model": "claude-prod" } ``` Reference: [`dome_gateway_model_add`](/reference/mcp/gateway#dome_gateway_model_add) ```text title="Publish a direct model" theme={"system"} Add the "claude-prod" model directly to the "prod-llms" Gateway. ``` ### Verify model Call the model through the Gateway with an agent API key. A successful response confirms the model is reachable, rules permit the request, and the upstream accepted the configured credential. ```python title="OpenAI-compatible client" theme={"system"} from openai import OpenAI client = OpenAI( api_key="{{DOME_AGENT_KEY}}", base_url="https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}}/v1", ) response = client.chat.completions.create( model="claude-prod", messages=[{"role": "user", "content": "Return OK."}], ) ``` If the model is missing or denied, check Gateway membership, the agent's allowed resources, and rules. If the upstream rejects the call, check the model's provider configuration and credentials. ## Manage OAuth With shared OAuth, an administrator completes consent once and Dome uses the resulting provider tokens for every call. With per-user OAuth, each verified end user completes consent separately. When an end user has not connected OAuth, the first request returns a `provision_url` where the user can complete consent. Configure the OAuth client when you add or update the model. If the provider publishes OAuth metadata, Dome can discover the required endpoints. Otherwise, you must provide the authorization, token, revocation, and registration URLs. A manually registered client requires a client ID and secret. With dynamic client registration, Dome obtains them from the provider. ### Connect OAuth Connect OAuth after adding a model configured to use shared OAuth. Dome returns a one-time URL that remains valid for about ten minutes. Open the URL to complete consent at the provider. Requires `gateway.manage`. ```bash theme={"system"} dome model oauth-connect claude-prod ``` Reference: [`dome model oauth-connect`](/cli/connect/models#oauth-connect) Tool: `dome_model_oauth_connect` ```json theme={"system"} { "name": "claude-prod" } ``` Reference: [`dome_model_oauth_connect`](/reference/mcp/models#dome_model_oauth_connect) ```http theme={"system"} POST /dome.mgmt.v1.Management/BeginLLMOAuthConnect Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}" } ``` Reference: [`BeginLLMOAuthConnect`](/api/management/begin-llm-oauth-connect) ```text title="Connect model OAuth" theme={"system"} Start shared OAuth consent for the "claude-prod" model. ``` ### Disconnect OAuth Disconnect shared OAuth to revoke the stored provider tokens. The OAuth client configuration remains, so you can reconnect without configuring the client again. Disconnecting an already disconnected model has no effect. Requires `gateway.manage`. ```bash theme={"system"} dome model oauth-disconnect claude-prod ``` Reference: [`dome model oauth-disconnect`](/cli/connect/models#oauth-disconnect) Tool: `dome_model_oauth_disconnect` ```json theme={"system"} { "name": "claude-prod" } ``` Reference: [`dome_model_oauth_disconnect`](/reference/mcp/models#dome_model_oauth_disconnect) ```http theme={"system"} POST /dome.mgmt.v1.Management/DisconnectLLMOAuth Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}" } ``` Reference: [`DisconnectLLMOAuth`](/api/management/disconnect-llm-oauth) ```text title="Disconnect model OAuth" theme={"system"} Disconnect shared OAuth credentials from the "claude-prod" model. ``` ## Next steps * [Models](/concepts/resources/models) concept for how connections and attributes work and [Models](/reference/resources/models) reference for providers and credentials * [Create a model pool](/connect/resources/models/pools) for routing, traffic distribution, and failover * Manage membership and grants in [Gateways](/connect/gateways) * Authenticate clients and route model traffic in [Develop](/develop#authenticate) # Pools Source: https://docs.domesystems.ai/connect/resources/models/pools Route and fail over LLM traffic across model connections ## Overview A pool contains one or more [models](/connect/resources/models). When an agent puts the pool name in a request's `model` field, Dome orders the members according to the routing strategy. Dome sends the request to the first eligible member. If that attempt fails, Dome can try the remaining eligible members. The typical workflow is: 1. [Connect the models](/connect/resources/models#add-model) that can serve the traffic. 2. [Create a pool](#create-pool). 3. [Add the models as members](#add-member). 4. Configure the pool's [routing strategy](#routing-strategies) and [failover](#failover). 5. [Attach the pool to a Gateway](#attach-to-a-gateway). 6. Allow the Gateway on the [agent](/connect/agents#allowed-resources). 7. [Verify the pool](#verify-pool) with that agent's API key. ### Members Each member represents one model connection in the pool. With priority-weighted routing, the priority places the member in a failover tier. The weight determines how often Dome selects the member within that tier. Round-robin and least-loaded routing treat every member equally, so they ignore priority and weight. A model can belong to more than one pool. Removing a member changes only that pool and leaves the model connection available elsewhere. The management interfaces store an `enabled` value, but the current gateway routes to members regardless of that value. To stop sending traffic to a member, remove it from the pool. ### Routing strategies The routing strategy chooses the primary model and orders any failover candidates. | Strategy | Primary selection | Member fields it uses | | ------------------- | ------------------------------------------------------------------------------ | --------------------- | | `priority_weighted` | Chooses by weight from the lowest priority tier | Priority and weight | | `round_robin` | Rotates through all members in order | Neither | | `least_loaded` | Chooses the member with the fewest in-flight requests and breaks ties randomly | Neither | Use `priority_weighted` when some models should take traffic before others. Use `round_robin` or `least_loaded` when members are peers. The `strategy_scope` setting determines whether round-robin rotation and least-loaded request counts are shared across the workspace or tracked per agent. Priority-weighted routing does not use this setting. You can choose a strategy when you [create a pool](#create-pool) or [change it later](#change-routing). ### Failover The failover limit controls how many additional members Dome may try when the request to the primary member fails. | Value | Runtime behavior | | ----------------- | ------------------------------------------------ | | No limit or `all` | Try every eligible member until one succeeds | | `0` | Try only the primary | | `N` | Try the primary and up to `N` additional members | Dome authorizes the primary before sending the request. If the primary fails, Dome authorizes each remaining member before trying it and skips any member that rules deny. A streaming request can fail over only before Dome sends the first response data to the caller. An error after streaming begins terminates the response. You can set failover when you [create a pool](#create-pool) or [change it later](#change-failover). ### Pool resolution When a request reaches a Gateway, Dome resolves its `model` value in this order: 1. A pool with that exact name 2. A direct model connection with that exact name 3. A direct connection whose upstream model identifier matches 4. The first pool in the configured order whose `match_when` predicate matches 5. The workspace default pool An exact pool or model name takes precedence over conditional routing. When the requested name does not match directly, a `match_when` condition can select a pool based on the request or verified identity (for example `prompt_tokens.gt`). The full `match_when` dialect is on the [Pools](/reference/resources/model-pools#pool-resolution) reference. You can set a condition when you [create a pool](#create-pool), [change it later](#change-request-routing), and [move the pool](#move-pool) to control evaluation order. ### Response caching Pool records store a cache TTL and cache scope for reusing responses to identical requests. The gateway does not read these settings during model dispatch. Setting `cache_ttl_secs` or `cache_scope` does not cache responses. ## Requirements Before you begin: * Authenticate to Dome and select a workspace * Connect at least one [model](/connect/resources/models) * Have a [Gateway](/connect/gateways) ready when you want the pool reachable ### Permissions Pool operations require platform permissions. Each operation states its required permission inline. | Default roles | Permission | Grants | | ------------------- | ---------------- | -------------------------------------------- | | All workspace roles | `gateway.view` | List and inspect pools | | `admin`, `operator` | `gateway.manage` | Create, update, and remove pools and members | ## Create pool Create a pool with a name that clients can put in the request's `model` field. A new pool has no members until you add them. Requires `gateway.manage`. ```bash theme={"system"} dome model pool create production \ --routing-strategy priority_weighted \ --failover-max all \ --gateway prod-llms ``` By default, the CLI uses priority-weighted routing and shares routing state across the workspace. It sets the cache TTL to `0` and allows Dome to try every eligible member after a failure. Reference: [`dome model pool create`](/cli/connect/models#pool-create) Tool: `dome_model_pool_create` ```json theme={"system"} { "name": "production", "routing_strategy": "priority_weighted", "failover_max": "all", "gateways": ["prod-llms"] } ``` Reference: [`dome_model_pool_create`](/reference/mcp/models#dome_model_pool_create) ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateLLMPool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "name": "production", "routing_strategy": "priority_weighted", "strategy_scope": "workspace" } ``` Omit `failover_max` to let Dome try every eligible member after a failure. The endpoint returns the pool's UUID. The other management endpoints use this UUID to identify the pool. Attach the pool to a Gateway in a separate operation. Reference: [`CreateLLMPool`](/api/management/create-llm-pool) ```text title="Create a model pool" theme={"system"} Create a model pool named "production" with priority-weighted routing and no failover limit. Attach it to the "prod-llms" Gateway. ``` Agents cannot call a pool until it has at least one model member and belongs to a Gateway. ## Update pool Update a pool to change its name, description, routing strategy, conditional routing criteria, cache settings, or failover limit. Setting a different default pool and changing the order of conditional routes are separate operations. Requires `gateway.manage`. ```bash theme={"system"} dome model pool update production \ --description "Production chat routing" ``` Only the flags you pass change. Reference: [`dome model pool update`](/cli/connect/models#pool-update) Tool: `dome_model_pool_update` ```json theme={"system"} { "name": "production", "description": "Production chat routing" } ``` Only the parameters you send change. Reference: [`dome_model_pool_update`](/reference/mcp/models#dome_model_pool_update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateLLMPool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}", "description": "Production chat routing" } ``` Omitted fields keep their stored values. An empty description does not clear the existing description. To clear `match_when`, set `match_when_provided` to `true`. To set the cache TTL to `0`, set `cache_ttl_secs_provided` to `true`. To let Dome retry every eligible member, set `failover_max_clear` to `true`. Reference: [`UpdateLLMPool`](/api/management/update-llm-pool) ```text title="Update a model pool" theme={"system"} Change the description of "production" to "Production chat routing". Keep its members and routing configuration unchanged. ``` ### Change routing You can change how the pool selects its primary member. Refer to [Routing strategies](#routing-strategies) for when to use each strategy. When switching away from priority-weighted routing, existing priorities and weights remain stored but no longer affect selection. ```bash theme={"system"} dome model pool update production \ --routing-strategy round_robin \ --strategy-scope caller ``` Reference: [`dome model pool update`](/cli/connect/models#pool-update) Tool: `dome_model_pool_update` ```json theme={"system"} { "name": "production", "routing_strategy": "round_robin", "strategy_scope": "caller" } ``` Reference: [`dome_model_pool_update`](/reference/mcp/models#dome_model_pool_update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateLLMPool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}", "routing_strategy": "round_robin", "strategy_scope": "caller" } ``` Reference: [`UpdateLLMPool`](/api/management/update-llm-pool) ```text title="Change pool routing" theme={"system"} Change "production" to caller-scoped round-robin routing. ``` ### Change request routing You can replace the conditions that select this pool during conditional routing. Send an empty `match_when` object to remove those conditions. ```bash theme={"system"} dome model pool update long-context \ --match-when '{"prompt_tokens":{"gt":50000}}' ``` Reference: [`dome model pool update`](/cli/connect/models#pool-update) Tool: `dome_model_pool_update` ```json theme={"system"} { "name": "long-context", "match_when": { "prompt_tokens": { "gt": 50000 } } } ``` Reference: [`dome_model_pool_update`](/reference/mcp/models#dome_model_pool_update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateLLMPool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}", "match_when": { "prompt_tokens": { "gt": 50000 } }, "match_when_provided": true } ``` Reference: [`UpdateLLMPool`](/api/management/update-llm-pool) ```text title="Change conditional routing" theme={"system"} Route prompts over 50,000 tokens through the "long-context" pool. ``` ### Change failover You can change how many additional members Dome may try after the primary fails. Refer to [Failover](#failover) for limit values and streaming behavior. After setting a numeric limit, you can use the API to let Dome try every eligible member again. ```bash theme={"system"} dome model pool update production \ --failover-max 2 ``` Reference: [`dome model pool update`](/cli/connect/models#pool-update) Tool: `dome_model_pool_update` ```json theme={"system"} { "name": "production", "failover_max": "2" } ``` Reference: [`dome_model_pool_update`](/reference/mcp/models#dome_model_pool_update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateLLMPool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}", "failover_max": 2 } ``` To let Dome try every eligible member, send a separate update with `failover_max_clear`. ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateLLMPool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}", "failover_max_clear": true } ``` Reference: [`UpdateLLMPool`](/api/management/update-llm-pool) ```text title="Change pool failover" theme={"system"} Allow the "production" pool to retry up to two additional models. ``` ## Get pool Retrieve a pool with its routing configuration and members. Check this information before an update or when you need to determine which models can serve requests to the pool. Requires `gateway.view`. ```bash theme={"system"} dome model pool get production ``` Reference: [`dome model pool get`](/cli/connect/models#pool-get) Tool: `dome_model_pool_get` ```json theme={"system"} { "name": "production" } ``` Reference: [`dome_model_pool_get`](/reference/mcp/models#dome_model_pool_get) ```http theme={"system"} POST /dome.mgmt.v1.Management/GetLLMPool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}" } ``` The API returns the pool and its members. Reference: [`GetLLMPool`](/api/management/get-llm-pool) ```text title="Get a model pool" theme={"system"} Get the "production" pool with its routing configuration and members. ``` ## List pools Retrieve the pools in a workspace with their routing strategies, conditional routing criteria, and default status. Use the list to find a pool to inspect, reorder, update, or remove. Requires `gateway.view`. ```bash theme={"system"} dome model pool list ``` Reference: [`dome model pool list`](/cli/connect/models#pool-list) Tool: `dome_model_pool_list` ```json theme={"system"} {} ``` Reference: [`dome_model_pool_list`](/reference/mcp/models#dome_model_pool_list) ```http theme={"system"} POST /dome.mgmt.v1.Management/ListLLMPools Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}" } ``` Reference: [`ListLLMPools`](/api/management/list-llm-pools) ```text title="List model pools" theme={"system"} List the model pools in this workspace with their routing strategy, predicate, and default state. ``` ## Set default pool Set a pool as the workspace default. Dome uses the default only when a request does not match an exact model name, exact pool name, or conditional route. Setting a new default removes the previous default. Requires `gateway.manage`. ```bash theme={"system"} dome model pool set-default production ``` Reference: [`dome model pool set-default`](/cli/connect/models#pool-set-default) Tool: `dome_model_pool_set_default` ```json theme={"system"} { "name": "production" } ``` Reference: [`dome_model_pool_set_default`](/reference/mcp/models#dome_model_pool_set_default) ```http theme={"system"} POST /dome.mgmt.v1.Management/SetDefaultLLMPool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}" } ``` Send an empty `pool_id` to clear the default without setting another pool. Reference: [`SetDefaultLLMPool`](/api/management/set-default-llm-pool) ```text title="Set the default model pool" theme={"system"} Set "production" as the default model pool for this workspace. ``` ## Move pool Move a conditional pool earlier or later in the order Dome checks `match_when` predicates. This order affects only conditional routing and does not change routes that use an exact model or pool name. Requires `gateway.manage`. ```bash theme={"system"} dome model pool move long-context \ --before production ``` Pass exactly one of `--before` or `--after`. Reference: [`dome model pool move`](/cli/connect/models#pool-move) Tool: `dome_model_pool_move` ```json theme={"system"} { "name": "long-context", "before": "production" } ``` Reference: [`dome_model_pool_move`](/reference/mcp/models#dome_model_pool_move) ```text title="Move a conditional pool" theme={"system"} Evaluate "long-context" immediately before "production". ``` ## Manage members Pool members are the model connections that Dome can select to serve requests to the pool. ### Add member Add a model to a pool. For a priority-weighted pool, priority defaults to `0` and weight defaults to `1`. Round-robin and least-loaded pools reject explicit priority or weight values. Requires `gateway.manage`. ```bash theme={"system"} dome model pool member add production claude-prod \ --priority 0 \ --weight 4 ``` Run a separate command for each model because each member can have a different priority and weight. Reference: [`dome model pool member add`](/cli/connect/models#pool-member-add) Tool: `dome_model_pool_member_add` ```json theme={"system"} { "pool": "production", "connection": "claude-prod", "priority": 0, "weight": 4 } ``` Reference: [`dome_model_pool_member_add`](/reference/mcp/models#dome_model_pool_member_add) ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateLLMPoolMember Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}", "llm_model_connection_id": "{{CONNECTION_ID}}", "priority": 0, "weight": 4, "enabled": true } ``` Reference: [`CreateLLMPoolMember`](/api/management/create-llm-pool-member) ```text title="Add a pool member" theme={"system"} Add "claude-prod" to "production" at priority zero with weight four. ``` ### List members Retrieve the models in a pool with each member's priority, weight, and stored `enabled` value. Requires `gateway.view`. ```bash theme={"system"} dome model pool member list production ``` Reference: [`dome model pool member list`](/cli/connect/models#pool-member-list) Tool: `dome_model_pool_member_list` ```json theme={"system"} { "pool": "production" } ``` Reference: [`dome_model_pool_member_list`](/reference/mcp/models#dome_model_pool_member_list) ```http theme={"system"} POST /dome.mgmt.v1.Management/ListLLMPoolMembers Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}" } ``` Reference: [`ListLLMPoolMembers`](/api/management/list-llm-pool-members) ```text title="List pool members" theme={"system"} List the members of "production" with their priority and weight. ``` ### Update member Update the priority or weight of a member in a priority-weighted pool. To move a model to another pool, remove it from the current pool and add it to the destination pool. Requires `gateway.manage`. ```bash theme={"system"} dome model pool member update production claude-prod \ --priority 1 \ --weight 1 ``` Reference: [`dome model pool member update`](/cli/connect/models#pool-member-update) Tool: `dome_model_pool_member_update` ```json theme={"system"} { "pool": "production", "connection": "claude-prod", "priority": 1, "weight": 1 } ``` Reference: [`dome_model_pool_member_update`](/reference/mcp/models#dome_model_pool_member_update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateLLMPoolMember Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}", "member_id": "{{MEMBER_ID}}", "priority": 1, "weight": 1 } ``` Reference: [`UpdateLLMPoolMember`](/api/management/update-llm-pool-member) ```text title="Update a pool member" theme={"system"} Change "claude-prod" in "production" to priority one and weight one. ``` ### Remove member Remove a model from a pool to stop the pool from sending requests to that model. Removing the member does not delete the model connection, so other pools and direct Gateway routes can still use it. Requires `gateway.manage`. ```bash theme={"system"} dome model pool member rm production claude-prod ``` Reference: [`dome model pool member rm`](/cli/connect/models#pool-member-remove) Tool: `dome_model_pool_member_remove` ```json theme={"system"} { "pool": "production", "connection": "claude-prod" } ``` Reference: [`dome_model_pool_member_remove`](/reference/mcp/models#dome_model_pool_member_remove) ```http theme={"system"} POST /dome.mgmt.v1.Management/DeleteLLMPoolMember Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}", "member_id": "{{MEMBER_ID}}" } ``` Reference: [`DeleteLLMPoolMember`](/api/management/delete-llm-pool-member) ```text title="Remove a pool member" theme={"system"} Remove "claude-prod" from "production" without deleting the model. ``` ## Remove pool Remove a pool to delete its routing configuration and memberships. Removing the pool does not delete its model connections. Requires `gateway.manage`. Removing a pool also removes it from every Gateway. Requests that use the deleted pool name fail unless a conditional route or the workspace default selects another pool. ```bash theme={"system"} dome model pool rm production ``` Reference: [`dome model pool rm`](/cli/connect/models#pool-remove) Tool: `dome_model_pool_remove` ```json theme={"system"} { "name": "production" } ``` Reference: [`dome_model_pool_remove`](/reference/mcp/models#dome_model_pool_remove) ```http theme={"system"} POST /dome.mgmt.v1.Management/DeleteLLMPool Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "pool_id": "{{POOL_ID}}" } ``` Reference: [`DeleteLLMPool`](/api/management/delete-llm-pool) ```text title="Remove a model pool" theme={"system"} Remove the "production" pool after confirming no clients still use its name. ``` ## Attach to a Gateway Attach a pool to a Gateway so agents can request the pool by name at that gateway's model endpoints. The attachment makes the pool reachable but does not authorize an agent to call it. The agent must allow the Gateway, and rules must permit both the pool and selected model. Requires `gateway.manage`. ```bash theme={"system"} dome model pool gateways add production prod-llms ``` Reference: [`dome model pool gateways add`](/cli/connect/models#pool-gateways) Tool: `dome_gateway_model_pool_add` ```json theme={"system"} { "gateway": "prod-llms", "pool": "production" } ``` Reference: [`dome_gateway_model_pool_add`](/reference/mcp/gateway#dome_gateway_model_pool_add) ```text title="Publish a model pool" theme={"system"} Add the "production" model pool to the "prod-llms" Gateway. ``` ### Verify pool Call the pool through the Gateway with an agent API key. A successful response confirms the pool is reachable, rules permit the route, and at least one member can serve the request. ```python title="OpenAI-compatible client" theme={"system"} from openai import OpenAI client = OpenAI( api_key="{{DOME_AGENT_KEY}}", base_url="https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}}/v1", ) response = client.chat.completions.create( model="production", messages=[{"role": "user", "content": "Return OK."}], ) ``` If Dome cannot find the pool for the request, check the pool's Gateway membership and model members. If Dome denies the request, check the agent's allowed resources and rules. Audit events record which model served the request and whether Dome attempted failover. ## Next steps * [Pools](/concepts/resources/model-pools) concept for how routing and failover work and [Pools](/reference/resources/model-pools) reference for strategies and `match_when` * [Connect more models](/connect/resources/models) to add providers or failover capacity * Manage membership and grants in [Gateways](/connect/gateways) * Configure model and pool spend limits in [Cost Quotas](/govern/quotas) # Tools Source: https://docs.domesystems.ai/connect/resources/tools Attach MCP servers, configure upstream authentication, and publish governed tools ## Overview Tools are one of the resource types Dome makes available to agents, alongside [models](/connect/resources/models). When you add a tool, Dome stores its server endpoint, credentials, and egress headers. Dome records the operations the server exposes once the gateway connects to it. The typical workflow is: 1. [Add a tool](#add-tool) for the upstream MCP server. 2. If the server requires credentials, configure [upstream authentication](#upstream-authentication). 3. [Attach the tool to a Gateway](#attach-to-a-gateway) to make it available through a Dome endpoint. 4. Allow the Gateway on the [agent](/connect/agents#allowed-resources). 5. [Verify discovery](#verify-discovery) to confirm which operations the agent can use. ### Upstream authentication The gateway authenticates to the upstream with credentials you configure on the tool. Agent API keys authenticate inbound requests to Dome and never pass through as upstream credentials. Dome stores upstream secrets and injects them only at egress. Two settings control which credential the gateway sends. 1. The authentication method defines how the server verifies a request. | Auth method | When the upstream | | ----------- | ---------------------------------------- | | `none` | Requires no credential | | `api-key` | Accepts a static API key or bearer token | | `oauth` | Supports OAuth 2.0 or 2.1 | 2. The credential type defines whose credential the gateway sends. With shared credentials, every request reaches the upstream under the same service account. With per-user credentials, each request uses a credential linked to the verified end user, so the upstream applies that user's permissions. | Credential type | What the gateway sends | | --------------- | ----------------------------------------------- | | `none` | No credential | | `shared` | One workspace-managed credential for every call | | `per-user` | One credential per verified act-as subject | The two settings are not independent. The `none` method accepts only the `none` credential type, and `api-key` and `oauth` each require `shared` or `per-user`. When you omit the credential type, Dome pairs it with the method: `none` with `none`, `api-key` with `shared`, and `oauth` with `per-user`. OAuth also requires the `streamable-http` protocol, so a stdio tool cannot use it. You can set authentication when you [add a tool](#add-tool) or [change it later](#change-upstream-authentication). ### Egress headers Egress headers are values the gateway adds on the outbound request to the upstream. A header can be a literal string, a stored secret, or the verified act-as identity from the inbound request. Only a Streamable HTTP tool can carry them, because a stdio tool has no HTTP request to attach them to. An act-as header sends the verified end-user identity to the upstream, and Dome accepts only OIDC or HMAC verification. Dome denies any call that carries no verified identity or that used a weaker verification method. An act-as header is not valid on a tool that authenticates with OAuth. You can set headers when you [add a tool](#add-tool), [change them on update](#change-egress-headers), or [manage them individually](#manage-egress-headers). ### Discovered tools After the gateway connects to an upstream server, every operation that server exposes appears in the tool's catalog. You can change the status of one operation without removing the entire tool. For example, you can block `delete_page` while keeping `create_page` available to agents. | Status | Behavior | | ------------ | ---------------------------------------------- | | `active` | Discoverable and callable when rules allow | | `deprecated` | Callable, with a catalog warning | | `blocked` | Hidden from discovery and denied on invocation | Blocking an operation denies it even if another rule would permit it. Status persists when the gateway observes the operation again. Only a restore returns it to `active`. Refer to [Manage discovered tools](#manage-discovered-tools) for the operations that change these statuses. ## Requirements Before you begin: * Authenticate to Dome and select a workspace * Have the upstream MCP endpoint (URL for Streamable HTTP, or a local command for stdio) * Have upstream credentials when the server requires them * Have a [Gateway](/connect/gateways) ready when you want the tool reachable ### Permissions Tool connection operations require platform permissions. Each operation states its required permission inline. | Default roles | Permission | Grants | | ------------------- | ---------------- | -------------------------------------------------------------- | | All workspace roles | `gateway.view` | List tools and inspect discovered operations | | `admin`, `operator` | `gateway.manage` | Add, update, and remove tools and manage discovered operations | ## Add tool Add a tool for one upstream MCP server. Use Streamable HTTP for a remote server or stdio for a local process. You can set [upstream authentication](#upstream-authentication), [egress headers](#egress-headers), and Gateway membership in the same call. Requires `gateway.manage`. ```bash theme={"system"} dome tool add \ --name notion \ --url "https://mcp.notion.com/mcp" \ --protocol streamable-http \ --gateway prod-tools ``` If you do not define `--gateway`, the tool stays unreachable until you attach it. Add `--auth-method`, `--credential-type`, and header flags if the upstream needs them. Reference: [`dome tool add`](/cli/connect/tools#add) Tool: `dome_tool_add` ```json theme={"system"} { "name": "notion", "url": "https://mcp.notion.com/mcp", "protocol": "streamable-http", "gateways": ["prod-tools"] } ``` Reference: [`dome_tool_add`](/reference/mcp/tools#dome_tool_add) ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateMCPServerConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "name": "notion", "protocol": "streamable-http", "protocol_config": { "url": "https://mcp.notion.com/mcp" } } ``` The endpoint returns the new tool's UUID, which every other management call uses to address it. Reference: [`CreateMCPServerConnection`](/api/management/create-mcp-server-connection) ```text title="Attach an MCP server" theme={"system"} Add a Streamable HTTP tool named "notion" at https://mcp.notion.com/mcp. Attach it to the "prod-tools" Gateway. ``` A tool remains unavailable to agents until you attach it to a Gateway. You can configure authentication and headers before attaching it. ## Update tool Update a tool to change its connection or security settings. You can change its URL, protocol, authentication, headers, or the operations Dome treats as writes. Switching between `api-key` and `oauth` clears the previously stored credentials. A header list always replaces the existing list rather than merging into it. How the other fields merge depends on the interface you use. Requires `gateway.manage`. ```bash theme={"system"} dome tool update notion \ --url "https://mcp.notion.com/mcp" ``` Only the flags you pass change. Reference: [`dome tool update`](/cli/connect/tools#update) Tool: `dome_tool_update` ```json theme={"system"} { "name": "notion", "url": "https://mcp.notion.com/mcp" } ``` Only the parameters you send change. Reference: [`dome_tool_update`](/reference/mcp/tools#dome_tool_update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateMCPServerConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}", "protocol": "streamable-http", "protocol_config": { "url": "https://mcp.notion.com/mcp" } } ``` Send the record's full state. The endpoint overwrites `protocol`, `protocol_config`, `field_classifications`, and `write_tools` with whatever the request carries, so omitting `protocol_config` clears the URL and headers. Read the tool with `GetMCPServerConnection` first and send back everything you want to keep. `auth_method`, `credential_type`, `auth_config`, and `name` are the exceptions, where an empty value preserves the stored one. Reference: [`UpdateMCPServerConnection`](/api/management/update-mcp-server-connection) ```text title="Update a tool" theme={"system"} Update the "notion" tool URL to https://mcp.notion.com/mcp. ``` ### Change upstream authentication You can change how the gateway authenticates to the upstream. Refer to [Upstream authentication](#upstream-authentication) for the methods and credential types. #### Shared API key Use a shared bearer token when every call should present the same upstream credential. Dome stores the secret and injects it at egress. Agents never receive the raw upstream credential. ```bash theme={"system"} dome tool update internal-search \ --auth-method api-key \ --credential-type shared \ --authorization "Bearer $SEARCH_TOKEN" ``` On create, the same flags work with `dome tool add`. Reference: [`dome tool update`](/cli/connect/tools#update) Tool: `dome_tool_update` ```json theme={"system"} { "name": "internal-search", "auth_method": "api-key", "credential_type": "shared", "authorization": "Bearer {{SEARCH_TOKEN}}" } ``` Reference: [`dome_tool_update`](/reference/mcp/tools#dome_tool_update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateMCPServerConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}", "auth_method": "api-key", "credential_type": "shared", "secret_values": { "Authorization": "Bearer {{SEARCH_TOKEN}}" } } ``` Send `protocol` and `protocol_config` alongside these fields, as described in [Update tool](#update-tool). Reference: [`UpdateMCPServerConnection`](/api/management/update-mcp-server-connection) ```text title="Set shared API-key authentication" theme={"system"} Set the "internal-search" tool to shared API-key authentication with my bearer token. ``` #### Shared OAuth Configure the OAuth client on the tool, then [connect OAuth](#connect-oauth) to complete admin consent. When the upstream publishes RFC 8414 metadata, the gateway discovers its OAuth endpoints. Otherwise, you can provide the authorization, token, revocation, and registration URLs. The gateway supports manual and dynamic client registration. #### Per-user credentials You can set credential type to `per-user` when upstream authorization must follow the end user. Each request must include a verified act-as identity. Configure runtime act-as authentication in [Develop](/develop). When an end user has no credential yet, Dome returns `401` with a short-lived `provision_url`. The user opens that URL to enter an API key or complete OAuth consent, and the call succeeds on retry. Discovery flags the tool as needing credentials until provisioning finishes. Refer to [Errors and denials](/develop#errors-and-denials) for how a runtime should surface the prompt and retry. For per-user tools, [sync the catalog](#sync-catalog) after the admin attaches a per-user credential, so observed operations appear before agent traffic. ### Change egress headers You can change which egress headers the gateway sends, including an act-as header when the upstream needs the verified end user. Refer to [Egress headers](#egress-headers). An act-as header is not valid on a tool that authenticates with OAuth, and the update fails when you combine them. ```bash theme={"system"} dome tool update crm \ --header-actas X-Verified-User ``` To append without replacing the full list, use [Add headers](#add-headers). Reference: [`dome tool update`](/cli/connect/tools#update) Tool: `dome_tool_update` ```json theme={"system"} { "name": "crm", "headers": [ {"name": "X-Verified-User", "source": "actas"} ] } ``` Reference: [`dome_tool_update`](/reference/mcp/tools#dome_tool_update) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateMCPServerConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}", "protocol": "streamable-http", "protocol_config": { "url": "https://crm.example.com/mcp", "headers": [ {"name": "X-Verified-User", "source": "actas"} ] } } ``` Headers live inside `protocol_config`, which the endpoint overwrites, so send the URL and any headers you want to keep in the same call. Reference: [`UpdateMCPServerConnection`](/api/management/update-mcp-server-connection) ```text title="Forward verified act-as identity" theme={"system"} Update the "crm" tool to forward verified act-as identity in X-Verified-User. ``` ## Get tool Retrieve a tool by name to inspect its endpoint, protocol, authentication, and Gateway membership. This information helps you verify its configuration before an update or troubleshoot why agents cannot reach it. Requires `gateway.view`. ```bash theme={"system"} dome tool get notion ``` Reference: [`dome tool get`](/cli/connect/tools#get) Tool: `dome_tool_get` ```json theme={"system"} { "name": "notion" } ``` Reference: [`dome_tool_get`](/reference/mcp/tools#dome_tool_get) ```http theme={"system"} POST /dome.mgmt.v1.Management/GetMCPServerConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}" } ``` The management API addresses a tool by UUID rather than by name. Call `ListMCPServerConnections` to resolve a name to its ID. Reference: [`GetMCPServerConnection`](/api/management/get-mcp-server-connection) ```text title="Get a tool" theme={"system"} Get the "notion" tool and confirm its auth method and Gateway membership. ``` ## List tools Retrieve the tools in a workspace with their protocols and authentication methods. Use the list to find the tool you want to update or remove. Requires `gateway.view`. ```bash theme={"system"} dome tool list ``` Reference: [`dome tool list`](/cli/connect/tools#list) Tool: `dome_tool_list` ```json theme={"system"} {} ``` Reference: [`dome_tool_list`](/reference/mcp/tools#dome_tool_list) ```http theme={"system"} POST /dome.mgmt.v1.Management/ListMCPServerConnections Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}" } ``` Reference: [`ListMCPServerConnections`](/api/management/list-mcp-server-connections) ```text title="List tools" theme={"system"} List the tools in this workspace. ``` ## Remove tool Remove a tool to delete its upstream record from the workspace, along with the authentication and header configuration stored on it. Requires `gateway.manage`. Removing a tool disconnects every agent currently routing through it. Update or re-route agents first. ```bash theme={"system"} dome tool remove notion ``` Reference: [`dome tool remove`](/cli/connect/tools#remove) Tool: `dome_tool_remove` ```json theme={"system"} { "name": "notion" } ``` Reference: [`dome_tool_remove`](/reference/mcp/tools#dome_tool_remove) ```http theme={"system"} POST /dome.mgmt.v1.Management/DeleteMCPServerConnection Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}" } ``` Reference: [`DeleteMCPServerConnection`](/api/management/delete-mcp-server-connection) ```text title="Remove a tool" theme={"system"} Remove the "notion" tool after confirming no agents still depend on it. ``` ## Manage discovered tools Catalog status lets you block or deprecate individual operations without removing the entire tool. Status persists when the gateway observes the operation again. Only a restore returns it to `active`. Refer to [Discovered tools](#discovered-tools) for the behavior of each status. You can block, deprecate, and restore operations through the CLI and MCP. The API reference documents catalog sync only. ### List catalog Retrieve the operations Dome has recorded for a tool. Because Dome stores the catalog, you can inspect it when the upstream server is unavailable. The result covers everything Dome discovered on the server, not what one agent can call. Requires `gateway.view`. ```bash theme={"system"} dome tool catalog list notion --seen-since 7d ``` Blocked entries are hidden unless you pass `--show-blocked`. Use `--with-schema` when you need input schemas. Reference: [`dome tool catalog list`](/cli/connect/tools#catalog-list) Tool: `dome_tools_for_connection` ```json theme={"system"} { "name": "notion", "with_schema": false } ``` Reference: [`dome_tools_for_connection`](/reference/mcp/tools#dome_tools_for_connection) ```text title="List discovered operations" theme={"system"} List the operations Dome has discovered on the "notion" tool. ``` ### Sync catalog Sync the catalog to discover operations on a per-user tool before agent traffic begins. The gateway calls upstream `tools/list` with the administrator's per-user credential, so that credential must already be attached. Shared tools sync when the gateway starts and reject manual sync. Requires `gateway.manage`. ```bash theme={"system"} dome tool catalog sync notion ``` Reference: [`dome tool catalog sync`](/cli/connect/tools#catalog-sync) ```http theme={"system"} POST /dome.mgmt.v1.Management/SyncMCPServerConnectionTools Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}" } ``` Add `act_as_sub` when the administrator's act-as subject is not their email. Dome resolves the credential by that subject, and the call fails when no credential is stored for it. Reference: [`SyncMCPServerConnectionTools`](/api/management/sync-mcp-server-connection-tools) ### Block Block a discovered operation to hide it from agents and deny calls to it. A block overrides any rule that would otherwise permit the operation. A blocked operation drops out of the catalog listing, so confirming a block means asking for blocked entries as well. Requires `gateway.manage`. ```bash theme={"system"} dome tool catalog block notion delete_page \ --message "Use archive_page instead" ``` Reference: [`dome tool catalog block`](/cli/connect/tools#catalog-block) Tool: `dome_tool_block` ```json theme={"system"} { "connection": "notion", "tool": "delete_page", "message": "Use archive_page instead" } ``` Reference: [`dome_tool_block`](/reference/mcp/tools#dome_tool_block) ```text title="Block a discovered tool" theme={"system"} Block "delete_page" on the "notion" tool and recommend "archive_page". ``` ### Deprecate Deprecate a discovered operation to keep it callable while directing users to a replacement. The deprecation message appears in the dashboard, and the catalog lists the operation as `deprecated`. Requires `gateway.manage`. ```bash theme={"system"} dome tool catalog deprecate notion create_page \ --message "use create_page_v2" ``` Reference: [`dome tool catalog deprecate`](/cli/connect/tools#catalog-deprecate) Tool: `dome_tool_deprecate` ```json theme={"system"} { "connection": "notion", "tool": "create_page", "message": "use create_page_v2" } ``` Reference: [`dome_tool_deprecate`](/reference/mcp/tools#dome_tool_deprecate) ```text title="Deprecate a discovered tool" theme={"system"} Deprecate "create_page" on the "notion" tool and recommend "create_page_v2". ``` ### Restore Restore a discovered operation to make it available again when rules permit. Restoring clears its block or deprecation. When no blocked operations remain, Dome removes the managed denies. Requires `gateway.manage`. ```bash theme={"system"} dome tool catalog restore notion delete_page ``` Reference: [`dome tool catalog restore`](/cli/connect/tools#catalog-restore) Tool: `dome_tool_restore` ```json theme={"system"} { "connection": "notion", "tool": "delete_page" } ``` Reference: [`dome_tool_restore`](/reference/mcp/tools#dome_tool_restore) ```text title="Restore a discovered tool" theme={"system"} Restore "delete_page" on the "notion" tool to active. ``` ## Attach to a Gateway Attach a tool to a Gateway to expose its operations through the gateway's MCP endpoint. Attaching the entire tool also exposes operations discovered later. Membership makes the tool reachable but does not authorize an agent to call it. The agent must allow the Gateway, and rules must permit the call. You can attach a tool when you [add it](#add-tool) or later from either the tool or Gateway. Refer to [Gateways](/connect/gateways) for membership and grants. Requires `gateway.manage`. ```bash theme={"system"} dome tool gateways add notion prod-tools ``` The Gateway side of the same membership is `dome gateway tool-sources add`. Reference: [`dome tool gateways`](/cli/connect/tools#gateways) Tool: `dome_gateway_tool_source_add` ```json theme={"system"} { "gateway": "prod-tools", "connection": "notion" } ``` Reference: [`dome_gateway_tool_source_add`](/reference/mcp/gateway#dome_gateway_tool_source_add) ```text title="Publish a tool" theme={"system"} Add the entire "notion" tool to the "prod-tools" Gateway. ``` ### Verify discovery Call `tools/list` on the Gateway endpoint with an agent's API key. The response applies Gateway membership and rules, so it shows what that one agent can use. The stored catalog in [List catalog](#list-catalog) applies neither check. ```bash theme={"system"} curl -X POST "https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}}/mcp" \ -H "Authorization: Bearer {{DOME_AGENT_KEY}}" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` If an operation is missing, check the tool's health, catalog status, Gateway membership, the agent's allowed resources, and rules. ## Manage OAuth With shared OAuth, an administrator completes consent once after configuring the OAuth client. With per-user OAuth, each end user completes consent on their first call. ### Connect OAuth Connect OAuth to begin the admin consent flow. The response includes a one-shot URL valid for about ten minutes. Open it and complete consent at the upstream authorization server. Dome stores the resulting OAuth tokens. On a shared tool, those tokens serve every call. On a per-user tool, the flow attaches the calling administrator's own credential, which [Sync catalog](#sync-catalog) then reads to discover operations before agent traffic starts. End users still complete their own consent on their first call. Requires `gateway.manage`. The tool must use `auth_method=oauth`. ```bash theme={"system"} dome tool oauth-connect notion ``` The CLI and the MCP tool accept shared tools only. To attach an administrator's per-user credential, call the API with that administrator's act-as subject. Reference: [`dome tool oauth-connect`](/cli/connect/tools#oauth-connect) Tool: `dome_tool_oauth_connect` ```json theme={"system"} { "name": "notion" } ``` Reference: [`dome_tool_oauth_connect`](/reference/mcp/tools#dome_tool_oauth_connect) ```http theme={"system"} POST /dome.mgmt.v1.Management/BeginMCPServerOAuthConnect Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}" } ``` On a per-user tool, add `act_as_sub` to name the administrator whose credential this flow attaches. It defaults to the calling administrator's email, and it is required when the workspace's act-as verifier issues opaque subjects. Reference: [`BeginMCPServerOAuthConnect`](/api/management/begin-mcp-server-oauth-connect) ```text title="Connect shared OAuth" theme={"system"} Start shared OAuth consent for the "notion" tool. ``` ### Disconnect OAuth Disconnect shared OAuth to revoke its stored tokens. The OAuth client configuration remains, so you can reconnect without configuring the client again. Disconnecting an already disconnected tool has no effect. Requires `gateway.manage`. ```bash theme={"system"} dome tool oauth-disconnect notion ``` Reference: [`dome tool oauth-disconnect`](/cli/connect/tools#oauth-disconnect) Tool: `dome_tool_oauth_disconnect` ```json theme={"system"} { "name": "notion" } ``` Reference: [`dome_tool_oauth_disconnect`](/reference/mcp/tools#dome_tool_oauth_disconnect) ```http theme={"system"} POST /dome.mgmt.v1.Management/DisconnectMCPServerOAuth Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "connection_id": "{{CONNECTION_ID}}" } ``` Reference: [`DisconnectMCPServerOAuth`](/api/management/disconnect-mcp-server-oauth) ```text title="Disconnect shared OAuth" theme={"system"} Disconnect shared OAuth credentials for the "notion" tool. ``` ## Manage egress headers You can add or remove individual headers without replacing the full list. These operations are available only through the CLI. MCP and API callers change headers through [Update tool](#update-tool), which replaces the whole list. ### List headers Retrieve the outbound headers configured on a tool. Requires `gateway.view`. ```bash theme={"system"} dome tool header list notion ``` Reference: [`dome tool header list`](/cli/connect/tools#header-list) ### Add headers Add one or more outbound headers without replacing the headers already configured on a tool. Requires `gateway.manage`. ```bash theme={"system"} dome tool header add crm --header-actas X-Verified-User ``` You can combine literal, secret, and act-as header flags in one call. Reference: [`dome tool header add`](/cli/connect/tools#header-add) ### Remove headers Remove outbound headers by name from a tool. Requires `gateway.manage`. ```bash theme={"system"} dome tool header remove crm --name X-Verified-User ``` Reference: [`dome tool header remove`](/cli/connect/tools#header-remove) ## Next steps * [Tools](/concepts/tools) concept for how connections and catalogs work and [Tools](/reference/resources/tools) reference for auth methods and catalog statuses * [Register agents](/connect/agents) and allow the resources they need * Manage Gateway membership and grants in [Gateways](/connect/gateways) * Authenticate and route runtime traffic in [Develop](/develop#authenticate) # Develop with Dome Source: https://docs.domesystems.ai/develop/index Authenticate as an agent, call tools and models through a Gateway, and handle denials **Develop** wires an agent into product code against Agents, Gateways, and Resources already configured in Dome. Authenticate as an agent, call tools and models through a Gateway, pass identity for delegated agents when needed, and handle denials. Refer to [Architecture](/concepts/architecture) concept for the request path. Refer to [Gateways](/concepts/gateways) concept for reachability. ## Overview Dome separates configuration from the client path. Connect and Govern define what an agent may reach and do. Develop covers the runtime client path: which credential to send, which Gateway URL to call, whether to attach act-as identity, and how to handle denials. Point every client at a Gateway path that includes `/gateways/{{GATEWAY_ID}}`. A bare host fails closed. The typical workflow is: 1. [Authenticate](#authenticate) with an agent key or short-lived JWT. 2. [Route traffic](#route-traffic) to MCP and model endpoints on the Gateway. 3. Optionally [pass identity for delegated agents](#pass-identity-for-delegated-agents) when access depends on the person behind the agent. 4. [Handle errors and denials](#errors-and-denials) from the gateway. ### Runtime credentials The gateway accepts either credential form as a bearer (or Anthropic `x-api-key`): * **Agent API key** is a long-lived secret for services and hosted clients. Send it on every request, or exchange it for a JWT. * **Short-lived JWT** is exchanged from the key through the Dome API when your runtime rotates credentials. Cache until expiry. Do not exchange on every call. The management API accepts JWTs only. Store agent keys as secrets. Never send an upstream model or MCP credential from application code. The gateway injects configured credentials after authorization. Details are under [Authenticate](#authenticate). ### Gateway URL Use one Gateway URL for each runtime client: ```bash theme={"system"} export DOME_AGENT_KEY="{{DOME_AGENT_KEY}}" export DOME_GATEWAY_URL="https://{{GATEWAY_HOST}}/gateways/{{GATEWAY_ID}}" ``` Client bases append protocol paths to that URL: | Client | Runtime URL | | --------- | ----------------------- | | MCP | `$DOME_GATEWAY_URL/mcp` | | OpenAI | `$DOME_GATEWAY_URL/v1` | | Anthropic | `$DOME_GATEWAY_URL` | Membership on the Gateway determines which tools and models the endpoint exposes. Grants and Rules still decide admission and actions. Build and verify URLs on [Gateways](/connect/gateways#build-the-runtime-endpoint). Call examples are under [Route traffic](#route-traffic). ### Delegated identity on the wire When the agent is delegated, every governed request carries verified end-user evidence in `X-Dome-Act-As`. Configure providers and the agent method in [Delegated agents](/connect/agents/delegated). Application code only attaches the header. It does not verify the claim. Refer to [Pass identity for delegated agents](#pass-identity-for-delegated-agents). ## Requirements Before you begin: * Register an [agent](/connect/agents), create its key, and [grant it access](/connect/gateways#manage-agent-access) to a Gateway that already has the tools or models you need as members * Confirm Rules (and Guards or Quotas, if used) allow the calls you will make * Set `DOME_AGENT_KEY` and `DOME_GATEWAY_URL` as shown in [Gateway URL](#gateway-url) Configure MCP servers, model connections, pools, credentials, and Gateway membership in [Connect](/connect). Configure authorization and filtering in [Govern](/govern). ### Permissions Runtime calls use the agent key and Cedar Rules, not workspace RBAC. Platform permissions apply when you manage agents, Gateways, or Rules in Connect and Govern. ## Authenticate Present the agent key on every runtime request. The gateway authenticates the agent before it evaluates Gateway admission or Cedar policy. Use either credential form: * **Agent API key:** Send the key directly for long-running services and hosted clients. * **Short-lived JWT:** Exchange the key for a token when your runtime rotates credentials. The gateway accepts both forms as bearer credentials. The management API accepts JWTs only. ### Send the agent key Send the key in the `Authorization` header for MCP and OpenAI-shaped requests: ```http theme={"system"} Authorization: Bearer {{DOME_AGENT_KEY}} ``` Anthropic clients send their configured `api_key` as `x-api-key` on `/v1/messages` and `/v1/messages/count_tokens`. The gateway promotes it into the same agent identity pipeline. ```http theme={"system"} x-api-key: {{DOME_AGENT_KEY}} anthropic-version: 2023-06-01 ``` `Authorization` takes precedence when both headers are present. Other runtime routes require bearer authentication. ### Exchange the key for a JWT Exchange the key against the Dome API, then cache the token until its expiry: ```python theme={"system"} import os import httpx response = httpx.post( "https://{{DOME_API_HOST}}/dome.identity.v1.Identity/ExchangeToken", json={ "grant_type": "api_key", "api_key": os.environ["DOME_AGENT_KEY"], }, ) response.raise_for_status() token = response.json()["access_token"] expires_in = response.json()["expires_in"] ``` The default lifetime is 10 minutes. Refresh before `expires_in`. Do not exchange a token for every gateway call. Store agent keys as secrets. Never send an upstream model or MCP credential from application code. The gateway injects configured credentials after authorization. ## Route traffic Send runtime traffic to the Gateway that contains the required tools or pools. Dome resolves the resource, authorizes the call, injects upstream credentials, and records the result. ### Call MCP tools Use an MCP Streamable HTTP client against the Gateway's `/mcp` endpoint: ```python theme={"system"} import asyncio import os from mcp import ClientSession from mcp.client.streamable_http import streamable_http_client async def main(): headers = { "Authorization": f"Bearer {os.environ['DOME_AGENT_KEY']}", } async with streamable_http_client( f"{os.environ['DOME_GATEWAY_URL']}/mcp", headers=headers, ) as (read, write, _): async with ClientSession(read, write) as session: await session.initialize() catalog = await session.list_tools() print([tool.name for tool in catalog.tools]) result = await session.call_tool( "github/list_issues", {"repo": "dome"}, ) print(result.content) asyncio.run(main()) ``` `tools/list` returns only tools in the selected Gateway that the agent may discover. `tools/call` evaluates authorization again for the requested tool. MCP Streamable HTTP can return normal JSON or an SSE response. Keep the session open until the client consumes the complete result. ### Call models Point an OpenAI or Anthropic client at the Gateway. Set `model` to a Dome pool or connection name. The gateway preserves each client's native response shape and translates requests upstream only after resource resolution and authorization. Point the OpenAI client at the Gateway's `/v1` base. Pass the agent key as `api_key`. ```python theme={"system"} import os from openai import OpenAI client = OpenAI( api_key=os.environ["DOME_AGENT_KEY"], base_url=f"{os.environ['DOME_GATEWAY_URL']}/v1", ) response = client.chat.completions.create( model="production", messages=[ {"role": "user", "content": "Summarize the open incidents."}, ], ) print(response.choices[0].message.content) ``` The same base supports `/chat/completions`, `/responses`, `/embeddings`, `/moderations`, and `/models`. Provider support can vary for embeddings, moderation, and Responses API calls. `POST /v1/embeddings` honors OpenAI's `encoding_format` on the request body. With `float` (or omitted), the response is a JSON array of floats. With `base64`, the response is little-endian IEEE-754 float32 bytes as a string. The gateway always fetches float vectors upstream and re-encodes for the caller. Token accounting is unchanged. Any other value returns HTTP `400`. Ingress routes are on the [LLM gateway](/concepts/gateways/llm-gateway#endpoints) concept. Stream Chat Completions by setting `stream=True`: ```python theme={"system"} stream = client.chat.completions.create( model="production", messages=[ {"role": "user", "content": "Draft a short incident update."}, ], stream=True, ) for chunk in stream: delta = chunk.choices[0].delta.content if delta: print(delta, end="", flush=True) ``` Consume the iterator until completion. Once the first SSE event reaches the client, the gateway cannot fail over to another model connection. Point the Anthropic client at the Gateway root. The SDK appends `/v1/messages` and sends the agent key through `x-api-key`. ```python theme={"system"} import os from anthropic import Anthropic client = Anthropic( api_key=os.environ["DOME_AGENT_KEY"], base_url=os.environ["DOME_GATEWAY_URL"], ) message = client.messages.create( model="production", max_tokens=512, messages=[ {"role": "user", "content": "Summarize the open incidents."}, ], ) print(message.content[0].text) ``` Use the Anthropic streaming helper for SSE: ```python theme={"system"} with client.messages.stream( model="production", max_tokens=512, messages=[ {"role": "user", "content": "Draft a short incident update."}, ], ) as stream: for text in stream.text_stream: print(text, end="", flush=True) ``` Choose the target by setting the request's `model`: 1. An exact pool name selects that pool. 2. An exact connection name selects that connection. 3. A provider model ID selects a matching connection. 4. A configured routing predicate can select a pool. 5. The workspace default pool handles the remaining request. Application code chooses a logical target. Resolution order and `match_when` are on the [Pools](/reference/resources/model-pools#pool-resolution) reference. Define membership, strategies, and failover on [Pools](/connect/resources/models/pools) and connections on [Models](/connect/resources/models). ## Pass identity for delegated agents When the agent is [delegated](/connect/agents/delegated), attach the human identity on each request. The gateway verifies it before Cedar, routing, per-user credentials, or backend forwarding uses it. Send the evidence in `X-Dome-Act-As` on MCP, OpenAI, and Anthropic requests. Match the wire value to the agent's configured verification method. The method-to-header table is on [Delegated agents](/connect/agents/delegated#verification-methods). Prefer OIDC or bound identity for production traffic. Configure providers on [Delegated agents](/connect/agents/delegated#create-a-verification-provider). ### Pass OIDC identity to MCP Add the end-user token to the session headers: ```python theme={"system"} headers = { "Authorization": f"Bearer {os.environ['DOME_AGENT_KEY']}", "X-Dome-Act-As": end_user_oidc_token, } ``` Reuse the same headers for `tools/list` and `tools/call`. Discovery can include per-user credential advisories specific to that identity. ### Pass OIDC identity to OpenAI Create a client for the current user or pass the header per request: ```python theme={"system"} client = OpenAI( api_key=os.environ["DOME_AGENT_KEY"], base_url=f"{os.environ['DOME_GATEWAY_URL']}/v1", default_headers={"X-Dome-Act-As": end_user_oidc_token}, ) ``` Do not reuse a user-bound client across users. Pool clients by verified user only when token lifetime and isolation rules permit it. ### Pass OIDC identity to Anthropic Set the same header through the Anthropic client: ```python theme={"system"} client = Anthropic( api_key=os.environ["DOME_AGENT_KEY"], base_url=os.environ["DOME_GATEWAY_URL"], default_headers={"X-Dome-Act-As": end_user_oidc_token}, ) ``` The gateway keeps the agent and end user distinct. Agent authentication identifies the workload. Act-as evidence identifies the human represented by that workload. ### Use identity at runtime Verified claims populate `principal.act_as` for authorization and routing: * `principal.act_as.sub` * `principal.act_as.email` * `principal.act_as.roles` * `principal.act_as.groups` * `principal.act_as.claims.` Per-user connections key credentials by the verified `sub`. If the user has not connected a credential, follow the provisioning response in [Errors and denials](#errors-and-denials). The gateway forwards act-as identity upstream only when the connection explicitly configures an act-as-sourced header. Keep that egress choice in [Connect](/connect/resources/tools#change-egress-headers). ## Errors and denials Separate authentication failures, missing user credentials, authorization denials, and upstream failures. Each requires a different application response. | Status | Meaning | Application action | | ------ | ---------------------------------------------------------------------------------- | ------------------------------------------ | | `400` | Missing or malformed Gateway path, or invalid request | Fix the client URL or request | | `401` | Invalid agent credential, required act-as identity, or missing per-user credential | Reauthenticate or start user provisioning | | `403` | Dome policy denied the call | Surface the reason. Do not retry unchanged | | `404` | Requested tool, model, pool, or connection is unavailable | Refresh discovery or fix the target | | `429` | Rate or usage limit reached | Back off or wait for the configured window | | `5xx` | Gateway or upstream service failure | Retry with bounded exponential backoff | ### Agent-facing prompt protocol A missing per-user credential returns `401 Unauthorized` before an LLM stream starts. Give the returned `provision_url` to the end user, then retry after completion. Shared surfaces on every ingress: * **HTTP status:** `401 Unauthorized` * **Header:** `WWW-Authenticate: Bearer realm="dome", error="invalid_token", resource_metadata=""` * **Body:** a native permission error plus a `dome.credential_required` / `dome_credential_required` extension with `provision_url` and `expires_at` The URL appears in the message text, the extension, and `WWW-Authenticate` so HTTP-aware tooling, Dome-aware clients, and SDKs that only render `error.message` can all recover. Per-user LLM requests also need a verified `X-Dome-Act-As` identity. Without act-as, the gateway returns `401` with a plain permission error and does not mint a magic link. For MCP: * `tools/list` includes `_meta.dome.auth_required` advisories (one entry per unprovisioned backend). * `tools/call` sets `error.data.type` to `dome.credential_required`. For OpenAI- and Anthropic-shaped routes, the body includes a top-level `dome_credential_required` sibling with `connection`, `provision_url`, and `expires_at`. Do not mint or open the URL in a background service. Present it to the represented end user. Magic links are single-use and short-lived (10 minutes by default). If a link expires, the next call against the same connection mints a fresh one. ```json title="JSON-RPC error response" theme={"system"} { "jsonrpc": "2.0", "id": 1, "error": { "code": -32001, "message": "Per-user credential required for connection \"atlassian\". Provision your token at https://app.domesystems.ai/u/oauth/start?token=…", "data": { "type": "dome.credential_required", "provision_url": "https://app.domesystems.ai/u/oauth/start?token=…", "expires_at": "2026-04-29T18:30:00Z" } } } ``` ```json title="tools/list excerpt for a partially provisioned user" theme={"system"} { "tools": [], "_meta": { "dome": { "auth_required": [ { "backend_name": "atlassian", "provision_url": "https://app.domesystems.ai/u/oauth/start?token=…", "expires_at": "2026-04-29T18:30:00Z" } ] } } } ``` ```json title="OpenAI-flavored 401 body" theme={"system"} { "error": { "message": "Per-user credential required for connection \"openai-prod\". Provision your token at https://app.domesystems.ai/u/creds/start?token=…", "type": "permission_error", "code": "dome.credential_required" }, "dome_credential_required": { "connection": "openai-prod", "provision_url": "https://app.domesystems.ai/u/creds/start?token=…", "expires_at": "2026-04-29T18:30:00Z" } } ``` ```json title="Anthropic-flavored 401 body" theme={"system"} { "type": "error", "error": { "type": "permission_error", "message": "Per-user credential required for connection \"anthropic-prod\". Provision your token at https://app.domesystems.ai/u/creds/start?token=…" }, "dome_credential_required": { "connection": "anthropic-prod", "provision_url": "https://app.domesystems.ai/u/creds/start?token=…", "expires_at": "2026-04-29T18:30:00Z" } } ``` ### Authorization denial protocol When the gateway denies a request, it returns a structured `dome_authorization_denied` extension alongside the native error body. The extension explains which rule fired so you can surface feedback in agent UIs and alert on the deciding policy. * **MCP** — JSON-RPC `error.data`, with `error.data.type = "dome.authorization_denied"` * **LLM (OpenAI)** — top-level `dome_authorization_denied`, plus `error.code = "dome.authorization_denied"` * **LLM (Anthropic)** — top-level `dome_authorization_denied`. The Anthropic envelope has no `code` field Detect the deny on the **presence of the `dome_authorization_denied` sibling** (OpenAI/Anthropic) or `error.data.type == "dome.authorization_denied"` (MCP). Do not key off `error.code`. The Anthropic envelope omits it, and a client that branches on `code` silently misses every Anthropic denial. | Field | Type | Description | | -------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | | `reason_code` | string | Closed vocabulary. Today every Cedar / fail-closed deny resolves to `permission_denied`. Treat unknown values as a generic deny. | | `reason` | string | Verbatim human reason from the decision (for example `denied by rule: workspace/billing.policy0`). Secret-free by construction. | | `depth` | string | `deterministic` when Cedar decided the call. `judges` when The Court determined the outcome. | | `determining_policy` | string (optional) | The Cedar rule/policy id that fired an explicit `forbid`. Empty on a no-match default-deny or a Court determination. | | `court` | object (optional) | Present only when `depth == "judges"`. Court fields are on [Judges](/concepts/judges#gateway-denial-extension-court). | ```json title="JSON-RPC error response" theme={"system"} { "jsonrpc": "2.0", "id": 1, "error": { "code": -32001, "message": "denied by rule: workspace/billing.policy0", "data": { "type": "dome.authorization_denied", "reason_code": "permission_denied", "reason": "denied by rule: workspace/billing.policy0", "depth": "deterministic", "determining_policy": "workspace/billing.policy0" } } } ``` ```json title="OpenAI-flavored 403 body" theme={"system"} { "error": { "message": "denied by rule: workspace/billing.policy0", "type": "permission_error", "code": "dome.authorization_denied" }, "dome_authorization_denied": { "reason_code": "permission_denied", "reason": "denied by rule: workspace/billing.policy0", "depth": "deterministic", "determining_policy": "workspace/billing.policy0" } } ``` ```json title="Anthropic-flavored 403 body" theme={"system"} { "type": "error", "error": { "type": "permission_error", "message": "denied by rule: workspace/billing.policy0" }, "dome_authorization_denied": { "reason_code": "permission_denied", "reason": "denied by rule: workspace/billing.policy0", "depth": "deterministic", "determining_policy": "workspace/billing.policy0" } } ``` ```python theme={"system"} def explain_deny(body: dict) -> str | None: deny = body.get("dome_authorization_denied") if not deny: return None reason = deny.get("reason", "Request denied") policy = deny.get("determining_policy") return f"{reason} (policy: {policy})" if policy else reason ``` Allow responses carry no `dome_authorization_denied` field. Treat policy denials as final for the unchanged request. Show a safe reason to the user and record the request's activity or trace identifier. ### Handle streaming failures Authentication, act-as validation, per-user credential checks, and initial authorization run before the first model SSE event. These failures arrive as normal HTTP errors. After streaming begins: * Treat a broken connection as an incomplete response. * Do not assume the gateway retried another model. * Discard partial structured output unless your application validates it. * Retry only when the operation is safe and your client can prevent duplicates. Output filtering can buffer streamed text before release. Do not set client read timeouts so low that normal filter buffering appears as an outage. ### Retry safely Retry timeouts, connection failures, `429`, and transient `5xx` responses with bounded exponential backoff. Honor `Retry-After` when present. Do not automatically retry `400`, `401`, or `403`. Refresh an expired agent token, complete user provisioning, or change the denied request first. ## Next steps * [Connect](/connect) to attach resources, group them in Gateways, and configure routing * [Govern](/govern) to authorize actions, assign Guards, and set Quotas * [Stream Live Events](/operate/observe) to trace calls, denials, latency, and model usage * [Agent Identity](/concepts/agents/identity) concept for keys, tokens, and act-as claims in more depth # Guards Source: https://docs.domesystems.ai/govern/guards Inspect, redact, and constrain content on tool and model connections ## Overview Guards sit on Dome's content path and decide what of an authorized call's body may leave a backend or reach the agent. They attach to a model or tool connection and to a direction (`request` or `response`). When an assigned Guard cannot be evaluated, the gateway blocks that connection and direction. **Filters** are a Guard type. Model connections accept only `text` Filters. Tool connections accept only `json` Filters. The typical workflow is: 1. [Create a Filter](#create-a-filter) for the connection kind you need. 2. [Assign ordered Filter chains](#assign-ordered-filter-chains) on the connection and direction. 3. [Configure the streaming window](#configure-the-streaming-window) when model response Filters need more context. ## Requirements Before you begin: * Authenticate to Dome and select a workspace * Have at least one [model](/connect/resources/models) or [tool](/connect/resources/tools) connection to assign ### Permissions Guard Filter operations require platform permissions. Each operation restates its permission inline. | Default roles | Permission | Grants | | ------------------- | ---------------- | ---------------------------------------------------------------------- | | All workspace roles | `filters.view` | List Filters and assignments | | `admin`, `operator` | `filters.manage` | Create, update, roll back, or delete Filters and set assignment chains | | `admin`, `operator` | `gateway.manage` | Set a per-model filter window | | `admin`, `operator` | `config.manage` | Set the workspace streaming filter window floor | ## Filters Filters are a Guard type. Create and version Filters here, assign them to connections, and tune the streaming window used when response Filters inspect model output. ### Create a Filter Create a Filter to define its first active version. Text Filters use convenience flags. JSON Filters use a config file. Requires `filters.manage`. When more than one JSON action targets the same path, precedence is `BLOCK` > `OMIT` > `REDACT`. `REDACT` rewrites the matched span or value, `OMIT` removes a JSON field, and `BLOCK` withholds the whole message and stops the chain. ```bash title="Text Filter" theme={"system"} dome guards filters create pii-redact \ --description "Redact PII in model responses" \ --redact-ssn \ --redact-substring "internal-only" \ --block-substring "TOP-SECRET" ``` ```bash title="JSON Filter" theme={"system"} dome guards filters create tool-scrub \ --config-from ./tool-scrub.json ``` ```json title="tool-scrub.json" theme={"system"} { "json": { "components": [ { "field_actions": [ {"matcher": {"path": "ssn"}, "action": "FILTER_ACTION_OMIT"}, {"matcher": {"path": "email"}, "action": "FILTER_ACTION_REDACT"}, {"matcher": {"path": "card"}, "action": "FILTER_ACTION_BLOCK"} ] } ] } } ``` Convenience flags build at most two text components. Redact runs first, then block. Use `--config-from` for a JSON Filter or a multi-component text chain. Reference: [`dome guards filters create`](/cli/secure/guards#filters-create) Tool: `dome_guards_create_filter` ```json theme={"system"} { "name": "pii-redact", "description": "Redact PII in model responses", "redact_ssn": true, "redact_substrings": ["internal-only"], "block_substrings": ["TOP-SECRET"] } ``` Pass `config_json` with a protojson `dome.guards.v1.FilterConfig` to author a JSON Filter or a multi-component text chain. Convenience params only produce text Filters. Reference: [`dome_guards_create_filter`](/reference/mcp/guards#create-filter) ```text title="Create a Filter" theme={"system"} Create a text Filter named "pii-redact" that redacts SSNs and the substring "internal-only", and blocks on "TOP-SECRET". ``` ### Update a Filter Update Filter metadata in place, or pass any config flag to deploy a new active version. The previous version remains in history. Assigned connections follow the active version on the next gateway sync. Requires `filters.manage`. ```bash theme={"system"} dome guards filters update pii-redact \ --description "Redact PII and secrets in model responses" \ --redact-ssn \ --block-substring "TOP-SECRET" ``` Reference: [`dome guards filters update`](/cli/secure/guards#filters-update) Tool: `dome_guards_update_filter` ```json theme={"system"} { "filter": "pii-redact", "description": "Redact PII and secrets in model responses", "redact_ssn": true, "block_substrings": ["TOP-SECRET"] } ``` Reference: [`dome_guards_update_filter`](/reference/mcp/guards#update-filter) ### Roll back a Filter Roll a Filter back to an earlier version. The target version's config is copied forward into a new active version. History is never mutated. Requires `filters.manage`. ```bash theme={"system"} dome guards filters rollback pii-redact --to-version 1 ``` Reference: [`dome guards filters rollback`](/cli/secure/guards#filters-rollback) Tool: `dome_guards_rollback_filter` ```json theme={"system"} { "filter": "pii-redact", "to_version": 1 } ``` Reference: [`dome_guards_rollback_filter`](/reference/mcp/guards#rollback-filter) ### Delete a Filter Delete a Filter and its connection assignments. Corrupt Filters can still be addressed by UUID when their config fails to render. Requires `filters.manage`. Delete removes the Filter and every assignment that references it. ```bash theme={"system"} dome guards filters delete pii-redact ``` Reference: [`dome guards filters delete`](/cli/secure/guards#filters-delete) Tool: `dome_guards_delete_filter` ```json theme={"system"} { "filter": "pii-redact" } ``` Reference: [`dome_guards_delete_filter`](/reference/mcp/guards#delete-filter) ### Assign ordered Filter chains Each assignment binds Filters to a `(connection, direction)` slot. Setting a chain replaces the complete ordered list for that slot. Clear a chain with an empty filter list. Requires `filters.manage`. Model connections accept only `text` Filters. Tool connections accept only `json` Filters. ```bash title="Model connection" theme={"system"} dome model guards filters set claude-prod \ --direction response \ --filters pii-redact,secrets-block ``` ```bash title="Tool connection" theme={"system"} dome tool guards filters set github-mcp \ --direction request \ --filters tool-scrub ``` ```bash title="Clear a chain" theme={"system"} dome model guards filters set claude-prod \ --direction request \ --filters "" ``` Inspect assignments with: ```bash theme={"system"} dome model guards filters list dome tool guards filters list ``` Reference: [`dome model guards filters set`](/cli/secure/guards#model-guards-filters-set) · [`dome tool guards filters set`](/cli/secure/guards#tool-guards-filters-set) Tool: `dome_model_guards_filters_set` ```json theme={"system"} { "connection": "claude-prod", "direction": "response", "filters": ["pii-redact", "secrets-block"] } ``` Tool: `dome_tool_guards_filters_set` ```json theme={"system"} { "connection": "github-mcp", "direction": "request", "filters": ["tool-scrub"] } ``` Reference: [`dome_model_guards_filters_set`](/reference/mcp/guards#set-model-connection-filters) · [`dome_tool_guards_filters_set`](/reference/mcp/guards#set-tool-connection-filters) ```text title="Assign Filter chains" theme={"system"} Assign Filters "pii-redact" and "secrets-block" in that order to the response direction on model connection "claude-prod". ``` ### Configure the streaming window Configure how much streamed model output the gateway buffers before applying response Filters. Layering rules are on the [Guards](/concepts/controls/guards#streaming-model-responses) concept and [streaming window](/reference/controls/guards#streaming-window) reference. Requires `config.manage` for the workspace floor. Per-connection windows require `gateway.manage`. Per-request overrides are set by the calling agent in the request body. In the dashboard, set the workspace floor under **Settings → Config**. From the API: ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateWorkspaceLLMFilterWindow Content-Type: application/json { "tenant_id": "", "workspace_id": "", "llm_filter_window_bytes": 4096, "llm_filter_window_tokens": 1024 } ``` Reference: [`UpdateWorkspaceLLMFilterWindow`](/api/management/update-workspace-llm-filter-window) Agents can request a wider buffer for a single call by adding a `_dome` block to the OpenAI- or Anthropic-shaped body: ```json theme={"system"} { "model": "production", "messages": [{"role": "user", "content": "Summarize the notes."}], "_dome": {"filter_window_bytes": 8192, "filter_window_tokens": 64} } ``` The override is validated against the same bounds the workspace floor uses and is invisible to the upstream provider. Refer to [Models](/connect/resources/models) for the per-connection flags. ## Next steps * [Guards](/concepts/controls/guards) concept for how content inspection works * [Guards](/reference/controls/guards) reference for Filter kinds, actions, path syntax, and streaming layers * [Connect Tools](/connect/resources/tools) to add tool connections * [Models](/connect/resources/models) to set per-connection filter windows * [Authorize Access](/govern/rules) when a call should be allowed or denied before Guards run * [Architecture](/concepts/architecture#guards) concept for where Guards sit in the request path # Govern Source: https://docs.domesystems.ai/govern/index Authorize actions, filter content, and set usage limits for governed agent traffic **Govern** applies the controls that decide what agents can do, what content may pass, and how much spend is allowed. You authorize with Rules, inspect traffic with Guards, and cap model spend with Quotas. Refer to [Architecture](/concepts/architecture) concept for the request path. ## How Govern controls a request The following controls shape each governed request: * **Rules** make deterministic allow and deny decisions for an agent, action, and resource. They can read request arguments and verified end-user claims. * Use the [Rules assistant](/govern/rules/assistant) when you prefer natural-language drafts, and [simulate](/govern/rules/simulate) before changing production behavior. * **Guards** inspect request and response content on model and tool connections. Filters are a Guard type. * **Quotas** cap LLM spend over a window for a workspace, agent, Gateway, pool, model, or verified end user. Exhausted total caps reject the call. Exhausted model budgets can spill to another pool member. At runtime, Dome evaluates applicable Quotas, then Rules, then Guards on the content path. Each control fails closed: exhausted limits, unavailable authorization, and blocking Guards stop the request or response. The typical workflow is: 1. [Authorize access](/govern/rules) by writing, validating, simulating, and applying Cedar Rules. 2. Create [Guards](/govern/guards) and assign Filters to the connections that need inspection. 3. [Set usage limits](/govern/quotas) at the scopes that own the budget. After setup, you can revise Rules, roll Filters forward or back, and adjust Quotas without rewiring Connect entities. ## Next steps * [Connect](/connect) when you still need agents, delegated agents, resources, or Gateways * [Operate](/operate) to investigate audit events, stream live events, and subscribe to events * [Manage](/manage) to configure platform access, environments, integrations, and data exports * [Develop](/develop) to send runtime credentials, Gateway endpoints, and Act-As envelopes from the application # Quotas Source: https://docs.domesystems.ai/govern/quotas Cap governed LLM spend across workspaces, agents, Gateways, pools, and models ## Overview The gateway meters token usage on completed calls, prices it at per-model rates, updates spend for the window, and enforces every applicable Quota. Limit changes reach the gateway on the next config sync. Refer to [Quotas](/concepts/controls/quotas#when-a-quota-change-takes-effect) concept for how that differs from spend accrual. Quotas are workspace-scoped records. Each Quota has a subject, a USD limit, a reset window, an optional name, and an enabled flag. Subject identity is fixed at create time. Create is insert-only for each subject identity and window pair. Disabled Quotas remain listed but are not enforced. The typical workflow is: 1. [Set a Quota](#set-a-quota) for the subject that owns the budget. 2. Optionally [set a Gateway Quota](#set-a-gateway-quota) when spend should follow a gateway. 3. [Update](#update-a-quota), [list](#list-quotas), or [remove](#remove-a-quota) Quotas as budgets change. ## Requirements Before you begin: * Authenticate to Dome and select a workspace * Have the subject already configured (agent, model, pool, Gateway, or a subject verified via [Delegated agents](/connect/agents/delegated) as needed) * Use a plan that allows cost Quotas for the workspace. Cost Quotas are included on the **Pro** plan. Free workspaces ship with a create limit of zero. ### Permissions Quota operations require platform permissions. Each operation restates its permission inline. | Default roles | Permission | Grants | | ------------------- | ---------------- | -------------------------------- | | All workspace roles | `gateway.view` | List Quotas | | `admin`, `operator` | `gateway.manage` | Create, update, or delete Quotas | ## Set a Quota Create a USD spend cap for a workspace, agent, act-as subject, pool, model, or Gateway (`gateway`). Subject identity cannot change after create. Pass `--disabled` / `disabled` to create a Quota that is stored but not enforced until you enable it. Windows are `daily` (UTC midnight) or `monthly` (UTC month start, default `monthly`). Exhaustion behavior and event names are on the [Quotas](/reference/controls/quotas) reference. Requires `gateway.manage`. In the dashboard, open **Models → Cost quotas**, choose the subject and target, set the USD limit and window, then save. Model Quotas can cover a model globally or only within a selected pool, with an optional per-caller toggle. ```bash title="Workspace monthly limit" theme={"system"} dome model quota set \ --subject workspace \ --limit 5000 \ --window monthly \ --name workspace-monthly ``` ```bash title="Agent daily limit" theme={"system"} dome model quota set \ --subject agent \ --agent \ --limit 25 \ --window daily ``` ```bash title="Pool monthly limit" theme={"system"} dome model quota set \ --subject pool \ --pool production \ --limit 2500 \ --window monthly ``` ```bash title="Model budget within a pool" theme={"system"} dome model quota set \ --subject model \ --model claude-sonnet \ --pool production \ --limit 1000 \ --window monthly ``` | Flag | Required | Description | | -------------- | :---------: | ---------------------------------------------------------------------- | | `--subject` | Yes | `workspace`, `agent`, `act-as`, `pool`, `model`, or `gateway` | | `--limit` | Yes | Cap in US dollars | | `--window` | No | `daily` or `monthly` (default `monthly`) | | `--name` | No | Human-readable label | | `--agent` | Conditional | Agent id when `--subject agent` | | `--act-as` | Conditional | Verified end-user OIDC `sub` when `--subject act-as` | | `--pool` | Conditional | Pool name when `--subject pool`, or to scope a model Quota to one pool | | `--model` | Conditional | Model connection name when `--subject model` | | `--gateway` | Conditional | Gateway name or id when `--subject gateway` | | `--per-caller` | No | Model only: independent budget per agent and verified end user | | `--disabled` | No | Create without enforcement until enabled | Reference: [`dome model quota set`](/cli/connect/models#quota-set) Tool: `dome_model_quota_set` ```json theme={"system"} { "subject": "model", "model": "claude-sonnet", "pool": "production", "limit_usd": 1000, "window": "monthly" } ``` Reference: [`dome_model_quota_set`](/reference/mcp/models#dome_model_quota_set) ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateLLMQuota Content-Type: application/json { "workspace_id": "", "subject_type": "model", "subject_id": "", "pool_id": "", "limit_micros": 1000000000, "window": "monthly", "name": "sonnet-in-production" } ``` Limits are micro-USD on the API (`1000000000` is \$1,000.00). `scope` defaults to `total`. Set `per_caller` for per-caller model Quotas. For `act_as`, set `subject_key` to the OIDC `sub` instead of `subject_id`. Reference: [`CreateLLMQuota`](/api/management/create-llm-quota) ```text title="Set a model Quota" theme={"system"} Create a monthly \$1000 Quota on model "claude-sonnet" within pool "production". ``` ### Set an Act-As Quota Create a Quota for one verified end-user subject. The Quota applies only when [Delegated agents](/connect/agents/delegated) verification has confirmed that subject on the call. ```bash theme={"system"} dome model quota set \ --subject act-as \ --act-as user-123 \ --limit 10 \ --window daily ``` ### Set a per-caller model Quota Create a model Quota that tracks each agent and each verified end user independently instead of combining their spend. ```bash theme={"system"} dome model quota set \ --subject model \ --model gpt-4o \ --per-caller \ --limit 5 \ --window daily ``` ## Set a Gateway Quota Create a Quota that covers all LLM calls routed through a Gateway's model pools and direct model connections. This is the Gateway-first spelling of the same `gateway` subject available on `dome model quota set`. Requires `gateway.manage`. ```bash theme={"system"} dome gateway quota set prod-tools \ --limit 500 \ --window monthly \ --name prod-monthly ``` Reference: [`dome gateway quota set`](/cli/connect/gateway#quota-set) Tool: `dome_gateway_quota_set` ```json theme={"system"} { "gateway": "prod-tools", "limit_usd": 500, "window": "monthly", "name": "prod-monthly" } ``` Reference: [`dome_gateway_quota_set`](/reference/mcp/gateway#dome_gateway_quota_set) ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateLLMQuota Content-Type: application/json { "workspace_id": "", "subject_type": "gateway", "subject_id": "", "limit_micros": 500000000, "window": "monthly", "name": "prod-monthly" } ``` Reference: [`CreateLLMQuota`](/api/management/create-llm-quota) ```text title="Set a Gateway Quota" theme={"system"} Create a monthly \$500 Quota on Gateway "prod-tools" named "prod-monthly". ``` ## Update a Quota Update a Quota's name, USD limit, window, or enabled state. Subject identity cannot change. Create a new Quota to cover a different subject. Requires `gateway.manage`. Only the fields you pass change. Disable a Quota to pause enforcement without deleting its definition or history. ```bash theme={"system"} dome model quota update \ --limit 7500 \ --enabled true ``` For Gateway Quotas, `dome gateway quota update` accepts the same mutable fields and refuses non-Gateway Quota ids. Reference: [`dome model quota`](/cli/connect/models#quota-list) Tool: `dome_model_quota_update` ```json theme={"system"} { "quota_id": "", "limit_usd": 7500, "enabled": true } ``` Use `dome_gateway_quota_update` for Gateway Quotas. It refuses ids that belong to other subjects. Reference: [`dome_model_quota_update`](/reference/mcp/models#cost-quotas) ```http theme={"system"} POST /dome.mgmt.v1.Management/UpdateLLMQuota Content-Type: application/json { "workspace_id": "", "quota_id": "", "limit_micros": 7500000000, "enabled": true } ``` Omitted optional fields keep their current values. Reference: [`UpdateLLMQuota`](/api/management/update-llm-quota) ## List Quotas List Quotas in the workspace, including disabled ones. Listing returns each Quota's id, subject, USD limit, window, and enabled state. Requires `gateway.view`. ```bash theme={"system"} dome model quota list ``` ```bash theme={"system"} dome gateway quota list [gateway] ``` The Gateway list can filter to one gateway. It is a client-side filter over the workspace list. Reference: [`dome model quota list`](/cli/connect/models#quota-list) · [`dome gateway quota list`](/cli/connect/gateway#quota-list) Tool: `dome_model_quota_list` ```json theme={"system"} { "name": "dome_model_quota_list", "arguments": {} } ``` Tool: `dome_gateway_quota_list` ```json theme={"system"} { "gateway": "prod-tools" } ``` Reference: [`dome_model_quota_list`](/reference/mcp/models#dome_model_quota_list) · [`dome_gateway_quota_list`](/reference/mcp/gateway#dome_gateway_quota_list) ```http theme={"system"} POST /dome.mgmt.v1.Management/ListLLMQuotas Content-Type: application/json { "workspace_id": "" } ``` Reference: [`ListLLMQuotas`](/api/management/list-llm-quotas) ## Remove a Quota Remove a Quota by id. Listing first is the usual way to obtain the id. Requires `gateway.manage`. Remove deletes the Quota definition. Disable the Quota instead when you need to pause enforcement and keep the record. ```bash theme={"system"} dome model quota rm ``` ```bash theme={"system"} dome gateway quota rm ``` The Gateway remove command refuses ids that belong to other subjects. Reference: [`dome model quota rm`](/cli/connect/models#quota-remove) · [`dome gateway quota rm`](/cli/connect/gateway#quota-remove) Tool: `dome_model_quota_remove` ```json theme={"system"} { "quota_id": "" } ``` Tool: `dome_gateway_quota_remove` ```json theme={"system"} { "quota_id": "" } ``` Reference: [`dome_model_quota_remove`](/reference/mcp/models#dome_model_quota_remove) · [`dome_gateway_quota_remove`](/reference/mcp/gateway#dome_gateway_quota_remove) ```http theme={"system"} POST /dome.mgmt.v1.Management/DeleteLLMQuota Content-Type: application/json { "workspace_id": "", "quota_id": "" } ``` Reference: [`DeleteLLMQuota`](/api/management/delete-llm-quota) ## Next steps * [Quotas](/concepts/controls/quotas) concept for how spend caps work * [Quotas](/reference/controls/quotas) reference for subjects, windows, and event names * [Pools](/connect/resources/models/pools) for routing strategies and failover * [Delegated agents](/connect/agents/delegated) so Act-As Quotas match live verified subjects * [Models](/connect/resources/models) for the models and pools a Quota can target # Rules Source: https://docs.domesystems.ai/govern/rules Control which tools and models each agent is allowed to use Rules decide which tool and model requests each agent is allowed to make. Dome evaluates them on every governed request, so you can grant, restrict, or deny access down to the specific action and condition. ## Overview Rules ship as scoped **bundles** of `.cedar` files. The gateway merges every active bundle that covers the agent and evaluates the request against that set. The typical workflow is: 1. [Write Rules](#write-rules) manually or with the [Rules assistant](/govern/rules/assistant). 2. [Validate the Rules](#validate-rules) for Cedar errors and advisory warnings. 3. [Simulate representative decisions](#simulate-rules) before changing production behavior. 4. [Apply the Rules](#apply-rules) to activate the change. 5. [Inspect the Rules that apply to an agent](#show-effective-rules) and [roll back](#roll-back-rules) if the result is not expected. ## Requirements Before you begin: * Authenticate to Dome and select a workspace * Register at least one [agent](/connect/agents) * Identify the tools or models the Rules will govern * Choose the workspace or agent scope where the Rules should apply ### Permissions Rule bundle operations require platform permissions. Each operation states its required permission inline. | Default roles | Permission | Grants | | ------------------------------- | ---------------- | ------------------------ | | All workspace roles | `rules.view` | View bundles and history | | `admin`, `operator`, `security` | `rules.deploy` | Deploy Rules | | `admin`, `operator`, `security` | `rules.rollback` | Roll back a Rule bundle | ## Write Rules Write each authorization requirement as a sentence before translating it to Cedar. For example, “Allow this agent to call the `github/list_issues` tool.” You can also use the [Rules assistant](/govern/rules/assistant) to translate natural-language requirements into a draft. Assistant drafts never deploy automatically. Writing local files requires no Dome permission. Using the Rules assistant to draft or apply changes requires `rules.deploy`. Map the sentence to a Rule in five steps. 1. Choose `permit` for an allowed request or `forbid` for a denied request. 2. Set the principal to the agent's UUID or use a broader form when the Rule should cover every agent in scope. 3. Choose the action that represents the operation. 4. Set the resource to the specific tool or model, or use a resource type when the Rule should cover every resource of that type. 5. Add a `when` condition if the Rule should apply only under certain circumstances or add `unless` to define an exception. Inside the parentheses, name `principal`, `action`, and `resource`. Omit a constraint to match every value. Use `==` for one entity, `is` for every entity of a type, and `in` for several actions. Common actions are `mcp:call`, `mcp:discover`, and LLM actions such as `llm:invoke`, `llm:embed`, and `llm:moderate`. Connection attributes appear as `resource.`. Per-call values appear as `resource.arguments.` after `resource has arguments`. The full action catalog and attribute tables are on the [Rules](/reference/controls/rules) reference. ### Permit a tool call Replace `AGENT_ID` with the registered agent's UUID. The tool resource uses `CONNECTION_NAME/TOOL_NAME`. ```cedar title="rules.cedar" theme={"system"} permit( principal == Dome::Agent::"AGENT_ID", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"github/list_issues" ); ``` Discovery is a different action. A Rule that only permits `mcp:discover` does not allow tool calls. ```cedar title="discover.cedar" theme={"system"} permit( principal, action == Dome::Action::"mcp:discover", resource ); ``` ### Forbid with an exception Deny a sensitive tool for everyone, then carve out agents that declare a capability. ```cedar title="restrict-deploy.cedar" theme={"system"} forbid( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"deployment/production-deploy" ) unless { principal.capabilities.contains("deploy-production") }; ``` ### Condition on attributes or arguments Gate an LLM call on a connection attribute (`resource.`) or a per-call argument (`resource.arguments.`). Guard argument reads with `resource has arguments` first. Dereferencing a missing argument errors the Rule out. ```cedar title="attribute-and-args.cedar" theme={"system"} permit( principal, action == Dome::Action::"llm:embed", resource is Dome::LLMModel ) when { resource.pii_certified == true }; forbid( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"vector-search" ) when { resource has arguments && resource.arguments has query && resource.arguments.query like "*Atlas*" }; ``` A `.cedar` file can contain multiple Rules. Include every user-authored file that should remain active at the target scope because [Apply Rules](#apply-rules) replaces the current user-authored bundle. After writing the files, [validate the Rules](#validate-rules) before simulation or deployment. ## Validate Rules Validate one or more Cedar files without deploying them. Validation returns blocking Cedar errors and advisory warnings for resource references. Scoped validation and the MCP tool require `rules.view`. Scope-less CLI and API validation require authentication. ```bash theme={"system"} dome rules validate rules.cedar ``` Add `--agent data-pipeline` to check tool references against that agent's workspace catalog. Without `--agent`, the CLI checks Cedar syntax and semantics only. Reference: [`dome rules validate`](/cli/secure/rules#validate) Tool: `dome_rules_validate` ```json theme={"system"} { "files": [ { "name": "rules.cedar", "content": "permit(principal == Dome::Agent::\"data-pipeline\", action == Dome::Action::\"mcp:call\", resource == Dome::MCPTool::\"database-query\");" } ] } ``` Reference: [`dome_rules_validate`](/reference/mcp/rules#rules-validate) ```http theme={"system"} POST /dome.authz.v1.Authorization/ValidateRules Content-Type: application/json { "files": [ { "name": "rules.cedar", "content": "permit(principal == Dome::Agent::\"data-pipeline\", action == Dome::Action::\"mcp:call\", resource == Dome::MCPTool::\"database-query\");" } ], "scope_kind": "workspace", "scope_id": "{{WORKSPACE_ID}}" } ``` Include a workspace or agent scope to check catalog references. Scope-less validation checks Cedar syntax and semantics. Reference: [`ValidateRules`](/api/authorization/validate-rules) ```text title="Validate Rules" theme={"system"} Validate rules.cedar for the active workspace and explain every error or warning. ``` ## Simulate Rules Simulate an authorization decision against the Rules currently in effect without performing the requested action. Supply the agent, action, resource, and any request context the Rules inspect. Requires `rules.view`. ```bash theme={"system"} dome rules simulate \ --agent data-pipeline \ --action mcp:call \ --resource database-query \ --resource-type mcp_tool ``` Reference: [`dome rules simulate`](/cli/secure/rules#simulate) Tool: `dome_rules_simulate` ```json theme={"system"} { "agent_id": "data-pipeline", "action": "mcp:call", "resource": "database-query", "resource_type": "mcp_tool" } ``` Reference: [`dome_rules_simulate`](/reference/mcp/rules#rules-simulate) ```http theme={"system"} POST /dome.authz.v1.Authorization/Evaluate Content-Type: application/json { "caller": { "agent_id": "{{AGENT_ID}}" }, "action": "mcp:call", "resource": "database-query", "resource_type": "mcp_tool", "workspace_id": "{{WORKSPACE_ID}}" } ``` `workspace_id` selects simulation mode and loads that workspace's effective Rules. Reference: [`Evaluate`](/api/authorization/evaluate) ```text title="Simulate Rules" theme={"system"} Simulate whether "data-pipeline" may call the "database-query" MCP tool. ``` Refer to [Simulate Rules](/govern/rules/simulate) for request arguments, Agent Act-As claims, and historical replay. ## Apply Rules Apply one or more Cedar files to replace the active user-authored bundle at a target scope. Each successful apply creates a bundle with a new sequence number and content hash. Requires `rules.deploy`. Applying Rules replaces the active user-authored bundle at the selected scope. Include every user-authored file that should remain active. Dome also keeps generated bundles (allowed resources, Act-As, blocked tools, and similar). Change those through the feature that created them, not by applying over them. ```bash theme={"system"} dome rules apply rules.cedar --name "production-v2" ``` The active workspace is the default scope. Add `--agent data-pipeline` to apply the bundle to one agent instead. The CLI preserves managed `tool-*.cedar` files when they are omitted. Advisory warnings identify MCP tool references that do not match the workspace catalog. Warnings do not block the apply. Reference: [`dome rules apply`](/cli/secure/rules#apply) Tool: `dome_rules_deploy` ```json theme={"system"} { "files": [ { "name": "rules.cedar", "content": "permit(principal == Dome::Agent::\"data-pipeline\", action == Dome::Action::\"mcp:call\", resource == Dome::MCPTool::\"database-query\");" } ], "name": "production-v2", "scope_kind": "workspace", "scope_id": "{{WORKSPACE_ID}}" } ``` The tool defaults to the active workspace when you omit the scope. The tool preserves managed `tool-*.cedar` files and returns advisory validation warnings without blocking the apply. Reference: [`dome_rules_deploy`](/reference/mcp/rules#rules-deploy) ```http theme={"system"} POST /dome.authz.v1.Authorization/DeployBundle Content-Type: application/json { "files": [ { "name": "rules.cedar", "content": "permit(principal == Dome::Agent::\"data-pipeline\", action == Dome::Action::\"mcp:call\", resource == Dome::MCPTool::\"database-query\");" } ], "scope_kind": "workspace", "scope_id": "{{WORKSPACE_ID}}", "name": "production-v2" } ``` The API requires `scope_kind` and `scope_id`. Use `org`, `tenant`, `workspace`, or `agent`. An agent scope also requires `workspace_id`. Reference: [`DeployBundle`](/api/authorization/deploy-bundle) ```text title="Apply Rules" theme={"system"} Apply rules.cedar to the active workspace as a bundle named "production-v2". ``` After applying Rules, [show the effective Rules](#show-effective-rules) to confirm that the new bundle contributes to the expected agents. Gateways pick up the change through [effective Rules caching](/concepts/architecture/authorization-model#effective-policy). Allow time for the next sync before testing requests. ## Show effective Rules Show the Rules that apply to an agent after Dome assembles organization, tenant, workspace, agent, and generated bundles. The result includes a content hash for gateway synchronization and the contributing bundles that formed the [effective Rules](/concepts/architecture/authorization-model#effective-policy). Requires `rules.view`. ```bash theme={"system"} dome rules show --agent data-pipeline ``` Omit `--agent` to show the effective Rules for every agent in the active workspace. Reference: [`dome rules show`](/cli/secure/rules#show) ```http theme={"system"} POST /dome.authz.v1.Authorization/GetAgentEffectivePolicy Content-Type: application/json { "workspace_id": "{{WORKSPACE_ID}}", "agent_id": "{{AGENT_ID}}" } ``` The API uses `policy` in this endpoint name and response schema. The returned `effective_policy` is the assembled effective Rule set. Reference: [`GetAgentEffectivePolicy`](/api/authorization/get-agent-effective-policy) ```text title="Show effective Rules" theme={"system"} Show every Rule that applies to "data-pipeline" and identify the contributing bundles. ``` ## Get active Rules at a scope Get the currently active user-authored Rules at one scope when you need that scope's files rather than the assembled [effective Rules](#show-effective-rules). Requires `rules.view`. Tool: `dome_rules_get_active` ```json theme={"system"} { "scope_kind": "workspace", "scope_id": "{{WORKSPACE_ID}}" } ``` Reference: [`dome_rules_get_active`](/reference/mcp/rules#rules-get-active) ```http theme={"system"} POST /dome.authz.v1.Authorization/GetBundle Content-Type: application/json { "scope_kind": "workspace", "scope_id": "{{WORKSPACE_ID}}" } ``` Reference: [`GetBundle`](/api/authorization/get-bundle) ```text title="Get active Rules at a scope" theme={"system"} Get the active user-authored Rules for the current workspace. ``` ## List Rule history List deployment history at a scope to retrieve IDs, names, sequence numbers, timestamps, and active status. Use an ID when you [roll back Rules](#roll-back-rules). Requires `rules.view`. ```bash theme={"system"} dome rules list --limit 20 ``` Add `--agent data-pipeline` to list the history for one agent. Reference: [`dome rules list`](/cli/secure/rules#list) Tool: `dome_rules_list_versions` ```json theme={"system"} { "scope_kind": "workspace", "scope_id": "{{WORKSPACE_ID}}", "limit": 20 } ``` Reference: [`dome_rules_list_versions`](/reference/mcp/rules#rules-list-versions) ```http theme={"system"} POST /dome.authz.v1.Authorization/ListBundles Content-Type: application/json { "scope_kind": "workspace", "scope_id": "{{WORKSPACE_ID}}", "limit": 20 } ``` Reference: [`ListBundles`](/api/authorization/list-bundles) ```text title="List Rule history" theme={"system"} List the 20 most recent Rule deployments for the active workspace. ``` ## Roll back Rules Roll back to a historical user-authored bundle when a deployment produces an unexpected authorization result. Rollback creates a new bundle from the selected historical content and preserves the original deployment. Requires `rules.rollback`. Rollback changes the active user-authored Rules at the bundle's stored scope. You cannot roll back the currently active bundle or a system-generated bundle. ```bash theme={"system"} dome rules rollback {{BUNDLE_ID}} ``` The bundle ID identifies its scope, so no scope flag is required. Reference: [`dome rules rollback`](/cli/secure/rules#rollback) Tool: `dome_rules_rollback` ```json theme={"system"} { "bundle_id": "{{BUNDLE_ID}}" } ``` Reference: [`dome_rules_rollback`](/reference/mcp/rules#rules-rollback) ```http theme={"system"} POST /dome.authz.v1.Authorization/RollbackBundle Content-Type: application/json { "bundle_id": "{{BUNDLE_ID}}" } ``` Reference: [`RollbackBundle`](/api/authorization/rollback-bundle) ```text title="Roll back Rules" theme={"system"} Roll back to Rule bundle "{{BUNDLE_ID}}" and confirm the new active bundle. ``` After rollback, [show the effective Rules](#show-effective-rules) to confirm that the restored content contributes at the expected scope. ## Delete Rules Delete the active user-authored Rules at a scope when that scope should no longer contribute custom Rules. Deletion preserves bundle history for later inspection. Requires `rules.deploy`. Deleting Rules removes the active authorization contribution from the selected scope. Broader, narrower, and system-generated Rules can still apply. ```http theme={"system"} POST /dome.authz.v1.Authorization/DeleteRules Content-Type: application/json { "scope_kind": "workspace", "scope_id": "{{WORKSPACE_ID}}" } ``` Reference: [`DeleteRules`](/api/authorization/delete-rules) ```text title="Delete Rules" theme={"system"} Delete the active user-authored Rules from the current workspace while preserving bundle history. ``` After deletion, [list Rule history](#list-rule-history) and [show effective Rules](#show-effective-rules) to confirm the resulting authorization state. ## Generate starter Rules `dome rules generate` is deprecated. It writes a static discovery permit and a commented tool-call example, and `--from-tools` does not change the output. Prefer [writing Rules](#write-rules) or the [Rules assistant](/govern/rules/assistant). Generate a static Cedar template with one active Rule that lets every agent discover available tools. The template also includes a commented example for permitting calls to one MCP tool. The generated Rules do not permit tool calls, inspect registered tools, or change active Rules. The `--from-tools` flag produces the same static template. The MCP tool requires `rules.view`. CLI generation runs locally. ```bash theme={"system"} dome rules generate --output starter.cedar ``` Omit `--output` to print the generated Rules to standard output. Reference: [`dome rules generate`](/cli/secure/rules#generate) Tool: `dome_rules_generate` ```json theme={"system"} {} ``` Reference: [`dome_rules_generate`](/reference/mcp/rules#rules-generate) ```text title="Generate starter Rules" theme={"system"} Generate a starter Cedar template with a tool-discovery permit and a commented example for permitting a tool call. ``` Validate and simulate the generated file before applying it. ## Next steps * [Rules](/concepts/controls/rules) concept for how authorization decisions work * [Rules](/reference/controls/rules) reference for actions and attributes * [Draft with the Rules assistant](/govern/rules/assistant) when you prefer natural-language drafts * [Simulate Rules](/govern/rules/simulate) to probe decisions and replay history * [Authorization model](/concepts/architecture/authorization-model) concept for evaluation semantics # Rules assistant Source: https://docs.domesystems.ai/govern/rules/assistant Draft, validate, and refine Dome Rules from the dashboard with an LLM-powered assistant The Rules assistant drafts and refines Dome authorization Rules from natural language in the dashboard Rules editor. Dome validates each draft and routes assistant calls through the Model Broker like any other agent. ## Overview The assistant runs as a Dome agent in the workspace. 1. Enable the assistant on the workspace. Dome registers a `cedar-assistant` agent and provisions its key. 2. Open the Rules editor. The chat thread lives in the right rail and shares context with the editor, including open files, agents, tools, and models. 3. Send a prompt. Dome calls the model through the Dome Model Broker, validates each draft, and surfaces it as a draft you can apply. 4. Apply or discard. Applied drafts land in the editor with changed lines highlighted. Deploy through the normal [Apply Rules](/govern/rules#apply-rules) flow. Drafts are never deployed automatically. The assistant proposes. You apply and deploy. ### Editor scope The assistant grounds every draft to the bundle you have open. | Editor scope | What the assistant proposes | | ------------ | -------------------------------------------------------------------------------------------------------------------------------- | | Workspace | Rules in the workspace bundle. Tightening a single agent's access surfaces a suggestion to move the Rule to that agent's bundle. | | Agent | Rules in that agent's bundle only. The model refuses to author cross-agent Rules from this scope. | Switch scope by opening the agent's Rules tab in the editor. The chat picks up the new scope on the next message. ### Limits The assistant enforces these limits. * **Per-workspace rate limit** on chat messages and draft requests. Bursts beyond the limit surface as a `rate_limited` error in the chat. * **No tool-call replay across turns.** The assistant sees current workspace state (open files, agents, tools, models) on every turn but not its own prior tool calls. * **Limited chat markdown.** Bold, inline code, and `cedar`-fenced code blocks render in chat. ## Requirements Before you begin, authenticate to Dome and select a workspace. ### Permissions Assistant settings, chat, and draft apply use platform permissions. The same permissions apply across the dashboard, CLI, and MCP. Each operation restates its permission inline. | Default roles | Permission | Grants | | ------------------------------- | --------------- | -------------------------------------------------- | | `admin`, `operator` | `config.manage` | Enable, disable, purge, or edit assistant settings | | `admin`, `operator`, `security` | `rules.deploy` | Chat and apply drafts | | All workspace roles | `rules.view` | View threads and messages or read settings | ## Enable the assistant Enable the Rules assistant for the workspace from **Settings → Assistant**. Enabling provisions the `cedar-assistant` agent and key. The assistant then becomes available in the Rules editor for every user with `rules.deploy`. Requires `config.manage`. ## Disable the assistant Disable the assistant from **Settings → Assistant** when you want to suspend the integration without deleting state. Re-enable to resume with the same threads and history. Requires `config.manage`. ## Purge the assistant Purge the assistant from **Settings → Assistant** when you need to remove it completely. Purge deletes the agent, key, threads, and messages for this workspace. Use purge before re-onboarding a workspace from scratch. Requires `config.manage`. Purge is destructive. It deletes the agent, key, threads, and messages for this workspace. ## Configure context Configure which workspace context each message ships to the model. Toggle individual categories off to keep that data out of every prompt. Requires `config.manage`. | Toggle | Sends to the model | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Editor file contents | The Rule files currently open in the editor. | | Agents catalog | Agent names and IDs. | | Tools catalog | MCP tool names and IDs. | | Models catalog | LLM connection names and IDs. | | Schema attribute keys | The custom attribute keys your workspace contributes to authorization entities. | | Rule deployment history | Recent Rule-bundle deployments for the workspace and the agent the editor is open on, so the assistant can reason about what changed and roll-back history. | ## Govern assistant traffic Govern the assistant the way you govern any other agent. Every request carries the metadata key `cedar_asst=true`, so workspace-scope Rules can target it without enumerating per-workspace agent IDs. ```cedar title="restrict-rules-assistant.cedar" theme={"system"} // Limit the assistant to a single approved model forbid( principal, action == Dome::Action::"llm:invoke", resource is Dome::LLMModel ) when { principal.metadata.cedar_asst == "true" } unless { resource.name == "anthropic-claude-sonnet" }; ``` The same pattern applies to model lists, embeddings, and any other action. Every call the assistant makes is a normal gateway request you can shape with Rules. ## Chat behavior The chat surfaces draft work and diagnostics while you work. * **Streaming responses.** Text streams token-by-token. If no tokens arrive for 60 seconds, the chat surfaces a "Stream stalled" error. * **Tool chips.** Each Rule validation and draft proposal appears as a chip with a spinner while it runs. Click a chip to inspect inputs and results. * **Stop.** Cancels the in-flight response and clears any running chips. * **Validation diagnostics.** Failures auto-expand with `file:line` and a message. After three consecutive validation failures the assistant pauses and asks for a draft instead. Empty messages return a prompt to type something first. * **Drafts.** Render inline in the chat. Apply moves the files into the editor with changed lines highlighted. The highlight clears on a successful deploy. * **Thread titles.** The assistant sets a title on the first turn. ## Next steps * [Write and apply Rules](/govern/rules#write-rules) through the normal Rule lifecycle * [Simulate Rules](/govern/rules/simulate) before changing production behavior * [Authorization model](/concepts/architecture/authorization-model) concept for entity attributes a draft may reference # Simulate rules Source: https://docs.domesystems.ai/govern/rules/simulate Test Rules against real and synthetic scenarios before they reach production ## Overview Simulation takes candidate Rules and replays them against recorded authorization decisions from your audit log. For each historical event, it compares the original decision with the decision the candidate Rules would produce. The output is a set of DecisionDiffs: entries where the allow/deny outcome differs between the current Rules and the candidate Rules. Use simulation to answer questions like: * Would this new `forbid` have blocked any requests that were previously allowed? * Does this `permit` open access to anything that was previously denied? You can also probe a single synthetic request against the active effective Rules without performing the action. That path returns the decision and the Rule that determined it. Use it when you want a fast check for one agent, action, and resource before you look at historical diffs. Simulation sits between writing Rules and deploying them. Refer to [Authorize Access](/govern/rules) for deploy and rollback. The typical workflow is: 1. [Run a simulation](#run-a-simulation) for one agent, action, and resource against the Rules in effect. 2. Optionally [simulate with Agent Act-As](#simulate-with-agent-act-as) or [simulate with arguments](#simulate-with-arguments). 3. [Read results](#read-results) for historical DecisionDiffs when you replay candidate Rules against audit history. ## Requirements Before you begin: * Authenticate to Dome and select a workspace * Have an active Rule bundle * For historical replay, have authorization events available in the audit log ### Permissions Simulation and DecisionDiff reads require `rules.view`. All workspace roles hold it. | Default roles | Permission | Grants | | ------------------- | ------------ | --------------------------------------------- | | All workspace roles | `rules.view` | Run simulations and read DecisionDiff results | ## Run a simulation Simulate an authorization decision for one agent, action, and resource against the Rules currently in effect. Requires `rules.view`. Write or refine the Cedar Rules you want to test before you deploy them: ```cedar title="candidate-rules.cedar" theme={"system"} permit( principal == Dome::Agent::"data-pipeline", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"database-query" ); ``` Then run the simulation with the caller, action, and resource you care about: ```bash theme={"system"} dome rules simulate \ --agent data-pipeline \ --action mcp:call \ --resource database-query \ --resource-type mcp_tool ``` | Flag | Type | Default | Description | | ------------------ | ----------- | ------------ | --------------------------------------------------------------------------------------------- | | `--agent` | string | **Required** | Agent name or ID to simulate as the caller | | `--action` | string | **Required** | Action to evaluate (for example `mcp:call`) | | `--resource` | string | **Required** | Resource identifier (for example `database-query`) | | `--resource-type` | string | `mcp_tool` | `mcp_tool`, `llm_model`, or `resource` | | `--eval-context` | key=value | — | Context key=value pair (repeatable) | | `--eval-arguments` | JSON object | — | Per-call arguments surfaced as `resource.arguments.` (for example `'{"query":"Atlas"}'`) | | `--actas-sub` | string | — | Act-as subject identifier | | `--actas-email` | string | — | Act-as email | | `--actas-roles` | string | — | Act-as roles (comma-separated) | | `--actas-groups` | string | — | Act-as groups (comma-separated) | Reference: [`dome rules simulate`](/cli/secure/rules#simulate) Tool: `dome_rules_simulate` ```json theme={"system"} { "agent_id": "data-pipeline", "action": "mcp:call", "resource": "database-query", "resource_type": "mcp_tool" } ``` | Param | Type | Default | Description | | --------------- | ------ | ------------ | ---------------------------------------------------------- | | `agent_id` | string | **Required** | Agent ID or name (names are resolved to UUIDs by the tool) | | `action` | string | **Required** | Action to evaluate | | `resource` | string | **Required** | Resource identifier | | `resource_type` | string | **Required** | `mcp_tool`, `llm_model`, or `resource` | | `context` | object | — | Additional context key-value pairs | | `actas_sub` | string | — | Act-as subject identifier | | `actas_email` | string | — | Act-as email | | `actas_roles` | string | — | Act-as roles (comma-separated) | | `actas_groups` | string | — | Act-as groups (comma-separated) | `dome_rules_simulate` calls `Authorization.Evaluate` synchronously and returns the decision immediately. The separate `dome_rules_simulate_result` tool polls the historical-replay Simulation service by simulation ID. Reference: [`dome_rules_simulate`](/reference/mcp/rules#rules-simulate) ```http theme={"system"} POST /dome.authz.v1.Authorization/Evaluate Content-Type: application/json { "caller": { "agent_id": "" }, "action": "mcp:call", "resource": "database-query", "resource_type": "mcp_tool", "workspace_id": "" } ``` | Field | Type | Default | Description | | ----------------- | ------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `caller.agent_id` | string | **Required** | Agent UUID | | `action` | string | **Required** | Action to evaluate | | `resource` | string | **Required** | Resource identifier | | `resource_type` | string | **Required** | `mcp_tool`, `llm_model`, or `resource` (lowercase string) | | `workspace_id` | string | **Required** (simulation) | When set, the API server fetches the workspace effective Rules and evaluates against them (simulation mode). When empty, the pre-loaded global evaluator is used (gateway path). | | `context` | map\ | — | Additional context key-value pairs | | `act_as` | ActAs | — | End-user identity (`sub`, `email`, `roles`, `groups`, `claims`) | | `arguments` | bytes | — | JSON object encoded as bytes. Top-level keys become `resource.arguments.` (for example `{"query":"Atlas"}`) | Reference: [`Evaluate`](/api/authorization/evaluate) ```text title="Simulate Rules" theme={"system"} Simulate authorization for agent "data-pipeline" doing mcp:call on mcp_tool "database-query". ``` ## Simulate with Agent Act-As Include Act-As claims when your Cedar Rules reference end-user identity and you want to verify behavior for specific profiles. Requires `rules.view`. The Act-As flags populate the `act_as` record on the principal that Cedar Rules can reference. For example, a Rule that checks `principal.act_as.roles.contains("admin")` evaluates against the roles you provide. Simulation accepts these claims as test input. Live requests must pass [Delegated agents](/connect/agents/delegated) verification before the same values become available to Rules. ```bash theme={"system"} dome rules simulate \ --agent support-agent \ --action mcp:call \ --resource customer-records \ --resource-type mcp_tool \ --actas-sub user-123 \ --actas-email admin@example.com \ --actas-roles admin,manager \ --actas-groups support ``` | Flag | Cedar value | | ---------------- | ------------------------- | | `--actas-sub` | `principal.act_as.sub` | | `--actas-email` | `principal.act_as.email` | | `--actas-roles` | `principal.act_as.roles` | | `--actas-groups` | `principal.act_as.groups` | Reference: [`dome rules simulate`](/cli/secure/rules#simulate) Tool: `dome_rules_simulate` ```json theme={"system"} { "agent_id": "support-agent", "action": "mcp:call", "resource": "customer-records", "resource_type": "mcp_tool", "actas_sub": "user-123", "actas_email": "admin@example.com", "actas_roles": "admin,manager", "actas_groups": "support" } ``` Reference: [`dome_rules_simulate`](/reference/mcp/rules#rules-simulate) ```http theme={"system"} POST /dome.authz.v1.Authorization/Evaluate Content-Type: application/json { "caller": { "agent_id": "" }, "action": "mcp:call", "resource": "customer-records", "resource_type": "mcp_tool", "workspace_id": "", "act_as": { "sub": "user-123", "email": "admin@example.com", "roles": ["admin", "manager"], "groups": ["support"] } } ``` | Field | Type | Default | Description | | --------------- | ------------------- | ------- | ------------------------ | | `act_as.sub` | string | — | Subject identifier | | `act_as.email` | string | — | Email claim | | `act_as.roles` | string\[] | — | Roles claim | | `act_as.groups` | string\[] | — | Groups claim | | `act_as.claims` | map\ | — | Additional custom claims | Reference: [`Evaluate`](/api/authorization/evaluate) ```text title="Simulate with Act-As" theme={"system"} Simulate authorization for agent "support-agent" doing mcp:call on mcp_tool "customer-records" acting as user "user-123" with email "admin@example.com" and roles "admin,manager". ``` ## Simulate with arguments Pass per-call arguments when your Rules reference `resource.arguments.` and you want to verify behavior for a specific payload. Top-level keys of the JSON object become Cedar keys on the resource. Refer to [conditions on attributes or arguments](/govern/rules#condition-on-attributes-or-arguments) for the full shape. Requires `rules.view`. A Rule like `resource.arguments.query like "*Atlas*"` matches the example below. Keys whose JSON values are unrepresentable in Cedar (fractional floats, `null`, mixed-type lists) are dropped silently. Rules that reference them will not match. ```bash theme={"system"} dome rules simulate \ --agent data-pipeline \ --action mcp:call \ --resource vector-search \ --resource-type mcp_tool \ --eval-arguments '{"query":"Atlas","limit":50}' ``` | Flag | Type | Default | Description | | ------------------ | ----------- | ------- | -------------------------------------------------------------------------- | | `--eval-arguments` | JSON object | — | Per-call arguments as a JSON object surfaced as `resource.arguments.` | Reference: [`dome rules simulate`](/cli/secure/rules#simulate) In the Simulate panel, open the **Arguments** section and add one key-value row per argument. Each row becomes `resource.arguments.` for the evaluation. ```http theme={"system"} POST /dome.authz.v1.Authorization/Evaluate Content-Type: application/json { "caller": { "agent_id": "" }, "action": "mcp:call", "resource": "vector-search", "resource_type": "mcp_tool", "workspace_id": "", "arguments": "eyJxdWVyeSI6IkF0bGFzIn0=" } ``` | Field | Type | Default | Description | | ----------- | ----- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | | `arguments` | bytes | — | Base64-encoded JSON object. Top-level keys are surfaced as `resource.arguments.`. Non-object payloads are dropped silently. | Reference: [`Evaluate`](/api/authorization/evaluate) ## Read results DecisionDiffs show where allow/deny outcomes would change between the current effective Rules and the candidate Rules. Each diff entry includes: * The original decision (allow or deny) under the current Rules * The new decision under the candidate Rules * The specific event that produced the diff (agent, action, resource) * The Rule that matched in each case ```json theme={"system"} { "status": "completed", "total_events": 142, "decision_diffs": [ { "event_id": "evt-...", "current": "allow", "candidate": "deny", "rule_id": "rule-..." } ] } ``` Review diffs carefully before deploying. A simulation that shows no diffs means the candidate Rules produce identical outcomes for all historical events. It does not guarantee behavior for requests absent from that history. Add synthetic simulations for important unobserved cases. Use historical replay to answer: * Which previously allowed requests would a new `forbid` block? * Which previously denied requests would a new `permit` allow? * Do the candidate Rules preserve all observed outcomes? `dome_rules_simulate` evaluates one request synchronously. `dome_rules_simulate_result` retrieves the result of a historical replay by simulation ID. Both tools support AI-assisted Rule authoring: an agent writes Rules, simulates them, and iterates on results. ## Next steps * [Write and apply Rules](/govern/rules#write-rules) through the normal Rule lifecycle * [Delegated agents](/connect/agents/delegated) so live Act-As claims match what you simulated * [Authorization model](/concepts/architecture/authorization-model) concept for entity attributes a simulation may reference # Install Source: https://docs.domesystems.ai/install Install the Dome CLI and authenticate Install the Dome CLI to manage agents, deploy rules, and operate the platform. The CLI also hosts the MCP server for AI assistants. Dome is invitation-only. Your team and workspace must be provisioned before you can authenticate. Contact [support@domesystems.ai](mailto:support@domesystems.ai). ## Install the CLI ```bash theme={"system"} brew trust dome-systems/tap brew install dome-systems/tap/dome ``` Homebrew 6 requires you to trust a third-party tap before it loads its formulae. On older Homebrew, `brew trust` is unnecessary and harmless. Set your platform, then extract the binary onto your `PATH`: ```bash theme={"system"} VERSION=0.2.0 # latest: https://github.com/dome-systems/releases/releases/latest OS=darwin # or linux ARCH=arm64 # or amd64 curl -sSL "https://github.com/dome-systems/releases/releases/download/v${VERSION}/dome_${VERSION}_${OS}_${ARCH}.tar.gz" | tar xz sudo mv dome /usr/local/bin/dome ``` Each release also publishes `checksums.txt` and a per-archive SPDX SBOM. Verify a download with `shasum -a 256 -c checksums.txt --ignore-missing`. Verify the installation. ```bash theme={"system"} dome version ``` ## Authenticate Log in with your provisioned account. ```bash theme={"system"} dome auth login ``` This opens a browser-based SSO flow. On success, the CLI stores a session token locally. ## Select a Workspace List available workspaces and select an active context. ```bash theme={"system"} dome context list dome context use ``` All subsequent commands operate within this workspace. Switch at any time with `dome context use`. ## Verify Confirm your session and workspace: ```bash theme={"system"} dome auth status dome context current ``` ## Next steps * [Quickstart](/quickstart) to register your first agent and verify governed traffic * [Setup AI assistant](/setup-ai-assistant) to connect an assistant through MCP * [Personas](/personas) to identify your role and find relevant docs * [CLI](/cli) for the full command reference # Dome documentation Source: https://docs.domesystems.ai/introduction Connect, govern, and operate AI agents with Dome Production agents need to know which agent acted, what it was allowed to reach, whether sensitive data left the boundary, and how to prove it afterward. Dome gives each agent an identity, enforces Rules, Guards, and Quotas on every request, protects backend credentials, and records an attributable audit trail. ## Start here Build and verify your first governed agent. Install the CLI and connect your workspace. Install the CLI with Homebrew: ```bash theme={"system"} brew trust dome-systems/tap brew install dome-systems/tap/dome dome auth login ``` Refer to [Install](/install) for direct downloads, authentication, and workspace selection. ## Learn the platform See what Dome governs and how it fits between agents and resources. Identify your role and find relevant docs. Learn the architecture, identities, resources, controls, Gateways, and audit model. ## Explore Dome Register agents, attach tools and models, and expose them through Gateways. Author Rules, inspect content with Guards, and cap spend with Quotas. Investigate audit events, stream live activity, and subscribe external systems. Administer access, environments, integrations, settings, and exports around the pillars. Wire your application to authenticate as an agent, call tools and models, and handle denials. Work through role-based and production workflows from setup through verification. # Access Source: https://docs.domesystems.ai/manage/access Assign workspace roles and manage scoped platform API keys ## Overview Dome separates platform administration from runtime agent access. A workspace role or platform API key governs CLI commands, API endpoints, and MCP management tools. [Rules](/govern/rules) govern what a registered agent may do at the gateway. Refer to [Permissions](/concepts/platform/permissions) concept for the two-layer model. Memberships attach people to a workspace with one role. Platform API keys attach automation to a `workspace`, `tenant`, or `org` scope with an explicit permission list. Agent API keys authenticate agent runtimes and use a separate lifecycle. Refer to [create an agent API key](/connect/agents#create-key) for agent credentials. The typical workflow is: 1. [Assign a workspace member](#manage-workspace-members) a role that matches their job. 2. [Create a platform API key](#manage-platform-api-keys) for CI/CD or user automation that must call the Dome API. 3. Revoke keys and remove memberships when access is no longer needed. Apply least privilege to memberships and platform API keys. The sections below explain the roles you can assign and how platform API keys are scoped. ### Workspace roles Assign one role per member in each workspace. Roles apply per workspace. The same user can hold different roles across workspaces. | Role | Access | | ---------- | ------------------------------ | | `admin` | Full workspace management. | | `operator` | Agent and gateway operations. | | `security` | Authorization rules and audit. | | `viewer` | Read-only access. | You set the role when you [assign a workspace member](#manage-workspace-members). ### Platform API keys Platform API keys authenticate automation against the Dome API. Scope a key to `workspace`, `tenant`, or `org`. Set expiration for temporary automation and revoke unused keys immediately. Agent credentials use a separate lifecycle. For agent authentication, [create an agent API key](/connect/agents#create-key). You create, list, and revoke keys when you [manage platform API keys](#manage-platform-api-keys). ## Requirements Before you begin, authenticate the Dome CLI and select a workspace context. ### Permissions Member and platform API key management require elevated roles. Each operation states the permission it requires. For exhaustive flags, tool parameters, and endpoint contracts, refer to the [CLI](/cli/settings/workspace#members), [MCP](/reference/mcp/workspaces#members), and [API](/api/management/set-workspace-membership) references. | Default roles | Permission | Grants | | ----------------------------------------- | -------------------------- | --------------------------------------------- | | Workspace `admin` (or equivalent cascade) | Workspace admin membership | Assign and remove workspace members and roles | | `admin`, `operator` (at the target scope) | `apikey.manage` | Create, list, and revoke platform API keys | ## Manage workspace members Assign one role per member in each workspace. Choose from the [workspace roles](#workspace-roles) above. Requires workspace `admin` (or an equivalent cascaded role). ```bash theme={"system"} dome workspace members list dome workspace members set --user user@example.com --role operator dome workspace members remove --user user@example.com --revoke-all-keys ``` | Flag | Applies to | Description | | ------------------- | ----------- | --------------------------------------------- | | `--user` | set, remove | User email address. | | `--role` | set | `admin`, `operator`, `security`, or `viewer`. | | `--revoke-keys` | remove | API key IDs to revoke during removal. | | `--revoke-all-keys` | remove | Revokes every API key created by the user. | Reference: [`dome workspace members`](/cli/settings/workspace#members) Tools: `dome_workspace_members_list`, `dome_workspace_members_set`, `dome_workspace_members_remove` Reference: [`dome_workspace_members_set`](/reference/mcp/workspaces#workspace-members-set) RPCs: `ListWorkspaceMemberships`, `SetWorkspaceMembership`, `RemoveWorkspaceMembership` ```http theme={"system"} POST /dome.mgmt.v1.Management/SetWorkspaceMembership Content-Type: application/json { "tenant_id": "{{TENANT_ID}}", "workspace_id": "{{WORKSPACE_ID}}", "user_email": "user@example.com", "role": "operator" } ``` Reference: [`SetWorkspaceMembership`](/api/management/set-workspace-membership) ```text title="Assign a workspace role" theme={"system"} Set member "user@example.com" to the operator role. ``` ## Manage platform API keys Create scoped keys for CI/CD pipelines and user automation. Agent credentials use a separate lifecycle. Requires `apikey.manage` at the scope where you create the key. ```bash theme={"system"} dome platform-keys list dome platform-keys create \ --name "ci-deploy" \ --permissions rules.deploy,agent.register \ --expires-at 2026-12-31T00:00:00Z dome platform-keys revoke ci-deploy ``` Reference: [`dome platform-keys`](/cli/settings/platform-keys) Tools: `dome_platform_keys_list`, `dome_platform_keys_create`, `dome_platform_keys_revoke` Reference: [`dome_platform_keys_create`](/reference/mcp/platform#platform-keys-create) RPCs: `ListAPIKeys`, `CreateAPIKey`, `RevokeAPIKey` ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateAPIKey Content-Type: application/json { "name": "ci-deploy", "permissions": ["rules.deploy", "agent.register"], "expires_at": "2026-12-31T00:00:00Z", "scope_kind": "workspace", "scope_id": "{{WORKSPACE_ID}}" } ``` Reference: [`CreateAPIKey`](/api/management/create-api-key) ```text title="Create a platform API key" theme={"system"} Create a platform API key named "ci-deploy" with permissions rules.deploy and agent.register. ``` Scope keys to `workspace`, `tenant`, or `org`. Set expiration for temporary automation and revoke unused keys immediately. For agent authentication, [create an agent API key](/connect/agents#create-key). ## Next steps * [Configure Platform](/manage/settings) * [Permissions](/concepts/platform/permissions) concept * [Create an agent API key](/connect/agents#create-key) # Environments Source: https://docs.domesystems.ai/manage/environments Create tenants and workspaces, select a CLI context, or provision a disposable sandbox ## Overview Dome nests environments under an organization. An organization contains tenants. A tenant contains workspaces. Agents, resources, Gateways, rules, Guards, Quotas, and audit events live in a workspace. Tenants isolate data. Workspaces separate teams and environments inside a tenant. Refer to [Scope Hierarchy](/concepts/platform/scopes) concept for isolation and rule inheritance. Creating a tenant or workspace defines a durable boundary. A CLI context does not create a boundary. It selects which workspace subsequent CLI and MCP commands use. A sandbox creates a throwaway tenant or workspace for a try-out instead of a durable boundary. The typical workflow is: 1. [Create a tenant](#create-a-tenant) when you need a new hard isolation boundary. 2. [Create a workspace](#create-a-workspace) for the team or environment inside that tenant. 3. [Select a CLI context](#select-a-cli-context) so commands run against that workspace. 4. Optionally [scaffold a project](#scaffold-a-project) against the selected context. Use a [sandbox](#provision-a-sandbox) when you need a disposable try-out instead of steps 1–2. The sections below explain the attributes that shape a tenant or workspace at creation, and when a sandbox is the better path. ### Plans Each tenant receives a plan that determines available features. | Plan | Use for | | ------------ | ----------------------------------------- | | `sandbox` | Experimentation and testing | | `essential` | Core platform features | | `managed` | Dome-managed platform + gateway | | `enterprise` | Full feature set with SLA | | `complete` | Everything, including custom integrations | You set the plan when you [create a tenant](#create-a-tenant). ### Join policies `join_policy` controls how non-members enter a tenant or a workspace. Every tenant and every workspace carries its own value. The workspace policy is independent of its tenant's. New tenants and workspaces default to `invite_only` when the field is omitted at creation. | `join_policy` | Who can enter | | ------------- | ---------------------------------------- | | `open` | Verified-domain users join automatically | | `request` | Users request access. Admins approve | | `invite_only` | Admins must invite users (default) | You set `join_policy` when you [create a tenant](#create-a-tenant) or [create a workspace](#create-a-workspace). ### Sandboxes A sandbox is a disposable shortcut. A workspace-scoped sandbox creates a throwaway workspace in the current tenant. A tenant-scoped sandbox creates a throwaway tenant, workspace, and one-time API key. Neither path replaces a durable tenant or workspace for production work. Use a sandbox when you need a try-out. Use [create a tenant](#create-a-tenant) and [create a workspace](#create-a-workspace) when the boundary must persist. ## Requirements Before you begin, authenticate the Dome CLI. ### Permissions Tenant and workspace creation use org- and tenant-scoped roles. Each operation states the permission it requires. For exhaustive flags, tool parameters, and endpoint contracts, refer to the [CLI](/cli/settings/tenants), [MCP](/reference/mcp/platform), and [API](/api/management/create-tenant) references. | Default roles | Permission | Grants | | ----------------------------------------- | -------------------------- | -------------------------------------------------- | | Org `admin` or `owner` | `tenant.manage` | Create tenants or provision tenant sandboxes | | Tenant `admin`, `operator`, or `security` | Workspace / sandbox create | Create workspaces or provision workspace sandboxes | ## Create a tenant Create a tenant under your organization when you need a hard isolation boundary. You can set [plan](#plans) and [join policy](#join-policies) in the same call. Requires org-level `tenant.manage` (held by org `admin` or `owner`). ```bash theme={"system"} dome tenants create --name acme-prod --plan managed dome tenants list ``` | Flag | Description | | ---------- | -------------------------------------------------------------- | | `--name` | Tenant display name (required). | | `--plan` | `essential` (default), `managed`, `enterprise`, or `complete`. | | `--org-id` | Target org. Defaults to the active context's org. | Reference: [`dome tenants create`](/cli/settings/tenants#create) Tools: `dome_tenants_create`, `dome_tenants_list`, `dome_tenants_get` Reference: [`dome_tenants_create`](/reference/mcp/platform#tenants-create) ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateTenant { "name": "acme-prod", "plan": "managed", "join_policy": "request" } ``` Reference: [`CreateTenant`](/api/management/create-tenant) ```text title="Create a tenant" theme={"system"} Create a tenant named "acme-prod" on the managed plan. ``` ## Create a workspace Create a durable workspace for a team or environment inside a tenant. You can set [join policy](#join-policies) in the same call. List or delete workspaces as teams and projects change. Requires a tenant `admin`, `operator`, or `security` role. ```bash theme={"system"} dome workspace create --name "payments-team" dome workspace list dome workspace delete ``` Reference: [`dome workspace`](/cli/settings/workspace) Tools: `dome_workspaces_create`, `dome_workspaces_list`, `dome_workspaces_get`, `dome_workspaces_delete` Reference: [`dome_workspaces_create`](/reference/mcp/workspaces) ```http theme={"system"} POST /dome.mgmt.v1.Management/CreateWorkspace { "tenant_id": "{{TENANT_ID}}", "slug": "payments", "name": "Payments", "join_policy": "invite_only" } ``` Reference: [`CreateWorkspace`](/api/management/create-workspace) ```text title="Create a workspace" theme={"system"} Create a workspace named "payments-team". ``` ## Select a CLI context Sync after creating a tenant or workspace, then select the target so CLI and MCP commands run against that workspace. ```bash theme={"system"} dome context sync dome context list dome context use production dome context current ``` Use `workspace`, `tenant/workspace`, or `org/tenant/workspace` shorthand when names overlap. ## Provision a sandbox Provision a sandbox for a disposable try-out instead of a durable [tenant](#create-a-tenant) or [workspace](#create-a-workspace). A workspace-scoped sandbox creates a throwaway workspace in the current tenant. A tenant-scoped sandbox creates a throwaway tenant, workspace, and one-time API key. * **Workspace scope:** Disposable workspace in the current tenant. * **Tenant scope:** Isolated tenant, workspace, and one-time API key. Workspace sandboxes require a tenant `admin`, `operator`, or `security` role. Tenant sandboxes require org `admin` or `owner`. ```bash theme={"system"} dome sandbox provision --scope=workspace --workspace-name my-sandbox dome sandbox provision --scope=tenant --tenant-name my-sandbox ``` | Flag | Default | Description | | ------------------ | --------- | ------------------------------- | | `--scope` | Required | `tenant` or `workspace`. | | `--tenant-name` | Generated | Tenant suffix for tenant scope. | | `--workspace-name` | Generated | Workspace suffix. | Reference: [`dome sandbox`](/cli/developer/sandbox) Tool: `dome_provision_sandbox` ```json theme={"system"} { "scope": "workspace", "workspace_name": "my-sandbox" } ``` Reference: [`dome_provision_sandbox`](/reference/mcp/workspaces#provision-sandbox) Workspace scope: ```http theme={"system"} POST /dome.mgmt.v1.Management/ProvisionSandboxWorkspace { "name": "my-sandbox" } ``` Tenant scope: ```http theme={"system"} POST /dome.mgmt.v1.Management/ProvisionSandboxTenant { "org_id": "{{ORG_ID}}", "tenant_name": "my-sandbox", "workspace_name": "my-sandbox" } ``` Reference: [`ProvisionSandboxWorkspace`](/api/management/provision-sandbox-workspace) ```text title="Provision a sandbox" theme={"system"} Provision a workspace sandbox named "my-sandbox". ``` Tenant-scoped responses include the tenant, workspace, API key ID, and one-time API key secret. Sync context after the sandbox is ready. ## Scaffold a project Create a starter project against the selected environment. ```bash theme={"system"} dome quickstart my-agent-project ``` The command generates SDK setup, `dome.yaml`, environment placeholders, a Dockerfile, and starter Cedar rules. ## Next steps * [Manage Access](/manage/access) * [Configure Platform](/manage/settings) * [Quickstart](/quickstart) # Export Source: https://docs.domesystems.ai/manage/export Configure export destinations, formats, schedules, runs, and forwarder health ## Overview Forwarders and one-shot destination pushes read the same audit trail. Configuration and runtime events ship together unless you filter. Refer to [Audit events](/operate/audit) to query or reconstruct events, including gateway decisions. Refer to [Audit events](/concepts/audit) concept for the unified trail model. Ad-hoc exports to stdout or a file use [`dome audit export`](/cli/operate/audit#export) and [`dome_audit_export`](/reference/mcp/audit#audit-export). The typical workflow is: 1. [Choose a format](#choose-a-format) your downstream pipeline understands. 2. [Configure a destination](#configure-a-destination) and store its credentials as integration secrets. 3. [Schedule recurring exports](#schedule-recurring-exports) or push once with the export REST endpoint. 4. [Inspect export runs](#inspect-export-runs) and [monitor forwarder health](#monitor-forwarder-health). [Map SIEM fields](#map-siem-fields) when you consume OCSF or CEF. [Ingest external audit events](#ingest-external-audit-events) when systems outside the gateway must contribute to the same trail. The sections below cover formats, destinations, schedules, run inspection, forwarder health, SIEM field mapping, and external ingestion. ## Requirements Before you begin: * Authenticate to Dome and select a workspace * Have destination credentials ready to store as integration secrets * Know which format and connector your pipeline requires ### Permissions Export configuration and schedules require platform permissions for integrations and audit export. Each operation states what it configures or returns. | Default roles | Permission | Grants | | ------------------------------- | --------------------- | -------------------------------------------------- | | `admin`, `operator` | `integrations.manage` | Configure export destinations and related settings | | `admin`, `operator`, `security` | `audit.export` | Run and schedule audit export | ## Choose a format Pick the format your downstream pipeline understands. Destination connectors and the CLI / MCP export surfaces accept the same values where noted. | Format | Extension | Content-Type | Use for | | ------- | ------------ | ---------------------- | --------------------------------------------------------------------------------- | | `jsonl` | `.jsonl` | `application/x-ndjson` | NDJSON, one event per line for log shippers, `tail -f`, and `jq`. CLI / MCP only. | | `json` | `.json` | `application/json` | Pretty-printed JSON array for archives. | | `csv` | `.csv` | `text/csv` | Spreadsheet-friendly flat rows. File-based connectors only. | | `ocsf` | `.ocsf.json` | `application/json` | OCSF v1.1.0 for Splunk, Microsoft Sentinel, and Google Chronicle. | | `cef` | `.cef` | `text/plain` | Common Event Format for ArcSight, QRadar, and generic syslog. | ## Configure a destination Configure an export integration before creating a schedule. Each destination reads the same audit query and serializes matching events in its selected format. Refer to [Audit events](/operate/audit) for the filter model those queries use. | Connector | Formats | Destination | | ------------------------ | ---------------------------- | ----------------------------------------------------------------------------- | | **Amazon S3** | `csv`, `json`, `ocsf`, `cef` | Bucket + prefix. Object key suffixed with the format extension | | **Google Cloud Storage** | `csv`, `json`, `ocsf`, `cef` | Bucket + prefix. Object key suffixed with the format extension | | **Datadog Logs** | `json`, `ocsf`, `cef` | Datadog Log Intake API (Cloud SIEM consumes `ocsf` natively. CSV is rejected) | Write connector credentials as integration secrets, then push once with the export REST endpoint. ```http theme={"system"} POST /v1/audit/export Content-Type: application/json { "instance_id": "{{INTEGRATION_INSTANCE_ID}}", "connector": "s3", "format": "ocsf", "filter": { "start_time": "2026-05-01T00:00:00Z", "end_time": "2026-06-01T00:00:00Z" } } ``` The handler validates `format` up front and rejects unknown values with `400`. S3 and GCS write a single object per call, named `_to_.` under the configured prefix. Datadog batches events in groups of 1000 to the Log Intake API. ## Schedule recurring exports Create an `audit_export` job for the configured destination. Set the interval, time, format, and event filters together. ```http theme={"system"} POST /api/v1/workspaces/{{WORKSPACE_ID}}/jobs Content-Type: application/json { "job_type": "audit_export", "config": { "instance_id": "{{INTEGRATION_INSTANCE_ID}}", "format": "ocsf", "filters": { "event_classes": ["EVENT_CLASS_GOVERNING"] } }, "interval_value": 1, "interval_unit": "day", "time_of_day": "02:00", "enabled": true } ``` You can use `hour`, `day`, `week`, or `month` intervals. Weekly schedules accept `day_of_week`. Daily, weekly, and monthly schedules use UTC `time_of_day`. Update or disable a schedule with `PATCH /api/v1/workspaces/{{WORKSPACE_ID}}/jobs/{{JOB_ID}}`. Delete it when the forwarder should stop permanently. ## Inspect export runs Read a scheduled job to inspect its ten most recent runs. ```http theme={"system"} GET /api/v1/workspaces/{{WORKSPACE_ID}}/jobs/{{JOB_ID}} ``` Each run reports `running`, `success`, or `failed`, plus start time, finish time, error, and result. Investigate failures before the next scheduled window. List every export schedule in a workspace. ```http theme={"system"} GET /api/v1/workspaces/{{WORKSPACE_ID}}/jobs?job_type=audit_export ``` ## Monitor forwarder health Treat a configured integration as connected, then use recent run state to assess delivery. The table below maps each state to the next action. | State | Action | | -------------- | -------------------------------------------------------------------------- | | Not configured | Create credentials and an integration instance. | | No runs yet | Confirm the schedule is enabled and wait for its first window. | | Running | Check again after the current window completes. | | Success | Verify the destination received the expected object or batch. | | Failed | Read the run error, repair credentials or destination settings, and retry. | The dashboard's **Integration & Forwarder Health** panel lists available export destinations and their connection state. ## Map SIEM fields OCSF and CEF exports carry the request-origin fields from the audit envelope so SOC pivots by IP, user agent, or HTTP verb work without re-joining sources. Configuration events typically populate these fields. Many gateway decisions leave `request_surface` unset. Refer to [Audit events](/concepts/audit#why-attribution-differs-by-source) concept for why attribution differs by event type. | Audit envelope | OCSF (`ocsf`) | CEF (`cef`) | | ----------------------------- | --------------------------- | -------------------------- | | `request_surface.source_ip` | `src_endpoint.ip` | `src` | | `request_surface.user_agent` | `unmapped.dome_user_agent` | `requestClientApplication` | | `request_surface.http_method` | `unmapped.dome_http_method` | `requestMethod` | | `actor.id` | `actor.user.uid` | `suid` | ## Ingest external audit events External systems push audit events into the unified trail via the REST ingestion endpoint, consolidating agent activity from systems outside the gateway. Refer to [Audit events](/operate/audit) to query ingested events. ## Next steps * [Audit events](/concepts/audit) concept for the one trail every surface reads from * [Audit events](/operate/audit) how-to to query retained and ingested events * [Subscribe to Events](/operate/webhooks) how-to for near-real-time signed deliveries * [Events](/reference/events) reference for envelope and catalog contracts # Manage Source: https://docs.domesystems.ai/manage/index Configure platform defaults, access, environments, integrations, and data exports **Manage** administers the Dome platform. You configure workspace defaults, who can administer Dome, tenants and workspaces, enterprise identity and secrets, and audit export. Refer to [Scopes](/concepts/platform/scopes) concept for tenancy boundaries and [Permissions](/concepts/platform/permissions) concept for platform RBAC versus runtime agent authorization. ## How Manage administers the platform The following capabilities are what you configure for platform administration: * **Settings** set workspace defaults such as audit retention, policy refresh, and agent token lifetime. They apply inside an existing workspace. They do not create tenants or memberships. * **Access** assigns workspace roles and scoped platform API keys for people and automation. These grants do not authorize agent tool or model calls. * **Environments** create tenants and workspaces, select a CLI context, or provision a disposable sandbox. Organization provisioning stays outside this surface. * **Integrations** connect SSO for platform users and store secrets Dome injects at egress. They do not attach agent-facing tools or models, and they do not configure export destinations. * **Export** configures destinations, formats, schedules, and forwarder health so retained audit records reach your SIEM or object store. Investigation still happens under [Operate](/operate). Manage is supporting administration. Connect and Govern decide what agents can reach and do. Operate makes that activity visible. The typical workflow is: 1. [Configure platform](/manage/settings) to list or set workspace defaults, optionally tune token lifetime, and review configuration changes in audit. 2. [Manage access](/manage/access) to assign workspace members and create or revoke scoped platform API keys. 3. [Prepare environments](/manage/environments) to create tenants and workspaces, select a CLI context, or provision a sandbox when you need a throwaway try-out. 4. [Integrate systems](/manage/integrations) to configure single sign-on and store integration secrets. 5. [Export data](/manage/export) to choose a format, configure a destination, schedule runs, and monitor forwarder health. If you are standing up a new boundary, start with [Prepare Environments](/manage/environments) before Settings and Access. Those pages assume a workspace already exists. ## Next steps * [Connect](/connect) to register agents, attach resources, and route requests through Gateways * [Govern](/govern) to authorize actions, set usage limits, and inspect content * [Operate](/operate) to investigate audit events, stream live events, and subscribe to events * [Develop](/develop) to send runtime credentials, Gateway endpoints, and Act-As envelopes from the application # Integrations Source: https://docs.domesystems.ai/manage/integrations Connect identity, secrets, and enterprise services ## Overview Platform integrations wire Dome to systems your operators already run. Single sign-on authenticates dashboard and CLI users through your identity provider. Dome stores backend credentials, HMAC secrets, and integration settings so egress can retrieve them without exposing raw secrets to agents. Export destinations and agent-facing MCP or LLM backends are separate surfaces. Configure export destinations under [Export Data](/manage/export). Attach tools and models through Connect. The typical workflow is: 1. [Configure single sign-on](#configure-single-sign-on) for dashboard and CLI access. 2. [Store integration secrets](#store-integration-secrets) for backends and connectors that need credentials at egress. 3. Attach agent-facing backends and export destinations on their own pages when those paths apply. The sections below explain what each integration path covers and what it does not. ### Single sign-on Single sign-on authenticates platform users for dashboard and CLI access through WorkOS. Agent identities and act-as verification use separate credentials. Session expiration is independent from agent token lifetime. Configure the provider under [Configure single sign-on](#configure-single-sign-on). ### Integration secrets Dome stores backend credentials, HMAC secrets, and integration settings for egress. Provider credentials are write-only. Reads return masked values. Store and rotate secrets under [Store integration secrets](#store-integration-secrets). ### Agent backends and export Platform integrations do not register tools or models. Attach agent-facing backends and export destinations on their own pages. Continue under [Connect agent backends](#connect-agent-backends). ## Requirements Before you begin: * Authenticate to the Dome dashboard or CLI * Have identity-provider and integration secret details available for the paths you intend to configure ### Permissions Integration paths require permission to manage workspace integrations. Each section states what that path covers. | Default roles | Permission | Grants | | ------------------- | --------------------- | ----------------------------------------------------------------- | | All workspace roles | `integrations.view` | View workspace integrations | | `admin`, `operator` | `integrations.manage` | Configure SSO, integration secrets, and related integration paths | ## Configure single sign-on Connect your identity provider through WorkOS for dashboard and CLI access. * Use OAuth 2.0 with PKCE. * Provision platform users on first login. * Match verified domains to organizations. * Apply session expiration independently from agent token lifetime. Single sign-on authenticates platform users. Agent identities and act-as verification use separate credentials. ## Store integration secrets Store backend credentials, HMAC secrets, and integration settings as Dome integration secrets. * Retrieve backend credentials only at egress time. * Keep raw secrets hidden from agents. * Refresh expiring credentials before their TTL. * Rotate secrets without restarting agents. Treat provider credentials as write-only. Reads return masked values. ## Connect agent backends Platform integrations do not register tools or models. Attach agent-facing MCP and LLM backends through the gateway. ## Next steps * [Tools](/connect/resources/tools#add-tool) and [Models](/connect/resources/models) * [Export Data](/manage/export) * [Manage Access](/manage/access) # Settings Source: https://docs.domesystems.ai/manage/settings Set workspace defaults and operational controls ## Overview Each workspace carries its own configuration. Unset keys resolve to their defaults. Set only values that differ from the platform baseline, and validate each change against the supported range before rollout. Configuration writes are audited. Every write emits a `workspace.config.changed` event with previous and new values. Sensitive values use `` in the audit payload. The typical workflow is: 1. [List or set workspace defaults](#configure-workspace-defaults) for the keys you need to change. 2. Optionally tune [token lifetime](#token-lifetime) via `identity.token_ttl` for agent access tokens. 3. [Review changes](#review-changes) in audit before and after a rollout. The sections below explain the keys you can set and how token lifetime relates to other sessions. ### Workspace configuration keys These keys control retention, policy sync, bundle history, and agent token lifetime. | Key | Default | Range | Effect | | ----------------------------------- | ------- | ---------- | ------------------------------------- | | `audit.retention_days` | `90` | `1`–`3650` | Retains audit events before pruning. | | `authorization.rule_sync_interval` | `30s` | `5s`–`10m` | Refreshes the gateway policy bundle. | | `authorization.max_bundle_versions` | `10` | `1`–`100` | Retains policy versions for rollback. | | `identity.token_ttl` | `10m` | `1m`–`24h` | Sets agent access-token lifetime. | You list and set these keys when you [configure workspace defaults](#configure-workspace-defaults). ### Token lifetime `identity.token_ttl` controls tokens returned by `Identity.ExchangeToken`. New exchanges read the current value without restarting services. Use shorter lifetimes for ephemeral jobs. Increase the value only when refresh overhead justifies the larger exposure window. Dashboard sessions, credential links, and upstream OAuth tokens use separate lifetimes. Set the value with the same [configure workspace defaults](#configure-workspace-defaults) operation used for other keys. ## Requirements Before you begin, authenticate the Dome CLI and select a workspace context. ### Permissions Workspace settings writes require an `admin` role (or equivalent cascade) with `config.manage`. Each operation states the permission it requires. For exhaustive flags, tool parameters, and endpoint contracts, refer to the [CLI](/cli/settings/workspace), [MCP](/reference/mcp/workspaces), and [API](/api/management/get-config) references. | Default roles | Permission | Grants | | ----------------------------------------- | --------------- | ----------------------------------------- | | Workspace `admin` (or equivalent cascade) | `config.manage` | List and set workspace configuration keys | ## Configure workspace defaults List or set [workspace configuration keys](#workspace-configuration-keys). Unset keys resolve to their defaults. Set only values that differ from the platform baseline. Requires workspace `admin` (or an equivalent cascaded role) with `config.manage`. ```bash theme={"system"} dome workspace config list dome workspace config set --key audit.retention_days --value 90 dome workspace config set --key identity.token_ttl --value 5m ``` Reference: [`dome workspace`](/cli/settings/workspace) Tools: `dome_workspace_config_list`, `dome_workspace_config_set` Reference: [`dome_workspace_config_set`](/reference/mcp/workspaces) ```http theme={"system"} POST /dome.mgmt.v1.Management/GetWorkspaceConfig POST /dome.mgmt.v1.Management/SetWorkspaceConfig ``` Reference: [`GetConfig`](/api/management/get-config) ```text title="Configure workspace defaults" theme={"system"} List the workspace configuration. Set workspace config key "audit.retention_days" to "90". ``` Every write emits a `workspace.config.changed` event with previous and new values. Sensitive values use `` in the audit payload. ## Review changes Query configuration changes before and after a rollout. ```bash theme={"system"} dome audit query \ --types workspace.config.changed \ --primary-resource-kind workspace ``` Use [Audit events](/operate/audit) to filter the full configuration history. ## Next steps * [Manage Access](/manage/access) * [Prepare Environments](/manage/environments) * [Audit events](/operate/audit) — filter on `workspace.config.changed` # Audit events Source: https://docs.domesystems.ai/operate/audit Query audit events by class, result, actor, resource, correlation, and payload Refer to [Audit events](/concepts/audit) concept for how the trail is shaped. ## Overview Audit v1 is the durable evidence trail for governed infrastructure. Stream Live Events shows the same record in real time. Export delivers retained history downstream. The typical workflow is: 1. [Query events](#query-events) with class, actor, resource, correlation, or payload filters. 2. [Get an event](#get-event) by ID when you need the full envelope. 3. [List](#list-activity-chains) or [get](#get-activity-chain) an activity chain, or [emit](#emit-activity-chain) an `activity_id` for a run. ## Requirements Before you begin: * Authenticate to Dome and select a workspace * Have audit events in the workspace, or emit activity so chains appear ### Permissions Audit reads require platform permissions. People and scoped API keys query the trail. Agent credentials are not the usual path for investigation. All workspace roles can query events, get an event by ID, and list or get activity chains with `audit.view`. Live streaming on [Stream Live Events](/operate/observe) uses the same permission. Batch and scheduled delivery live under [Export Data](/manage/export). | Default roles | Permission | Grants | | ------------------- | ------------ | ----------------------------------------------------- | | All workspace roles | `audit.view` | Query events, get by ID, list and get activity chains | ## Query events Query recent audit events. The default page returns the latest 50. Combine class, result, actor, resource, correlation, payload, and time filters to narrow the trail. Requires `audit.view`. Common flags: `--limit`, `--types`, `--classes`, `--results`, `--agent-id`, `--since`, `--until`, and repeatable `--payload-filter`. Full flags: refer to the CLI, MCP, and API references in the callouts below. ```bash theme={"system"} dome audit query --limit 10 ``` Reference: [`dome audit query`](/cli/operate/audit#query) Tool: `dome_audit_query` ```json theme={"system"} { "limit": 10 } ``` Reference: [`dome_audit_query`](/reference/mcp/audit#audit-query) ```http theme={"system"} POST /dome.audit.v1.Audit/QueryEvents Content-Type: application/json { "page_size": 10 } ``` Filtered: ```json theme={"system"} { "event_types": ["mcp.tool_call.completed", "mcp.tool_call.attempted"], "event_classes": ["EVENT_CLASS_GOVERNING"], "results": ["EVENT_RESULT_DENIED"], "agent_id": "", "page_size": 50 } ``` Reference: [`QueryEvents`](/api/audit/query-events) ```text title="Query recent audit events" theme={"system"} Query the last 10 audit events. ``` The response returns matched events plus a cursor. CLI, MCP, and dashboard use short enum tokens. The raw API uses full constants. Refer to [enum rendering](/reference/events#enum-rendering) reference. ```json theme={"system"} { "events": [{ "event_id": "", "event_type": "mcp.tool_call.completed", "event_class": "governing", "result": "succeeded", "occurred_at": "2026-05-30T10:30:00Z", "scope": { "kind": "workspace", "workspace_id": "ws_..." }, "actor": { "kind": "agent", "id": "agent_..." }, "primary_resource": { "kind": "tool_connection", "id": "conn_..." }, "request_surface": { "surface": "gateway_mcp" }, "correlation": { "trace_id": "0af7651916cd43dd8448eb211c80319c" }, "payload": { "@type": "type.googleapis.com/dome.audit.v1.MCPToolCallCompletedV1", "tool": "database-query" } }], "next_page_token": "..." } ``` The pre-v1 `total` counter is gone. Use the cursor to walk the full result set. ### Paginate results Each response includes `next_page_token`. An empty token means the page was the last one. ```bash theme={"system"} dome audit query --limit 200 # ... copy the printed "Next page token: ..." value dome audit query --limit 200 --page-token ``` ```json theme={"system"} { "page_size": 200, "page_token": "" } ``` ### Filter by class and result Use class and result together to find denials, security signals, or audit access. Refer to [event classes](/reference/events#event-classes) reference for the catalog. ```bash theme={"system"} # Every authorization deny in the workspace dome audit query --classes governing --results denied # All audit-access events — who looked at what dome audit query --classes audit_access # Failed logins and rejected agent-token exchanges dome audit query --classes security --results denied --since 2026-06-01T00:00:00Z ``` ### Filter by actor and producer Slice by who acted and which service emitted the event. ```bash theme={"system"} dome audit query --actor-kind scheduled_job dome audit query --producer-service gateway --request-surface gateway_mcp ``` For a Gateway, use `producer_gateway_id` through the [QueryEvents API](/api/audit/query-events). ### Filter by resource ```bash theme={"system"} dome audit query \ --primary-resource-kind rule_bundle \ --primary-resource-id ``` ### Reconstruct a request chain Filter on a `trace_id` to return events from one request. Refer to [correlation](/reference/events#correlation) reference for the `trace_id`, `operation_id`, and `parent_event_id` contract. ```bash theme={"system"} dome audit query --trace-id 0af7651916cd43dd8448eb211c80319c ``` ### Filter by payload Apply [payload filters](/reference/events#payload-filters) to a query. ```bash theme={"system"} # Every tool call that targeted database-query dome audit query --payload-filter "mcp.tool_call.completed:tool=database-query" # Multiple values, multiple filters dome audit query \ --payload-filter "mcp.tool_call.completed:tool=database-query,vector-search" \ --payload-filter "authorization.decision:decision=deny" ``` Use the JSON form for `exists`: ```bash theme={"system"} dome audit query --payload-filter '[ {"event_type":"mcp.tool_call.completed","field":"tool","operator":"exists"} ]' ``` `mcp.tool_call.*` and `llm.model_call.*` payloads do not include request arguments by default. Contact support to enable argument capture on a workspace. Cedar rules still evaluate `resource.arguments.`. ### Filter by time range ```bash theme={"system"} dome audit query \ --since 2026-05-01T00:00:00Z \ --until 2026-06-01T00:00:00Z ``` ## Get event Retrieve a single audit event by ID. Scope is derived from the caller's session. There is no `workspace_id` argument. Requires `audit.view`. ```bash theme={"system"} dome audit get ``` Reference: [`dome audit get`](/cli/operate/audit#get) Tool: `dome_audit_get_event` ```json theme={"system"} { "event_id": "" } ``` Reference: [`dome_audit_get_event`](/reference/mcp/audit#audit-get-event) ```http theme={"system"} POST /dome.audit.v1.Audit/GetEvent Content-Type: application/json { "event_id": "" } ``` Reference: [`GetEvent`](/api/audit/get-event) Governing decision events that applied a loaded Rule bundle include [decision provenance](/reference/events#decision-provenance) in the payload (`bundle_etag`, `contributing_bundles`, `rule_id`). The dashboard decision explainer deep-links those bundles to `/rules/bundles?scope=&scopeId=`. ## List activity chains List activity chains in the active workspace. A chain is listed when any of its events matches the filter. Summary counts reflect the full chain. Refer to [activity chains](/reference/events#correlation) reference for their contract. Requires `audit.view`. ```bash theme={"system"} # Recent chains in the active workspace dome audit chains --limit 20 # Chains that contain a denied governing event dome audit chains --classes governing --results denied # Chains for one agent in the last 24 hours dome audit chains --agent-id agent_01HQ... --since 2026-06-05T00:00:00Z ``` Reference: [`dome audit chains`](/cli/operate/audit#chains) ```json theme={"system"} { "chains": [{ "activity_id": "case-12345", "workspace_id": "ws_...", "event_count": 47, "first_at": "2026-06-05T10:30:00Z", "last_at": "2026-06-05T10:34:21Z", "allowed_count": 41, "denied_count": 3, "filtered_count": 2, "error_count": 1, "agents": ["agent_..."], "event_types": ["mcp.tool_call.attempted", "mcp.tool_call.completed", "authorization.decision"], "server_owned_count": 0, "caller_asserted_count": 47 }], "next_page_token": "" } ``` ## Get activity chain Retrieve every event in one activity chain. Pass an `activity_id` to QueryEvents (or `dome audit chain`) to return events in cursor order. ```bash theme={"system"} # All events in one chain dome audit chain case-12345 # Live tail one chain dome audit stream --activity-id case-12345 # Filter the chain's events further dome audit query --activity-id case-12345 --classes governing --results denied ``` Reference: [`dome audit chain`](/cli/operate/audit#chain) ## Emit activity chain Set `activity_id` on the emit side so events from one unit of work correlate. Pick an ID that maps to that unit, such as a case ID, job run, or conversation UUID. Chains are workspace-local, so human-meaningful IDs are safe. Set `--activity` (or `DOME_ACTIVITY_ID`) so every Connect RPC in the run carries `X-Dome-Activity-Id`. ```bash theme={"system"} export DOME_ACTIVITY_ID="case-12345" dome agents register --name worker dome rules deploy ./rules dome audit query --limit 5 dome audit chain case-12345 ``` `--activity` overrides `DOME_ACTIVITY_ID`. There is no implicit mint. Wrap Dome API reads in `client.session()`. Pass an explicit ID to correlate work across processes. ```python theme={"system"} with client.session() as session: page = client.control.query_audit_events( AuditQuery(event_types=("mcp.tool_call.completed",), page_size=50), ) event = client.control.get_audit_event(page.events[0].id) ``` Send `X-Dome-Activity-Id` on every Connect RPC. Dome stamps the ID with caller-asserted trust and strips the header before third-party LLM or MCP egress. ```http theme={"system"} POST /dome.audit.v1.Audit/QueryEvents X-Dome-Activity-Id: case-12345 ``` ## Next steps * [Events](/reference/events) reference for the Audit v1 envelope and event catalog * [Stream Live Events](/operate/observe) to stream events as they happen * [Export Data](/manage/export) to ship retained records to SIEM and archival sinks * [Subscribe to Events](/operate/webhooks) to push signed events to external systems # Operate Source: https://docs.domesystems.ai/operate/index Investigate audit events, stream live events, and subscribe to outbound notifications **Operate** makes governed traffic visible and actionable. You query the durable audit trail, stream live events, and subscribe external systems to signed deliveries. Refer to [Audit events](/concepts/audit) concept for how the one trail works and [Events](/reference/events) reference for the shared envelope and catalogs. ## How Operate surfaces events The following surfaces share one operational story: * **Audit Events** queries and filters the durable Audit v1 trail. You investigate by class, actor, resource, correlation, and payload, then follow activity chains across configuration and runtime events. * **Stream Live Events** shows that same trail in real time and adds live operational signals such as calls and denials, health and latency, token usage and cost, Quotas, OpenTelemetry, and product telemetry. * **Subscribe to Events** pushes a public projection of selected events to your HTTPS endpoint or a provider. Deliveries are signed, retried, and replayable. They are not a substitute for the audit trail or for batch export under [Manage](/manage/export). Audit Events is the durable record. Stream Live Events and Subscribe to Events are the live surfaces: stream inside Dome, subscribe when another system must react as events occur. The typical workflow is: 1. [Audit events](/operate/audit) to query, get, and correlate retained events when you investigate a decision or prepare evidence. 2. [Stream live events](/operate/observe) for governed traffic in real time, and optionally review token usage, cost, OpenTelemetry, or product telemetry. 3. [Subscribe to events](/operate/webhooks) to create destinations and subscriptions for the event types your automation must handle. After setup, you can add destinations, widen subscriptions, or change retention without touching Connect or Govern configuration. ## Next steps * [Connect](/connect) when you still need agents, resources, or Gateways * [Govern](/govern) to authorize actions, set usage limits, and inspect content * [Manage](/manage) to configure platform access, environments, integrations, and data exports * [Develop](/develop) to send runtime credentials, Gateway endpoints, and Act-As envelopes from the application # Stream live events Source: https://docs.domesystems.ai/operate/observe Stream live governed events, calls, denials, health, latency, failures, usage, quotas, and telemetry Refer to [Audit events](/concepts/audit) concept for how the one trail works. Contracts are on the [Events](/reference/events) reference. ## Overview Streaming correlates several signals on the same governed traffic. Each signal answers a different operational question. | Signal | What it shows | | ------------------- | ------------------------------------------------------------- | | Live events | Governed requests as they occur, without polling | | Calls and denials | Allowed, denied, filtered, and failed outcomes | | Health and failures | Unavailable services, backends, and policy paths | | Latency | Slow tools, models, gateways, and Rule evaluations | | Tokens and cost | LLM consumption by agent, API key, identity, and served model | | Quotas | Rate-limit and resource-exhausted events by scope | | OpenTelemetry | Traces, metrics, and logs on your collector | | Product telemetry | Feature adoption. Not compliance evidence | Streaming reads the same Audit v1 trail as [Audit events](/operate/audit). Stream for live operations. Query and export for history and compliance. The typical workflow is: 1. [Stream events](#stream-events) for live governed traffic. 2. Optionally review [token usage and cost](#token-usage-and-cost). 3. Optionally [configure OpenTelemetry](#configure-opentelemetry) or [product usage telemetry](#configure-product-usage-telemetry) for your own stack. ### Token classes Every LLM call resolves to a disjoint partition across five token classes. The window total is their sum. | Class | Description | | ------------------------ | ------------------------------------------------------------------------------ | | `llm_input_tokens` | Fresh input tokens that bypassed the prompt cache. | | `llm_cache_read_tokens` | Prompt-cache hits. Cheapest class. | | `llm_cache_write_tokens` | Prompt-cache writes. Carry a premium over fresh input. | | `llm_output_tokens` | Tokens emitted in the visible response. | | `llm_reasoning_tokens` | Hidden reasoning tokens (e.g. OpenAI `o1` reasoning). Bill at the output rate. | | `llm_calls` | Completed LLM calls that reported usage in the window. | Adapters normalize provider-specific usage records into this partition. Per-class pricing is applied at read time and surfaced as `estimated_cost_usd`. ### Usage cubes Token usage and call counts fan out across the workspace aggregate and four read-time **cubes** so you can attribute consumption by the dimension you care about. | Cube | Key | What it answers | | ------------------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Workspace summary | workspace | What did this workspace consume in the window? | | By agent | `agent_id` | Which agents drive cost? | | By API key | `key_id` | Which key (and therefore which workload) is responsible? Survives key revocation. | | By act-as identity | `act_as_sub` | Which assumed human identity ran up the bill? Keyed on the stable OIDC subject. | | By model | `connection_id` | Which upstream model connection actually served the call? Failover-aware: surfaces the served connection, not the requested one. | ## Requirements Before you begin: * Authenticate to Dome and select a workspace * Have traffic in the workspace when you want live events to appear ### Permissions Live event streams and token-usage reads require platform permissions. People and scoped API keys call these surfaces. Agent credentials are not the usual path. All workspace roles can stream events and read token usage with `audit.view`. OpenTelemetry and product usage telemetry are configured on the api-server and gateway processes with environment variables. They do not use workspace permissions. | Default roles | Permission | Grants | | ------------------- | ------------ | ---------------------------------- | | All workspace roles | `audit.view` | Stream events and read token usage | ## Stream events Stream audit events in real time with server-sent events (SSE). The stream delivers events as they occur, without polling. Requires `audit.view`. ```bash theme={"system"} dome audit stream ``` Filter by class, type, agent, or trace: ```bash theme={"system"} dome audit stream --classes governing --results denied --agent-id ``` For the complete stream flag reference, refer to [`dome audit stream`](/cli/operate/audit#stream). Find valid event types in the [Events catalog](/reference/events#event-types) reference. Reference: [`dome audit stream`](/cli/operate/audit#stream) Real-time streaming is not available via MCP tools. Poll recent events with `dome_audit_query` instead. Reference: [`dome_audit_query`](/reference/mcp/audit#audit-query) ```http theme={"system"} POST /dome.audit.v1.Audit/StreamEvents Content-Type: application/json { "event_classes": ["EVENT_CLASS_GOVERNING"], "results": ["EVENT_RESULT_DENIED"], "agent_id": "" } ``` Server-streaming RPC. Returns a stream of `AuditEventV1` envelopes as they occur. Accepts the same filter set as `QueryEvents` except time-range and pagination fields. Reference: [`StreamEvents`](/api/audit/stream-events) ```text title="Query recent audit events" theme={"system"} Query the most recent 20 audit events of type authorization.decision for agent "a1b2c3d4-...". ``` Each streamed event is a full `AuditEventV1` envelope. ```json theme={"system"} { "event_id": "", "event_type": "mcp.tool_call.completed", "event_class": "EVENT_CLASS_GOVERNING", "result": "EVENT_RESULT_SUCCEEDED", "occurred_at": "2026-05-30T10:30:00Z", "scope": { "workspace_id": "ws_..." }, "actor": { "kind": "ACTOR_KIND_AGENT", "id": "agent_..." }, "correlation": { "trace_id": "0af7651916cd43dd8448eb211c80319c" }, "payload": { "@type": "type.googleapis.com/dome.audit.v1.MCPToolCallCompletedV1", "tool": "database-query", "backend": "my-mcp-server", "latency_ms": 142 } } ``` ### Filter the stream Narrow the stream further by combining multiple type filters. Requires `audit.view`. ```bash theme={"system"} dome audit stream --types mcp.tool_call.completed,mcp.tool_call.attempted ``` Reference: [`dome audit stream`](/cli/operate/audit#stream) Real-time streaming is not available via MCP tools. Poll with `dome_audit_query` and `since` / `until` instead. Reference: [`dome_audit_query`](/reference/mcp/audit#audit-query) ```http theme={"system"} POST /dome.audit.v1.Audit/StreamEvents Content-Type: application/json { "event_types": ["mcp.tool_call.completed", "mcp.tool_call.attempted"] } ``` Reference: [`StreamEvents`](/api/audit/stream-events) ```text title="Query filtered audit events" theme={"system"} Query the most recent 20 audit events of type mcp.tool_call.completed,mcp.tool_call.attempted. ``` The stream remains open until you terminate it. It fits live monitoring during deployments, incident investigation, or development testing. ## Token usage and cost Query LLM consumption across every agent, API key, identity, and served model in the workspace. The dashboard token panel, the `dome usage` CLI, and the `dome_usage` MCP tool share one summary endpoint, so numbers line up everywhere. Requires `audit.view`. The estimated cost is a blended cross-model rate intended for trend-watching and demos, not a bill. Each token class is priced at its own per-million rate and re-priced at read time, so a price change re-prices history without a migration. Pricing is never persisted. Print the workspace token total and estimated cost for the last 24 hours. ```bash theme={"system"} dome usage ``` The output mirrors the dashboard token panel. Add `--json` for machine-readable output and pipe into `jq` for scripted budgets. Reference: [`dome usage`](/cli/operate/usage) Agents and assistants read the same numbers through `dome_usage`. Reference: [`dome_usage`](/reference/mcp/audit#audit-usage) All token-usage reads are workspace-scoped. Time-range params `from` and `to` accept RFC 3339. Absent values default to the last 24 hours. `limit` defaults vary per endpoint and clamps at 200. | Route | Returns | | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GET /api/v1/metrics/summary` | Workspace totals (`evaluations_*`, `tool_calls_*`, the five token classes, `llm_total_tokens`, `llm_calls`, `estimated_cost_usd`) plus time-series buckets. | | `GET /api/v1/metrics/agents` | Top agents ranked by activity or denies, with token totals and cost per agent. `sort=activity\|denies`. | | `GET /api/v1/metrics/agents/{agent_id}` | Per-agent detail with time-series buckets. | | `GET /api/v1/metrics/agents/{agent_id}/keys` | Per-API-key token usage and cost for one agent. Revoked keys still surface their historical usage. | | `GET /api/v1/metrics/models` | Top served model connections ranked by tokens. Joins the connection table for the live display name. | | `GET /api/v1/metrics/act-as` | Top act-as identities ranked by tokens. `email` is a last-wins display snapshot. `sub` is the stable key. | Sample summary response: ```json theme={"system"} { "evaluations_allow": 1842, "evaluations_deny": 17, "tool_calls_total": 921, "llm_input_tokens": 412000, "llm_cache_read_tokens": 1820000, "llm_cache_write_tokens": 88000, "llm_output_tokens": 64000, "llm_reasoning_tokens": 12000, "llm_total_tokens": 2396000, "llm_calls": 318, "estimated_cost_usd": 3.4521, "agents_active": 12, "buckets": [ { "window_start": "2026-06-05T10:00:00Z", "llm_input_tokens": 18000, "llm_output_tokens": 2400, "estimated_cost_usd": 0.092 } ] } ``` Reference: REST metrics under `/api/v1/metrics/*` ## Configure OpenTelemetry Every Dome service exports OpenTelemetry traces, metrics, and logs over OTLP/HTTP. Point the standard `OTEL_*` env vars at your collector (Jaeger, Tempo, Datadog Agent, OTel Collector, Axiom, or anything that speaks OTLP) to wire Dome into your existing observability stack. Dome is vendor-neutral: it emits standard OTLP and reads standard `OTEL_*` variables. The collector decides where data lands. Swap backends without redeploying Dome. ### Enable Set `OTEL_EXPORTER_OTLP_ENDPOINT` on the api-server and gateway processes. When the variable is unset, the SDK runs in silent no-op mode. That is useful for local development without a collector. ```bash theme={"system"} export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 export OTEL_SERVICE_NAME=dome-api-server # also: dome-gateway export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production ``` | Env var | Description | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP/HTTP collector URL. Unset disables the SDK. | | `OTEL_SERVICE_NAME` | Service name on every signal (e.g. `dome-api-server`, `dome-gateway`). | | `OTEL_RESOURCE_ATTRIBUTES` | Comma-separated resource attrs (`key=value,key=value`). | | `OTEL_SDK_DISABLED` | Set to `true` to force the no-op provider. | | `OTEL_TRACES_EXPORTER` | Set to `none` to drop only traces while keeping metrics and logs. Same pattern for `OTEL_METRICS_EXPORTER` and `OTEL_LOGS_EXPORTER`. | | `OTEL_TRACES_SAMPLER_ARG` | Head-sampling ratio for root spans. Defaults to `0.01` (1%). Use `1.0` for full traces in local dev. | | `DOME_BUILD_VERSION` | Overrides the `service.version` resource attribute. Defaults to the build-time version. | Health probes (`/healthz`, `/readyz`, `/health`, `/ready`) are filtered out of traces and HTTP metrics automatically. ### Identity attributes Every span and log record carries the same `dome.*` vocabulary so you can pivot from a trace to its logs with a single tag filter: | Attribute | Value | | --------------------------- | --------------------------------------------- | | `dome.caller_type` | `agent`, `platform_user`, or `gateway` | | `dome.tenant_id` | Tenant UUID | | `dome.org_id` | Org UUID | | `dome.workspace_id` | Workspace UUID | | `dome.agent_id` | Agent UUID (agent callers) | | `dome.gateway_id` | Gateway UUID (gateway callers) | | `dome.platform_user_key_id` | Platform API key UUID (platform-user callers) | Logs also carry the OTel `trace_id`, so a single query (`dome.tenant_id="..."`) returns the full request story across spans and logs for that tenant. ### Product metrics Eight product-level instruments emit on the global OTel meter. Counters omit `tenant_id` to keep cardinality bounded. Pivot per-tenant via the span attributes above. | Instrument | Type | Tags | Source | | ---------------------------------- | ---------------- | ----------------------------------------------------------------------------------------- | ------------------------------ | | `dome.authz.decisions` | Counter | `decision` (`allow`\|`deny`) | Every authorization evaluation | | `dome.authz.deny` | Counter | `reason` (`permission_denied`\|`wrong_caller_type`\|`tenant_mismatch`\|`unauthenticated`) | Every deny audit event | | `dome.audit.events_ingested` | Counter | `event_type` | Audit ingest endpoint | | `dome.gateway.tool_calls` | Counter | `tool_name`, `outcome` (`allowed`\|`denied`\|`error`) | Gateway tool calls | | `dome.gateway.sync_cycle.duration` | Histogram (s) | `outcome` (`applied`\|`skipped`\|`error`) | Gateway rule-sync cycle | | `dome.rules.evaluation.duration` | Histogram (s) | `engine`, `scope_kind` | Authorization rule evaluation | | `dome.agents.active` | Observable gauge | `tenant_id` | Active agents per tenant | ### Sampling and cost Trace volume is head-sampled at 1% by default. Boilerplate database spans from `otelpgx` (transaction state, pool acquire, prepare statements) are dropped at the source. They accounted for \~99% of trace volume in early staging. Tail-sampling decisions belong at your collector. ```bash theme={"system"} # Local dev: full traces, full logs export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 export OTEL_TRACES_SAMPLER_ARG=1.0 ``` ## Configure product usage telemetry Stream product-usage events to your own analytics backend to see which features each tenant exercises. Telemetry is separate from audit. It answers "which orgs are active, which features get used, where do users get stuck", not "what did this agent do". Use audit for compliance evidence. Use telemetry for product analytics. Telemetry is best-effort: events drop on queue overflow, sink errors, or shutdown, and the request path is never blocked or failed because of telemetry. Telemetry is **opt-in** and **off by default**. Set `USAGE_TELEMETRY_ENABLED=true` on the api-server and gateway processes to turn it on. Any other value, including unset, installs a no-op client and emits nothing. ### Sinks Select a sink with `USAGE_TELEMETRY_SINK`. Leave it unset to use the env-driven default. | Sink | When to use | Default for | | -------- | ------------------------------------------------------------------------------------- | --------------------------------------------- | | `logger` | Local development. Emits one `product_usage_event` line per event at **DEBUG** level. | `DOME_ENV=local`, `dev`, `test` | | `otlp` | Production. Ships events as OTLP HTTP/logs to your collector. | — (explicit) | | `noop` | Disable without unsetting the feature. | Fallback when no other sink can be configured | A misconfigured sink (missing token, unreachable endpoint at startup, malformed headers) falls back to `noop` and logs the error. Startup never fails because of telemetry. ### Configure Set the telemetry envvars in the api-server and gateway environments. One client per process. The same configuration applies to both. | Env var | Required | Description | | ------------------------------- | ---------- | ----------------------------------------------------------------------------------------- | | `USAGE_TELEMETRY_ENABLED` | yes | Opt-in switch. `1`, `true`, `yes`, `on`, `enabled` turn it on. Anything else disables it. | | `USAGE_TELEMETRY_SINK` | no | `logger` \| `otlp` \| `noop`. Unset uses the env-driven default above. | | `USAGE_TELEMETRY_OTLP_ENDPOINT` | for `otlp` | Collector base URL. `/v1/logs` is appended if the path is empty. | | `USAGE_TELEMETRY_OTLP_HEADERS` | no | Comma-separated `key=value` headers (e.g. collector auth). | | `USAGE_TELEMETRY_PII_HASH_KEY` | no | HMAC-SHA256 key for hashing sensitive identifiers in future events. Secret. | Local development (logger sink, visible at DEBUG): ```bash theme={"system"} export DOME_ENV=local export DOME_LOG_LEVEL=debug export USAGE_TELEMETRY_ENABLED=true ``` Self-hosted OTLP collector: ```bash theme={"system"} export USAGE_TELEMETRY_ENABLED=true export USAGE_TELEMETRY_SINK=otlp export USAGE_TELEMETRY_OTLP_ENDPOINT={{COLLECTOR_URL}} export USAGE_TELEMETRY_OTLP_HEADERS=Authorization=Bearer {{COLLECTOR_TOKEN}} ``` ### Event coverage Telemetry covers two surfaces: **lifecycle** events when resources are created, changed, or removed, and **runtime** events on every request the gateway evaluates. | Event | When it fires | | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | | `org.created` | Organization created | | `tenant.created` | Tenant created | | `workspace.created`, `workspace.deleted` | Workspace lifecycle | | `agent.registered`, `agent.activated`, `agent.updated`, `agent.suspended`, `agent.reactivated`, `agent.revoked`, `agent.deleted` | Agent lifecycle | | `api_key.created`, `api_key.revoked`, `api_key.rotated` | API key lifecycle | | `mcp_connection.created`, `mcp_connection.updated`, `mcp_connection.deleted` | Gateway MCP backend lifecycle | | `llm_connection.created`, `llm_connection.updated`, `llm_connection.deleted` | LLM backend lifecycle | | `rule_bundle.deployed`, `rule_bundle.rolled_back`, `cedar_rules.deleted` | Rule bundle lifecycle | | `credential_link.issued`, `credential_link.consumed` | Per-user credential magic links | | `oauth.consent_granted` | OAuth consent completed | | `llm_pool.created`, `llm_pool.defaulted` | LLM pool lifecycle | | Event | When it fires | Key fields | | --------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | | `rule_evaluation.completed` | Every authorization decision | `decision` (`allow`\|`deny`), `reason_class`, `action`, `resource_type`, `duration_bucket`, `trace_id` | | `tool_call.completed` | Every tool call, allowed or denied | `result` (`success`\|`denied`\|`failure`), `tool`, `backend`, `duration_ms`, `status_code`, `fields_redacted`, `trace_id` | | `llm_call.completed` | Every LLM dispatch | `result`, `model`, `pool`, `duration_ms`, `status_code`, `trace_id` | | `tool_list.completed` | Every `tools/list` MCP call | `result`, `backend`, `tool_count`, `trace_id` | Each runtime event carries `trace_id` so you can join a usage event to the matching audit event for the same call. ### Event envelope Every event, regardless of sink, has the same normalized shape: ```json theme={"system"} { "name": "tool_call.completed", "version": 1, "timestamp": "2026-05-15T14:22:18.041Z", "scope": { "org_id": "org_...", "tenant_id": "tenant_...", "workspace_id": "ws_..." }, "entity": { "type": "agent", "id": "agent_..." }, "properties": { "result": "success", "tool": "database-query", "backend": "my-mcp-server", "duration_ms": 142, "trace_id": "trace_..." } } ``` Add a new field to an existing event freely. Additive changes do not bump `version`. Renaming or repurposing a field requires a new event name or a version bump. ### PII handling Telemetry drops sensitive properties before any sink sees them. Property keys matching `email`, `username`, `password`, `secret`, `token`, `prompt`, `response`, `arguments`, or `args`, and keys containing `oauth_code`, `tool_result`, `user_name`, `raw_*`, or `act_as_subject`, are stripped at the client. Runtime constructors never accept raw tool args, prompts, or model responses in the first place. Putting PII under a non-matching key name does not bypass this. The package is the second line of defense. ### Tuning behavior V1 ships with fixed batching parameters: 100 events per batch, 10 s flush interval, 10,000-event in-memory queue, 5 s send timeout, 2 retries with 250 ms backoff. These are not configurable via env in V1. * **Queue full** → event drops, warn log, `dropped` counter increments. * **Sink error** → batch retries once, then drops, error log, `dropped` increments by batch size. * **Shutdown** → queue drains, final batch flushes, sink shuts down. ## Next steps * [Audit events](/operate/audit) for historical queries, exports, and the full event envelope * [Subscribe to Events](/operate/webhooks) to push signed events to external systems * [Set Usage Limits](/govern/quotas) when quota exhaustion shows up in the stream # Webhooks Source: https://docs.domesystems.ai/operate/webhooks Subscribe to Dome events and deliver signed webhooks to your endpoints or off-the-shelf providers, with retries and replay Refer to [Webhooks](/concepts/audit/webhooks) concept for how signed delivery works. Refer to [Events](/reference/events#webhooks) reference for the wire contract. ## Overview A **subscription** matches [Events](/reference/events#webhooks) by type and filter. A **destination** receives them as signed HTTP POSTs. Create both in one call with `subscriptions create`, or attach a second subscription to an existing destination. Providers and custom URLs use the same model. Discover provider fields with `dome webhooks providers list`. Operating limits (body cap, rate, retention) are on [Webhooks](/concepts/audit/webhooks#operating-limits). The typical workflow is: 1. [Create a subscription](#create-a-subscription) and optional [filters](#filters). 2. [Test](#test-a-subscription) the destination, then [verify signatures](#verify-a-signature) on custom HTTPS endpoints. 3. [Inspect](#inspect-deliveries) attempt history and [replay](#replay-deliveries) failures when needed. 4. [Rotate the signing secret](#rotate-the-signing-secret) when credentials change. ## Requirements Before you begin, authenticate to Dome and select a workspace. ### Permissions Webhook operations require platform permissions. People and scoped API keys call the webhooks API. Agent credentials cannot. Most roles can inspect subscriptions and deliveries. Creating destinations, rotating secrets, and changing subscriptions need `eventing.manage`. Replay re-sends requests to your systems and can trigger downstream automation, so it uses a separate `eventing.replay` permission. | Default roles | Permission | Grants | | ------------------- | ----------------- | --------------------------------------------------------------------- | | All workspace roles | `eventing.view` | List and inspect subscriptions, deliveries, providers | | `admin`, `operator` | `eventing.manage` | Create, update, disable, delete, rotate secrets, test, cancel retries | | `admin`, `operator` | `eventing.replay` | Single and bulk replay | ## Create a subscription Create a subscription and its destination atomically. A failed request leaves neither record behind. The response reveals the destination's `whsec_…` signing secret **once**. Store it before moving on. It is only rotatable afterward, never readable. On the API, pass either `delivery_endpoint` (create a new destination) or `destination_id` (attach to an existing one). Never both. Standalone destination RPCs (`ListDestinations`, `CreateDestination`, `UpdateDestination`, `RotateDestinationSecret`, `DeleteDestination`) support shared-destination setups. CLI and MCP create the destination inline with the subscription. The dashboard can also pick an existing destination. Requires `eventing.manage`. Custom HTTPS endpoint: ```bash theme={"system"} dome webhooks subscriptions create alerts \ --url https://ops.example.com/hooks/dome \ --event-type agent.suspended \ --event-type authorization.rule_bundle.deployed ``` Slack via incoming-webhook URL: ```bash theme={"system"} dome webhooks subscriptions create slack-alerts \ --provider slack \ --setting webhook_url=https://hooks.slack.com/services/T.../B.../XXXX \ --event-type access.denied \ --event-type agent.suspended ``` For provider-specific settings, credentials, shared destinations, MCP, and API examples, see the [webhooks CLI reference](/cli/operate/webhooks). ### Filters You can narrow which events deliver. An event must satisfy every filter that applies to its type. A filter constrains only event types that carry its field. Exact match ```bash theme={"system"} dome webhooks subscriptions create prod-only \ --url https://... --event-type agent.suspended \ --filter 'agent_name=checkout-worker' ``` Glob pattern (`*` any run, `?` one char, full-value match) ```bash theme={"system"} dome webhooks subscriptions create demo-tools \ --url https://... --event-type mcp.tool_call.completed \ --filter 'tool~=demo-mcp/*' ``` OR set (repeat the same key) ```bash theme={"system"} dome webhooks subscriptions create critical-agents \ --url https://... --event-type agent.suspended \ --filter 'agent_name=checkout-worker' \ --filter 'agent_name=billing-worker' ``` Filter keys: `agent_name`, `connection_name` (resolved from IDs at delivery time), `agent_id`, `tool_name`, plus each event type's own filterable payload fields. The dashboard Event catalog marks them. Repeated keys OR. Distinct keys AND. Caps: 16 keys, 16 alternatives per key, 256-byte patterns with up to 8 wildcards. Per-request security decisions (`mcp.tool_call.completed`, `mcp.tool_result.filtered`, `llm.output.filtered`, `access.denied`) are high volume. Point them at a log sink rather than a chat channel, and narrow with filters. ## Test a subscription Send a fixed `eventing.test` event through a subscription's destination to verify signing, headers, and the destination's own routing. Requires `eventing.manage`. ```bash theme={"system"} dome webhooks subscriptions test alerts ``` The command prints the queued delivery ID. Check the outcome with `dome webhooks deliveries get `. Reference: [`dome webhooks subscriptions test`](/cli/operate/webhooks#subscriptions-test) Tool: `dome_webhooks_subscription_test` Reference: [`dome_webhooks_subscription_test`](/reference/mcp/webhooks#subscription-test) ```text title="Test a webhook subscription" theme={"system"} Send a test event through the webhook subscription named alerts. ``` ## Verify a signature Verify custom HTTPS deliveries with any Standard Webhooks library. Return `2xx` within 10 seconds to acknowledge. Acknowledge first, process async. Slow handlers time out and burn retry budget. ```javascript theme={"system"} import { Webhook } from "standardwebhooks"; const wh = new Webhook(process.env.DOME_WEBHOOK_SECRET); // whsec_... app.post("/webhooks/dome", express.raw({ type: "application/json" }), (req, res) => { try { const event = wh.verify(req.body, req.headers); // Dedupe on req.headers["dome-event-id"], then process async. res.status(202).send(); } catch { res.status(400).send("invalid signature"); } }); ``` ```python theme={"system"} from standardwebhooks.webhooks import Webhook wh = Webhook(os.environ["DOME_WEBHOOK_SECRET"]) # whsec_... @app.post("/webhooks/dome") def receive(request): try: event = wh.verify(request.body, dict(request.headers)) # Dedupe on request.headers["dome-event-id"], then process async. return HttpResponse(status=202) except Exception: return HttpResponse(status=400) ``` ## Inspect deliveries Inspect delivery attempt history, including the retained body while it is available. Requires `eventing.view`. Failed deliveries in a time window: ```bash theme={"system"} dome webhooks deliveries list --status failed --start 2026-07-14T00:00:00Z ``` One delivery with attempt history and the retained body: ```bash theme={"system"} dome webhooks deliveries get --body ``` Reference: [`dome webhooks deliveries list`](/cli/operate/webhooks#deliveries-list), [`get`](/cli/operate/webhooks#deliveries-get) Tools: `dome_webhooks_delivery_list`, `dome_webhooks_delivery_get`. Reference: [`dome_webhooks_delivery_list`](/reference/mcp/webhooks#delivery-list), [`dome_webhooks_delivery_get`](/reference/mcp/webhooks#delivery-get) ```text title="List failed webhook deliveries" theme={"system"} List failed webhook deliveries since 2026-07-14T00:00:00Z. ``` ## Replay deliveries Replay one delivery or bulk-replay failures in a window. A replay creates a **new** delivery from the retained body (fresh `webhook-id`, same `Dome-Event-Id`) so consumers that dedupe correctly ignore the overlap. Bulk replay processes up to 500 deliveries per run. Repeat to continue. Canceled deliveries are never recovered. Requires `eventing.replay`. Replay one delivery: ```bash theme={"system"} dome webhooks deliveries replay ``` Bulk-replay terminal failures from the last 24 hours: ```bash theme={"system"} dome webhooks deliveries replay-failed --since 24h ``` Narrow by subscription and status: ```bash theme={"system"} dome webhooks deliveries replay-failed --since 7d --subscription alerts --status failed,dlq ``` `replay-failed` prints `matched · replayed · skipped (body expired) · failed`. It is safe to re-run: deliveries with a live or succeeded replay are not matched again. Reference: [`dome webhooks deliveries replay`](/cli/operate/webhooks#deliveries-replay), [`replay-failed`](/cli/operate/webhooks#deliveries-replay-failed) Tools: `dome_webhooks_delivery_replay`, `dome_webhooks_delivery_replay_failed`, `dome_webhooks_delivery_cancel`. ```json theme={"system"} { "name": "dome_webhooks_delivery_replay_failed", "arguments": { "since": "24h", "subscription": "alerts" } } ``` Reference: [`dome_webhooks_delivery_replay_failed`](/reference/mcp/webhooks#delivery-replay-failed) ```http theme={"system"} POST /dome.eventing.v1.EventingService/ReplayFailedDeliveries Content-Type: application/json { "workspace_id": "ws_...", "since": "2026-07-14T00:00:00Z", "destination_id": "dst_...", "statuses": ["failed", "dlq"], "max_deliveries": 500 } ``` The response returns `matched`, `replayed`, `skipped_body_expired`, and `failed` counts plus `replay_delivery_ids`. Also available: `ReplayDelivery`, `CancelDeliveryRetry`. Reference: Eventing `ReplayFailedDeliveries` RPC ```text title="Replay failed webhook deliveries" theme={"system"} Replay all failed webhook deliveries from the last 24 hours. ``` ## Rotate the signing secret Rotate a destination's signing secret without dropping deliveries. For 24 hours, the `webhook-signature` header carries a signature per key (current first, previous second) so a consumer verifying with either secret keeps working. Update your verifier to the new secret within the 24-hour grace window. Requires `eventing.manage`. ```bash theme={"system"} dome webhooks subscriptions rotate-secret alerts ``` The command prints the new signing secret once (`whsec_…`) with its key ID. Reference: [`dome webhooks subscriptions rotate-secret`](/cli/operate/webhooks#subscriptions-rotate-secret) Tool: `dome_webhooks_subscription_rotate_secret` Reference: [`dome_webhooks_subscription_rotate_secret`](/reference/mcp/webhooks#subscription-rotate-secret) ```text title="Rotate a webhook signing secret" theme={"system"} Rotate the signing secret for the webhook subscription named alerts. ``` ## Next steps * [Audit events](/operate/audit) for the durable record * [Export Data](/manage/export) for scheduled batch export * [Stream Live Events](/operate/observe) for live Audit v1 streaming # Personas Source: https://docs.domesystems.ai/personas Identify your role and find relevant docs Pick the persona that matches your job. Each path lists where to start and a hands-on tutorial. ## Developer Developers own the agent as a workload. They register it, mint credentials, point the client at a Gateway, and verify governed calls. They also wire Dome into the application when the agent runs in product code. * [Agent identity](/concepts/agents/identity) and [lifecycle](/concepts/agents/agent-lifecycle) * [Register agents](/connect/agents) and issue API keys * [Standing or delegated identity](/concepts/identity-patterns) when the agent acts for end users * [Develop](/develop) to authenticate, call tools and models, and handle denials * [Audit events](/operate/audit) to confirm outcomes Hands-on: [Developer tutorial](/tutorials/role/developer). ## Security Security owns what each agent may call, what content may leave a backend, and whether the audit trail can prove it. They author and simulate Rules, apply Guards, investigate denials, and export evidence. * [Authorization model](/concepts/architecture/authorization-model) and [scope hierarchy](/concepts/platform/scopes) * [Rules](/govern/rules), [simulate](/govern/rules/simulate), and [Guards](/govern/guards) * [Quotas](/govern/quotas) when spend is a security control * [Audit events](/operate/audit) and [Export Data](/manage/export) Hands-on: [Security tutorial](/tutorials/role/security). ## Operator Operators own tool and model connections, Gateways, grants, and the live signals that show the platform is healthy. They attach backends, expose Gateways, grant agents access, and triage live traffic. * [Prepare environments](/manage/environments) and [scope hierarchy](/concepts/platform/scopes) * [Tools](/connect/resources/tools), [models](/connect/resources/models), and [Gateways](/connect/gateways) * [Stream Live Events](/operate/observe) * [Settings](/manage/settings) and [integrations](/manage/integrations) Hands-on: [Operator tutorial](/tutorials/role/operator). ## Platform admin Platform admins own tenancy, membership, who may administer Dome, and whether the estate stays operable and auditable as teams grow. They set access boundaries, prepare environments, and run export for oversight. * [Scopes](/concepts/platform/scopes) and [permissions](/concepts/platform/permissions) * [Manage access](/manage/access) and [prepare environments](/manage/environments) * [Stream Live Events](/operate/observe) and [Export Data](/manage/export) * [Settings](/manage/settings) and [integrations](/manage/integrations) Hands-on: [Platform Admin tutorial](/tutorials/role/platform-admin). ## Next steps * [Tutorials](/tutorials) — Get Started and Role track * [What is Dome?](/what-is-dome) if you want the product overview first * [Setup AI assistant](/setup-ai-assistant) if you want an MCP-connected assistant while you work # Quickstart Source: https://docs.domesystems.ai/quickstart Quickstart # Guards Source: https://docs.domesystems.ai/reference/controls/guards Guard types, Filter kinds and actions, path syntax, tool payload shapes, and streaming window layers This page lists Guard types and the catalogs that back them. Filters are the Guard type available today; validators are coming soon. For how content inspection works, see the [Guards](/concepts/controls/guards) concept. To configure Guards, see [Filter responses](/govern/guards). ## Filters Filters inspect and rewrite model or tool responses before they return to the agent. ### Kinds | Kind | Connection | Matchers | Actions | | ------ | ---------- | ------------------------------------------ | ------------------------- | | `text` | Model | Substring, SSN, credit card, phone, digits | `redact`, `block` | | `json` | Tool | JSON field path | `redact`, `omit`, `block` | Filter kind is immutable after create. ### Actions and precedence | Action | Effect | | --------------------------------- | -------------------------------------------------------- | | `redact` / `FILTER_ACTION_REDACT` | Rewrites the matched value with a redaction sentinel | | `omit` / `FILTER_ACTION_OMIT` | Removes the key and value (JSON only) | | `block` / `FILTER_ACTION_BLOCK` | Withholds the whole message and short-circuits the chain | When more than one JSON action targets the same path, precedence is `BLOCK` > `OMIT` > `REDACT`. ### Path syntax JSON Filter `matcher.path` values use a locked-down dialect. It is not JSONPath. | Form | Matches | Example | | ----------------------------------- | -------------------------------------------------- | ----------------------------------------------------------- | | `key` | A top-level field | `email` | | `a.b` | A nested field at an exact path | `user.email` | | `a.b[3].c` | A specific array element by index | `contacts[0].phone` | | `a.b.c` against an array of objects | Every element (implicit fan-out) | `employees.ssn` redacts the field on every employee | | `**.key` or `**.a.b` | The trailing path at **any depth** in the response | `**.phone` redacts every `phone` field, nested at any level | Exact-path matching is the default. The `**.` prefix redacts a field by name wherever it appears. Recursive descent (`**.phone`) fits when the field name is a reliable sensitivity signal regardless of depth, when response shapes vary across tools, or when enumerating every exact path is brittle. Exact paths (`primary_contact.phone`) fit when only specific occurrences are sensitive and a same-named field elsewhere should pass through. `**` is only recognized as a leading prefix. Mid-path `**` (for example `a.**.b`) is treated as literal key characters and never matches. There is no JSONPath `..`, no wildcards, and no slice ranges. ### Tool payload coverage JSON Filters traverse every tool-result shape the MCP server can return. | Payload | Coverage | | ------------------- | --------------------------------------------- | | Content body | Raw JSON object or array | | Content items | Top-level MCP content array | | Embedded JSON | JSON encoded inside `text` or `resource.text` | | `structuredContent` | Structured mirror attached by the MCP server | Field matchers apply at any depth. A matching `BLOCK` short-circuits the Guard chain and replaces the result with a safe placeholder. The audit event identifies the blocking Filter. ### Streaming window The effective window at stream time is `max(workspace floor, connection override, per-request override)`. Any layer set to `0` drops out of the max. | Layer | Where it's set | Notes | | --------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | Workspace floor | **Settings → Config** in the dashboard, or `UpdateWorkspaceLLMFilterWindow` on the API | Bytes ≤ 1 MiB, tokens ≤ 4096. | | Per-connection | `--filter-window-bytes` / `--filter-window-tokens` on `dome model add` or `dome model update` | `0` inherits the workspace floor. On `update`, passing `0` with the flag present clears the override back to the floor. | | Per-request | `_dome.filter_window_bytes` / `_dome.filter_window_tokens` in the OpenAI or Anthropic request body | The `_dome` key is stripped before the request reaches the upstream provider. | Workspace floor fields are `llm_filter_window_bytes` and `llm_filter_window_tokens`. Per-connection flags live on [Models](/connect/resources/models). Setting the layers is on [Configure the streaming window](/govern/guards#configure-the-streaming-window). ## Next steps * [Guards](/concepts/controls/guards) concept for Filters, directions, and fail-closed behavior * [Configure Guards](/govern/guards) to create, assign, and tune windows * [Models](/connect/resources/models) for per-connection filter windows # Quotas Source: https://docs.domesystems.ai/reference/controls/quotas Quota subjects, windows, model Quota options, and related audit events This page lists subject types and exhaustion behavior, reset windows, model Quota options, and audit event names. For how spend caps work, see the [Quotas](/concepts/controls/quotas) concept. To create and manage Quotas, see [Set Usage Limits](/govern/quotas). ## Subjects | Subject | What the Quota covers | Exhaustion behavior | | --------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | Workspace | Aggregate governed LLM usage in one workspace | Rejects affected calls with HTTP `429` | | Agent | Calls made by one agent | Rejects that agent's affected calls with HTTP `429` | | Act-as | Calls made under one verified end-user OIDC subject | Rejects that subject's affected calls with HTTP `429` | | Gateway | Every LLM call routed through one Gateway | Rejects calls through that Gateway with HTTP `429` | | Pool | Every call routed through one model pool | Rejects calls to that pool with HTTP `429` | | Model | Calls dispatched to one model, globally or within one pool | Removes the exhausted model from pool routing when another member remains; otherwise rejects with HTTP `429` | A model Quota can also use **per-caller** scope. Each agent and each verified end user then receives an independent budget against that model. Quota scope is cumulative. A request proceeds only when every applicable limit has remaining budget. ## Windows Cost limits are stored in micro-USD. CLI, MCP, and dashboard expose them in USD. On the API, `1000000000` micro-USD is \$1,000.00. | Window | Reset | | --------- | -------------------------------- | | `daily` | UTC midnight | | `monthly` | Start of each UTC calendar month | The default window on create is `monthly`. One Quota may exist for each subject identity and window pair. ## Model Quotas A model Quota can be global, limited to one pool, or per-caller: | Option | What it counts | | --------------- | ----------------------------------------------------- | | Global | Direct calls and calls through every pool | | Within one pool | Only dispatches from that pool | | Per-caller | Separate counter for each agent and verified end user | Exhausted within-pool model Quotas spill to another eligible pool member when one remains. Direct exhausted model calls do not spill. Refer to [Quotas](/concepts/controls/quotas#exhaustion-reject-or-spill-inside-a-pool) concept. ## Observability | Event | When it fires | | ------------------------------------- | -------------------------------------------------- | | `llm.model_call.completed` | Completed model call with token counts | | `access.denied` with `QUOTA_EXCEEDED` | Quota exhaustion (includes exhausted subject type) | | `llm.quota.created` | Quota created | | `llm.quota.updated` | Quota updated | | `llm.quota.deleted` | Quota deleted | ## Next steps Create Quotas from the how-to. Routing and Act-As subjects show up in pools and delegated agents: * [Quotas](/concepts/controls/quotas) concept for cumulative scopes, spillover, and passthrough * [Set Usage Limits](/govern/quotas) to create, update, list, and remove Quotas * [Pools](/connect/resources/models/pools) for routing strategies and failover * [Delegated agents](/connect/agents/delegated) for Act-As subjects * [Events](/reference/events) reference for the broader audit catalog # Rules Source: https://docs.domesystems.ai/reference/controls/rules Entity types, actions, attributes, constraint forms, and argument keys for Dome Rules This page lists `Dome::*` entity types, actions the gateways set, attributes and argument keys Rules can read, and constraint forms. It does not catalog customer-owned `resource.` vocabulary. Dome Rules are based on [Cedar](https://www.cedarpolicy.com/) policies. For how evaluation works, see the [Authorization Model](/concepts/architecture/authorization-model) concept. To write and deploy Rules, see [Authorize Access](/govern/rules). ## Entity types Each request maps to one principal, one action, and one resource. Conditions may also read `context`. | Entity | Type | Role | | ------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------- | | **Principal** | `Dome::Agent` | Authenticated agent. Carries `capabilities`, `metadata`, and optionally verified `act_as` claims. | | **Action** | `Dome::Action` | Operation the gateway set for this request. | | **Resource** | `Dome::MCPTool` or `Dome::LLMModel` | Target tool or model. | | **Context** | map | Request-time metadata (timestamp, source IP, and related fields) available to `when` / `unless`. | A specific entity takes the form `Dome::::""`. An agent's identifier is its UUID. A tool's identifier is `CONNECTION_NAME/TOOL_NAME`. ## Actions Actions are string literals. There is no separate registration step. The gateway that receives the request sets the action. There is no default action. | Gateway | Action | Wire `resource_type` | Resource entity | | ------- | ----------------------------------------------------------------- | -------------------- | ---------------- | | MCP | `mcp:call` | `mcp_tool` | `Dome::MCPTool` | | MCP | `mcp:discover` | `mcp_tool` | `Dome::MCPTool` | | LLM | `llm:invoke` (chat / completion / responses) | `llm_model` | `Dome::LLMModel` | | LLM | `llm:embed` | `llm_model` | `Dome::LLMModel` | | LLM | `llm:moderate` | `llm_model` | `Dome::LLMModel` | | LLM | `llm:list-models` | `llm_model` | `Dome::LLMModel` | | LLM | `llm:count-tokens` (Anthropic pre-flight token counting) | `llm_model` | `Dome::LLMModel` | | LLM | `llm:passthrough` (opaque-body forwarding to a `custom` provider) | `llm_model` | `Dome::LLMModel` | `llm:count-tokens` is distinct from `llm:invoke`, so token counting can be allowed broadly while invocation stays tightly gated. `llm:passthrough` is likewise distinct from `llm:invoke`. The LLM protocol endpoint catalog lives on the [LLM gateway](/concepts/gateways/llm-gateway#endpoints) concept. ## Constraint forms Inside the parentheses, a Rule names three constraints in order: `principal`, `action`, `resource`. | Constraint | Available forms | Example | | ----------- | ------------------- | -------------------------------------------------------------------- | | `principal` | Omitted, `is`, `==` | `principal == Dome::Agent::"AGENT_ID"` | | `action` | Omitted, `==`, `in` | `action in [Dome::Action::"mcp:call", Dome::Action::"mcp:discover"]` | | `resource` | Omitted, `is`, `==` | `resource is Dome::MCPTool` | An omitted constraint matches every value. `==` matches one entity. `is` matches every entity of one type. `in` with a list matches several actions. ## Principal attributes | Attribute | Type | Description | | --------------- | -------------- | ----------------------------------------------- | | `capabilities` | set of strings | Agent capability tags | | `metadata` | record | Custom key-value pairs | | `act_as.sub` | string | End-user ID when act-as is present and verified | | `act_as.email` | string | End-user email | | `act_as.roles` | set of strings | End-user roles | | `act_as.groups` | set of strings | End-user groups | | `act_as.claims` | record | Additional customer-defined claims | Act-as attributes are available only when the act-as header is present and verified. Claim names and verification methods for setup are on [Delegated agents](/connect/agents/delegated#identity-claims). ## Resource attributes Reserved structural attributes cannot be overridden. | Attribute | Type | Source | | ---------- | ------------------------------- | ------------------------------------------------------------------------ | | `type` | string | Resource entity type (`mcp_tool`, `llm_model`) | | `name` | string | Resource name (tool or model identifier) | | `gateways` | set | Gateway IDs that include this resource (stamped at sync from membership) | | `` | string, bool, long, set, record | Any top-level key in the connection's `attributes` JSONB | The custom attribute namespace is customer-owned. Dome does not define keys such as `pii_certified` or `data_locality`. Those come from whatever your team stores on the connection. A Gateway grant can test membership with `resource.gateways.contains("")`. Configure membership on [Gateways](/connect/gateways#resource-membership). Supported value shapes: strings, booleans, whole-number JSON numbers (`Long`), homogeneous sets, and nested records. Fractional numbers, `null`, and mixed-type lists are dropped. Reserved keys (`type`, `name`, `arguments`) are ignored if present in `attributes`. ### LLM-injected attributes For LLM actions, Dome also injects server-side keys that describe the upstream actually dispatched. | Attribute | Meaning | | -------------------------- | -------------------------------------------------------------------------------------- | | `resource.name` | Model or pool name the request asked for. Unchanged by pool resolution. | | `resource.resolved_model` | Upstream native model id that will actually be called. | | `resource.connection_name` | Resolved connection's name. | | `resource.pool` | Pool that routed the call, or absent when the request addressed a connection directly. | `resolved_model`, `connection_name`, and `pool` win over any same-named key in `attributes`. On failover, each candidate upstream is evaluated against its own attributes and resolved model. Use `resource.pool` to grant every model behind a pool without enumerating members. Direct connection calls leave `resource.pool` unset, so a pool grant does not authorize them. Ingress behavior is on the [LLM gateway](/concepts/gateways/llm-gateway#authorization) concept. ## Per-call arguments Values that change per call appear as `resource.arguments.`. | Gateway | Keys under `resource.arguments` | | ----------- | ----------------------------------------------------------------------------------------------------------------------- | | MCP | Top-level keys of the `tools/call` `arguments` object | | LLM (chat) | `model`, `stream`, `message_count`, `max_tokens`, `temperature`, `top_p`, `tool_choice`, `tool_names`, `stop_sequences` | | LLM (embed) | `model`, `input_count`, `user` | Guard with `resource has arguments` before reading `resource.arguments.`. The attribute is absent when the call sends no arguments, an empty object, or a non-object payload. Dereferencing a missing attribute errors the policy out, so the Rule does not fire. Argument values keep their JSON types when they are strings, booleans, whole-number integers, homogeneous lists, or nested records. Fractional numbers (including `temperature` such as `0.7`), `null`, and mixed-type lists are dropped, so `resource.arguments has ` returns false for those args. Message bodies and embedding inputs are not surfaced. `resource.arguments` is distinct from `context`: * `resource.arguments.*` is what the call carries from the agent. * `context.*` is request-time metadata Dome attaches to the evaluation. By default the audit event for a call does not persist `resource.arguments`, because tool payloads are PII-shaped. Rule evaluation still reads them. Contact support to enable `data.arguments` capture on a workspace. ## Evaluation outcomes | Situation | Decision | | -------------------------------------------------- | --------------------- | | Any matching `forbid` | Denied | | No `forbid`, at least one matching `permit` | Allowed | | No rule matches | Denied (default-deny) | | Evaluator error, stale policy, or no policy loaded | Denied | `forbid` always wins over `permit`, regardless of scope. Refer to [Authorization Model](/concepts/architecture/authorization-model) concept for effective policy assembly and fail-closed detail. ## Next steps Write and simulate against this catalog: * [Authorization Model](/concepts/architecture/authorization-model) concept for permit/forbid semantics and effective policy * [Authorize Access](/govern/rules) to write, validate, simulate, and apply Rules * [Simulate](/govern/rules/simulate) to probe decisions before deploy * [Permissions](/reference/permissions) reference for platform RBAC (separate from Rules) # Events Source: https://docs.domesystems.ai/reference/events Audit v1 and webhook event contracts: envelopes, enums, catalogs, filters, and headers This page lists event types, shared envelope fields, and filter operators. Typed `payload` / `data` field schemas are defined per event type in the product API and dashboard catalog. For how the one trail works, see the [Audit events](/concepts/audit) concept. To query and reconstruct events, see [Audit events](/operate/audit). | Contract | Schema | Surfaces | | --------------------------------- | ----------------------------------- | ------------------------------------------- | | Audit v1 | Full envelope + typed `payload` | Query, stream, export, ingest | | Webhooks | `dome.webhook.v1` envelope + `data` | Signed HTTP delivery | | OpenTelemetry / product telemetry | Separate models | Observability only; not compliance evidence | ## Audit v1 Audit v1 is the durable workspace event trail. Every query, stream, export, and ingest record uses this envelope and type catalog. ### Envelope Every audit event uses this envelope. Envelope fields are filterable without inspecting `payload`. The shape is identical across CLI, MCP, SDK, dashboard, stream, query, and export. | Field | Type | Description | | ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------- | | `event_id` | UUID | Globally unique event identifier | | `event_type` | string | Concrete event (for example `mcp.tool_call.completed`) | | `event_class` | enum | `governing`, `state_change`, `security`, `audit_access` | | `result` | enum | `attempted`, `succeeded`, `denied`, `failed`, `filtered`, `noop`, `permitted` | | `occurred_at` | timestamp | When the event happened at the producer | | `accepted_at` | timestamp | When the audit pipeline durably accepted it | | `scope` | object | `organization_id`, `tenant_id`, `workspace_id`, `agent_id` | | `actor` | object | `kind`, `id`, `display`, `email`, plus [caller authentication context](#caller-authentication-context) | | `identity_chain` | object | Delegation hops; act-as hops carry [end-user attributes](#act-as-end-user-attributes) | | `primary_resource` | object | `kind` + `id` of the resource the event is about | | `related_resources` | object\[] | Additional resources referenced by the event | | `request_surface` | object | Origin surface and [network context](#network-and-transport-context); unset on many runtime authorization decisions | | `correlation` | object | `trace_id`, `activity_id`, `activity_trust`, `idempotency_key` — refer to [Correlation](#correlation) | | `operation_id` | UUID | Stable ID for a multi-event operation | | `parent_event_id` | UUID | The event that caused this one | | `payload` | object | Event-specific typed payload (`dome.audit.v1.V1`) | | `data_handling` | object | Flags: `omitted`, `summarized`, `truncated`, `redacted`, `hashed`, `encrypted` | The fields above are the complete customer envelope. Internal pipeline metadata is not returned. ### Enum rendering Enum fields (`event_class`, `result`, `actor.kind`, `scope.kind`, `primary_resource.kind`, `request_surface.surface`) render in one of two forms. Filters use the same form the surface returns. | Surface | Enum form | | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | CLI (table and `--format json`), MCP tool results, dashboard, exports (`json`, `jsonl`, `csv`, `ocsf`, `cef`) | Short tokens: `governing`, `denied`, `gateway_mcp`, `rule_bundle` | | gRPC / Connect API and generated SDK clients | Full constants: `EVENT_CLASS_GOVERNING`, `EVENT_RESULT_DENIED`, `INITIATOR_SURFACE_GATEWAY_MCP` | A short token is the constant with its type prefix stripped and lowercased. CLI flags and MCP arguments take short tokens. API request fields take full constants or their integer values. ### Network and transport context Configuration events carry network origin under `request_surface`. | Field | Type | Description | | ----------------- | ------ | ------------------------------------------------------------------------------------- | | `source_ip` | string | Caller IP from the inbound connection, resolved through the trusted-hops boundary | | `user_agent` | string | Raw `User-Agent` header, capped at 512 bytes (`data_handling.truncated` when clipped) | | `http_method` | string | HTTP verb (`GET`, `POST`, …) for HTTP-surfaced requests | | `source_ip_class` | string | Derived zone: `private`, `vpc`, `public`, `unknown` | OCSF exports map `source_ip` to `src_endpoint.ip`; `user_agent` and `http_method` are carried under `unmapped`. CEF exports map them to `src`, `requestClientApplication`, and `requestMethod`. `source_ip` is the **rightmost trusted** entry of `X-Forwarded-For + [connecting peer]`. `DOME_TRUSTED_HOPS` on the api-server and gateway is the number of appending reverse-proxy hops in front of Dome. | `DOME_TRUSTED_HOPS` | Topology | Trusted entry | | ------------------- | ---------------------------------------- | ----------------------------- | | `0` | Dome exposed directly to clients | Connecting peer (XFF ignored) | | `1` *(default)* | Single fronting ALB in XFF `append` mode | Entry the ALB appended | | `2` | `CDN → ALB → Dome` | Entry the inner hop appended | | `N` | N appending hops | Nth from the right | Set `DOME_TRUSTED_HOPS` to match your actual edge. Over-trusting lets a caller forge `source_ip` by injecting headers; under-trusting records the proxy IP instead of the client. Spoofed values reach audit events, OCSF/CEF exports, the dashboard Authority card, and the credential-provision rate-limiter key. The resolver clamps to the leftmost entry when fewer hops are present — it never reads past the boundary. The default `1` assumes one AWS ALB in XFF `append` mode. The ALB appends the connecting client on the right without stripping a caller-supplied `X-Forwarded-For`, so the rightmost entry is the only edge-guaranteed value. ### Caller authentication context `actor` includes how the immediate principal authenticated. | Field | Type | Description | | -------------------- | ------ | ------------------------------------------------------------------------------------ | | `auth_method` | string | `session`, `api_key`, `agent`, `gateway`, `admin_token` | | `session_id` | string | Browser session ID when session-backed | | `api_key_id` | string | Platform or agent API key ID when key-backed | | `api_key_scope_kind` | string | `workspace`, `tenant`, `org`, or `platform` | | `api_key_scope_id` | string | ID of the key's scope | | `assurance_level` | string | Authentication assurance level (for example `aal1`, `aal2`) when surfaced by the IdP | ### Act-as end-user attributes When an agent acts on behalf of an end user, the corresponding `identity_chain` hop carries these fields. Producers cap each list at 32 entries. | Field | Type | Description | | --------------------- | --------------------- | ------------------------------------- | | `roles` | string\[] | End-user roles supplied by the caller | | `groups` | string\[] | End-user groups | | `claims` | `map` | Additional verified claims | | `verification_method` | string | `oidc`, `hmac`, or `none` | ### Correlation | Field | Scope | | ------------------------- | ------------------------------------------------------------ | | `operation_id` | One action phase pair (`attempted` → `succeeded` / `denied`) | | `correlation.trace_id` | One HTTP request cascade | | `correlation.activity_id` | One session, LLM turn, or scripted run across planes | An activity chain is the set of audit events that share one `activity_id` within a workspace. Chains are computed at read time; there is no stored chain object. | `correlation.activity_trust` | Meaning | | ---------------------------- | -------------------------------------------------- | | `server_owned` | Dome set the id. A caller cannot forge this label. | | `caller_asserted` | The id arrived on the `X-Dome-Activity-Id` header. | | `unspecified` | No `activity_id` on this event. | `activity_id` can be set by a CLI run (`--activity` or `DOME_ACTIVITY_ID`), an SDK session, an agent request header, or Dome for browser and assistant threads. Chain reads return only the active workspace's slice, so human-meaningful IDs (`case-12345`) are safe to use. `activity_trust` is provenance metadata, not a grouping key. ### Event classes | Class | Description | | -------------- | ----------------------------------- | | `governing` | Authorization and runtime decisions | | `state_change` | Configuration mutations | | `security` | Authentication front door | | `audit_access` | Reads of the trail itself | ### Event types Types by `event_class`. #### `governing` | Type | Description | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `authorization.decision` | Cedar evaluation produced an allow or deny | | `authorization.act_as.rejected` | Caller cannot impersonate the requested subject | | `authorization.staleness_rejected` | Effective policy too stale to authorize | | `mcp.tool_call.attempted` | Tool call arrived at the gateway | | `mcp.tool_call.completed` | Tool call resolved; `result` is `succeeded`, `failed`, or `denied` | | `mcp.tool_result.filtered` | Tool result fields redacted before return | | `mcp.tools_list.completed` | Agent listed available tools | | `llm.model_call.attempted` | LLM dispatch arrived at the gateway | | `llm.model_call.completed` | LLM dispatch resolved; carries token usage and cost | | `llm.model_stream.started` | First chunk of a streaming response reached the caller | | `llm.model_call.failover` | A candidate failed and the router moved to the next one | | `llm.model_request.filtered` | Filter changed the request before it left the gateway | | `llm.model_result.filtered` | LLM output fields redacted before return | | `llm.models_list.completed` | Agent listed available models | | `assistant.message.attempted` | Assistant message governing event | | `assistant.message.stream.started` | Assistant stream governing event | | `access.denied` | Ingress deny (unauthenticated, wrong caller type, Cedar deny, kill switch, tenant mismatch, exhausted Quota, or no pool/connection matched) | | `credential.required` | Tool call denied; per-user credential missing | Every `llm.model_*` event carries `provider` — the vendor identity from the connection. Filter on `provider` to attribute spend, latency, or denial rate per vendor. Denials on the LLM path may carry `data.reason`: | `data.reason` | Meaning | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------- | | `pool_unknown` | No pool, connection, or `match_when` predicate matched, and the workspace has no default pool. Returned as HTTP `404`. | | `platform_kill_switch` | The platform-wide LLM kill switch is active. | | `workspace_kill_switch` | The workspace's LLM kill switch is active. | | `tenant_mismatch` | The workspace's owning tenant did not match the caller's session tenant. | | `permission_denied` | Cedar denied the call. Failover candidate skips may also carry `data.detail = "cedar_deny"`. | Reasons are short, snake\_case, and stable. Treat unknown values as a generic deny. Runtime handling is on [Develop](/develop#errors-and-denials). Ingress behavior is on the [LLM gateway](/concepts/gateways/llm-gateway) concept. #### `state_change` | Type | Description | | ------------------------------------------- | ----------------------------------------------------------------------------------------- | | `agent.registered` | Agent created | | `agent.activated` | Agent became active | | `agent.updated` | Agent record updated | | `agent.suspended` | Agent suspended | | `agent.reactivated` | Agent reactivated | | `agent.revoked` | Agent revoked | | `agent.deleted` | Agent deleted | | `agent.api_key.created` | Agent API key created | | `agent.api_key.revoked` | Agent API key revoked | | `agent.api_key.rotated` | Agent API key rotated | | `workspace.created` | Workspace created | | `workspace.deleted` | Workspace deleted | | `workspace.config.changed` | Workspace config write. Payload carries `previous_value` and `new_value` per changed path | | `workspace.role.assigned` | Workspace role assigned | | `workspace.role.removed` | Workspace role removed | | `tenant.created` | Tenant created | | `tenant.deleted` | Tenant deleted | | `tenant.role.assigned` | Tenant role assigned | | `tenant.role.removed` | Tenant role removed | | `organization.created` | Organization created | | `organization.updated` | Organization updated | | `organization.deleted` | Organization deleted | | `organization.domain.added` | Organization domain added | | `organization.domain.verified` | Organization domain verified | | `organization.membership.set` | Organization membership set | | `authorization.rule_bundle.deployed` | Rule bundle deployed | | `authorization.rule_bundle.rolled_back` | Rule bundle rolled back | | `authorization.rule_bundle.deploy_failed` | Rule bundle deploy failed | | `authorization.rule_bundle.validated` | Rule bundle validated | | `authorization.rule_bundle.deleted` | Rule bundle deleted | | `authorization.policy_snapshot.applied` | Policy snapshot applied | | `gateway.connection.created` | Gateway connection created | | `gateway.connection.updated` | Gateway connection updated | | `gateway.connection.deleted` | Gateway connection deleted | | `gateway.connection.credential.rotated` | Connection credential rotated | | `gateway.connection.classification.updated` | Field classifications updated | | `llm.pool.created` | LLM pool created | | `llm.pool.updated` | LLM pool updated | | `llm.pool.deleted` | LLM pool deleted | | `llm.pool.default_set` | Default LLM pool set | | `llm.pool.member.added` | LLM pool member added | | `llm.pool.member.removed` | LLM pool member removed | | `mcp.tool.status_changed` | MCP tool status changed | | `mcp.tool_catalog.sync.completed` | MCP tool catalog sync completed | | `mcp.tool_catalog.observed` | MCP tool catalog observed | | `credential.link.issued` | Credential link issued | | `credential.link.consumed` | Credential link consumed | | `credential.oauth.consent_granted` | OAuth consent granted | | `credential.oauth.disconnected` | OAuth disconnected | | `scheduler.job.created` | Scheduler job created | | `scheduler.job.updated` | Scheduler job updated | | `scheduler.job.deleted` | Scheduler job deleted | | `scheduler.job.run.completed` | Scheduler job run completed | | `scheduler.job.run.failed` | Scheduler job run failed | | `platform.api_key.created` | Platform API key created | | `platform.api_key.revoked` | Platform API key revoked | | `integration.instance.deleted` | Integration instance deleted | | `integration.catalog.deleted` | Integration catalog entry deleted | #### `security` | Type | Description | | ----------------------------------- | ---------------------------------------------------------- | | `identity.login.completed` | Platform user finished IdP login | | `identity.login.failed` | IdP login rejected | | `identity.session.terminated` | Session ended (logout, forced revocation, expiry) | | `identity.impersonation.started` | Operator began impersonating another user | | `identity.impersonation.terminated` | Impersonation ended | | `agent.token.issued` | Agent exchanged an API key for a short-lived token | | `agent.token.rejected` | Token exchange denied | | `platform.user.created` | First-time platform user provisioning on initial IdP login | | `credential.link.rejected` | Credential-link consume rejected before state change | | `credential.oauth.consent_rejected` | OAuth consent callback rejected before state change | Not in the audit catalog: `identity.session.created`, `identity.session.switched`, and `identity.session.rotated` (operational telemetry only — session rotation is a background \~5-minute token refresh). Login, token issuance, and impersonation start fail closed until their audit event is durable. Forced terminations complete first and audit with retry so an unavailable audit store cannot prevent revocation. #### `audit_access` | Type | Description | | ------------------------------------- | ----------------------------------- | | `audit.query.completed` | `QueryEvents` | | `audit.event.read` | `GetEvent` | | `audit.stream.opened` | `StreamEvents` opened | | `audit.stream.closed` | `StreamEvents` closed | | `audit.export.requested` | Export requested | | `audit.export.completed` | Export completed | | `simulation.result.read` | Simulation result read | | `assistant.tool.simulated` | Assistant tool simulation | | `assistant.tool.validated` | Assistant tool validation | | `gateway.policy_simulation.completed` | Gateway policy simulation completed | ### Decision provenance These types stamp policy snapshot fields into `payload` when a bundle was loaded: `authorization.decision`, `mcp.tool_call.completed`, `mcp.tool_call.attempted`, `llm.model_call.completed`, `mcp.tool_result.filtered`, `llm.model_result.filtered`. | Field | Type | Description | | ------------------------------ | --------- | ---------------------------------------------------------------------------------------------------- | | `payload.bundle_etag` | string | Content hash of the effective policy at decision time | | `payload.contributing_bundles` | object\[] | Bundles merged into the effective policy (`bundle_id`, `name`, `scope_kind`, `scope_id`, `sequence`) | | `payload.rule_id` | string | File identifier of the matched rule | Events with no loaded bundle omit these fields. The dashboard decision explainer deep-links contributing bundles to `/rules/bundles?scope=&scopeId=`; the same fields are available on `dome audit get`, `dome_audit_get_event`, and `QueryEvents`. ### Payload filters Payload filters match fields inside the event-specific payload (for example `mcp.tool_call.completed.tool`). Each filter is scoped to one `event_type` and one `field`. Non-filterable fields return a validation error. | Operator | Behavior | | -------- | ----------------------------------------------------------------------------------- | | `equals` | Default when a value is provided. Payload field matches any of the supplied values. | | `exists` | Payload field is present and non-empty. | Stream and query share this vocabulary. ### Ingest | Path | Description | | -------------------------------- | ----------------------------- | | `POST /api/v1/audit/events` | Gateway batch upload to Dome | | Connect RPC `Audit/IngestEvents` | Programmatic ingestion (SDKs) | Delivery semantics: at-least-once. ## Webhooks Webhooks deliver a signed public projection of selected events for automation. The wire shape and type catalog differ from Audit v1 even when type strings overlap. Operating limits and retry status handling are on [Webhooks](/concepts/audit/webhooks#operating-limits). ### Envelope ```json theme={"system"} { "schema": "dome.webhook.v1", "event_id": "0b9c1e2a-6f4d-4c3b-9a1e-2f5d7c8b0a11", "event_type": "agent.suspended", "event_version": 1, "occurred_at": "2026-07-14T10:30:00.000Z", "scope": { "kind": "workspace", "tenant_id": "…", "workspace_id": "…" }, "actor": { "kind": "platform_user", "id": "…" }, "resource": { "type": "agent", "id": "…" }, "correlation": { "trace_id": "0af7651916cd43dd8448eb211c80319c", "operation_id": "…" }, "data": { "agent_id": "…", "status": "suspended", "agent_name": "checkout-worker", "reason": "policy_violation" } } ``` | Field | Description | | --------------- | ---------------------------------------------------------------- | | `schema` | Always `dome.webhook.v1` | | `event_id` | Delivery source event ID (random UUID; order by `occurred_at`) | | `event_type` | Webhook catalog type | | `event_version` | Public contract version for `data` | | `occurred_at` | Event time | | `scope` | Scope object | | `actor` | `kind` + `id` only (no email or display name) | | `resource` | `type` + `id` | | `correlation` | Correlation object | | `data` | Typed public payload (no PII, credentials, or raw tool payloads) | ### Delivery headers | Header | Description | | ---------------------- | ----------------------------------------------------------------------------------------- | | `webhook-id` | Unique ID of this delivery. Stable across retry attempts; a replay gets a fresh one. | | `webhook-timestamp` | Unix seconds when the body was signed. | | `webhook-signature` | Space-delimited `v1,` tokens over `..`. | | `Dome-Event-Id` | Source event ID; dedupe key (stable across retries and replays). | | `Dome-Event-Type` | Event type. | | `Dome-Event-Version` | Contract version of the payload. | | `Dome-Subscription-Id` | Subscription that produced the delivery. | | `Dome-Attempt` | 1-based attempt counter. | ### Bundles Subscriptions accept any combination of the catalog types. Bundles are presets: | Bundle | Count | Types | | ------------------ | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | Agent lifecycle | 8 | `agent.registered`, `agent.updated`, `agent.enrolled`, `agent.activated`, `agent.suspended`, `agent.reactivated`, `agent.revoked`, `agent.deleted` | | Governance changes | 24 | Rule bundle, workspace config and roles, gateway connections, Gateway membership, MCP tool status, verification providers | | Security signals | 8 | `access.denied`, `llm.output.filtered`, `mcp.tool_result.filtered`, `platform.api_key.{created,revoked}`, `agent.api_key.{created,rotated,revoked}` | | Incidents | 4 | `access.denied`, `agent.suspended`, `agent.revoked`, `audit.export.failed` | ### Event types #### Identity and access `platform.api_key.created`, `platform.api_key.revoked`, `agent.api_key.created`, `agent.api_key.rotated`, `agent.api_key.revoked`, `workspace.role.assigned`, `workspace.role.removed`, `verification.provider.created`, `verification.provider.updated`, `verification.provider.deleted` #### Agent lifecycle `agent.registered`, `agent.updated`, `agent.activated`, `agent.suspended`, `agent.reactivated`, `agent.revoked`, `agent.deleted`, `agent.enrolled` #### Policy and configuration `workspace.config.changed`, `workspace.freshness_window.updated`, `authorization.rule_bundle.deployed`, `authorization.rule_bundle.rolled_back`, `authorization.rule_bundle.deleted` #### Gateway and connections `gateway.connection.created`, `gateway.connection.updated`, `gateway.connection.deleted`, `mcp.tool_catalog.sync.completed`, `mcp.tool.status_changed`, `gateway.created`, `gateway.updated`, `gateway.deleted`, `gateway.tool_member.added`, `gateway.tool_member.removed`, `gateway.tool_source_member.added`, `gateway.tool_source_member.removed`, `gateway.llm_pool_member.added`, `gateway.llm_pool_member.removed`, `gateway.llm_model_member.added`, `gateway.llm_model_member.removed` #### Security decisions `mcp.tool_call.completed`, `mcp.tool_result.filtered`, `llm.output.filtered`, `access.denied` These fire per governed request. Point them at a log sink rather than a chat channel, and narrow with [subscription filters](/operate/webhooks#filters). #### Audit exports `audit.export.completed`, `audit.export.failed` #### Webhooks administration `eventing.test`, `eventing.destination.created`, `eventing.destination.updated`, `eventing.destination.deleted`, `eventing.subscription.created`, `eventing.subscription.updated`, `eventing.subscription.disabled`, `eventing.subscription.deleted` ### Audit v1 name map | Webhook `event_type` | Audit v1 `event_type` | | --------------------- | --------------------------- | | `llm.output.filtered` | `llm.model_result.filtered` | Where names diverge, the webhook wire name is authoritative for webhook deliveries. ## Next steps Use the catalogs from Operate and Manage: | Page | Contents | | ---------------------------------------- | --------------------------------------------- | | [Audit events](/concepts/audit) | How the one trail works | | [Webhooks](/concepts/audit/webhooks) | Signed public delivery model, limits, retries | | [Audit events](/operate/audit) | Query, get, activity chains | | [Stream Live Events](/operate/observe) | Stream Audit v1. OpenTelemetry | | [Subscribe to Events](/operate/webhooks) | Destinations, subscriptions, retries | | [Export Data](/manage/export) | Forwarders and ingest | # Glossary Source: https://docs.domesystems.ai/reference/glossary Concise definitions for Dome's core entities and terms, with links to full coverage The core terms you'll meet across Dome, each with a one-line definition and a link to the page that covers it in depth. ### Access **Related:** [Manage access](/manage/access) · [Permissions](/concepts/platform/permissions) concept ### Access grant **Related:** [Architecture](/concepts/architecture#gateways) concept · [Gateways](/connect/gateways) ### Act-as **Related:** [Delegated agents](/connect/agents/delegated) · [Identity Patterns](/concepts/identity-patterns) concept ### Agent **Related:** [Agents](/concepts/agents) concept · [Agent Identity](/concepts/agents/identity) concept · [Agents](/connect/agents) ### Agent identity **Related:** [Agent Identity](/concepts/agents/identity) concept · [Agents](/connect/agents) · [Identity Patterns](/concepts/identity-patterns) concept ### Agent lifecycle **Related:** [Agent lifecycle](/concepts/agents/agent-lifecycle) concept · [Agents](/connect/agents) ### API key **Related:** [Agent Identity](/concepts/agents/identity) concept · [Agents](/connect/agents) ### Audit event **Related:** [Audit events](/concepts/audit) concept · [Audit events](/operate/audit) ### Authorization model **Related:** [Authorization Model](/concepts/architecture/authorization-model) concept · [Rules](/govern/rules) · [Rules](/reference/controls/rules) reference ### Capability **Related:** [Agent Identity](/concepts/agents/identity) concept · [Authorization Model](/concepts/architecture/authorization-model) concept ### Cedar **Related:** [Authorization Model](/concepts/architecture/authorization-model) concept · [Rules](/govern/rules) ### Rules assistant **Related:** [Rules assistant](/govern/rules/assistant) · [Rules](/govern/rules) ### Control plane **Related:** [Architecture](/concepts/architecture) concept · [Data plane](/concepts/architecture/dataplane) concept ### Data plane **Related:** [Data plane](/concepts/architecture/dataplane) concept · [Architecture](/concepts/architecture#how-requests-move-through-dome) concept ### Delegated agent **Related:** [Delegated agents](/connect/agents/delegated) · [Identity Patterns](/concepts/identity-patterns) concept · [Act-as](/reference/glossary#act-as) ### Effective Rules **Related:** [Rules](/concepts/controls/rules) concept · [Authorize Access](/govern/rules) · [Authorization Model](/concepts/architecture/authorization-model) concept ### Egress header **Related:** [Tools](/concepts/resources/tools) concept · [Tools](/connect/resources/tools) ### Environment **Related:** [Prepare environments](/manage/environments) · [Scope hierarchy](/concepts/platform/scopes) concept ### Export **Related:** [Export data](/manage/export) · [Audit events](/operate/audit) ### Filter **Related:** [Guards](/concepts/controls/guards) concept · [Configure Guards](/govern/guards) ### Gateways **Related:** [Gateways](/concepts/gateways) concept · [Gateways](/connect/gateways) ### Guard **Related:** [Guards](/concepts/controls/guards) concept · [Configure Guards](/govern/guards) · [Architecture](/concepts/architecture#guards) concept ### Identity patterns **Related:** [Identity Patterns](/concepts/identity-patterns) concept · [Delegated agents](/connect/agents/delegated) ### Integrations **Related:** [Integrate systems](/manage/integrations) ### LLM gateway **Related:** [LLM gateway](/concepts/gateways/llm-gateway) concept · [Data plane](/concepts/architecture/dataplane) concept ### Membership **Related:** [Architecture](/concepts/architecture#gateways) concept · [Gateways](/connect/gateways) ### Model connection **Related:** [Models](/concepts/resources/models) concept · [Models](/connect/resources/models) · [LLM gateway](/concepts/gateways/llm-gateway) concept ### Organization **Related:** [Scope hierarchy](/concepts/platform/scopes) concept ### Permissions **Related:** [Permissions](/concepts/platform/permissions) concept · [Manage access](/manage/access) ### Platform API key **Related:** [Manage access](/manage/access) · [Permissions](/concepts/platform/permissions) concept ### Pool **Related:** [Pools](/concepts/resources/model-pools) concept · [Pools](/connect/resources/models/pools) · [LLM gateway](/concepts/gateways/llm-gateway) concept ### Quota **Related:** [Quotas](/concepts/controls/quotas) concept · [Set Usage Limits](/govern/quotas) · [Architecture](/concepts/architecture#quotas) concept ### Resource **Related:** [Resources](/concepts/resources) concept · [Architecture](/concepts/architecture#resources) concept · [Connect](/connect) ### Rule **Related:** [Rules](/govern/rules) · [Authorization Model](/concepts/architecture/authorization-model) concept ### Rule bundle **Related:** [Rules](/govern/rules) ### Scope **Related:** [Scope hierarchy](/concepts/platform/scopes) concept ### Settings **Related:** [Configure platform](/manage/settings) ### Simulation **Related:** [Simulate rules](/govern/rules/simulate) · [Rules](/govern/rules) ### Standing identity **Related:** [Identity Patterns](/concepts/identity-patterns) concept · [Agents](/connect/agents) ### Stream Live Events **Related:** [Stream Live Events](/operate/observe) · [Audit events](/concepts/audit) concept · [Events](/reference/events) reference ### Tenant **Related:** [Scope hierarchy](/concepts/platform/scopes) concept ### Tool **Related:** [Tools](/concepts/resources/tools) concept · [Tools](/connect/resources/tools) · [Architecture](/concepts/architecture#resources) concept ### Tools gateway **Related:** [Tools gateway](/concepts/gateways/tools-gateway) concept · [Data plane](/concepts/architecture/dataplane) concept ### Verification provider **Related:** [Delegated agents](/connect/agents/delegated) · [Identity Patterns](/concepts/identity-patterns) concept ### Webhook **Related:** [Webhooks](/operate/webhooks) · [Webhooks](/concepts/audit/webhooks) concept ### Workspace **Related:** [Scope hierarchy](/concepts/platform/scopes) concept ## Next steps Definitions stay short on purpose. Concepts carry the full model: * [Core Concepts](/concepts) for how Dome's pieces fit together * [What is Dome?](/what-is-dome) for platform purpose * [Quickstart](/quickstart) to register an agent and verify governed traffic # Agent Registry Source: https://docs.domesystems.ai/reference/mcp/agents Register agents, manage lifecycle states, and control API keys Agent registry tools manage the full agent lifecycle — from registration through suspension and revocation — plus API key management for agent authentication. These 13 tools require workspace context and appropriate permissions. ## Agents List `dome_agents_list` List registered agents. Returns agent ID, name, status, and capabilities. | Parameter | Type | Required | Description | | -------------- | ------ | -------- | ----------------------------------------------------------------- | | `status` | string | No | Filter by status: `active`, `suspended`, `provisioned`, `revoked` | | `workspace_id` | string | No | Filter by workspace ID (defaults to active workspace) | | `limit` | number | No | Maximum results (default: 100) | | `offset` | number | No | Pagination offset | **Permission:** `agent.view` ```json title="Example tool call" theme={"system"} { "name": "dome_agents_list", "arguments": { "status": "active", "limit": 25 } } ``` ## Agents Get `dome_agents_get` Get detailed information about a specific agent by ID. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------ | | `agent_id` | string | Yes | Agent ID (UUID) | | `gateway_id` | string | No | Gateway ID used to return complete endpoints; required when the agent can access more than one Gateway | **Permission:** `agent.view` ## Agents Register `dome_agents_register` Register a new AI agent on the platform. After registration, create an API key with `dome_agents_create_key`. | Parameter | Type | Required | Description | | ---------------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | string | Yes | Agent name (unique within workspace) | | `capabilities` | array | No | Custom capability strings. System capabilities (`llm:invoke`, `mcp:call`, `mcp:discover`) are derived automatically from the selections below | | `parent_id` | string | No | Parent agent ID for hierarchical agents | | `workspace_id` | string | No | Workspace to register in (defaults to active workspace) | | `metadata` | object | No | Key-value metadata | | `allowed_pool_names` | array | No | LLM pools the agent may route to. Emits a `permit` against `Dome::LLMModel` matched via `resource.pool == ""` | | `allowed_direct_model_names` | array | No | LLM model connections the agent may invoke directly (advanced). Emits a `permit` against `Dome::LLMModel` | | `allowed_tools` | array | No | MCP tools the agent may call, each as `connection/tool`. Emits `mcp:call` and `mcp:discover` permits against `Dome::MCPTool` | | `allowed_gateway_ids` | array | No | Gateway IDs the agent may reach. Emits per-Gateway permit rules | | `actas_allowed_groups` | array | No | Groups the verified act-as identity may belong to. Emits a `forbid` unless the verified group matches | | `actas_allowed_emails` | array | No | Emails the agent may act on behalf of. Emits a `forbid` unless the verified email matches | | `actas_allowed_subjects` | array | No | Subjects the agent may act on behalf of. Emits a `forbid` unless the verified subject matches | | `actas_method` | string | No | Act-as verification method: `none`, `oidc`, `hmac` | | `actas_required` | bool | No | Require act-as header on every request | | `actas_provider_id` | string | No | Workspace verification provider ID | | `actas_oidc_url` | string | No | Inline OIDC discovery URL | | `actas_hmac_secret` | string | No | Inline HMAC shared secret | | `actas_config_name` | string | No | Label for agent-specific verification config | **Permission:** `agent.register` ```json title="Example tool call" theme={"system"} { "name": "dome_agents_register", "arguments": { "name": "code-reviewer", "capabilities": ["code-review", "pr-comments"], "metadata": { "team": "platform", "owner": "infra@example.com" } } } ``` ## Agents Update `dome_agents_update` Update an existing agent's capabilities, metadata, or act-as verification config. | Parameter | Type | Required | Description | | ---------------------------- | ------ | -------- | -------------------------------------------------------------------- | | `agent_id` | string | Yes | Agent ID or name | | `capabilities` | array | No | Updated custom capability list | | `metadata` | object | No | Updated key-value metadata | | `allowed_pool_names` | array | No | Replace the agent's allowed LLM pool list | | `allowed_direct_model_names` | array | No | Replace the agent's allowed direct-model list | | `allowed_tools` | array | No | Replace the agent's allowed MCP tool list, each as `connection/tool` | | `allowed_gateway_ids` | array | No | Replace the agent's allowed Gateway ID list | | `actas_allowed_groups` | array | No | Replace the agent's act-as allowed-groups list | | `actas_allowed_emails` | array | No | Replace the agent's act-as allowed-emails list | | `actas_allowed_subjects` | array | No | Replace the agent's act-as allowed-subjects list | | `actas_method` | string | No | Act-as verification method: `none`, `oidc`, `hmac` | | `actas_required` | bool | No | Require act-as header on every request | | `actas_provider_id` | string | No | Workspace verification provider ID | | `actas_oidc_url` | string | No | Inline OIDC discovery URL | | `actas_hmac_secret` | string | No | Inline HMAC shared secret | | `actas_config_name` | string | No | Label for agent-specific verification config | **Permission:** `agent.register`. Changing any pool, model, tool, or `actas_allowed_*` field regenerates the agent's rule bundle and additionally requires `rules.deploy`. ## Agents Suspend `dome_agents_suspend` Suspend an active agent, preventing it from making requests. Optionally cascade to child agents. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | -------------------------------------------- | | `agent_id` | string | Yes | Agent ID (UUID) | | `reason` | string | No | Reason for suspension | | `cascade` | bool | No | Also suspend child agents (default: `false`) | **Permission:** `agent.suspend` ```json title="Example tool call" theme={"system"} { "name": "dome_agents_suspend", "arguments": { "agent_id": "code-reviewer", "reason": "Investigating anomalous behavior", "cascade": true } } ``` ## Agents Reactivate `dome_agents_reactivate` Reactivate a previously suspended agent. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | --------------- | | `agent_id` | string | Yes | Agent ID (UUID) | **Permission:** `agent.suspend` ## Agents Revoke `dome_agents_revoke` Permanently revoke an agent. This cannot be undone. Marked destructive. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | --------------------- | | `agent_id` | string | Yes | Agent ID (UUID) | | `reason` | string | No | Reason for revocation | **Permission:** `agent.revoke` Revocation is permanent and cannot be undone. ## Agents Delete `dome_agents_delete` Permanently delete a revoked agent and all its data. Agent must already be in the revoked state. Marked destructive. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | ----------------------------- | | `agent_id` | string | Yes | Agent ID (UUID) | | `reason` | string | No | Reason for deletion | | `cascade` | bool | No | Also delete descendant agents | **Permission:** `agent.delete` Hard-delete removes all agent data irreversibly. The agent must be revoked first. ## Tools List `dome_tools_list` List registered agents and their tools/capabilities in the active workspace. Only returns agents in `active` or `provisioned` status. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** `agent.view` ## Agent API Keys ### Agents Create Key `dome_agents_create_key` Create a new API key for an agent. Returns the token, selected Gateway ID, and complete Gateway endpoints once — save the token immediately. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------ | | `agent` | string | Yes | Agent name | | `name` | string | Yes | Key name (unique per agent) | | `gateway_id` | string | No | Gateway ID used to return complete endpoints; required when the agent can access more than one Gateway | **Permission:** `agentkey.manage` ```json title="Example tool call" theme={"system"} { "name": "dome_agents_create_key", "arguments": { "agent": "code-reviewer", "name": "production-v1", "gateway_id": "{{GATEWAY_ID}}" } } ``` Store the returned key secret securely. It cannot be retrieved after creation — only rotated or revoked. ### Agents List Keys `dome_agents_list_keys` List active API keys for an agent. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `agent` | string | Yes | Agent name | **Permission:** `agent.view` ### Agents Rotate Key `dome_agents_rotate_key` Rotate an agent's API key — revokes the old secret and issues a new one with the same name. Returns the new token once. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------ | | `agent` | string | Yes | Agent name | | `key_name` | string | Yes | Name of the key to rotate | | `gateway_id` | string | No | Gateway ID used to return complete endpoints; required when the agent can access more than one Gateway | **Permission:** `agentkey.manage` ### Agents Revoke Key `dome_agents_revoke_key` Revoke an agent's API key. The key is no longer usable for authentication. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | ------------------------- | | `agent` | string | Yes | Agent name | | `key_name` | string | Yes | Name of the key to revoke | **Permission:** `agentkey.manage` ## Next steps Full agent workflows live under Connect. Lifecycle and Rules stay one hop away: * [Register Agents](/connect/agents) * [Agent Lifecycle](/concepts/agents/agent-lifecycle) concept * [Rules](/reference/mcp/rules) # Audit Source: https://docs.domesystems.ai/reference/mcp/audit Query, inspect, and export audit events Audit tools provide access to the Dome Platform audit trail. Query events by type, agent, or time range, inspect individual events, and export records for external analysis. These 4 tools require workspace context. Tool results render enum fields as short tokens (`governing`, `denied`, `gateway_mcp`), and filter arguments accept those same tokens. Refer to [Enum rendering](/reference/events#enum-rendering) reference. Audit reads are scoped to the active workspace. The server checks the workspace-level `audit.view` permission, so a workspace member can read their own workspace's audit even without a tenant role. Tenant- or org-scoped API keys reading without a workspace context fall back to the tenant-level `audit.view`. ## Audit Query `dome_audit_query` Query audit events with optional filters. Returns events with type, agent, timestamp, and data. | Parameter | Type | Required | Description | | ----------------------- | ------- | -------- | ------------------------------------------------------------------------- | | `agent_id` | string | No | Filter by agent ID | | `type` / `types` | string | No | Event types, comma-separated | | `classes` | string | No | `governing`, `state_change`, `security`, `audit_access` (comma-separated) | | `results` | string | No | Event results, comma-separated | | `agent_ids` | string | No | Multiple agent IDs, comma-separated | | `primary_resource_id` | string | No | Primary resource ID | | `primary_resource_kind` | string | No | Primary resource kind | | `trace_id` | string | No | Every event for one request | | `activity_id` | string | No | Every event in one [activity chain](/reference/events#correlation) | | `operation_id` | string | No | Multi-event operation ID | | `parent_event_id` | string | No | Children of a given event | | `actor_kind` | string | No | Actor kind | | `actor_id` | string | No | Actor ID | | `producer_service` | string | No | Producer service | | `producer_gateway_id` | string | No | Producer Gateway ID | | `request_surface` | string | No | Origin surface | | `caller_surface` | string | No | Caller application surface | | `payload_filter` | string | No | `event.type:field=value`, semicolon-separated or JSON array | | `since` | string | No | Start time (RFC3339) | | `until` | string | No | End time (RFC3339) | | `limit` | number | No | Maximum results (default: 50) | | `page_token` | string | No | Cursor from a previous call | | `compact` | boolean | No | Bounded investigation rows without full payload (default: false) | **Permission:** `audit.view` ```json title="Example tool call" theme={"system"} { "name": "dome_audit_query", "arguments": { "type": "agent.registered,agent.suspended", "limit": 10, "since": "2025-01-01T00:00:00Z" } } ``` Returns events in reverse chronological order. Each event includes an ID, type, timestamp, actor, and event-specific details. ## Audit Get Event `dome_audit_get_event` Get a single audit event by its ID. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | -------------- | | `event_id` | string | Yes | Audit event ID | **Permission:** `audit.view` ```json title="Example tool call" theme={"system"} { "name": "dome_audit_get_event", "arguments": { "event_id": "evt_01HQXYZ..." } } ``` Returns the full event record including all metadata, the acting principal, and the complete event payload. ## Audit Export `dome_audit_export` Export audit events in a SIEM-friendly format for external processing or archival. Supports the same filters as `dome_audit_query`, plus a `format` selector. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | ------------------------------------------------------- | | `agent_id` | string | No | Filter by agent ID | | `type` | string | No | Filter by event type (comma-separated for multiple) | | `since` | string | No | Start time (RFC3339) | | `until` | string | No | End time (RFC3339) | | `limit` | number | No | Maximum results (default: 1000) | | `format` | string | No | Output format: `jsonl` (default), `json`, `ocsf`, `cef` | **Permission:** `audit.export` | Format | When to use | | ------- | ------------------------------------------------ | | `jsonl` | NDJSON, one event per line — log shippers, `jq`. | | `json` | Single pretty array — archival. | | `ocsf` | OCSF v1.1.0 JSON — Splunk, Sentinel, Chronicle. | | `cef` | Common Event Format — ArcSight, QRadar, syslog. | ```json title="Example tool call" theme={"system"} { "name": "dome_audit_export", "arguments": { "since": "2025-01-01T00:00:00Z", "until": "2025-02-01T00:00:00Z", "limit": 5000, "format": "ocsf" } } ``` The same format set is available on `dome audit export --format` and on the file-based export connectors in [Export Data](/manage/export). ## Audit Usage `dome_usage` Show LLM token consumption and a per-class estimated cost for the active workspace over the last 24 hours. Use it when an assistant needs to summarize workspace spend, build a budget guardrail, or confirm token attribution. This tool takes no parameters — it always reports the active workspace over the last 24 hours. For dimensional breakdowns (per-agent, per-API-key, per-identity, per-model), read the [REST query surface](/operate/observe#token-usage-and-cost) directly. **Permission:** `audit.view` ```json title="Example tool call" theme={"system"} { "name": "dome_usage", "arguments": {} } ``` Returns the workspace token summary as JSON: ```json theme={"system"} { "llm_input_tokens": 412000, "llm_cache_read_tokens": 1820000, "llm_cache_write_tokens": 88000, "llm_output_tokens": 64000, "llm_reasoning_tokens": 12000, "llm_total_tokens": 2396000, "estimated_cost_usd": 3.4521 } ``` The estimated cost is a blended cross-model rate intended for trend-watching, not a billable figure. Refer to [Token Usage and Cost](/operate/observe#token-usage-and-cost) for the full token-class breakdown. ## Next steps Prefer the Operate how-tos for query patterns. This page is the tool surface: * [Audit events](/operate/audit) * [Stream Live Events](/operate/observe) * [Events](/reference/events) reference # Context Source: https://docs.domesystems.ai/reference/mcp/context Switch workspaces, manage aliases, and check authentication status Switch workspaces, create aliases, and check authentication status. These 9 tools are always available — they do not require an active workspace context or any specific permissions. ## Context List `dome_context_list` List all available contexts in the org/tenant/workspace hierarchy. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | Returns each context with its name, alias (if set), server, and active status. ```json title="Example tool call" theme={"system"} { "name": "dome_context_list" } ``` ## Context Current `dome_context_current` Show the active context name and details. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | Returns the currently selected context including org, tenant, workspace, and server URL. ## Context Use `dome_context_use` Switch the active context by name, alias, or shorthand. | Parameter | Type | Required | Description | | --------- | ------ | -------- | --------------------------------- | | `name` | string | Yes | Context name, alias, or shorthand | Supports several shorthand formats: * **Exact name:** `prod/acme/backend` * **Alias:** `backend` (if alias is set) * **Workspace shorthand:** `backend` * **Tenant/workspace:** `acme/backend` * **Org/tenant/workspace:** `prod/acme/backend` If the shorthand is ambiguous, the tool returns an error listing all matching contexts. ```json title="Example tool call" theme={"system"} { "name": "dome_context_use", "arguments": { "name": "acme/backend" } } ``` After switching context, the available tool list updates to reflect your permissions in the new workspace. ## Context Sync `dome_context_sync` Refresh contexts from server memberships. Fetches all org/tenant memberships and creates or updates in-memory contexts with scoped session tokens across all authenticated servers. Does not write to the config file. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | ## Context Rename `dome_context_rename` Rename a context. Persists the change to the config file. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | ----------------------------- | | `old_name` | string | Yes | Current context name or alias | | `new_name` | string | Yes | New context name | ## Context Delete `dome_context_delete` Delete a context. Persists the change to the config file. If the active context is deleted, platform tools are removed. Marked destructive. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------------- | | `name` | string | Yes | Context name or alias to delete | Deleting a context removes the local session. Re-run `dome_context_sync` to recreate it from server memberships. ## Context Alias `dome_context_alias` Set or remove an alias for a context. Aliases provide short names for frequently used contexts. Persists the change to the config file. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ---------------------------------------------------------------------------------- | | `name` | string | Yes | Context name (or alias to remove when `remove=true`) | | `alias` | string | Yes | Alias value (ignored when `remove=true`, but the parameter must still be provided) | | `remove` | bool | No | Remove the alias instead of setting it (default: `false`) | ```json title="Example — set alias" theme={"system"} { "name": "dome_context_alias", "arguments": { "name": "prod/acme/backend", "alias": "backend" } } ``` ```json title="Example — remove alias" theme={"system"} { "name": "dome_context_alias", "arguments": { "name": "backend", "alias": "", "remove": true } } ``` ## Auth Status `dome_auth_status` Show authentication status for the current session. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | Returns the authenticated user, server URL, token expiry, and active workspace. ## Version `dome_version` Get the Dome MCP server version. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | Returns the server version string and build metadata. ## Next steps Create tenants and workspaces from Manage, then switch context here: * [Prepare Environments](/manage/environments) * [Scopes](/concepts/platform/scopes) concept * [Setup AI assistant](/setup-ai-assistant) # Gateways Source: https://docs.domesystems.ai/reference/mcp/gateway MCP tools for managing Gateways, membership, access grants, and cost quotas Gateway MCP tools manage workspace-scoped groupings of callable resources and the agent access grants that govern them. Refer to [Gateways](/connect/gateways) for the concept model. All tools require workspace context. ## Gateway CRUD ### dome\_gateway\_list List Gateways in the active workspace with state, membership counts, and the canonical endpoint path (`/gateways/`) — the single URL every client points at. The Dome SDK and Anthropic clients use it as-is; MCP clients append `/mcp`; OpenAI-compatible clients append `/v1`. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** `gateway.view` ### dome\_gateway\_get Get a Gateway with its members (individual tools, tool sources, LLM pools, direct model connections). | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------ | | `name` | string | Yes | Gateway name | **Permission:** `gateway.view` ### dome\_gateway\_create Create a Gateway. Starts active with empty membership. Until a resource is added it is unreachable. | Parameter | Type | Required | Description | | ------------- | ------ | -------- | -------------------------------------- | | `name` | string | Yes | Gateway name (unique within workspace) | | `description` | string | No | Free-form description | **Permission:** `gateway.manage` ```json title="Create a Gateway" theme={"system"} { "name": "dome_gateway_create", "arguments": { "name": "prod-tools", "description": "Production MCP tools for the ops agent" } } ``` ### dome\_gateway\_update Rename or edit the description. Use `dome_gateway_set_state` to enable/disable. | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ----------------------- | | `name` | string | Yes | Current Gateway name | | `new_name` | string | No | New name (omit to keep) | | `description` | string | No | New description | **Permission:** `gateway.manage` ### dome\_gateway\_set\_state Enable or disable a Gateway. A disabled Gateway fails closed at its endpoint. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ---------------------- | | `name` | string | Yes | Gateway name | | `state` | string | Yes | `active` or `disabled` | **Permission:** `gateway.manage` ### dome\_gateway\_set\_default Make a Gateway the workspace default — the Gateway pre-checked (but overridable) in dashboard create dialogs. Enforces no membership. Setting a new default clears the previous one. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------ | | `name` | string | Yes | Gateway name | **Permission:** `gateway.manage` ### dome\_gateway\_delete Delete a Gateway. Membership rows cascade; the underlying tools, connections, and pools are untouched. Marked destructive. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------ | | `name` | string | Yes | Gateway name | **Permission:** `gateway.manage` ## Membership ### dome\_gateway\_tool\_add Add a single MCP tool. Address the tool by its `(connection, tool)` pair from the observed catalog. Run `dome_tools_for_connection` to see observed names. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------ | | `gateway` | string | Yes | Gateway name | | `connection` | string | Yes | MCP connection name | | `tool` | string | Yes | Tool name as observed (e.g. `create_page`) | **Permission:** `gateway.manage` ### dome\_gateway\_tool\_remove Remove an individual tool. The underlying tool is untouched. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | --------------------- | | `gateway` | string | Yes | Gateway name | | `connection` | string | Yes | MCP connection name | | `tool` | string | Yes | Tool name as observed | **Permission:** `gateway.manage` ### dome\_gateway\_tool\_source\_add Add a tool source (an MCP server connection). The Gateway includes every tool observed on the connection; newly observed tools propagate automatically. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------- | | `gateway` | string | Yes | Gateway name | | `connection` | string | Yes | MCP connection name | **Permission:** `gateway.manage` ### dome\_gateway\_tool\_source\_remove Remove a tool source. Tools that joined only through this source stop being included; the connection itself is untouched. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------- | | `gateway` | string | Yes | Gateway name | | `connection` | string | Yes | MCP connection name | **Permission:** `gateway.manage` ### dome\_gateway\_model\_pool\_add Add an LLM pool. The Gateway exposes the pool's models at its `/gateways//v1` endpoint. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------ | | `gateway` | string | Yes | Gateway name | | `pool` | string | Yes | Pool name | **Permission:** `gateway.manage` ### dome\_gateway\_model\_pool\_remove Remove an LLM pool. The pool itself is untouched. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------ | | `gateway` | string | Yes | Gateway name | | `pool` | string | Yes | Pool name | **Permission:** `gateway.manage` ### dome\_gateway\_model\_add Add a direct (un-pooled) LLM model connection. The Gateway exposes the model without going through a pool. | Parameter | Type | Required | Description | | --------- | ------ | -------- | --------------------- | | `gateway` | string | Yes | Gateway name | | `model` | string | Yes | Model connection name | **Permission:** `gateway.manage` ### dome\_gateway\_model\_remove Remove a direct LLM model connection. The connection itself is untouched. | Parameter | Type | Required | Description | | --------- | ------ | -------- | --------------------- | | `gateway` | string | Yes | Gateway name | | `model` | string | Yes | Model connection name | **Permission:** `gateway.manage` ## Access grants Access grants control who can *use* a Gateway. An agent with no grant is rejected at the edge before Cedar evaluates. ### dome\_gateway\_access\_list List agents and their access state for a Gateway. Revoked agents are excluded to match the CLI and dashboard — a revoked agent cannot exchange its API key for a JWT, so it can never reach the gateway. Suspended agents remain in the list because suspension is reversible. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------ | | `gateway` | string | Yes | Gateway name | **Permission:** `agent.view` ### dome\_gateway\_agent\_grant Grant an agent access to a Gateway. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | --------------- | | `gateway` | string | Yes | Gateway name | | `agent_id` | string | Yes | Agent ID (UUID) | **Permission:** `agent.register` + `rules.deploy` ### dome\_gateway\_agent\_revoke Revoke an agent's access. Marked destructive. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | --------------- | | `gateway` | string | Yes | Gateway name | | `agent_id` | string | Yes | Agent ID (UUID) | **Permission:** `agent.register` + `rules.deploy` ### dome\_gateway\_all\_agents\_grant Grant every current and future workspace agent access to a Gateway. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------ | | `gateway` | string | Yes | Gateway name | **Permission:** `rules.deploy` + `gateway.manage` ### dome\_gateway\_all\_agents\_revoke Clear the all-agents grant. Per-agent grants are unaffected. Marked destructive. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------ | | `gateway` | string | Yes | Gateway name | **Permission:** `rules.deploy` + `gateway.manage` ## Cost quotas ### dome\_gateway\_quota\_list List Gateway cost quotas (USD spend caps on a whole Gateway's aggregate LLM spend). Optionally filter to one Gateway. | Parameter | Type | Required | Description | | --------- | ------ | -------- | --------------------- | | `gateway` | string | No | Filter to one Gateway | **Permission:** `gateway.view` ### dome\_gateway\_quota\_set Create a USD spend cap on a whole Gateway. Covers every LLM call routed through the Gateway's member pools and direct connections. Once window spend exceeds the cap, calls to the Gateway return HTTP `429`. | Parameter | Type | Required | Description | | ----------- | ------ | -------- | ------------------------------------------------------ | | `gateway` | string | Yes | Gateway name | | `limit_usd` | number | Yes | Spend cap in US dollars (e.g. `500` or `12.50`) | | `window` | string | No | Reset window: `daily` or `monthly` (default `monthly`) | | `name` | string | No | Human-readable label | | `disabled` | bool | No | Create disabled (no enforcement until enabled) | **Permission:** `gateway.manage` ```json title="Cap a Gateway at $500/month" theme={"system"} { "name": "dome_gateway_quota_set", "arguments": { "gateway": "prod-tools", "limit_usd": 500, "window": "monthly", "name": "prod-monthly" } } ``` ### dome\_gateway\_quota\_update Update a Gateway quota's name, limit, window, or enabled state. Omitted fields stay unchanged. The Gateway subject is immutable, and the tool refuses IDs for other quota subject types. | Parameter | Type | Required | Description | | ----------- | ------ | -------- | --------------------------------------- | | `quota_id` | string | Yes | Quota ID from `dome_gateway_quota_list` | | `name` | string | No | New human-readable label | | `limit_usd` | number | No | New spend cap in US dollars | | `window` | string | No | New reset window: `daily` or `monthly` | | `enabled` | bool | No | Enable or disable enforcement | **Permission:** `gateway.manage` ### dome\_gateway\_quota\_remove Remove a Gateway cost quota by ID. Refuses IDs of non-Gateway quotas — use `dome_model_quota_remove` for those. Marked destructive. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | --------------------------------------- | | `quota_id` | string | Yes | Quota ID from `dome_gateway_quota_list` | **Permission:** `gateway.manage` ## Next steps Membership and grants have a full how-to under Connect: * [Gateways](/connect/gateways) * [Tools](/connect/resources/tools) * [Develop](/develop) # Guards Source: https://docs.domesystems.ai/reference/mcp/guards Author content-inspection Filters and assign them to model and MCP server connections from an MCP client Author Filters — content-inspection Guards — and bind them to model or MCP server (tool) connections. Text Filters attach to model connections; JSON Filters attach to tool connections. All Guards tools require workspace context. Editing a Filter deploys a new active version. Assigned connections pick it up on the next gateway sync — history is preserved and can be rolled back to. A Filter whose stored config fails to decode blocks its `(connection, direction)` until the config is fixed — the gateway never relays unfiltered traffic. ## List filters `dome_guards_list_filters` List the workspace's Filters, each with its active version and config. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | ## Get filter `dome_guards_get_filter` Fetch one Filter's active version and config. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------- | | `filter` | string | Yes | Filter name or UUID | ## List filter versions `dome_guards_list_filter_versions` List a Filter's version history, newest first. Rollback-produced versions cite their source version id. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------- | | `filter` | string | Yes | Filter name or UUID | ## Create filter `dome_guards_create_filter` Create a Filter — its first active version. Supply the config via the convenience params (text-only) or `config_json` (any kind). | Parameter | Type | Required | Description | | ------------------- | --------------- | --------------------------------------- | -------------------------------------------------------------------------- | | `name` | string | Yes | Filter name (workspace-scoped) | | `description` | string | No | One-line description | | `redact_substrings` | array of string | No | Substrings to redact in matched responses (text) | | `block_substrings` | array of string | No | Substrings that block the response on a match (text) | | `redact_ssn` | bool | No | Redact US SSN patterns (text) | | `block_ssn` | bool | No | Block the response on an SSN match (text) | | `config_json` | string | For JSON Filters, or full-fidelity text | Protojson `dome.guards.v1.FilterConfig`. Overrides the convenience params. | The convenience params build at most two components — a `redact` component and a `block` component, redact ordered first — and only produce text Filters. Pass `config_json` to author a JSON Filter or a multi-component text chain. ### JSON Filter config ```json theme={"system"} { "json": { "components": [ { "field_actions": [ { "matcher": { "key": "ssn" }, "action": "FILTER_ACTION_OMIT" }, { "matcher": { "key": "email" }, "action": "FILTER_ACTION_REDACT" }, { "matcher": { "key": "card" }, "action": "FILTER_ACTION_BLOCK" } ] } ] } } ``` `BLOCK` withholds the whole message. `OMIT` removes the key and value. `REDACT` rewrites the value with a redaction sentinel. When multiple actions target the same key: `BLOCK` > `OMIT` > `REDACT`. ## Update filter `dome_guards_update_filter` Edit metadata in place (`new_name`, `description`), or deploy a new active version by passing any config param. History is preserved. | Parameter | Type | Required | Description | | ------------------------------------------------------------------------------------- | ------ | -------- | --------------------------------------------- | | `filter` | string | Yes | Filter name or UUID | | `new_name` | string | No | New name for the logical Filter | | `description` | string | No | New description | | `redact_substrings` / `block_substrings` / `redact_ssn` / `block_ssn` / `config_json` | — | No | Any config param deploys a new active version | Pass at least one of `new_name`, `description`, or a config param. ## Rollback filter `dome_guards_rollback_filter` Roll a Filter back to an earlier version. The target version's config is copied forward into a new active version. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------ | | `filter` | string | Yes | Filter name or UUID | | `to_version` | number | Yes | Version number to roll back to | ## Delete filter `dome_guards_delete_filter` Hard-delete a Filter and its connection assignments. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------- | | `filter` | string | Yes | Filter name or UUID | ## List model connection filters `dome_model_guards_filters_list` List the text Filters assigned to a model connection, in chain order, grouped by direction. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | --------------------- | | `connection` | string | Yes | Model connection name | ## Set model connection filters `dome_model_guards_filters_set` Replace the entire ordered Filter chain for one direction on a model connection. Only `text`-kind Filters may be assigned. `response` filters completions the agent receives. `request` filters the outbound prompt before dispatch to the model provider — a match can redact or block the call. | Parameter | Type | Required | Description | | ------------ | --------------- | -------- | ----------------------------------------------------- | | `connection` | string | Yes | Model connection name | | `direction` | string | Yes | `response` or `request` | | `filters` | array of string | Yes | Ordered Filter names or UUIDs; empty clears the chain | ## List tool connection filters `dome_tool_guards_filters_list` List the JSON Filters assigned to an MCP server (tool) connection, in chain order, grouped by direction. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | -------------------------- | | `connection` | string | Yes | MCP server connection name | ## Set tool connection filters `dome_tool_guards_filters_set` Replace the entire ordered Filter chain for one direction on a tool connection. Only `json`-kind Filters may be assigned. `request` filters run over tool arguments **before** the call reaches the upstream MCP server — matched fields are redacted or omitted, or the whole call is blocked. `response` filters run over the tool result before the agent sees it. | Parameter | Type | Required | Description | | ------------ | --------------- | -------- | ----------------------------------------------------- | | `connection` | string | Yes | MCP server connection name | | `direction` | string | Yes | `response` or `request` | | `filters` | array of string | Yes | Ordered Filter names or UUIDs; empty clears the chain | ## Next steps Create and assign Filters from Govern. Catalogs cover windows and path syntax: * [Configure Guards](/govern/guards) * [Guards](/concepts/controls/guards) concept * [Guards](/reference/controls/guards) reference # MCP Server Reference Source: https://docs.domesystems.ai/reference/mcp/index Complete reference for the Dome MCP tools available to AI agents The Dome MCP server exposes platform tools organized into groups. Tools are filtered based on your workspace, tenant, and org roles — you only see tools matching your permissions. ## Setup ```bash theme={"system"} dome auth login dome context use dome mcp serve ``` ### Claude Integration Register the Dome MCP server with Claude Code: ```bash theme={"system"} claude mcp add dome -- dome mcp serve ``` Claude discovers available tools automatically. Switch workspaces mid-session with `dome_context_use` — the tool list updates to reflect your new permissions. ## Guided prompts The MCP server ships three guided prompts that run end-to-end onboarding flows from your client. Invoke them with `/dome:quickstart`, `/dome:tutorial`, or `/dome:adopt` in Claude Code (other clients expose prompts through their own UI). Role-aware first-time setup. Guided governed-agent walkthrough. Brownfield onboarding — route an existing codebase's LLM traffic through the gateway with no code changes. ## Tool Groups 9 tools — switch workspaces, manage aliases, check auth status. Always available. 13 tools — register agents, manage lifecycle, create and rotate API keys. 10 tools — deploy Cedar rules, simulate decisions, rollback bundles. 4 tools — query, inspect, and export audit events, and report LLM token usage and cost. 14 tools — manage event subscriptions, inspect and replay deliveries, browse the provider catalog. 7 tools — platform status, API keys (list, create, revoke, available permissions), configuration, tenant creation. 11 tools — create workspaces, manage members, configure act-as enforcement and policy freshness, provision sandboxes. MCP server connection CRUD, catalog discovery, and shared-OAuth connect/disconnect. 24 tools — create Gateways, curate membership, grant agent access, set cost caps. LLM model connections and routing pools across Anthropic, OpenAI, Azure, Google, and Bedrock. 3 tools — manage OIDC and HMAC verification providers. ## Permission Model After selecting a context with `dome_context_use`, tools are filtered based on your workspace, tenant, and org roles. Higher-scope roles cascade into the tools visible inside a workspace context (an org owner sees every workspace tool). | Role | Access | | ------------- | ---------------------------------------------------------------------------------------------- | | **admin** | Every workspace tool — agents, gateway, rules, audit, members, API keys, configuration | | **operator** | Agents, gateway, rules (deploy and rollback), audit (view, no export), API keys, configuration | | **security** | Rules (deploy, rollback, simulate), audit (view and export), agent suspend, gateway view | | **developer** | Agents, agent keys, rules view, audit view, gateway view | | **viewer** | Read-only queries and listings | A handful of tools are gated only by an active context (no role requirement) — refer to [Context](/reference/mcp/context) and the platform tools `dome_status_get`, `dome_config_get`, `dome_workspaces_list`, `dome_workspaces_get`, `dome_platform_keys_available_permissions`. Tenant-level tools like `dome_workspaces_create` and `dome_provision_sandbox` require a tenant role; the tenant management tools `dome_tenants_list`, `dome_tenants_get`, and `dome_tenants_create` require an org role. Context management tools are always available regardless of role. You do not need workspace context to list or switch contexts. ## Tool Documentation Standard Each MCP tool page documents: * **Tool name** — the exact `dome_*` identifier used in JSON-RPC calls * **Parameters** — typed parameter table with required/optional markers * **Description** — one-line purpose * **Permission** — which RBAC role(s) can access the tool * **Destructive flag** — whether the tool modifies or deletes data Tools follow the [MCP specification](https://modelcontextprotocol.io/) for tool discovery and invocation. The server returns tool schemas via `tools/list` that MCP clients use for auto-discovery. ## Next steps Connect a client, then start with Agents or Rules: * [Setup AI assistant](/setup-ai-assistant) to connect Claude or another client * [Agents](/reference/mcp/agents) for the first tool group most operators use * [Rules](/reference/mcp/rules) to draft and deploy Cedar from an assistant * [Quickstart](/quickstart) to verify governed traffic end to end # LLM Models Source: https://docs.domesystems.ai/reference/mcp/models Configure LLM model connections and routing pools through the gateway Model tools register LLM upstreams the gateway proxies — Anthropic, OpenAI, Azure OpenAI, Google, Bedrock, hosted OpenAI-compatible vendors (Groq, Mistral, Together, Fireworks, DeepSeek, xAI, Perplexity, Cohere, OpenRouter, Cerebras, NVIDIA, DeepInfra, SambaNova, AI21, Databricks), and self-hosted endpoints — and group them into routing pools. Pools give agents one logical name that fans out across providers with priority, weighting, and failover. Models share workspace scope, gateway placement, and credential handling with [Tools](/reference/mcp/gateway). Use models for inference endpoints; use tools for MCP servers. ## Model CRUD ### dome\_model\_list List LLM model connections in the active workspace. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** `gateway.view` ### dome\_model\_get Get an LLM model connection by name. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `name` | string | Yes | Model name | **Permission:** `gateway.view` ### dome\_model\_add Add an LLM model connection. | Parameter | Type | Required | Description | | ----------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `name` | string | Yes | Connection name (unique within workspace) | | `provider` | string | Yes | `openai`, `anthropic`, `google`, `bedrock`, `azure_openai`, `mistral`, `groq`, `together`, `fireworks`, `deepseek`, `xai`, `perplexity`, `cohere`, `openrouter`, `cerebras`, `nvidia`, `deepinfra`, `sambanova`, `ai21`, `databricks`, `openai_compatible`, `custom`. Refer to [Supported providers](/reference/resources/models#providers) reference. | | `provider_config` | object | No | Provider-specific config. Common keys: `model`, `endpoint` (omit `endpoint` for hosted providers to use the registry's default base URL) | | `auth_method` | string | No | `none`, `api-key`, `oauth` (default: `api-key`) | | `credential_type` | string | No | `none`, `shared`, `per-user` (default: `shared`) | | `api_key` | string | No | API key for `api-key`+`shared`. Stored under the provider's managed auth header (`Authorization` for OpenAI/Bedrock, `x-api-key` for Anthropic, `api-key` for Azure, `x-goog-api-key` for Google) | | `secret_values` | object | No | Advanced: per-connection credential bundle for custom secret-source headers | | `attributes` | object | No | Cedar attributes used by authorization rules | | `gateways` | array | No | [Gateway](/connect/gateways) names to attach the model to directly at create time. Without it the model is unreachable until attached (`dome_gateway_model_add`). | **Permission:** `gateway.manage` ```json title="Add an Anthropic model" theme={"system"} { "name": "dome_model_add", "arguments": { "name": "claude-sonnet", "provider": "anthropic", "provider_config": { "model": "claude-3-5-sonnet-20241022" }, "api_key": "sk-ant-..." } } ``` ### dome\_model\_update Update an LLM model connection. Only provided fields change. | Parameter | Type | Required | Description | | ----------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | string | Yes | Model name | | `new_name` | string | No | Rename the connection | | `provider_config` | object | No | Replace `provider_config` | | `auth_method` | string | No | New auth method | | `credential_type` | string | No | New credential scope | | `api_key` | string | No | Rotate the shared API key. Stored under the provider's managed auth header (`Authorization` for OpenAI/Bedrock, `x-api-key` for Anthropic, `api-key` for Azure, `x-goog-api-key` for Google) | | `attributes` | object | No | Replace Cedar attributes (pass `{}` to clear) | **Permission:** `gateway.manage` Provider is immutable after create — to switch providers, add a new connection. ### dome\_model\_remove Remove an LLM model connection. Marked destructive. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `name` | string | Yes | Model name | **Permission:** `gateway.manage` ### dome\_model\_oauth\_connect Begin the shared-OAuth admin consent flow for a model configured with `auth_method=oauth`, `credential_type=shared` (Anthropic OAuth, Azure AAD service principal, Google Workload Identity for Vertex). Returns a one-shot URL valid for \~10 minutes. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `name` | string | Yes | Model name | **Permission:** `gateway.manage` ### dome\_model\_oauth\_disconnect Disconnect (revoke) shared-OAuth credentials for a model. Idempotent. Marked destructive. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `name` | string | Yes | Model name | **Permission:** `gateway.manage` ## Pool CRUD Pools are logical names agents call. The gateway picks a member at request time using the pool's routing strategy and falls back across priority buckets when a member fails. ### dome\_model\_pool\_list List LLM pools in the active workspace. **Permission:** `gateway.view` ### dome\_model\_pool\_get Get a pool with its members. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `name` | string | Yes | Pool name | **Permission:** `gateway.view` ### dome\_model\_pool\_create Create an LLM pool. | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `name` | string | Yes | Pool name (unique within workspace) | | `description` | string | No | Free-form description | | `match_when` | object | No | Predicate selecting this pool by request shape (e.g. `{"prompt_tokens":{"gt":100000}}`). Refer to [match\_when predicates](/reference/resources/model-pools#pool-resolution) reference for the full grammar — operators (`gt`/`lt`/`eq`, `in`, `prefix`, `suffix`, `regex`) and composition (`any`, `all`, `not`). | | `is_default` | bool | No | Mark as the workspace default pool | | `cache_ttl_secs` | int32 | No | Exact-match cache TTL (`0` disables cache) | | `cache_scope` | string | No | `workspace` or `caller` (default: `workspace`) | | `routing_strategy` | string | No | `priority_weighted`, `round_robin`, `least_loaded` (default: `priority_weighted`) | | `strategy_scope` | string | No | Scope of stateful routing counters: `workspace` (default) or `caller`. Ignored by `priority_weighted`. | | `failover_max` | string | No | Failover ceiling. `"all"` walks every member; integer `N` gives primary + N retries (`0` disables failover). Defaults to `"all"`. | | `gateways` | array | No | [Gateway](/connect/gateways) names to attach the pool to at create time. Without it the pool is unreachable until attached (`dome_gateway_model_pool_add`). | **Permission:** `gateway.manage` ### dome\_model\_pool\_update Update a pool. Only provided fields change. | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `name` | string | Yes | Pool name | | `new_name` | string | No | Rename | | `description` | string | No | Update description | | `match_when` | object | No | Replace match predicate (pass `{}` to clear). Refer to [match\_when predicates](/reference/resources/model-pools#pool-resolution) reference for the grammar. | | `cache_ttl_secs` | int32 | No | Update cache TTL (`0` disables) | | `cache_scope` | string | No | Update cache scope | | `routing_strategy` | string | No | `priority_weighted`, `round_robin`, or `least_loaded` | | `strategy_scope` | string | No | `workspace` or `caller` | | `failover_max` | string | No | `"all"` (walk every member) or non-negative integer `N`. Omit to leave unchanged. | **Permission:** `gateway.manage` ### dome\_model\_pool\_set\_default Set the workspace's default pool. Replaces any prior default. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `name` | string | Yes | Pool name | **Permission:** `gateway.manage` ### dome\_model\_pool\_move Reposition a pool in its workspace's `match_when` evaluation order. Pools with non-empty predicates evaluate in ascending `sort_key`; the first match wins. Exactly one of `before` or `after` is required. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------------------------------ | | `name` | string | Yes | Pool to move | | `before` | string | No | Anchor pool name; the moved pool lands immediately before it | | `after` | string | No | Anchor pool name; the moved pool lands immediately after it | **Permission:** `gateway.manage` ### dome\_model\_pool\_remove Remove an LLM pool. Marked destructive. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `name` | string | Yes | Pool name | **Permission:** `gateway.manage` ## Pool Member CRUD Members are model connections inside a pool, each with a priority and weight. Failover walks ascending priority; within a priority bucket, requests distribute by weight. ### dome\_model\_pool\_member\_list List members of a pool. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `pool` | string | Yes | Pool name | **Permission:** `gateway.view` ### dome\_model\_pool\_member\_add Add a model to a pool. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | ----------------------------------------------------------- | | `pool` | string | Yes | Pool name | | `model` | string | Yes | Model connection name | | `priority` | int32 | No | Priority bucket (`0` = primary, ascending fails over) | | `weight` | int32 | No | Weight within the priority bucket (default: `1`) | | `enabled` | bool | No | Whether the member is included in routing (default: `true`) | **Permission:** `gateway.manage` ```json title="Add a member with failover" theme={"system"} { "name": "dome_model_pool_member_add", "arguments": { "pool": "default", "model": "claude-sonnet", "priority": 0, "weight": 1 } } ``` ### dome\_model\_pool\_member\_update Update a pool member's priority, weight, or enabled flag. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | --------------------- | | `pool` | string | Yes | Pool name | | `model` | string | Yes | Model connection name | | `priority` | int32 | No | New priority | | `weight` | int32 | No | New weight | | `enabled` | bool | No | New enabled flag | **Permission:** `gateway.manage` ### dome\_model\_pool\_member\_remove Remove a model from a pool. Marked destructive. | Parameter | Type | Required | Description | | --------- | ------ | -------- | --------------------- | | `pool` | string | Yes | Pool name | | `model` | string | Yes | Model connection name | **Permission:** `gateway.manage` ## Cost quotas Cost quotas cap USD spend over a window and gate routing — total caps reject with HTTP 429 when exhausted, while per-model budgets lock a model out of routing so the pool spills to the next member. Refer to [Cost quotas](/govern/quotas#set-a-quota) for the enforcement model and subjects. ### dome\_model\_quota\_list List LLM cost quotas in the active workspace. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** `gateway.view` ### dome\_model\_quota\_set Create a USD spend cap. The `subject` decides what the cap covers. | Parameter | Type | Required | Description | | ------------ | ------ | ----------- | --------------------------------------------------------------------------------------- | | `subject` | string | Yes | `workspace`, `agent`, `act-as`, `pool`, or `model` | | `limit_usd` | number | Yes | Spend cap in US dollars (e.g. `500` or `12.50`) | | `window` | string | No | Reset window: `daily` or `monthly` (default `monthly`) | | `name` | string | No | Optional human-readable label | | `model` | string | Conditional | Model connection name (`subject=model`) | | `pool` | string | Conditional | Pool name (`subject=pool`, or to scope a model quota to one pool) | | `agent` | string | Conditional | Agent id (`subject=agent`) | | `act_as` | string | Conditional | Verified end-user OIDC subject (`subject=act-as`) | | `per_caller` | bool | No | `subject=model` only: apply the limit independently per agent and per verified end user | | `disabled` | bool | No | Create disabled; no enforcement until enabled | **Permission:** `gateway.manage` ```json title="Per-model in-pool budget (drives spill-over)" theme={"system"} { "name": "dome_model_quota_set", "arguments": { "subject": "model", "model": "claude-sonnet", "pool": "production", "limit_usd": 1000, "window": "monthly" } } ``` ### dome\_model\_quota\_remove Remove a cost quota by id. Marked destructive. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | --------------------------------------- | | `quota_id` | string | Yes | Quota id (from `dome_model_quota_list`) | **Permission:** `gateway.manage` ## Next steps Add connections and pools from Connect when you want the full workflow: * [Models](/connect/resources/models) * [Pools](/connect/resources/models/pools) * [Set Usage Limits](/govern/quotas) # Platform Source: https://docs.domesystems.ai/reference/mcp/platform Platform status, API keys, configuration, and tenant management Check platform status, create and revoke API keys, discover grantable permissions, read configuration, and list, inspect, and create tenants in your organization. These tools require an active context. `dome_status_get`, `dome_config_get`, and `dome_platform_keys_available_permissions` are visible to any role; the API key tools require `apikey.manage` at the active scope; and the tenant tools (`dome_tenants_list`, `dome_tenants_get`, `dome_tenants_create`) require the org-level `tenant.manage` permission. ## Status Get `dome_status_get` Get platform status and statistics for the current workspace. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** Authenticated only — visible to any caller with an active context, regardless of role. Returns workspace statistics including agent counts by status, active rule bundle info, and gateway backend count. ```json title="Example tool call" theme={"system"} { "name": "dome_status_get" } ``` ## Platform API Keys Platform API keys authenticate service-to-service integrations with the Dome Platform API. ### Platform Keys List `dome_platform_keys_list` List platform API keys. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** `apikey.manage` Returns key metadata (name, created date, last used, status) but not the key secret. ### Platform Keys Available Permissions `dome_platform_keys_available_permissions` List the permissions you can grant to a new platform API key at the current context's scope. Read-only. Call this before `dome_platform_keys_create` to discover the valid `permissions` values for your role. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** Authenticated only — the server filters the returned set against the caller's effective role. Returns an array of `{ id, label }` entries. The `id` is the value to pass to `dome_platform_keys_create`; the `label` is a human-readable name for display. The set is scope-aware: a workspace context returns workspace permissions only, a tenant context adds tenant permissions, and an org context adds org permissions. ```json title="Example tool call" theme={"system"} { "name": "dome_platform_keys_available_permissions" } ``` ### Platform Keys Create `dome_platform_keys_create` Create a new platform API key for programmatic access. The key is scoped to the current context level (workspace, tenant, or org). The secret is returned once. Call `dome_platform_keys_available_permissions` first to discover valid permission strings for your role. | Parameter | Type | Required | Description | | ------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `name` | string | Yes | Key name | | `permissions` | array | No | Permissions to grant (e.g., `agent.register`, `audit.view`). Use `dome_platform_keys_available_permissions` to enumerate valid values. | | `expires_at` | string | No | Expiration time (RFC3339) | **Permission:** `apikey.manage` Call `dome_platform_keys_available_permissions` first to discover valid permission strings for your role at the current scope. ```json title="Example tool call" theme={"system"} { "name": "dome_platform_keys_create", "arguments": { "name": "ci-pipeline", "permissions": ["agent.view", "audit.view"] } } ``` Store the returned key secret securely. It cannot be retrieved after creation — only revoked. ### Platform Keys Revoke `dome_platform_keys_revoke` Revoke a platform API key permanently. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ---------------- | | `key_id` | string | Yes | Key ID to revoke | **Permission:** `apikey.manage` Revocation is immediate. Any integrations using this key lose access immediately. ## Config Get `dome_config_get` Get a configuration value from the platform. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------- | | `key` | string | Yes | Configuration key | **Permission:** Authenticated only — visible to any caller with an active context, regardless of role. ## Tenants Tools for listing, inspecting, and creating tenants in your active organization. The org is sourced from the active context — switch contexts first if you need to target a different org. All three tools return "No active org context" if the caller has not selected an org context; run `dome_context_use` to switch. ### Tenants List `dome_tenants_list` List tenants under your active organization. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** Org-level `tenant.manage` (held by org `owner` or `admin`). Returns the array of tenants visible to the caller, including id, name, plan, org id, default gateway, and creation timestamp. ```json title="Example tool call" theme={"system"} { "name": "dome_tenants_list" } ``` ### Tenants Get `dome_tenants_get` Get a single tenant by id from your active organization. The caller is admitted if either the active context is scoped to the requested tenant, or the caller holds org `admin` or `owner` on the active org. | Parameter | Type | Required | Description | | ----------- | ------ | -------- | ----------- | | `tenant_id` | string | Yes | Tenant UUID | **Permission:** Tenant-scoped session for the requested tenant **or** org-level `tenant.manage` on the active org. The MCP tool listing surfaces this tool when org `tenant.manage` is held; tenant-scoped callers can still invoke the underlying RPC directly via the API. ```json title="Example tool call" theme={"system"} { "name": "dome_tenants_get", "arguments": { "tenant_id": "5b1e…" } } ``` ### Tenants Create `dome_tenants_create` Create a new tenant under your active organization. The caller becomes the initial tenant admin. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------------------------------------------- | | `name` | string | Yes | Tenant display name | | `plan` | string | No | Tenant plan: `essential` (default), `managed`, `enterprise`, `complete` | **Permission:** Org-level `tenant.manage` (held by org `owner` or `admin`). Creating a new organization is operator-only. Contact Dome to provision a new org. ```json title="Example tool call" theme={"system"} { "name": "dome_tenants_create", "arguments": { "name": "acme-prod", "plan": "managed" } } ``` If no active org context is set, the tool returns `No active org context. Use dome_context_use first.` ## Next steps Members, keys, and workspace defaults have how-tos under Manage: * [Manage Access](/manage/access) * [Configure Platform](/manage/settings) * [Permissions](/concepts/platform/permissions) concept # Authorization Rules Source: https://docs.domesystems.ai/reference/mcp/rules Deploy Cedar rules, simulate authorization decisions, and manage rule history Authorization rules tools manage Cedar policy bundles that govern what agents can do. Use these tools to deploy rules, validate syntax, simulate decisions before deploying, and roll back to previous versions. These 10 tools require workspace context. ## Rules Deploy `dome_rules_deploy` Deploy a bundle of Cedar authorization rules. Each file has a name and Cedar policy content. Defaults to workspace scope if `scope_kind`/`scope_id` are omitted. Use `dome_agents_assign_policy` for agent-scoped rules. | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- | | `files` | array | Yes | Array of `{name, content}` objects — Cedar rule files | | `name` | string | No | Optional friendly display name for this rule bundle | | `scope_kind` | string | No | Scope kind: `tenant`, `workspace`, `agent` (defaults to `workspace` if a workspace context is active) | | `scope_id` | string | No | Scope ID (workspace or agent UUID) | | `court_scope` | string | No | Directional [Court-review marker](/govern/judges): `none` (default), `reduce`, `expand`, or `both` | **Permission:** `rules.deploy` ```json title="Example tool call" theme={"system"} { "name": "dome_rules_deploy", "arguments": { "files": [ { "name": "allow-code-search.cedar", "content": "permit(\n principal,\n action == Dome::Action::\"mcp:call\",\n resource == Dome::MCPTool::\"code-search\"\n);" } ], "name": "allow-code-search" } } ``` Cedar policies use a declarative syntax. Use `dome_rules_validate` to check syntax before deploying. The response includes an advisory `warnings` array. After a successful deploy, the control plane walks every policy for `Dome::MCPTool` references and cross-checks each `(connection, tool)` pair against the workspace's persisted MCP tool catalog. Mismatches surface as `ValidationWarning` entries (file + message) — a typo or rename is flagged, but the deploy still succeeds. Warnings are only emitted for `workspace`- and `agent`-scoped bundles; `tenant`-scoped deploys skip this check because the catalog is workspace-keyed. ## Rules Get Active `dome_rules_get_active` Get the currently active rule bundle at the specified scope. Defaults to workspace scope. For agent-specific rules, use `scope_kind="agent"` and `scope_id=`, or use `dome_agents_get_policies`. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------ | | `scope_kind` | string | No | Scope kind: `tenant`, `workspace`, `agent` | | `scope_id` | string | No | Scope ID | **Permission:** `rules.view` ## Rules Validate `dome_rules_validate` Validate Cedar rule files without deploying. Returns errors and warnings. | Parameter | Type | Required | Description | | --------- | ----- | -------- | ----------------------------------------------------- | | `files` | array | Yes | Array of `{name, content}` objects — Cedar rule files | **Permission:** `rules.view` ```json title="Example tool call" theme={"system"} { "name": "dome_rules_validate", "arguments": { "files": [ { "name": "draft.cedar", "content": "permit(\n principal,\n action == Dome::Action::\"mcp:call\",\n resource\n);" } ] } } ``` ## Rules Simulate `dome_rules_simulate` Simulate an authorization decision against the active rules without actually performing the action. | Parameter | Type | Required | Description | | --------------- | ------ | -------- | ---------------------------------------------------------------- | | `agent_id` | string | Yes | Agent ID (or name — resolved to UUID) to simulate as the caller | | `action` | string | Yes | Action to evaluate (e.g., `mcp:call`, `mcp:discover`) | | `resource` | string | Yes | Resource identifier | | `resource_type` | string | Yes | Resource type: `mcp_tool`, `llm_model`, `resource` | | `context` | object | No | Additional context key-value pairs | | `actas_sub` | string | No | Act-as subject identifier (end-user the agent acts on behalf of) | | `actas_email` | string | No | Act-as email | | `actas_roles` | string | No | Act-as roles (comma-separated) | | `actas_groups` | string | No | Act-as groups (comma-separated) | **Permission:** `rules.view` ```json title="Example tool call" theme={"system"} { "name": "dome_rules_simulate", "arguments": { "agent_id": "code-reviewer", "action": "mcp:call", "resource": "mcp_tool/code-search", "resource_type": "mcp_tool" } } ``` ## Rules Simulate Result `dome_rules_simulate_result` Get the result of a rule simulation run. If the simulation is still running, the status is `pending` or `running` — call again after a moment to check for completion. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------- | | `id` | string | Yes | Simulation ID | **Permission:** `rules.view` ## Rules List Versions `dome_rules_list_versions` List historical rule bundle versions at the specified scope. Defaults to workspace scope. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------ | | `scope_kind` | string | No | Scope kind: `tenant`, `workspace`, `agent` | | `scope_id` | string | No | Scope ID | | `limit` | number | No | Maximum results (default: 20) | **Permission:** `rules.view` Returns each version with its bundle ID, name, deploy timestamp, and deployer. ## Rules Rollback `dome_rules_rollback` Rollback to a previously deployed rule bundle by its ID. The server reads the bundle's scope from the stored row — no `scope_kind`/`scope_id` needed. | Parameter | Type | Required | Description | | ----------- | ------ | -------- | ------------------------------------------------ | | `bundle_id` | string | Yes | Bundle ID (UUID) from `dome_rules_list_versions` | **Permission:** `rules.rollback` Rollback immediately changes the active rules. All subsequent authorization decisions use the rolled-back bundle. ## Rules Generate `dome_rules_generate` Generate starter Cedar authorization rules. Returns a Cedar policy with blanket `mcp:discover` permit and comments explaining how backend tool availability works. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** `rules.view` ## Agents Assign Policy `dome_agents_assign_policy` Deploy Cedar rules scoped to a specific agent. These rules are evaluated in addition to workspace-level rules. Cedar `forbid` at workspace scope overrides `permit` at agent scope. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | ----------------------------------------------------- | | `agent_id` | string | Yes | Agent ID to assign rules to | | `files` | array | Yes | Array of `{name, content}` objects — Cedar rule files | **Permission:** `rules.deploy` ```json title="Example tool call" theme={"system"} { "name": "dome_agents_assign_policy", "arguments": { "agent_id": "code-reviewer", "files": [ { "name": "agent-policy.cedar", "content": "permit(\n principal,\n action == Dome::Action::\"mcp:call\",\n resource == Dome::MCPTool::\"code-search\"\n);" } ] } } ``` ## Agents Get Policies `dome_agents_get_policies` Get the Cedar rules currently assigned to a specific agent (agent-scope only; does not include inherited workspace/tenant/org rules). To see workspace rules, use `dome_rules_get_active`. | Parameter | Type | Required | Description | | ---------- | ------ | -------- | ----------- | | `agent_id` | string | Yes | Agent ID | **Permission:** `rules.view` ## Next steps Write and simulate from Govern. Evaluation semantics and Cedar catalogs sit under Concepts and Reference: * [Authorize Access](/govern/rules) * [Authorization Model](/concepts/architecture/authorization-model) concept * [Rules](/reference/controls/rules) reference # Tools Source: https://docs.domesystems.ai/reference/mcp/tools MCP tools for managing upstream MCP server connections and their catalogs Tool-management tools register the MCP servers that Gateways proxy for agents. Each connection represents an upstream MCP server agents access through Dome. These tools require workspace context. For Gateway membership and access grants, refer to [Gateways](/reference/mcp/gateway). For LLM model connections, refer to [Models](/reference/mcp/models). ## Tool CRUD ### dome\_tool\_list List MCP server connections in the active workspace. Returns name, URL, protocol, auth method, outbound headers, and field classifications for each. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** `gateway.view` ```json title="Example tool call" theme={"system"} { "name": "dome_tool_list" } ``` ### dome\_tool\_get Get an MCP server connection by name. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `name` | string | Yes | Tool name | **Permission:** `gateway.view` ### dome\_tool\_add Add a new MCP server connection. Connections route the gateway to external MCP servers agents reach through Dome. | Parameter | Type | Required | Description | | ----------------------- | ------ | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | string | Yes | Tool name (unique within workspace) | | `url` | string | Yes (streamable-http) | Tool URL | | `command` | string | Yes (stdio) | Command to exec | | `args` | array | No | Command args for `stdio` | | `protocol` | string | No | `streamable-http`, `stdio` (default: `streamable-http`) | | `headers` | array | No | Outbound headers. Each entry: `{name, source, value, secret}`. `source` is `literal` (with `value`), `secret` (with `secret` key name), or `actas` (no value/secret — reinjects the verified `X-Dome-Act-As`). `stdio` rejects headers. `Authorization` is reserved for the managed auth flow. | | `authorization` | string | No | `Authorization` HTTP header value. REQUIRED when `auth_method=api-key` and `credential_type=shared`. Forbidden otherwise. The server auto-injects the matching managed `Authorization` header — do not declare it manually. | | `secret_values` | object | No | Advanced: per-tool credential bundle for custom secret-source headers. `Authorization` is reserved. | | `auth_method` | string | No | `none`, `api-key`, `oauth` | | `credential_type` | string | No | `shared`, `per-user` | | `oauth` | object | No | OAuth client config (required when `auth_method=oauth`) | | `field_classifications` | object | No | Field sensitivity classifications (e.g. `{"email": "PII"}`) | | `write_tools` | array | No | Tool names classified as write operations | | `gateways` | array | No | [Gateway](/connect/gateways) names to attach the connection to as a tool source at create time. Without it the connection is unreachable until attached (`dome_gateway_tool_source_add`). | **Permission:** `gateway.manage` The `oauth` object accepts: | Field | Description | | ---------------------------- | -------------------------------------------------------- | | `authorize_url` | Authorization endpoint URL | | `token_url` | Token endpoint URL | | `revoke_url` | Revocation endpoint URL | | `registration_url` | RFC 7591 registration endpoint (for `client_origin=dcr`) | | `client_id` | OAuth client ID | | `client_secret` | OAuth client secret | | `default_scopes` | Array of default scopes | | `client_origin` | `dcr` or `manual` | | `token_endpoint_auth_method` | `auto`, `client_secret_basic`, or `client_secret_post` | When `auth_method=oauth` and `credential_type=shared`, the response includes `oauth_connect_url` — open it in a browser to complete consent. ```json title="Shared OAuth tool" theme={"system"} { "name": "dome_tool_add", "arguments": { "name": "notion", "url": "https://mcp.notion.com/mcp", "auth_method": "oauth", "credential_type": "shared", "oauth": { "authorize_url": "https://api.notion.com/v1/oauth/authorize", "token_url": "https://api.notion.com/v1/oauth/token", "client_id": "...", "client_secret": "...", "client_origin": "manual", "default_scopes": ["read_content", "update_content"] } } } ``` ### dome\_tool\_update Update an existing MCP server connection. Only provided fields are changed. | Parameter | Type | Required | Description | | ------------------------------ | ------ | -------- | ------------------------------------------------------------------ | | `name` | string | Yes | Tool name to update | | `url` | string | No | New URL | | `protocol` | string | No | New protocol | | `headers` | array | No | Replace the full outbound header list. Omit to keep current. | | `authorization` | string | No | Rotate an `api-key`+`shared` tool's token | | `secret_values` | object | No | Advanced: replace per-tool credential bundle. Empty `{}` wipes it. | | `auth_method` | string | No | New auth method | | `credential_type` | string | No | New credential scope | | `oauth` | object | No | OAuth client config patch. Empty `client_secret` keeps existing. | | `field_classifications` | object | No | Add or update field classifications | | `remove_field_classifications` | array | No | Remove field classifications by name | | `write_tools` | array | No | Replace write-tools list | **Permission:** `gateway.manage` Switching `auth_method` between `api-key` and `oauth` clears the previously stored credential bundle. ### dome\_tool\_oauth\_connect Begin the shared-OAuth admin consent flow for a tool. Returns a one-shot URL the operator opens in a browser to consent at the upstream's authorization server. Only valid for `auth_method=oauth`, `credential_type=shared`. Per-user OAuth tools trigger consent automatically on each end user's first call. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `name` | string | Yes | Tool name | **Permission:** `gateway.manage` The response includes `oauth_connect_url` and `oauth_connect_expires_at`. Open the URL within \~10 minutes. ### dome\_tool\_oauth\_disconnect Disconnect (revoke) shared OAuth credentials. The OAuth client configuration is preserved, so re-running connect reuses the same client. Idempotent. Marked destructive. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `name` | string | Yes | Tool name | **Permission:** `gateway.manage` ### dome\_tool\_remove Remove an MCP server connection from the active workspace. Marked destructive. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------- | | `name` | string | Yes | Tool name to remove | **Permission:** `gateway.manage` Removing a tool disconnects all agents currently routing through it. Update or re-route agents first. ## Tool Catalog ### dome\_tools\_for\_connection List the operations Dome has discovered on one MCP server connection. This reads the stored catalog and does not apply Gateway membership or agent rules. | Parameter | Type | Required | Description | | ------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------- | | `name` | string | Yes | Tool name | | `seen_since` | string | No | Include operations seen within this duration, such as `30d` or `24h`. Pass `0` for all recorded operations | | `with_schema` | boolean | No | Include each operation's input schema. Defaults to `true` | **Permission:** `gateway.view` ```json title="List discovered operations" theme={"system"} { "name": "dome_tools_for_connection", "arguments": { "name": "notion", "with_schema": false } } ``` ## Tool Lifecycle Transition individual MCP tools (the items inside a connection's catalog) through `active` → `deprecated` → `blocked`. Status persists across re-observation — once set, only an explicit restore returns the tool to `active`. ### dome\_tool\_block Hide an MCP tool from agents. The Gateway filters it out of `tools/list` and denies any `tools/call` against it; blocked-call attempts emit a `tool.blocked_call_denied` audit event. A block takes precedence over ordinary connection-level access. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | --------------------------------------------------------- | | `connection` | string | Yes | MCP connection name | | `tool` | string | Yes | Tool name as observed in the catalog (e.g. `delete_page`) | | `message` | string | No | Operator note shown in dashboards | **Permission:** `gateway.manage` ```json title="Block a dangerous tool" theme={"system"} { "name": "dome_tool_block", "arguments": { "connection": "notion", "tool": "delete_page", "message": "dangerous mass-delete; use archive_page" } } ``` ### dome\_tool\_deprecate Mark a tool as deprecated. The tool stays callable and visible to agents — the dashboard surfaces a warning chip and the status flows through the catalog. Use when an upstream supersedes a tool and you want to soft-signal callers. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------- | | `connection` | string | Yes | MCP connection name | | `tool` | string | Yes | Tool name as observed in the catalog | | `message` | string | No | Operator note (e.g. `"use create_page_v2"`) | **Permission:** `gateway.manage` ### dome\_tool\_restore Restore a tool to `active`, clearing any prior block or deprecation. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------ | | `connection` | string | Yes | MCP connection name | | `tool` | string | Yes | Tool name as observed in the catalog | **Permission:** `gateway.manage` ## Next steps Attach backends under Connect. Named Gateways and grants are a separate tool group: * [Tools](/connect/resources/tools) * [Models](/connect/resources/models) * [Gateways](/reference/mcp/gateway) # Verification Providers Source: https://docs.domesystems.ai/reference/mcp/verification Manage OIDC and HMAC verification providers for act-as identity Verification provider tools manage the identity providers used to verify act-as claims. When an agent presents an end-user identity (act-as), Dome uses these providers to validate the claim via OIDC token verification or HMAC signature checking. These 3 tools require workspace context. ## Verification Providers List `dome_verification_providers_list` List verification providers configured in the current workspace. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** `config.manage` Returns each provider with its ID, name, verification method, and configuration details. ```json title="Example tool call" theme={"system"} { "name": "dome_verification_providers_list" } ``` ## Verification Providers Create `dome_verification_providers_create` Create a new verification provider. | Parameter | Type | Required | Description | | -------------------- | ------ | ----------- | ---------------------------------------------------------- | | `name` | string | Yes | Provider name | | `method` | string | Yes | Verification method: `oidc` or `hmac` | | `oidc_discovery_url` | string | Conditional | OIDC issuer discovery URL (required when method is `oidc`) | | `hmac_secret` | string | Conditional | HMAC shared secret (required when method is `hmac`) | **Permission:** `config.manage` ```json title="Example — OIDC provider" theme={"system"} { "name": "dome_verification_providers_create", "arguments": { "name": "google-workspace", "method": "oidc", "oidc_discovery_url": "https://accounts.google.com/.well-known/openid-configuration" } } ``` ```json title="Example — HMAC provider" theme={"system"} { "name": "dome_verification_providers_create", "arguments": { "name": "internal-signer", "method": "hmac", "hmac_secret": "your-shared-secret" } } ``` After creating a provider, reference it in agent registration with the `actas_method` and provider ID to enable act-as verification for that agent. ## Verification Providers Delete `dome_verification_providers_delete` Delete a verification provider. | Parameter | Type | Required | Description | | ------------- | ------ | -------- | --------------------- | | `provider_id` | string | Yes | Provider ID to delete | **Permission:** `config.manage` Deleting a provider breaks act-as verification for any agents referencing it. Update or remove agent verification configs before deleting the provider. ## Next steps Configure providers on delegated agents, then pass Act-As from the app: * [Delegated agents](/connect/agents/delegated) * [Identity Patterns](/concepts/identity-patterns) concept * [Pass identity for delegated agents](/develop#pass-identity-for-delegated-agents) # Webhooks Source: https://docs.domesystems.ai/reference/mcp/webhooks Manage webhook subscriptions, deliveries, and the provider catalog Webhook tools give an agent parity with the dashboard and CLI for event subscriptions, delivery history, and replay. These 14 tools require workspace context. The surface is subscription-first: creating a subscription creates its delivery destination in the same call, and the destination's URL, headers, secret, and credentials are managed through the subscription tools. There are no standalone destination tools. Refer to [Webhooks](/operate/webhooks) for setup, signature verification, and the event catalog. ## Permissions | Permission | Tools | | ----------------- | ----------------------------------------------------------------------------------------------------- | | `eventing.view` | `subscription_list`, `subscription_get`, `delivery_list`, `delivery_get`, `provider_list` | | `eventing.manage` | `subscription_create`, `_update`, `_disable`, `_rotate_secret`, `_test`, `_delete`; `delivery_cancel` | | `eventing.replay` | `delivery_replay`, `delivery_replay_failed` | Replay sends new outbound requests to your destinations and may trigger downstream automation — it carries its own permission, separate from `manage`. ## Subscription List `dome_webhooks_subscription_list` List event subscriptions in the active workspace, each with its delivery destination. No parameters. **Permission:** `eventing.view` ## Subscription Get `dome_webhooks_subscription_get` Get an event subscription by name. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------- | | `name` | string | Yes | Subscription name | **Permission:** `eventing.view` ## Subscription Create `dome_webhooks_subscription_create` Create a subscription with its delivery destination and event selection, atomically. A failed request leaves neither record behind. The response includes the destination's signing secret **once** — it is only rotatable afterward, never readable. | Parameter | Type | Required | Description | | ---------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------- | | `name` | string | Yes | Subscription name (unique within workspace) | | `event_types` | string\[] | Yes | Event types to subscribe to (e.g. `["agent.suspended"]`) | | `endpoint_url` | string | One of | Custom HTTPS delivery endpoint. Set this or `provider`. | | `provider` | string | One of | Provider key from `dome_webhooks_provider_list`. Set this or `endpoint_url`. | | `settings` | object | No | Provider non-secret settings (key → value) | | `credentials` | object | No | Provider credentials (key → value). Stored write-only. | | `filters` | object | No | Per-key predicates — refer to [Filters](#filters) | | `headers` | array | No | Custom-URL headers: `[{"name", "source": "literal"\|"secret", "value"}]`. Secret values are write-only, masked on read. | | `content_format` | string | No | Custom-URL body shape: `object` (default) or `array` | `endpoint_url`, `headers`, and `content_format` are custom-URL only — combining them with `provider` errors before any state change. **Permission:** `eventing.manage` ```json title="Example tool call" theme={"system"} { "name": "dome_webhooks_subscription_create", "arguments": { "name": "security-alerts", "endpoint_url": "https://ops.example.com/hooks/dome", "event_types": ["access.denied", "agent.suspended"], "filters": { "agent_name": ["checkout-worker", "billing-worker"] } } } ``` ### Filters The `filters` object keys on a field name — `agent_name`, `connection_name` (resolved from IDs at delivery time), `agent_id`, `tool_name`, or any filterable payload field. Values: | Value form | Meaning | | -------------------- | -------------------------------------------------------------------------------------- | | scalar | Exact match | | array | OR across alternatives | | `{"like": ""}` | Glob — `*` any run, `?` one char, `\` escapes. Matches the full value, case-sensitive. | An event delivers only if it satisfies every filter that applies to its type. ## Subscription Update `dome_webhooks_subscription_update` Update a subscription's events, filters, state, or delivery destination. Only supplied fields change. | Parameter | Type | Required | Description | | ----------------- | --------- | -------- | ------------------------------------------------------------------------------------- | | `name` | string | Yes | Current subscription name | | `new_name` | string | No | New name | | `event_types` | string\[] | No | Replace the full event-type set | | `filters` | object | No | Replace the full filter set; pass `{}` to clear | | `active` | boolean | No | Enable or pause fanout. No backfill on resume. | | `endpoint_url` | string | No | New custom HTTPS endpoint URL | | `endpoint_active` | boolean | No | Enable or disable the destination itself | | `headers` | array | No | Replace custom headers; `[]` clears them. Blank secret values keep the stored secret. | | `remove_headers` | string\[] | No | Header names to remove | | `content_format` | string | No | `object` or `array`. Custom URL only. | | `settings` | object | No | Replace the full provider settings set | | `credentials` | object | No | Merge write-only provider credentials | **Permission:** `eventing.manage` ## Subscription Disable `dome_webhooks_subscription_disable` Stop new fanout. Delivery history is preserved. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------- | | `name` | string | Yes | Subscription name | **Permission:** `eventing.manage` ## Subscription Rotate Secret `dome_webhooks_subscription_rotate_secret` Rotate the signing secret for the subscription's destination. The new secret is revealed once. During the grace window the `webhook-signature` header carries a signature per active key. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------- | | `name` | string | Yes | Subscription name | **Permission:** `eventing.manage` ## Subscription Test `dome_webhooks_subscription_test` Send a fixed `eventing.test` event through the subscription's destination. Verifies signing, headers, and the destination's own routing end-to-end. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------- | | `name` | string | Yes | Subscription name | **Permission:** `eventing.manage` ## Subscription Delete `dome_webhooks_subscription_delete` Delete the subscription: new routing stops and pending deliveries are canceled. Delivery history is preserved. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------- | | `name` | string | Yes | Subscription name | **Permission:** `eventing.manage` · **Destructive** ## Delivery List `dome_webhooks_delivery_list` List deliveries in the active workspace, optionally filtered. | Parameter | Type | Required | Description | | ----------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------ | | `subscription_id` | string | No | Narrow to one subscription | | `destination_id` | string | No | Narrow to one destination | | `event_type` | string | No | Narrow to one event type | | `status` | string | No | One of `created`, `enqueued`, `delivering`, `retry_scheduled`, `succeeded`, `failed`, `canceled`, `expired`, `dlq` | | `event_id` | string | No | Every delivery for one source event | | `start_time` | string | No | Inclusive RFC3339 lower bound | | `end_time` | string | No | Inclusive RFC3339 upper bound | | `limit` | number | No | Page size (0 = server default) | | `page_token` | string | No | Cursor from a previous call | **Permission:** `eventing.view` ## Delivery Get `dome_webhooks_delivery_get` Get a delivery with its attempt history. | Parameter | Type | Required | Description | | -------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------- | | `delivery_id` | string | Yes | Delivery ID (UUID) | | `include_body` | boolean | No | Include the delivered webhook body while retained — up to 30 days, or 24 hours after a successful delivery. | **Permission:** `eventing.view` ## Delivery Replay `dome_webhooks_delivery_replay` Replay a delivery. Creates a **new** delivery from the retained body — fresh `webhook-id`, same `Dome-Event-Id` — and sends another request to the destination. It may trigger downstream automation. | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ---------------------------- | | `delivery_id` | string | Yes | Delivery ID (UUID) to replay | **Permission:** `eventing.replay` ## Delivery Replay Failed `dome_webhooks_delivery_replay_failed` Bulk-replay terminally-failed deliveries in a time window — the outage-recovery counterpart to `delivery_replay`. Safe to re-run: a delivery with a live or succeeded replay is not matched again, and canceled deliveries are never recovered. | Parameter | Type | Required | Description | | -------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------- | | `since` | string | Yes | Duration back from now (`24h`, `7d`) or RFC3339 timestamp. Within the last 30 days — the body-retention horizon. | | `subscription` | string | No | Narrow to one subscription name. Omit for all. | | `until` | string | No | RFC3339 upper bound. Defaults to now. | | `statuses` | string\[] | No | Subset of `failed`, `expired`, `dlq`. Default all three. | | `max` | number | No | Deliveries per call, up to 500. Repeat to continue. | **Permission:** `eventing.replay` ```json title="Example tool call" theme={"system"} { "name": "dome_webhooks_delivery_replay_failed", "arguments": { "since": "24h", "subscription": "security-alerts", "statuses": ["failed", "dlq"] } } ``` Returns `matched`, `replayed`, `skipped (body expired)`, and `failed` counts. ## Delivery Cancel `dome_webhooks_delivery_cancel` Cancel a delivery's pending retry so no further outbound request is sent. | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ---------------------------- | | `delivery_id` | string | Yes | Delivery ID (UUID) to cancel | **Permission:** `eventing.manage` · **Destructive** ## Provider List `dome_webhooks_provider_list` List the off-the-shelf provider catalog: each provider's setting and credential fields, recommended event bundle, and authentication style. Call before `dome_webhooks_subscription_create` with `provider` to discover the required `settings` and `credentials`. No parameters. **Permission:** `eventing.view` **Dashboard-only views.** Delivery statistics and the browsable event catalog (payload fields, sample bodies, JSON schemas) live in the dashboard's Webhooks page. Agents list, inspect, and replay deliveries through the tools above. ## Next steps Create destinations and subscriptions from Operate: * [Subscribe to Events](/operate/webhooks) * [Webhooks](/concepts/audit/webhooks) concept * [Events](/reference/events) reference # Workspaces Source: https://docs.domesystems.ai/reference/mcp/workspaces Create workspaces, manage members, and configure act-as enforcement and policy freshness Create and delete workspaces, assign member roles, configure act-as identity enforcement and policy freshness, and provision sandboxes. These 11 tools span three permission tiers: list/get are visible to any caller with an active context; member, act-as, freshness, and delete operations require workspace `workspace.manage` or `config.manage`; workspace creation and sandbox provisioning require tenant- or org-level permissions. ## Workspaces List `dome_workspaces_list` List all workspaces in the current tenant. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** Authenticated only — visible to any caller with an active context. The server returns only the workspaces the caller actually has access to. ```json title="Example tool call" theme={"system"} { "name": "dome_workspaces_list" } ``` ## Workspaces Create `dome_workspaces_create` Create a new workspace in the current tenant. The server auto-generates a URL-safe slug from the display name. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ---------------------- | | `name` | string | Yes | Workspace display name | **Permission:** Tenant-level `tenant.manage` (held by tenant `admin`, or by org `owner` / `admin` via cascade). Workspace-only roles cannot create workspaces. ```json title="Example tool call" theme={"system"} { "name": "dome_workspaces_create", "arguments": { "name": "staging" } } ``` ## Workspaces Get `dome_workspaces_get` Get a single workspace in the current tenant. Accepts a UUID or a slug; defaults to the active workspace when omitted. | Parameter | Type | Required | Description | | ----------- | ------ | -------- | -------------------------------------------------------------- | | `workspace` | string | No | Workspace ID (UUID) or slug. Defaults to the active workspace. | **Permission:** Authenticated only — visible to any caller with an active context. The server enforces that the caller can read the requested workspace. ## Workspaces Delete `dome_workspaces_delete` Delete a workspace from the current tenant. Accepts a UUID or a slug. Marked destructive. | Parameter | Type | Required | Description | | ----------- | ------ | -------- | --------------------------- | | `workspace` | string | Yes | Workspace ID (UUID) or slug | **Permission:** `workspace.manage` Workspace deletion is permanent and cannot be undone. ## Provision Sandbox `dome_provision_sandbox` Provision a sandbox environment for development and testing. Use `scope="tenant"` to create an isolated sandbox tenant with workspace and API key (requires org admin/owner). Use `scope="workspace"` to create a sandbox workspace in the current tenant (requires tenant admin/operator/security). | Parameter | Type | Required | Description | | ---------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `scope` | string | Yes | `tenant` or `workspace` | | `tenant_name` | string | No | Sandbox tenant name suffix (auto-generated if omitted). Used only when `scope="tenant"`. Must be lowercase alphanumeric and hyphens, max 64 chars. | | `workspace_name` | string | No | Sandbox workspace name suffix (auto-generated if omitted). Used with both scopes. | **Permission:** Tenant-level `sandbox.workspace.create` for `scope="workspace"` (held by every tenant role, plus org `owner` / `admin` via cascade). Org-level `sandbox.tenant.create` for `scope="tenant"` (held by org `owner` / `admin` only). ```json title="Example — sandbox workspace" theme={"system"} { "name": "dome_provision_sandbox", "arguments": { "scope": "workspace", "workspace_name": "integration-testing" } } ``` ## Act-As Enforcement Act-as enforcement controls whether agents must present verified end-user identity on every request. ### Workspace Actas Get `dome_workspace_actas_get` Get the current act-as enforcement policy for the active workspace. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** `config.manage` Returns whether act-as is required and which verification methods are allowed. ### Workspace Actas Update `dome_workspace_actas_update` Update the act-as enforcement policy for the active workspace. | Parameter | Type | Required | Description | | ----------------------- | ----- | -------- | ---------------------------------------------------------------------------- | | `actas_required` | bool | No | Require act-as header on all requests | | `actas_allowed_methods` | array | No | Allowed verification methods (e.g., `["oidc", "hmac"]`). Empty = any method. | **Permission:** `config.manage` ```json title="Example tool call" theme={"system"} { "name": "dome_workspace_actas_update", "arguments": { "actas_required": true, "actas_allowed_methods": ["oidc"] } } ``` Enabling act-as enforcement affects all agents in the workspace. Agents without a configured act-as method are rejected. ## Policy Freshness Window Each workspace has a configurable freshness window that bounds how long the gateway may serve cached authorization policy before failing closed. Default is 90 seconds; range is 30–86400 seconds. Enforcement is per-workspace — one workspace failing closed does not affect others. ### `dome_workspace_set_freshness_window` Set the freshness window for the active workspace. | Parameter | Type | Required | Description | | --------- | ------ | -------- | -------------------------------------- | | `seconds` | number | Yes | Freshness window in seconds (30–86400) | **Permission:** `config.manage` ```json title="Example tool call" theme={"system"} { "name": "dome_workspace_set_freshness_window", "arguments": { "seconds": 120 } } ``` Once the window elapses, the gateway denies every request in the workspace and emits a `authorization.staleness_rejected` audit event until the workspace's policy is fresh again. ## Members ### Workspace Members List `dome_workspace_members_list` List all members of the active workspace with their roles. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ------------- | | — | — | — | No parameters | **Permission:** `workspace.manage` Returns each member with their email, role, and join date. ### Workspace Members Set `dome_workspace_members_set` Grant or update a user's role in the active workspace. | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ----------------------------------------------- | | `user_email` | string | Yes | User email address | | `role` | string | Yes | Role: `admin`, `operator`, `security`, `viewer` | **Permission:** `workspace.manage` ```json title="Example tool call" theme={"system"} { "name": "dome_workspace_members_set", "arguments": { "user_email": "alice@example.com", "role": "operator" } } ``` ### Workspace Members Remove `dome_workspace_members_remove` Remove a user from the active workspace. Optionally revoke their API keys at the same time by passing `revoke_key_ids` (specific IDs) or `revoke_all_keys=true`. If neither is passed and the user has active keys, the response lists them so the caller can decide which to revoke. Marked destructive. | Parameter | Type | Required | Description | | ----------------- | ------ | -------- | --------------------------------------------------------------------------------------- | | `user_email` | string | Yes | User email address | | `revoke_key_ids` | array | No | API key IDs to revoke alongside the removal. Mutually exclusive with `revoke_all_keys`. | | `revoke_all_keys` | bool | No | Revoke all API keys created by the user. Mutually exclusive with `revoke_key_ids`. | **Permission:** `workspace.manage` ## Next steps Tenancy and membership how-tos live under Manage: * [Prepare Environments](/manage/environments) * [Manage Access](/manage/access) * [Scopes](/concepts/platform/scopes) concept # Permissions Source: https://docs.domesystems.ai/reference/permissions Platform RBAC role and permission catalogs across organization, tenant, and workspace scopes Field and catalog reference for platform RBAC. Agent runtime authorization uses Cedar and is separate from this catalog. Refer to [Permissions](/concepts/platform/permissions) concept for how platform RBAC works. Assign members and keys on [Manage Access](/manage/access). ## Workspace roles Every workspace member has one of six roles. | Role | Description | Key capabilities | | ---------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **admin** | Full workspace management | All workspace operations including member management, configuration, and workspace deletion | | **operator** | Day-to-day workspace operations | Register, suspend, revoke, and delete agents. Manage agent and API keys. Deploy and roll back rules. Manage gateway and integrations. Export audit. Cannot delete the workspace or change the admin roster. | | **security** | Authorization rules and audit | Deploy and roll back rules, simulate, suspend agents, query and export audit | | **developer** | Agent integration work | Register and update agents, manage agent keys, view rules, audit, gateway, and integrations (no deploy, no export) | | **viewer** | Read-only access | List agents, view rules, gateway, audit, and integrations | | **impersonator** | Reserved for impersonation flows | No standing permissions. Used by sessions impersonating another user. | ### Workspace-scoped permissions ✅ means the role has the permission. ❌ means it does not. | Permission | admin | operator | security | developer | viewer | | ------------------------ | :---: | :------: | :------: | :-------: | :----: | | `agent.view` | ✅ | ✅ | ✅ | ✅ | ✅ | | `agent.register` | ✅ | ✅ | ❌ | ✅ | ❌ | | `agent.suspend` | ✅ | ✅ | ✅ | ✅ | ❌ | | `agent.revoke` | ✅ | ✅ | ❌ | ✅ | ❌ | | `agent.delete` | ✅ | ✅ | ❌ | ❌ | ❌ | | `agentkey.manage` | ✅ | ✅ | ❌ | ✅ | ❌ | | `rules.view` | ✅ | ✅ | ✅ | ✅ | ✅ | | `rules.deploy` | ✅ | ✅ | ✅ | ❌ | ❌ | | `rules.rollback` | ✅ | ✅ | ✅ | ❌ | ❌ | | `gateway.view` | ✅ | ✅ | ✅ | ✅ | ✅ | | `gateway.manage` | ✅ | ✅ | ❌ | ❌ | ❌ | | `audit.view` | ✅ | ✅ | ✅ | ✅ | ✅ | | `audit.export` | ✅ | ✅ | ✅ | ❌ | ❌ | | `workspace.manage` | ✅ | ✅ | ❌ | ❌ | ❌ | | `workspace.delete` | ✅ | ❌ | ❌ | ❌ | ❌ | | `workspace.manage_admin` | ✅ | ❌ | ❌ | ❌ | ❌ | | `apikey.manage` | ✅ | ✅ | ❌ | ❌ | ❌ | | `config.manage` | ✅ | ✅ | ❌ | ❌ | ❌ | | `integrations.view` | ✅ | ✅ | ✅ | ✅ | ✅ | | `integrations.manage` | ✅ | ✅ | ❌ | ❌ | ❌ | | `judges.view` | ✅ | ✅ | ✅ | ✅ | ✅ | | `judges.manage` | ✅ | ✅ | ❌ | ❌ | ❌ | | `eventing.view` | ✅ | ✅ | ✅ | ✅ | ✅ | | `eventing.manage` | ✅ | ✅ | ❌ | ❌ | ❌ | | `eventing.replay` | ✅ | ✅ | ❌ | ❌ | ❌ | | `filters.view` | ✅ | ✅ | ✅ | ✅ | ✅ | | `filters.manage` | ✅ | ✅ | ❌ | ❌ | ❌ | `judges.view` and `judges.manage` gate [Intelligent Authorization](/govern/judges). Preconfigured judges remain immutable regardless of `judges.manage`. Renamed from `moot.view` / `moot.manage` in the 2026-06 release. `eventing.*` gates [Subscribe to Events](/operate/webhooks). `filters.*` gates Guard filter configuration. See those how-tos for operation detail. `workspace.manage_admin` is the workspace-direct admin marker. It gates adding, removing, or changing the role of any user to or from workspace admin. It distinguishes workspace `admin` from workspace `operator`. The latter operates the workspace end-to-end but cannot reshape its admin roster. The distinction collapses at the tenant scope. Tenant `admin` and tenant `operator` cascades both grant `workspace.manage_admin` across every workspace in the tenant. Org `owner` and `admin` cascades grant it as well. ## Tenant roles Tenant roles grant tenant-scoped permissions and cascade workspace permissions into every workspace in the tenant. | Role | Description | Workspace cascade | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | **admin** | Full tenant management, including tenant deletion | Full workspace `admin` permissions in every workspace, including `workspace.delete` and `workspace.manage_admin` | | **operator** | Tenant operations end-to-end except tenant deletion | Identical to tenant `admin` cascade | | **security** | Sandbox creation and security review across the tenant | Workspace `security` permissions in every workspace | | **member** | Sandbox workspace creation only | None | | **impersonator** | Reserved for impersonation flows | None | | **unprivileged** | Cross-domain workspace invitee in an org with at least one verified domain. No tenant-scoped permissions. Access is limited to workspaces they were explicitly invited to. | None | ### Tenant-scoped permissions ✅ means the role has the permission. ❌ means it does not. | Permission | admin | operator | security | member | unprivileged | | -------------------------- | :---: | :------: | :------: | :----: | :----------: | | `tenant.view` | ✅ | ✅ | ✅ | ✅ | ❌ | | `tenant.manage` | ✅ | ✅ | ❌ | ❌ | ❌ | | `tenant.delete` | ✅ | ❌ | ❌ | ❌ | ❌ | | `sandbox.workspace.create` | ✅ | ✅ | ✅ | ✅ | ❌ | | `apikey.manage` | ✅ | ✅ | ❌ | ❌ | ❌ | Tenant admin, operator, and security inherit workspace permissions across every workspace in the tenant. Tenant member receives no workspace cascade. Assign a workspace role separately to grant workspace access. `tenant.delete` is the admin-only marker at the tenant-direct scope, analogous to `workspace.delete`. Tenant operators run the tenant end-to-end but cannot delete it. Org `owner` and `admin` cascade `tenant.delete` across every tenant. ## Organization roles Organization roles cascade into every tenant and workspace in the org. | Role | Description | Cascade | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | **owner** | Full org management. Create and delete tenants. Manage org members. | Full tenant admin and workspace admin grants across every tenant, including `tenant.delete` | | **admin** | Tenant management. Create workspaces. Assign workspace roles. | Same as owner | | **member** | Org membership without standing permissions | None | | **unprivileged** | Cross-domain workspace invitee in an org with at least one verified domain. No org-scoped permissions and no auto-enrollment into open tenants. Access is limited to workspaces they were explicitly invited to. | None | ### Org-scoped permissions ✅ means the role has the permission. ❌ means it does not. | Permission | owner | admin | member | unprivileged | | ----------------------- | :---: | :---: | :----: | :----------: | | `org.view` | ✅ | ✅ | ✅ | ❌ | | `tenant.manage` | ✅ | ✅ | ❌ | ❌ | | `sandbox.tenant.create` | ✅ | ✅ | ❌ | ❌ | | `apikey.manage` | ✅ | ✅ | ❌ | ❌ | `org.view` gates "I'm a real participant in this org" reads. The `ListJoinableTenants` endpoint requires it, so unprivileged invitees cannot enumerate the org's open tenants. ## Grantable permission discovery Platform API keys take an explicit permission list. Discover what the caller may grant with: * CLI: [`dome platform-keys permissions`](/cli/settings/platform-keys) * MCP: [`dome_platform_keys_available_permissions`](/reference/mcp/platform) * API: [`GetGrantablePermissions`](/api/management/get-grantable-permissions), [`ResolvePermissions`](/api/management/resolve-permissions) ## Next steps Grant these from Manage Access. Agent runtime auth is Cedar: * [Permissions](/concepts/platform/permissions) concept for how platform RBAC relates to agent authorization * [Manage Access](/manage/access) to assign members and create platform keys * [Scopes](/concepts/platform/scopes) concept for the org → tenant → workspace hierarchy * [Authorization Model](/concepts/architecture/authorization-model) concept for Cedar agent authorization * [Rules](/reference/controls/rules) reference for the entity, action, and attribute catalogs # Pools Source: https://docs.domesystems.ai/reference/resources/model-pools Routing strategies, failover limits, pool resolution order, and match_when dialect This page lists routing strategies, failover limits, resolution order, and the `match_when` predicate dialect. Refer to [Pools](/concepts/resources/model-pools) concept for how routing works. Configure on [Pools](/connect/resources/models/pools). ## Routing strategies | Strategy | Primary selection | Member fields it uses | | ------------------- | ------------------------------------------------------------------------------ | --------------------- | | `priority_weighted` | Chooses by weight from the lowest priority tier | Priority and weight | | `round_robin` | Rotates through all members in order | Neither | | `least_loaded` | Chooses the member with the fewest in-flight requests and breaks ties randomly | Neither | `strategy_scope`: `workspace` shares round-robin and least-loaded state across agents; `caller` tracks per agent. Priority-weighted routing ignores `strategy_scope`. ## Failover | Value | Runtime behavior | | ----------------- | ------------------------------------------------ | | No limit or `all` | Try every eligible member until one succeeds | | `0` | Try only the primary | | `N` | Try the primary and up to `N` additional members | Authorization and streaming failover rules are on [Pools](/connect/resources/models/pools#failover). ## Pool resolution Dome resolves a request's `model` value in this order: 1. A pool with that exact name 2. A direct model connection with that exact name 3. A direct connection whose upstream model identifier matches 4. The first pool in the configured order whose `match_when` predicate matches 5. The workspace default pool Exact names beat conditional routes. `match_when` inputs: | Input | Supported matching | | ----------------------------------------------------- | ------------------------------------------------------------------------ | | `prompt_tokens` | Estimated prompt size, characters ÷ 4, with numeric `gt`, `lt`, and `eq` | | `requested_max_tokens`, `tool_count` | Numeric `gt`, `lt`, and `eq` | | `endpoint` and `header.` | Equality, `in`, `prefix`, `suffix`, and RE2 `regex` | | `principal.metadata.` | String or numeric matching | | `principal.act_as.sub`, `.email`, and `.claims.` | String or numeric matching | | `principal.act_as.roles` and `.groups` | `contains`, `containsAny`, and `containsAll` | | Nested predicates | `any`, `all`, and `not` | By default every condition must match. Use `any`, `all`, or `not` to combine differently. An unknown condition, missing value, or unverified act-as attribute never matches. Routing does not replace authorization. ## Next steps Create pools and attach them to Gateways: * [Pools](/concepts/resources/model-pools) concept for members, routing, and reachability * [Pools](/connect/resources/models/pools) to create pools, manage members, and attach Gateways * [Models](/reference/resources/models) reference for provider and credential catalogs * [LLM gateway](/concepts/gateways/llm-gateway) concept for dispatch on the wire # Models Source: https://docs.domesystems.ai/reference/resources/models Provider IDs, credential pairings, and filter-window bounds for model connections This page lists supported providers, authentication pairings, and filter-window bounds. Refer to [Models](/concepts/resources/models) concept for how connections work. Configure on [Models](/connect/resources/models). ## Providers Provider IDs fall into three groups. You cannot change the provider after adding the model. * **Native providers.** Dome translates between the Gateway request and the provider's native wire format. `openai`, `anthropic`, `google`, `azure_openai`, and `bedrock`. * **Hosted OpenAI-compatible providers.** Dome sends requests through the provider's OpenAI-compatible API. `mistral`, `groq`, `together`, `fireworks`, `deepseek`, `xai`, `perplexity`, `cohere`, `openrouter`, `cerebras`, `nvidia`, `deepinfra`, `sambanova`, `ai21`, and `databricks`. * **Bring your own endpoint.** Use `openai_compatible` for an OpenAI-shaped endpoint such as vLLM or Ollama. Use `custom` when Dome should forward provider-specific requests without translating them. Dome supplies a base URL for hosted providers that have a default. For Google, Azure OpenAI, Bedrock, Databricks, `openai_compatible`, and `custom`, you must provide an endpoint or the provider-specific details needed to locate the model. These details can include an Azure deployment, a Google project and location, or a Bedrock region. Hosted OpenAI-compatible providers share the OpenAI chat and embeddings wire shape. Native and bring-your-own adapters differ: ### Provider capabilities | Provider | Embeddings | Distinct behavior | | ------------------- | ---------- | --------------------------------------------------------------------------------------------------- | | `openai` | Yes | `provider_config.base_url` defaults to `https://api.openai.com` | | `anthropic` | No | Only adapter with upstream `count_tokens` | | `google` | Yes | Gemini `contents[]/parts[]`; Vertex `:predict` for embeddings. Workload identity on GCP | | `bedrock` | No | AWS Converse / ConverseStream. SigV4 via static keys or the default credential chain | | `azure_openai` | Yes | Auth header is `api-key`, not `Authorization`. Requires `endpoint`, `deployment`, and `api_version` | | `openai_compatible` | Yes | Auth is optional | | `custom` | N/A | Opaque passthrough. See [Custom passthrough](#custom-passthrough) | An unsupported Gateway operation for the resolved provider returns HTTP `501` with a structured error. ### Custom passthrough `POST /gateways//v1/passthrough/:name` forwards the request body byte-exact to a `custom`-provider connection. Identity, tenant match, and Cedar (`llm:passthrough`) still apply. The canonical content filter is bypassed (no canonical shape to filter against). | Constraint | Behavior | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Body limit | 1 MiB | | Caller headers | Forwarded only when the name matches `provider_config.header_passthrough_allow` (glob list on the connection) | | Dropped headers | `Authorization`, `Cookie`, and `Proxy-Authorization` are dropped unconditionally so a caller cannot smuggle credentials past the connection's configured auth | Configure `header_passthrough_allow` (and related `custom` keys such as `endpoint` and `request_shape_doc_url`) on the model connection. See [Create LLM model connection](/api/management/create-llm-model-connection). ## Credentials | Authentication method | Credential type | What Dome sends | | --------------------- | --------------- | ------------------------------------------------------- | | `none` | `none` | No provider credential | | `api-key` | `shared` | One workspace-managed API key for every call | | `api-key` | `per-user` | An API key linked to the verified end user | | `oauth` | `shared` | One administrator-authorized OAuth grant for every call | | `oauth` | `per-user` | An OAuth grant linked to the verified end user | When the provider requires no authentication, set both to `none`. When the provider uses an API key or OAuth, the credential type must be `shared` or `per-user`. If you omit both settings, Dome uses `api-key` with `shared`. Per-user provisioning and workload identity are on [Models](/connect/resources/models#credentials). ## Filter window A model can buffer at most 1 MiB or 4,096 tokens of streamed output before outbound Guards run. Dome uses the largest window configured on the workspace, model, or request. A value of `0` uses the workspace setting. Layer sources and effective-max rules are on the [Guards](/reference/controls/guards#streaming-window) reference. ## Next steps Add connections, or jump to pools when you need routing: * [Models](/concepts/resources/models) concept for connections, attributes, and direct vs pool * [Models](/connect/resources/models) to add models, rotate credentials, and attach Gateways * [Pools](/reference/resources/model-pools) reference for routing across connections * [LLM gateway](/concepts/gateways/llm-gateway) concept for the model ingress # Tools Source: https://docs.domesystems.ai/reference/resources/tools Auth methods, credential types, egress header sources, discovered-tool statuses, and protocols This page lists upstream authentication pairings, egress header sources, discovered-tool statuses, and protocol constraints. Refer to [Tools](/concepts/resources/tools) concept for how connections work. Configure on [Tools](/connect/resources/tools). ## Upstream authentication | Auth method | When the upstream | | ----------- | ---------------------------------------- | | `none` | Requires no credential | | `api-key` | Accepts a static API key or bearer token | | `oauth` | Supports OAuth 2.0 or 2.1 | | Credential type | What the gateway sends | | --------------- | ----------------------------------------------- | | `none` | No credential | | `shared` | One workspace-managed credential for every call | | `per-user` | One credential per verified act-as subject | Valid pairings: `none` with `none` only; `api-key` and `oauth` each with `shared` or `per-user`. Defaults when credential type is omitted: `none`→`none`, `api-key`→`shared`, `oauth`→`per-user`. OAuth requires `streamable-http`. ## Egress headers | Source | Value | | --------------- | --------------------------------------------------- | | Literal | Fixed string on every outbound request | | Stored secret | Injected at egress | | Act-as identity | Verified end-user identity from the inbound request | Only `streamable-http` tools can carry egress headers. Act-as headers require OIDC or HMAC verification and are not valid with OAuth upstream auth. Configure headers on [Tools](/connect/resources/tools#egress-headers). ## Discovered tools | Status | Behavior | | ------------ | ---------------------------------------------- | | `active` | Discoverable and callable when rules allow | | `deprecated` | Callable, with a catalog warning | | `blocked` | Hidden from discovery and denied on invocation | Blocking denies the operation even if another rule would permit it. Status persists when the gateway observes the operation again. Only a restore returns it to `active`. ## Protocols | Protocol | Use case | Notes | | ----------------- | ------------------ | --------------------------------- | | `streamable-http` | Remote MCP servers | Supports OAuth and egress headers | | `stdio` | Local processes | No HTTP headers; no OAuth | Also refer to [Tools gateway](/concepts/gateways/tools-gateway#egress-protocols) concept for REST catalog egress. ## Next steps Add connections and attach them to Gateways: * [Tools](/concepts/resources/tools) concept for credentials, catalogs, and reachability * [Tools](/connect/resources/tools) to add tools, manage catalogs, and attach Gateways * [Tools gateway](/concepts/gateways/tools-gateway) concept for the MCP ingress # API Source: https://docs.domesystems.ai/reference/surfaces/api API Reference Docs # CLI Source: https://docs.domesystems.ai/reference/surfaces/cli CLI Reference Docs # SDK Source: https://docs.domesystems.ai/reference/surfaces/sdk SDK Reference Docs # SDKs Source: https://docs.domesystems.ai/sdks/index Call Dome's gateway and embed authorization in your agent from one client Use the Dome SDK as the single client for gateway-backed MCP tool calls, LLM model calls, audit reads, and optional in-process Cedar checks. Authentication, act-as encoding, activity correlation, and typed error decoding live in the SDK so agent code stays small. `pip install dome-sdk` — gateway-first `Client` and `AsyncClient`, framework adapters, optional local policy sync. ## What the SDK does The client wraps the Dome data plane and presents one surface per concern. | Surface | Purpose | | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | `client.gateway.tools` | List and call MCP tools through the [gateway](/concepts/architecture/dataplane). Bounded in-memory `tools/list` cache | | `client.gateway.llm` | OpenAI- and Anthropic-shaped LLM calls through the [LLM gateway](/connect/resources/models/pools), with typed Dome error decoding | | `client.gateway.openai_client()` / `anthropic_client()` | Provider-native clients pointed at the Dome ingress, for teams that want the upstream SDK shape | | `client.audit` | Cursor-paginated and streaming reads from hosted Audit v1 | | `client.activity(...)` | Stamp a run with an opaque activity ID across every gateway call and audit event | | `client.start_policy_sync()` + `client.check()` / `client.evaluate()` | Opt-in local Cedar evaluation for in-process self-enforcement | | `dome.bootstrap.ensure_agent(...)` | Idempotent setup helper for developer agents and gateway keys | ## When to use the SDK * **Gateway calls from agent code.** The default. The SDK authenticates, encodes act-as, surfaces typed errors, and correlates activity. No infrastructure to deploy alongside the service. * **In-process Cedar checks.** Call `client.start_policy_sync()` to enable `client.check()` / `client.evaluate()` against a synced bundle. Microsecond decisions, fail-closed when no bundle is loaded. * **External MCP traffic.** Route through the [gateway](/concepts/architecture/dataplane) directly. Agents that don't run Python use the gateway over MCP without an SDK. ## Lifecycle ```mermaid theme={"system"} flowchart LR A["Construct"] --> B["connect"] B --> C["gateway calls"] C -.->|optional| D["start_policy_sync + check"] C --> E["close"] D --> E ``` | Step | Purpose | Network | | ------------------------------------ | --------------------------------------------------------------------------- | --------------------------- | | **Construct** | Validate configuration, build transports | None | | **connect** | Prepare credentials. Token exchange when needed for control-plane discovery | Yes when exchanging | | **gateway calls** | `tools.list`, `tools.call`, `llm.chat`, `llm.messages`, `audit.query` | Yes — per call | | **start\_policy\_sync** *(optional)* | Launch background Cedar bundle sync for local checks | Yes — blocking initial sync | | **close** | Release transports, stop background sync, drain pending audit | Flush only | ## Next steps Python Client first. Adapters and Develop cover frameworks and credentials: * [Python reference](/sdks/python/reference) for Client, gateway calls, and act-as * [Adapters](/sdks/python/adapters) for LangChain and other frameworks * [Develop](/develop) for runtime credentials and Gateway endpoints * [Examples](/tutorials/examples/use-cases/code-agent) for governed agent patterns # Adapters Source: https://docs.domesystems.ai/sdks/python/adapters Framework adapters that wrap the Python SDK for specific AI frameworks Adapters wrap `dome.Client` for a specific AI framework so tool execution and LLM calls flow through Dome with no extra glue. Install the adapter alongside the core `dome-sdk` package — adapters version and release independently. ## Available adapters | Package | Install | Framework | | --------------------------- | ----------------------------------------- | ---------------------------- | | `dome-langchain` | `pip install dome-langchain` | LangChain (`langchain-core`) | | `dome-langchain[openai]` | `pip install 'dome-langchain[openai]'` | + `DomeChatOpenAI` | | `dome-langchain[anthropic]` | `pip install 'dome-langchain[anthropic]'` | + `DomeChatAnthropic` | ## LangChain `dome-langchain` exposes three surfaces over `dome.Client`: * **Gateway tools** — `DomeGatewayTool` / `gateway_tool` invoke MCP tools through the Dome gateway as native LangChain tools. * **Governed local tools** — `DomeGovernedTool` / `govern_tools` wrap existing LangChain tools so every call passes through a local `client.check()` before execution. Requires `client.start_policy_sync()`. * **Governed chat models** — `DomeChatOpenAI` and `DomeChatAnthropic` are provider subclasses that route every LLM call through the Dome gateway under an agent identity, with per-call `act_as`. ### Install ```bash theme={"system"} pip install dome-langchain ``` The core package depends on `dome-sdk` and `langchain-core`. Install `dome-langchain[openai]` or `dome-langchain[anthropic]` to enable the chat-model classes. ### Gateway tools Use `DomeGatewayTool` when the tool already lives behind the Dome gateway. The adapter calls `client.gateway.tools.call()` — authorization, credential resolution, and audit all happen server-side. `gateway_url` must include the `/gateways/{id}` segment naming the agent's [Gateway](/sdks/python/reference#gateways). A bare-root URL fails at `connect()` with `DomeGatewayConfigurationError`. ```python theme={"system"} import dome from dome_langchain import DomeGatewayTool client = dome.Client( token="{{AGENT_TOKEN}}", gateway_url="https://gateway.domesystems.ai/gateways/{{GATEWAY_ID}}", act_as_method="none", ) client.connect() search = DomeGatewayTool( dome_client=client, name="github/list_issues", description="List GitHub issues", act_as=dome.PlainActAs(email="alice@corp.com"), ) ``` Build a list from the gateway catalog with `gateway_tools_from_catalog`: ```python theme={"system"} from dome_langchain import gateway_tools_from_catalog catalog = client.gateway.tools.list(act_as=user) tools = gateway_tools_from_catalog(client, catalog.tools, act_as=user) ``` | Symbol | Signature | Purpose | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | | `DomeGatewayTool` | `DomeGatewayTool(*, dome_client, name, description="", gateway_tool_name=None, act_as=None, raise_on_tool_error=True)` | Wrap one gateway MCP tool as a LangChain `BaseTool` | | `gateway_tool` | Factory returning a `DomeGatewayTool` | Same args as the constructor | | `gateway_tools_from_catalog` | `(client, tools, *, act_as=None, raise_on_tool_error=True)` | Build a list from `client.gateway.tools.list()` results | Bind a per-user default with `tool.with_act_as(user)`. Pass `dome_act_as=` on a single invocation to override for that call. ### Governed local tools Use `govern_tools` to wrap LangChain tools that run in-process. The wrapper calls `client.check()` (local Cedar) and either runs the inner tool or returns a denial string the agent sees as the tool's output. ```python theme={"system"} import dome from dome_langchain import govern_tools from langchain.agents import create_agent client = dome.Client( token="{{AGENT_TOKEN}}", control_plane_url="https://api.domesystems.ai", ) client.connect() client.start_policy_sync() governed = govern_tools(client, [search_tool, db_tool]) agent = create_agent(model, tools=governed) ``` | Symbol | Signature | Purpose | | ------------------ | ------------------------------------------------------------------------------------- | ---------------------------------------------- | | `DomeGovernedTool` | `DomeGovernedTool(*, tool, dome_client, action="mcp:call", resource_type="mcp_tool")` | Wrap one LangChain tool with a local pre-check | | `govern_tools` | `(dome_client, tools, *, action="mcp:call", resource_type="mcp_tool")` | Wrap a list of tools | For non-MCP tools, override `action` and `resource_type` to match the Cedar entity types in your rules. ### Governed chat models `DomeChatOpenAI` and `DomeChatAnthropic` are subclasses of `ChatOpenAI` and `ChatAnthropic` whose requests flow through the Dome gateway. They `isinstance`-check, compose in LCEL, stream, tool-call, and produce structured output exactly like the upstream class. ```python theme={"system"} from dome_langchain import AgentIdentity, DomeChatOpenAI llm = DomeChatOpenAI.for_agent( agent, # any object with .token and .gateway_url model="prod-gpt", act_as=dome.PlainActAs(email="alice@corp.com"), ) llm.invoke("Summarize the open incidents") # default act_as llm.invoke("Hello", act_as=other_user) # per-call override per_user = llm.with_act_as(other_user) # bind once, reuse ``` The construction-time `act_as` is a default; any `invoke` / `stream` / `batch` call may override it. The header rides on that one request only. The agent's `gateway_url` must be the complete `/gateways/{id}` URL returned by key creation or token exchange. A chat model's `base_url` is fixed for the life of the instance, so one chat instance maps to one Gateway. A missing or unscoped value raises `DomeGatewayConfigurationError` at construction. ```python theme={"system"} import dome from dome_langchain import AgentIdentity, DomeChatOpenAI identity = AgentIdentity( token="{{AGENT_TOKEN}}", gateway_url="https://gateway.domesystems.ai/gateways/{{GATEWAY_ID}}", ) llm = DomeChatOpenAI.for_agent(identity, model="prod-gpt") ``` | Symbol | Purpose | | --------------------------------------------- | ------------------------------------------------------------ | | `DomeChatOpenAI` | `ChatOpenAI` subclass routed through the Dome LLM gateway | | `DomeChatAnthropic` | `ChatAnthropic` subclass routed through the Dome LLM gateway | | `broker_chat(...)` / `broker_chat_for(agent)` | Factory equivalents that return a `DomeChatOpenAI` | | `broker_chat_anthropic(...)` | Factory for `DomeChatAnthropic` | ### Compose primitives `dome-langchain` also ships chain primitives for building governed topologies on top of Dome identity: | Symbol | Purpose | | ------------------------------------------- | ------------------------------------------------------- | | `Gate` | A `Runnable` that runs a Cedar check at a node boundary | | `mint_ephemeral` / `session` | Spawn short-lived agent identities for fan-out runs | | `causal` / `current_causal_run_id` | Bind a causal run ID to a chain | | `gate_edge` / `gate_command` / `fleet_send` | LangGraph shims that gate transitions between nodes | ```python theme={"system"} from dome_langchain import gate_edge g.add_conditional_edges( "classifier", gate_edge( dome_client=client, from_node="classifier", to_node="retriever", resource="chain.hop_allowed", ), ) ``` #### Scope spawned children to a Gateway `mint_ephemeral`, `session`, and `FleetSession.spawn` accept `gateway_id`. Key creation selects that Gateway and returns its complete URL, so every child carries a call-ready `gateway_url` for `broker_chat_for(child)`. ```python theme={"system"} from dome_langchain import session with session( platform=admin, parent_agent_id=parent.id, gateway_id="{{GATEWAY_ID}}", ) as fleet: children = fleet.spawn(count=4, template="analyst") # Each child.gateway_url is complete and call-ready. ``` If a returned Gateway ID or endpoint conflicts with the requested selection, the adapter fails closed with `DomeGatewayConfigurationError`. Refer to [Gateways](/sdks/python/reference#gateways). ## Build a new adapter Adapters live alongside the core SDK in `packages/dome-/` within `sdk-dome-python`. Each is its own PyPI package depending on `dome-sdk` and the target framework. Use `packages/dome-langchain/` as the reference implementation. ## Next steps * [Python reference](/sdks/python/reference) for Client, act-as, and gateway calls * [SDKs](/sdks) for when to use the SDK versus raw gateway calls * [Gateways](/connect/gateways) to attach membership and grants * [Examples](/tutorials/examples/use-cases/tools-agent) for a multi-adapter tools agent # Quickstart Source: https://docs.domesystems.ai/sdks/python/quickstart Install the Python SDK and call a governed tool through the Dome gateway Call MCP tools and LLM models through the Dome gateway from a Python application. The SDK handles authentication, act-as encoding, audit correlation, and typed error decoding — your code talks to one client. **Prerequisites:** * Python 3.12+ * A registered agent and an agent API key (`dome_...`) — refer to [Developer Quickstart](/tutorials/role/developer) * The gateway URL for the [Gateway](/sdks/python/reference#gateways) the agent is granted (`https://gateway.../gateways/{id}`) ## Install ```bash theme={"system"} pip install dome-sdk ``` Provider SDKs are optional. `client.gateway.openai_client()` lazy-imports `openai`; `client.gateway.anthropic_client()` lazy-imports `anthropic`. Install those packages only in agents that use the provider-native factories. ## Initialize Construct a `Client` with the agent token and gateway URL. The SDK does not read environment variables — pass values directly. ```python theme={"system"} import dome client = dome.Client( token="{{AGENT_TOKEN}}", gateway_url="https://gateway.domesystems.ai/gateways/{{GATEWAY_ID}}", act_as_method="none", ) client.connect() ``` The `/gateways/{id}` segment names the Gateway the agent is granted. Pass the complete URL returned by agent-key creation or token exchange. Bare roots and URLs ending in `/mcp` or `/v1` raise `DomeGatewayConfigurationError` at `connect()` — the SDK does not compose a customer URL from an infrastructure endpoint. `connect()` prepares token and transport state. It does not block on local policy sync — call `start_policy_sync()` only when you want in-process Cedar checks. ## Call a tool `client.gateway.tools.call()` runs an MCP tool through the gateway. Authorization, credential resolution, and audit happen server-side. ```python theme={"system"} result = client.gateway.tools.call( "github/list_issues", {"repo": "dome"}, act_as=dome.PlainActAs(email="alice@corp.com"), ) print(result.structured_content or result.content) ``` `result.content` is the raw tuple of MCP content blocks. By default, an upstream `isError=true` result raises `DomeToolExecutionError`. Pass `raise_on_tool_error=False` when you need the failed `ToolCallResult` and its partial content. ## Call an LLM `client.gateway.llm.chat()` posts OpenAI-shaped chat requests through the gateway. The response is the provider-shaped dict. ```python theme={"system"} response = client.gateway.llm.chat( model="prod-gpt", messages=[{"role": "user", "content": "Summarize the open incidents"}], act_as=dome.PlainActAs(email="alice@corp.com"), ) ``` For Anthropic shapes, use `client.gateway.llm.messages(model=..., messages=..., max_tokens=...)`. For provider-native clients, refer to [Reference](/sdks/python/reference#provider-native-clients). ## Correlate work with an activity Wrap a run in `client.activity(...)` to stamp every gateway call and audit event with the same opaque activity ID. ```python theme={"system"} with client.activity(metadata={"case": "incident-123"}) as activity: client.gateway.tools.call("github/list_issues", {"repo": "dome"}) print("activity_id:", activity.activity_id) ``` Direct calls outside an activity carry no activity ID. The SDK never mints one implicitly. ## Shut down Always close the client to release transports. ```python theme={"system"} client.close() ``` Or use the context manager form: ```python theme={"system"} with dome.Client(token=..., gateway_url=...) as client: client.gateway.tools.call("github/list_issues", {"repo": "dome"}) ``` ## Next steps Full client configuration, act-as methods, errors, and audit reads. Wire the SDK into a service and verify in audit. Use gateway tools and governed chat models inside LangChain agents. How the gateway authorizes, resolves credentials, and audits every call. # Reference Source: https://docs.domesystems.ai/sdks/python/reference Configuration, client surfaces, errors, and act-as methods for the Python SDK Complete reference for the `dome` package on PyPI. All configuration is explicit — the SDK does not read environment variables. ## Client construction The SDK ships two clients with identical surfaces: `dome.Client` (sync) and `dome.AsyncClient` (async). Both accept keyword arguments only. | Argument | Type | Default | Description | | ---------------------- | ----------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `token` | `str` | required | Agent API key (`dome_...`) or exchanged JWT | | `control_plane_url` | `str \| None` | `None` | Dome control-plane URL. Required for token exchange, audit reads, local policy sync | | `gateway_url` | `str \| None` | `None` | Complete Gateway URL, including `/gateways/{id}`. Bare roots and protocol-suffixed URLs fail at `connect()` with `DomeGatewayConfigurationError` | | `gateway_id` | `str \| None` | `None` | Gateway UUID to select during token exchange. Omission succeeds only when the agent can access exactly one Gateway | | `gateway_auth_mode` | `"auto" \| "raw" \| "exchange"` | `"auto"` | Gateway bearer source. `auto` uses the raw `dome_*` key when `gateway_url` is set | | `act_as_method` | `"none" \| "hmac" \| "oidc" \| "bound" \| None` | `None` | Gateway act-as method. Required when the SDK cannot discover it from the control plane | | `request_timeout` | `float` | `30.0` | Per-request HTTP timeout | | `tools_list_cache_ttl` | `float` | `30.0` | In-memory `tools/list` cache TTL | | `rule_sync_interval` | `float` | `30.0` | Local policy sync interval (only used when `start_policy_sync()` runs) | | `audit_batch_size` | `int` | `100` | Legacy local audit buffer size | | `audit_flush_interval` | `float` | `5.0` | Legacy local audit drain interval | ```python theme={"system"} import dome client = dome.Client( token="{{AGENT_TOKEN}}", gateway_url="https://gateway.domesystems.ai/gateways/{{GATEWAY_ID}}", control_plane_url="https://api.domesystems.ai", act_as_method="none", ) client.connect() ``` `gateway_url` names the [Gateway](#gateways) this client uses (`/gateways/{uuid}`). A client maps to one Gateway; there is no implicit global endpoint. `connect()` validates the URL and verifies that `gateway_id`, when supplied, names the same Gateway. `connect()` prepares credentials and transport. It performs a token exchange when needed for control-plane discovery, but never blocks on local Cedar sync. ## Gateways A Gateway is a workspace-scoped grouping of MCP tools and LLM models with its own access grants and protocol endpoints. The SDK accepts only a complete scoped URL: | Surface | URL | | ---------------------- | ------------------------------------------------------- | | SDK and Anthropic base | `https://gateway.example.com/gateways/{gateway-id}` | | MCP | `https://gateway.example.com/gateways/{gateway-id}/mcp` | | OpenAI-compatible base | `https://gateway.example.com/gateways/{gateway-id}/v1` | Agent-key creation and token exchange return a `GatewayEndpoints` object containing all four call-ready values. Use `gateway_url` as returned; do not derive it from a bare data-plane host. If the client has `control_plane_url`, pass `gateway_id` to select a Gateway during exchange. Omission succeeds only when the agent can access exactly one Gateway; the workspace default marker does not resolve ambiguity. The tools-list cache is keyed by the complete Gateway URL, agent, workspace, and act-as identity, so clients pointed at different Gateways never share entries. Find IDs with `dome gateway list`, the dashboard, or the [admin surface](#gateway-admin). ## Client surface | Member | Description | | ---------------------------- | ----------------------------------------------------------------------------- | | `client.gateway` | Gateway data-plane API. Tools, models, LLM helpers, provider client factories | | `client.audit` | Control-plane audit reads. Requires `control_plane_url` | | `client.activity(...)` | Open an [activity context](#activity-correlation) for correlated calls | | `client.evaluate(...)` | Run a local Cedar evaluation. Requires `start_policy_sync()` | | `client.check(...)` | Callback-based local Cedar check. Requires `start_policy_sync()` | | `client.start_policy_sync()` | Start background Cedar bundle sync for in-process checks | | `client.connect()` | Prepare credentials. Idempotent | | `client.close()` | Release transports, stop background sync, drain pending audit | | `client.gateway_url` | Complete selected Gateway URL, or `""` before discovery | `AsyncClient` exposes the same members; methods marked synchronous below have async counterparts on `AsyncClient`. ## Gateway tools `client.gateway.tools` is the MCP tool client. | Method | Description | | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | `list(*, act_as=None, refresh=False, ttl_seconds=None) -> ToolsListResult` | List MCP tools. Real gateway call unless served from the in-memory cache | | `list_cached(*, act_as=None, ttl_seconds=None) -> ToolsListResult` | Cache-preferred read (calls `list` with `refresh=False`; refreshes when the entry is missing or expired) | | `invalidate_cache(*, act_as=None)` | Drop one cache entry or all entries | | `call(name, arguments=None, *, act_as=None, activity_id=None, raise_on_tool_error=True) -> ToolCallResult` | Invoke an MCP tool | `tools/list` can do real work: upstream discovery, per-user authorization, audit emission, and credential-link generation. The SDK never does an implicit list-before-call. ```python theme={"system"} catalog = client.gateway.tools.list(refresh=True, act_as=user) for tool in catalog.tools: print(tool.name, tool.description) for advisory in catalog.auth_required: print("credential required:", advisory.connection_name, advisory.provision_url) ``` The cache is in-memory, bounded, and keyed by gateway root URL, agent fingerprint, workspace, act-as method, and an act-as header hash. Cache hits do not call the gateway and do not emit gateway audit. Per-user credential advisories on `tools/list` are returned on the fresh response and are not cached. ### Calling a tool ```python theme={"system"} result = client.gateway.tools.call( "github/list_issues", {"repo": "dome"}, act_as=dome.PlainActAs(email="alice@corp.com"), ) if result.is_error: print("upstream tool error:", result.structured_content or result.content) else: print(result.structured_content or result.content) ``` `call()` distinguishes JSON-RPC errors from MCP tool errors. JSON-RPC errors raise typed `DomeGatewayError` subclasses. If the upstream tool returns `isError=true`, the SDK raises `DomeToolExecutionError` by default. Pass `raise_on_tool_error=False` to receive `ToolCallResult(is_error=True)` instead. ### Reading tool results `ToolCallResult` exposes convenience accessors so you do not have to walk the MCP content array by hand. | Accessor | Description | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `result.text` | Concatenate every `type=text` content block, newline-separated | | `result.first_text(default="")` | First `type=text` block, or `default` when there is none | | `result.json()` | Return `structured_content` when set; otherwise parse `text` as JSON. Raises `DomeToolExecutionError` when neither is available | | `result.require_success()` | Raise `DomeToolExecutionError` when `is_error=True`; otherwise return the result for chaining | ```python theme={"system"} result = client.gateway.tools.call( "github/list_issues", {"repo": "dome"}, act_as=user, ).require_success() issues = result.json() summary = result.first_text("no summary returned") ``` ## Gateway LLM `client.gateway.llm` posts OpenAI- and Anthropic-shaped requests through the Dome LLM ingress and decodes structured Dome errors. | Method | Description | | -------------------------------------------------------------------------------------- | ----------------------------- | | `chat(*, model, messages, act_as=None, **kwargs) -> Mapping[str, Any]` | OpenAI `/v1/chat/completions` | | `messages(*, model, messages, max_tokens, act_as=None, **kwargs) -> Mapping[str, Any]` | Anthropic `/v1/messages` | ```python theme={"system"} response = client.gateway.llm.chat( model="prod-gpt", messages=[{"role": "user", "content": "Summarize the open incidents"}], act_as=dome.PlainActAs(email="alice@corp.com"), ) ``` For a typed handle around one model, use `client.gateway.model(...)`: ```python theme={"system"} model = client.gateway.model("prod-claude", provider="anthropic", act_as=user) message = model.messages.create( messages=[{"role": "user", "content": "Draft a status update"}], max_tokens=512, ) ``` ### Provider-native clients For teams that want provider-native APIs, the gateway hands back a configured `openai`/`anthropic` client pointed at the Dome ingress: ```python theme={"system"} openai_client = client.gateway.openai_client(act_as=user) completion = openai_client.chat.completions.create( model="prod-gpt", messages=[{"role": "user", "content": "Hello"}], ) ``` Provider factories return provider-shaped clients and do not promise typed Dome exceptions unless Dome owns the transport for that path. Use `client.gateway.llm.*` or `client.gateway.model(...)` when you want SDK-owned typed error decoding. ## Gateway readiness `client.gateway.wait_ready()` polls unauthenticated `GET /ready` and is an explicit diagnostic helper — not a startup requirement. ```python theme={"system"} client.gateway.wait_ready(timeout=30) ``` For freshly created agent keys, use `dome.wait_for_agent_key(...)` or `dome.bootstrap.ensure_agent(..., wait_gateway=True)` in setup scripts to wait for the gateway's synced API-key snapshot. ## Act-as The gateway act-as trust model is driven by Dome configuration, not by the client choosing a header shape. Pass the SDK value that matches the gateway method: | SDK value | Gateway method | Header behavior | | ---------------------------------------- | -------------- | ------------------------------------------------------ | | `PlainActAs(...)` or legacy `ActAs(...)` | `none` | Canonical JSON, standard-base64 encoded | | `HMACActAs(secret=..., ...)` | `hmac` | Signed, timestamped, base64 encoded | | `OIDCActAs(jwt=...)` or raw JWT string | `oidc` | Raw JWT evidence | | `BoundActAs()` or no `act_as` | `bound` | No client act-as header; server-bound identity is used | ```python theme={"system"} # Plain caller-asserted identity user = dome.PlainActAs(email="alice@corp.com", roles=("admin",)) # Verified OIDC delegation user = dome.OIDCActAs(jwt="eyJ...") # HMAC-signed identity user = dome.HMACActAs(secret=hmac_secret, email="alice@corp.com") ``` For `bound`, the SDK fails closed if caller code tries to send an act-as header. ## Activity correlation Wrap a run in an activity context to stamp every gateway call and audit event with the same opaque activity ID. ```python theme={"system"} with client.activity(metadata={"case": "incident-123"}) as activity: client.gateway.tools.call("github/list_issues", {"repo": "dome"}) page = client.audit.query(event_types=("mcp.tool_call.completed",)) print(activity.activity_id) ``` Direct calls outside an activity carry no activity ID — the SDK never mints one implicitly. To thread an ID across processes, mint an opaque ID and put human-readable labels in metadata: ```python theme={"system"} activity_id = dome.new_activity_id() with client.activity(activity_id, metadata={"job": "nightly-batch"}) as activity: ... ``` `AuditEventV1.correlation.activity_id` and `activity_trust` surface the stamp on each event. ### `dome.new_activity_id() -> str` Mint a fresh UUIDv4 activity ID. Use when persisting an ID for later correlation. Equivalent to the ID `client.activity()` mints when none is passed. ## Audit reads `client.audit` queries the hosted Audit v1 read APIs through the configured control plane. The SDK does not write hosted audit events — gateway and control-plane services do that. | Method | Description | | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- | | `client.audit.query(query \| **filters) -> AuditQueryResponse` | Cursor-paginated query with time-range, actor, producer/surface, operation, resource, and payload filters | | `client.audit.get(event_id) -> AuditEventV1` | Fetch a single normalized event by ID | | `client.audit.stream(query \| **filters) -> Iterator[AuditEventV1]` | Server-stream events using the Connect JSON streaming protocol | | `client.audit.find_gateway_events(*, activity_id="", workspace_id=None, event_types=(), type_prefix="", page_size=50) -> tuple[AuditEventV1, ...]` | One-shot query scoped to gateway events, optionally filtered by activity and event-type prefix | | `client.audit.wait_for_activity(activity_id, *, timeout=30.0, interval=0.5, event_types=(), type_prefix="", page_size=50) -> tuple[AuditEventV1, ...]` | Poll until at least one matching event lands, or raise `TimeoutError` | ```python theme={"system"} from dome import AuditPayloadFilter, AuditQuery page = client.audit.query( AuditQuery( event_types=("llm.model_call.completed",), results=("EVENT_RESULT_SUCCEEDED",), producer_service="gateway", request_surface="INITIATOR_SURFACE_GATEWAY_LLM", start_time="2026-06-23T00:00:00Z", end_time="2026-06-24T00:00:00Z", payload_filters=( AuditPayloadFilter(field="model", values=("prod-gpt",)), ), page_size=50, ) ) for event in page.events: print(event.type, event.correlation.activity_id, event.payload) ``` `AuditQuery` enum fields use the proto JSON enum names (for example `EVENT_RESULT_SUCCEEDED`, `ACTOR_KIND_AGENT`, `INITIATOR_SURFACE_GATEWAY_MCP`). `stream` rejects query-only filters (`agent_ids`, `primary_resource_id`, `primary_resource_kind`, `trace_id`, `activity_id`, `workspace_id`, `start_time`, `end_time`, `page_size`) at call time instead of silently dropping them. ### Waiting on activity events Gateway audit lands asynchronously. Use `wait_for_activity` in tests and post-run verification to block until the events you correlated under an activity ID are visible. ```python theme={"system"} with client.activity() as run: client.gateway.tools.call("github/list_issues", {"repo": "dome"}, act_as=user) events = client.audit.wait_for_activity( run.activity_id, type_prefix="mcp.tool_call.", timeout=10.0, ) assert any(event.type == "mcp.tool_call.completed" for event in events) ``` `find_gateway_events` is the non-blocking form — it issues one query and returns whatever is already visible. Both helpers default the workspace to the client's authenticated workspace; pass `workspace_id=` to scope a platform-key reader explicitly. ### Audit envelope `AuditEventV1` is the normalized read-side envelope returned by every audit read method. | Sub-block | Fields | | --------------------- | --------------------------------------------------------------------------------------------------------------- | | `AuditScope` | `kind`, `org_id` (alias `organization_id`), `tenant_id`, `workspace_id`, `agent_id` | | `AuditActor` | `kind`, `id`, `email`, `display` | | `AuditResourceRef` | `kind`, `id`, `name`, `tenant_id`, `workspace_id` | | `AuditProducer` | `service`, `instance_id`, `version`, `region` | | `AuditRequestSurface` | `surface`, `rpc_service`, `rpc_method`, `http_route`, `user_agent_class` | | `AuditCorrelation` | `trace_id`, `request_id`, `operation_id`, `parent_event_id`, `idempotency_key`, `activity_id`, `activity_trust` | | `AuditDataHandling` | `omitted`, `summarized`, `truncated`, `redacted`, `hashed`, `encrypted`, `content_ref_used` | ## Local policy checks In-process Cedar evaluation is still available when an agent needs a fast, self-enforced decision. Local checks are opt-in — they require `start_policy_sync()` and a `control_plane_url`. ```python theme={"system"} client = dome.Client( token="{{AGENT_TOKEN}}", control_plane_url="https://api.domesystems.ai", ) client.start_policy_sync() decision = client.evaluate(tool="database/query", action="mcp:call") if decision.allowed: run_query() ``` For the two most common MCP decisions, use the typed shortcuts. Both wrap `evaluate()` with `resource_type="mcp_tool"` and the right action verb. | Method | Action | Use when | | -------------------------------------------------------------------- | -------------- | --------------------------------------------------- | | `client.evaluate_tool_call(tool, *, context=None, act_as=None)` | `mcp:call` | Gating an `invoke`/`call` before running it | | `client.evaluate_tool_discovery(tool, *, context=None, act_as=None)` | `mcp:discover` | Filtering a `tools/list` response before showing it | ```python theme={"system"} decision = client.evaluate_tool_call("database/query", act_as=user) if not decision.allowed: raise PermissionError(decision.reason) visible = [ tool for tool in catalog.tools if client.evaluate_tool_discovery(tool.name, act_as=user).allowed ] ``` The callback form mirrors the Go SDK: ```python theme={"system"} client.check( tool="database/query", action="mcp:call", on_allow=lambda result: run_query(), on_deny=lambda req, reason: log.warning("denied: %s", reason), ) ``` | Argument | Type | Default | Description | | --------------- | ------------------------------------ | ------------ | ---------------------------------------- | | `tool` | `str` | required | Resource being accessed | | `on_allow` | `Callable[[CheckResult], Any]` | required | Invoked on allow | | `on_deny` | `Callable[[CheckRequest, str], Any]` | required | Invoked with `(request, reason)` on deny | | `action` | `str` | `""` | Defaults to `mcp:call` if empty | | `context` | `dict[str, str] \| None` | `None` | Cedar attributes | | `act_as` | `ActAs \| str \| None` | `None` | End-user identity | | `resource_type` | `str` | `"mcp_tool"` | Cedar resource entity type | ## Admin client `DomeAdminClient` is the workspace-scoped provisioning surface. Use it from orchestrator code or setup scripts to register agents, issue keys, deploy Cedar bundles, and configure gateway MCP and LLM connections. It authenticates with a workspace platform key (`dome_pk_...`) — not an agent token — and every call hits the control plane. `Client` and `DomeAdminClient` are separate on purpose: `Client` is the runtime agent surface (authorization, audit reads, gateway calls); `DomeAdminClient` is the admin surface that *creates* the agents `Client` runs as. ```python theme={"system"} from dome import DomeAdminClient, AgentActAsConfig with DomeAdminClient( base_url="https://api.domesystems.ai", platform_key="{{PLATFORM_KEY}}", workspace_id="{{WORKSPACE_ID}}", ) as admin: agent = admin.register_agent( name="incident-bot", capabilities=["mcp:call"], act_as_config=AgentActAsConfig(method="oidc", required=True), allowed_tools=["github/list_issues"], allowed_gateway_ids=["{{GATEWAY_ID}}"], ) key = admin.ensure_agent_key( agent_id=agent.id, name="default", gateway_id="{{GATEWAY_ID}}", ) print(key.token, key.gateway_url) ``` ### Constructor | Argument | Type | Default | Description | | -------------- | ------- | -------- | -------------------------------------- | | `base_url` | `str` | required | Control-plane URL | | `platform_key` | `str` | required | Workspace platform key (`dome_pk_...`) | | `workspace_id` | `str` | required | Workspace UUID the key is scoped to | | `timeout` | `float` | `30.0` | Per-request HTTP timeout | ### Agent registry | Method | Description | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | `register_agent(*, name, parent_id=None, capabilities=None, tier="developer", metadata=None, act_as_config=None, allowed_pool_names=None, allowed_direct_model_names=None, allowed_tools=None, allowed_gateway_ids=None, actas_allowed_groups=None, actas_allowed_emails=None, actas_allowed_subjects=None) -> AgentRecord` | Create an agent record | | `list_agents(*, name_prefix="", ...) -> list[AgentRecord]` | List agents in the workspace | | `get_agent(*, agent_id) -> AgentRecord \| None` | Fetch a single agent | | `revoke_agent(*, agent_id, reason="")` | Revoke an agent without deleting it | | `delete_agent(*, agent_id, cascade=False, reason="")` | Hard-delete an agent | | `create_agent_key(*, agent_id, name="default", gateway_id=None) -> AgentKeyMaterial` | Issue a new bearer token and return the selected Gateway endpoints | | `ensure_agent_key(*, agent_id, name="default", gateway_id=None) -> AgentKeyMaterial` | Create the named key, or rotate it if it already exists. Idempotent | | `rotate_agent_key(*, agent_id, key_name, gateway_id=None) -> AgentKeyMaterial` | Rotate an existing key and return the selected Gateway endpoints | | `revoke_agent_key(*, agent_id, key_name)` | Revoke a single key | | `list_agent_keys(*, agent_id) -> list[dict]` | List key metadata. The bearer is never returned after issuance | | `exchange_token(*, api_key, gateway_id=None) -> TokenExchangeResult` | Exchange a `dome_*` key for a short-lived JWT and complete Gateway endpoints | `AgentKeyMaterial.token` is the only place the bearer is ever returned. Persist it before discarding the response. ### Workspace and gateway setup | Method | Description | | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | `current_identity() -> AdminIdentity` | Resolve the authenticated platform identity | | `list_workspaces(*, tenant_id=None) -> list[Workspace]` | List workspaces the key can see | | `get_workspace_runtime_capabilities(*, workspace_id=None) -> WorkspaceRuntimeCapabilities` | Customer-safe hosting and runtime capability traits | | `create_mcp_connection(*, name, transport, ...) -> MCPConnection` | Attach an MCP server to the workspace gateway | | `delete_mcp_connection(*, connection_id, workspace_id=None)` | Detach an MCP server | | `create_gateway(*, name, description="") -> Gateway` | Create a workspace-scoped [Gateway](#gateways). Requires `gateway.manage` | | `list_gateways() -> list[Gateway]` | List workspace Gateways and their complete endpoints | | `get_gateway(*, gateway_id) -> Gateway` | Get one Gateway and all resource memberships | | `update_gateway(*, gateway_id, name, description="") -> Gateway` | Update Gateway metadata | | `set_gateway_state(*, gateway_id, state) -> Gateway` | Set `active` or `disabled` | | `set_gateway_default(*, gateway_id) -> Gateway` | Set the non-enforcing workspace default marker | | `add_gateway_tool(*, gateway_id, tool_id)` | Add one discovered MCP tool | | `add_gateway_tool_source(*, gateway_id, connection_id)` | Expose every tool from an MCP connection through the Gateway. Requires `gateway.manage` | | `add_gateway_llm_pool(*, gateway_id, llm_pool_id)` | Add an LLM pool | | `add_gateway_llm_model(*, gateway_id, llm_model_connection_id)` | Add a direct model connection | | `set_agent_gateway_access(*, agent_id, gateway_id, granted)` | Add or remove one agent's explicit Gateway grant | | `set_gateway_all_agents_grant(*, gateway_id, granted)` | Toggle the Gateway's grant for all workspace agents. Requires `gateway.manage` **and** `rules.deploy` | | `delete_gateway(*, gateway_id)` | Delete a Gateway and remove its memberships; the underlying connections and pools are unchanged. Requires `gateway.manage` | | `deploy_bundle(*, scope_kind="workspace", scope_id=None, cedar_source, ...) -> BundleRecord` | Deploy a Cedar policy bundle | | `list_bundles(*, scope_kind="workspace", scope_id=None) -> list[BundleRecord]` | List deployed bundles | | `delete_rules(*, scope_kind="workspace", scope_id=None)` | Remove rules at a scope | | `create_llm_connection(*, name, provider, ...) -> LLMConnection` | Register an LLM provider connection | | `list_llm_connections() -> list[LLMConnection]` | List LLM provider connections | | `create_llm_pool(*, name, ...) -> LLMPool` | Create a model pool | | `list_llm_pools() -> list[LLMPool]` | List model pools | | `create_llm_pool_member(*, pool_id, ...) -> LLMPoolMember` | Add a model to a pool | | `list_llm_pool_members(*, pool_id) -> list[LLMPoolMember]` | List models in a pool | ### Gateway admin Create a Gateway, add tool sources, grant admission, and hand the resulting `/gateways/{id}` URL to agents. The admin client covers Gateway CRUD, state/default selection, resource membership, per-agent access, and the all-agents grant. Gateway cost quotas live in the [CLI](/cli/connect/gateway), dashboard, and [MCP tools](/reference/mcp/gateway). ```python theme={"system"} from dome import DomeAdminClient with DomeAdminClient( base_url="https://api.domesystems.ai", platform_key="{{PLATFORM_KEY}}", workspace_id="{{WORKSPACE_ID}}", ) as admin: gateway = admin.create_gateway( name="incident-response", description="Tools + models for the incident-response agents", ) # Expose every tool this MCP connection publishes through the Gateway. admin.add_gateway_tool_source( gateway_id=gateway.id, connection_id="{{MCP_CONNECTION_ID}}", ) # Admit every agent in the workspace. admin.set_gateway_all_agents_grant( gateway_id=gateway.id, granted=True, ) # The control plane returns complete call-ready endpoints. print(gateway.gateway_url) print(gateway.endpoints.mcp_url) ``` Deleting a Gateway (`delete_gateway`) removes its memberships but leaves the underlying connections and pools unchanged. Callers still pointed at the deleted Gateway's URL fail closed; repoint them first. ### Admin errors Every admin RPC raises `AdminAPIError` on non-2xx responses. The exception carries `path`, `status_code`, and the full response `body`. `body` is truncated in the exception message so a verbose HTML error page does not produce an unreadable `str()`. ```python theme={"system"} from dome import AdminAPIError try: admin.register_agent(name="incident-bot") except AdminAPIError as exc: if exc.status_code == 409: print("agent already exists") else: raise ``` ## Bootstrap helpers Setup scripts can provision a development agent and issue a fresh gateway key with one call. ```python theme={"system"} agent = await dome.bootstrap.ensure_agent( name="incident-bot-dev", control_plane_url="https://api.domesystems.ai", platform_key="dome_pk_...", workspace_id="{{WORKSPACE_ID}}", capabilities=["mcp:call"], gateway_id="{{GATEWAY_ID}}", wait_gateway=True, ) print(agent.gateway_url) ``` | Helper | Description | | ----------------------------------------------------------------- | --------------------------------------------------------------------------- | | `dome.bootstrap.ensure_agent(...)` | Async: find or create an agent and (optionally) issue a key | | `dome.bootstrap.ensure_agent_sync(...)` | Synchronous form | | `dome.wait_for_agent_key(gateway_url=..., token=..., timeout=30)` | Block until a freshly issued key shows up in the Gateway's API-key snapshot | Pass an existing `DomeAdminClient` as `admin_client=`, or pass `control_plane_url`, `platform_key`, and `workspace_id` and let the helper construct one. The returned `BootstrapAgent` exposes `agent_id`, `token`, `gateway_id`, and the complete `gateway_url`, plus the underlying `AgentRecord` and `AgentKeyMaterial`. `ensure_agent` is idempotent. If an agent with the exact name exists, it is reused; if `create_key=True`, the named key is created or rotated and fresh key material is returned. Pass `wait_gateway=True` to block until the Gateway has synced the new key. Select it with `gateway_id=`; `gateway_url=` is only an optional complete-URL override for the readiness probe and must name the same Gateway. ## Errors All error classes inherit from `dome.DomeError`. ### Control-plane / lifecycle | Class | Raised When | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DomeConfigurationError` | SDK configuration cannot support the requested operation | | `DomeGatewayConfigurationError` | `gateway_url` is missing, malformed, unscoped, protocol-suffixed, or conflicts with `gateway_id`. Subclass of `DomeConfigurationError`; carries `gateway_url` | | `NotInitializedError` | Method called before `start_policy_sync()` for local checks | | `AlreadyStartedError` | Lifecycle double-start | | `ShutdownError` | Methods called after `close()` | | `NoBundleLoadedError` | Local `check()` called with no Cedar bundle synced | | `DeniedError` | Typed deny used by callers who prefer exceptions from `on_deny` | | `GatewayNotReadyError` | `wait_ready()` did not observe a ready gateway in time | | `DomeMissingProviderDependency` | A lazy provider factory needs an uninstalled package | ### Gateway data-plane All gateway errors inherit from `DomeGatewayError` and carry `status_code` plus the raw response when available. `request_id` and `activity_id` are populated when the gateway response exposes them. `gateway_id` is stamped from the client's configured endpoint, so every gateway error names the Gateway the failing call was routed through. | Class | Raised When | | ------------------------- | ----------------------------------------------------------------------------------------------- | | `DomeGatewayError` | Generic gateway-side failure not matched by a typed subclass | | `DomeGatewayUnavailable` | Gateway is unreachable or reports a transient outage | | `DomeAuthorizationDenied` | Dome policy denies the operation. Carries `reason`, `reason_code`, `determining_policy` | | `DomeCredentialRequired` | Per-user credential must be connected. Carries `connection_name`, `provision_url`, `expires_at` | | `DomePolicyStale` | Gateway reports stale policy state | | `DomeToolNotFound` | Requested MCP tool does not exist | | `DomeToolExecutionError` | JSON-RPC error or `isError=true` (when `raise_on_tool_error=True`) | | `DomeActAsRequired` | Gateway requires a verified act-as identity | | `DomeRateLimited` | Gateway or upstream provider rate-limits the request | | `DomeModelNotFound` | Model or pool cannot be found | ```python theme={"system"} try: client.gateway.tools.call("github/list_issues", {"repo": "dome"}, act_as=user) except dome.DomeCredentialRequired as exc: print("connect", exc.connection_name, "at", exc.provision_url) except dome.DomeAuthorizationDenied as exc: print("denied:", exc.reason_code, exc.determining_policy) ``` ## Async client `dome.AsyncClient` has the same constructor and exposes the same surfaces with `await`-able methods. ```python theme={"system"} import asyncio import dome async def main() -> None: async with dome.AsyncClient( token="{{AGENT_TOKEN}}", gateway_url="https://gateway.domesystems.ai/gateways/{{GATEWAY_ID}}", act_as_method="none", ) as client: result = await client.gateway.tools.call( "github/list_issues", {"repo": "dome"}, act_as=dome.PlainActAs(email="alice@corp.com"), ) print(result.structured_content or result.content) asyncio.run(main()) ``` `client.audit.stream(...)` returns an async iterator on `AsyncClient`. `client.activity(...)` returns an `AsyncActivity` and supports `async with`. ## Framework adapters Adapters are separate PyPI packages that wrap `Client` for a specific framework. Refer to [Adapters](/sdks/python/adapters) for the catalog and integration details. ## Next steps Wrap the client for a framework, or wire credentials and Act-As from Develop: * [Adapters](/sdks/python/adapters) for framework wrappers * [Develop](/develop) for credentials, endpoints, and Act-As envelopes * [Delegated agents](/connect/agents/delegated) when access depends on the person * [Examples](/tutorials/examples/use-cases/code-agent) for a governed agent pattern # Tutorial Source: https://docs.domesystems.ai/sdks/python/tutorial Wire the Python SDK into a service and verify governance end-to-end Take a registered agent from the developer quickstart and embed the Python SDK in a real service. Tool calls route through the gateway, end-user identity flows on every request, and the audit trail correlates the run. **Prerequisites:** * Completed [Developer Quickstart](/tutorials/role/developer) — agent registered, API key minted, rules deployed * Agent token saved as `$DOME_AGENT_TOKEN` * Gateway URL saved as `$DOME_GATEWAY_URL` — must include the `/gateways/{id}` [Gateway](/sdks/python/reference#gateways) prefix (`https://gateway.../gateways/{id}`); a bare base URL fails closed * Control-plane URL saved as `$DOME_CONTROL_PLANE_URL` * Python 3.12+ ## 1. Add the SDK ```bash theme={"system"} pip install dome-sdk ``` ## 2. Initialize once at startup Construct the client during application startup. Share one `Client` across threads — the gateway clients are thread-safe. ```python theme={"system"} # governance.py import logging import os import dome log = logging.getLogger("agent") def build_client() -> dome.Client: client = dome.Client( token=os.environ["DOME_AGENT_TOKEN"], gateway_url=os.environ["DOME_GATEWAY_URL"], control_plane_url=os.environ["DOME_CONTROL_PLANE_URL"], act_as_method="none", ) client.connect() return client ``` `connect()` prepares credentials and transport state. It does not block on local Cedar sync — only call `client.start_policy_sync()` if you also want in-process checks. ## 3. Route tool calls through the gateway Replace direct tool invocations with `client.gateway.tools.call()`. The gateway evaluates authorization, resolves credentials, and audits the call before the upstream backend ever runs. ```python theme={"system"} # tools/database.py from typing import Any import dome class DatabaseTool: def __init__(self, client: dome.Client) -> None: self.client = client def query(self, sql: str, user_email: str) -> dict[str, Any]: result = self.client.gateway.tools.call( "database/query", {"sql": sql}, act_as=dome.PlainActAs(email=user_email), ) if result.is_error: return {"error": result.structured_content or result.content} return {"rows": result.structured_content or result.content} ``` `act_as` carries the end user the agent is acting on behalf of. Cedar rules read `principal.act_as.email`, `principal.act_as.sub`, and so on — refer to [Authorization model](/concepts/architecture/authorization-model). ## 4. Correlate a run with an activity Wrap each agent run in `client.activity(...)` so every gateway call and audit event shares an opaque activity ID. ```python theme={"system"} def handle_incident(client: dome.Client, case_id: str, user_email: str) -> str: user = dome.PlainActAs(email=user_email) with client.activity(metadata={"case": case_id}) as activity: issues = client.gateway.tools.call( "github/list_issues", {"repo": "dome"}, act_as=user, ) issue_payload = issues.structured_content or issues.content summary = client.gateway.llm.chat( model="prod-gpt", messages=[{"role": "user", "content": f"Summarize: {issue_payload}"}], act_as=user, ) return f"{summary['choices'][0]['message']['content']} (run={activity.activity_id})" ``` Outside an activity context, calls carry no activity ID — the SDK never mints one implicitly. ## 5. Shut down cleanly Call `close()` on shutdown to release transports. ```python theme={"system"} import atexit client = build_client() atexit.register(client.close) ``` Or use the context-manager form for short-lived scripts: ```python theme={"system"} with dome.Client(token=..., gateway_url=...) as client: ... ``` ## 6. Verify in audit Pull the run back with `client.audit.query(...)` (or the CLI): ```python theme={"system"} page = client.audit.query( event_types=("mcp.tool_call.completed", "llm.model_call.completed"), results=("EVENT_RESULT_SUCCEEDED",), page_size=50, ) for event in page.events: print(event.type, event.correlation.activity_id) ``` Or from the CLI: ```bash theme={"system"} dome audit query --activity-id {{ACTIVITY_ID}} --limit 20 ``` Each gateway call appears with the rule decision, the act-as identity, the upstream latency, and the activity ID. ## Next steps Skip manual wrapping — use the LangChain adapter for gateway tools and governed chat models. Full configuration, act-as methods, errors, and audit reads. # Setup AI assistant Source: https://docs.domesystems.ai/setup-ai-assistant Connect an AI assistant to Dome with the CLI, MCP, docs, and skills Set up an AI assistant to operate Dome alongside you. Use the CLI, MCP, docs, and skills for session, tools, and docs context: * **CLI** — install the binary, authenticate, and select the workspace the MCP server will inherit * **MCP** — `dome mcp serve` exposes role-scoped tools over JSON-RPC so the client can call Dome directly * **Docs** — `llms.txt`, per-page `.md`, and Agent-tab phrasing so the assistant follows how-tos instead of inventing steps * **Skills** — planned multi-step workflows for onboarding, policy deployment, and investigation Use the prompt below to hand setup to an assistant, or follow the sections in order yourself. Connect me to Dome from this assistant so I can operate the platform from chat. First, show me this plan and ask me to confirm before running anything: 1. Install the Dome CLI if it is missing (Homebrew with `brew trust`, or the direct download on [Install](https://docs.domesystems.ai/install)) 2. Sign me in with `dome auth login` (opens a browser), then sync contexts 3. Select a workspace with `dome context use` 4. Add the Dome MCP server to this client 5. Verify the connection (workspace + tool discovery) 6. Show me how you will use the Dome docs while we work Follow the commands at [https://docs.domesystems.ai/agent/setup-ai-assistant.md](https://docs.domesystems.ai/agent/setup-ai-assistant.md) exactly. Non-negotiable rules: * Narrate as you go. Before each step, tell me in one or two sentences what you are about to do and why it matters. Do not silently run the whole flow. * `dome auth login` opens a browser. Run it, then wait for me before continuing. After a successful sign-in, run `dome context sync`. * You cannot reload my MCP client. After writing MCP config, stop, tell me to reload if needed, and wait until I confirm Dome tools are visible. * Never print agent tokens, platform keys, or provider secrets in chat. * Prefer the Dome docs over guessing. Use [https://docs.domesystems.ai/llms.txt](https://docs.domesystems.ai/llms.txt) to find pages, and append `.md` to any docs URL for markdown. Read [https://docs.domesystems.ai/reference/mcp](https://docs.domesystems.ai/reference/mcp) for tool schemas and [https://docs.domesystems.ai/concepts/platform/permissions](https://docs.domesystems.ai/concepts/platform/permissions) for RBAC. * Missing MCP tools usually mean my workspace role, not a broken install. * Never report a step as done without showing the command or tool output. * This is setup only. Do not provision sandboxes, register demo agents, or deploy Rules unless I ask after setup. Write MCP config for whichever client I am in: Claude Code (`claude mcp add dome -- dome mcp serve`), Cursor MCP config, Claude Desktop `claude_desktop_config.json`, or stdio for other MCP clients. Once I confirm tools are live, verify with workspace + agent list, then offer first prompts such as listing agents, querying recent audit, or simulating a decision. ## Prerequisites Before you begin, you need: * An invitation-provisioned Dome account * An MCP-compatible client (Claude Code, Claude Desktop, Cursor, or any MCP client) Refer to [Install](/install) if you prefer to install the CLI yourself first. The MCP server uses your active CLI session for auth and role. It only exposes tools that role can use, so a `viewer` cannot register agents or deploy Rules. Refer to [Permissions](/concepts/platform/permissions) concept for the role-to-tool mapping. ## CLI The CLI is how you install Dome locally, sign in, and pick the workspace context the MCP server inherits. There is no separate MCP token. Switch workspaces or roles at any time and the tool list updates on the next request. ```bash theme={"system"} dome auth login dome context sync dome context use ``` Full install options are on [Install](/install). ## MCP `dome mcp serve` speaks JSON-RPC 2.0 over stdio. Your client launches that process and discovers tools filtered by the active workspace role. Pick the integration that matches your client: ```bash theme={"system"} dome auth login dome context use claude mcp add dome -- dome mcp serve ``` Restart or reload Claude Code if tools do not appear. Tools auto-discover. Add to `~/Library/Application Support/Claude/claude_desktop_config.json`: ```json theme={"system"} { "mcpServers": { "dome": { "command": "dome", "args": ["mcp", "serve"] } } } ``` Restart Claude Desktop to connect. Add to your Cursor MCP configuration: ```json theme={"system"} { "mcpServers": { "dome": { "command": "dome", "args": ["mcp", "serve"] } } } ``` Start the server on stdio: ```bash theme={"system"} dome auth login dome mcp serve ``` Point your MCP client at stdin/stdout. Any MCP-compatible client works. ### Verify the connection Single-tool round-trips are the cheapest sanity check. If the assistant returns your workspace name and an agent list, auth and discovery both work. ```text title="Verify MCP connection" theme={"system"} What workspace am I in? List the available agents. ``` The assistant calls `dome_context_current` and `dome_agents_list`. Each call lands in the audit trail with the same MCP attribution as any other modality. ### Example prompts Once MCP is connected, every CLI command has a prompt equivalent. The assistant fills defaults from the tool schema. Ask for specific values when you need them. ```text title="Register an agent" theme={"system"} Register an agent named "my-first-agent". ``` ```text title="Create an API key" theme={"system"} Create an API key named "default" for agent "my-first-agent". ``` The API key is returned exactly once. Ask the assistant to display it in full and store it before continuing. It cannot be retrieved again. ```text title="Deploy a Cedar rule" theme={"system"} Deploy a Cedar rule that permits my-first-agent to call any tool: permit( principal == Dome::Agent::"my-first-agent", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"*" ); ``` ```text title="Simulate an authorization decision" theme={"system"} Simulate whether my-first-agent can mcp:call on the database-query tool. ``` ```text title="Query recent audit" theme={"system"} Show the last 5 audit events. ``` ### Compose multi-step workflows Single-tool prompts work, but the value of an MCP-connected assistant is composition: chaining audit, rule lookup, simulation, and registry queries inside one request. ```text title="Investigate denied requests" theme={"system"} Query audit events of type access.denied from the last 24 hours. For each event, look up the agent (by agent_id) and the matched rule (from the active bundle). Pick one event and run a simulation with the same agent, action, and resource to confirm the denial would still happen against the current policy. ``` Other useful compositions: ```text title="Onboard a new agent end-to-end" theme={"system"} Run these four steps in order: (1) register an agent named "research-bot", (2) create an API key named "primary" for it, (3) deploy a Cedar bundle permitting principal == Dome::Agent::"research-bot" to mcp:call resource == Dome::MCPTool::"search-docs", (4) simulate that exact action and resource and confirm the decision is ALLOW. ``` ```text title="Switch workspaces" theme={"system"} Switch to the staging workspace. ``` The tool list refreshes automatically. Tools outside the new workspace's role are hidden. ### Tool groups The Dome MCP server exposes tools in these groups, filtered by your workspace role: | Group | Description | | ------------------------------------------- | -------------------------------------------------------------------------------- | | [Context](/reference/mcp/context) | Switch workspaces, manage aliases, check auth status | | [Agents](/reference/mcp/agents) | Register, update, suspend, revoke agents and API keys | | [Rules](/reference/mcp/rules) | Deploy, validate, simulate, rollback Cedar policies | | [Audit](/reference/mcp/audit) | Query, inspect, export audit events | | [Platform](/reference/mcp/platform) | Platform status, API keys, configuration | | [Workspaces](/reference/mcp/workspaces) | Create workspaces, manage members, configure act-as | | [Tools](/reference/mcp/tools) | MCP server connections, catalogs, and managed authentication | | [Gateways](/reference/mcp/gateway) | Group tools and models behind named endpoints, grant agent access, set cost caps | | [Verification](/reference/mcp/verification) | OIDC and HMAC verification providers | ### Role to tool exposure | Role | Tools available | | ------------- | ---------------------------------------------------------------------------------------------- | | **admin** | Every workspace tool: agents, gateway, rules, audit, members, configuration | | **operator** | Agents, gateway, rules (deploy and rollback), audit (view, no export), API keys, configuration | | **security** | Rules (deploy, rollback, simulate), audit (view and export), agent suspend, gateway view | | **developer** | Agents, agent keys, rules view, audit view, gateway view | | **viewer** | Read-only queries and listings | See the [MCP Server Reference](/reference/mcp) for the complete tool list with parameters. The AI assistant itself can be a registered Dome agent. Connecting Claude Code (or any MCP client) as a registered agent with its own credentials means every action it takes is governed by Cedar and recorded in audit. Refer to [Code Execution Agent](/tutorials/examples/use-cases/code-agent) for an end-to-end pattern. ## Docs These docs are published for AI consumption. Prefer them when the assistant needs bulk or targeted context instead of guessing CLI flags or MCP schemas. | Format | URL | Use case | | ---------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- | | **llms.txt** | [`/llms.txt`](/llms.txt) | Index of pages with titles and URLs | | **Full text** | [`/llms-full.txt`](/llms-full.txt) | Complete documentation as plain text | | **Per-page markdown** | Append `.md` to any docs URL | Targeted context injection | | **Agent setup instructions** | [`/agent/setup-ai-assistant.md`](/agent/setup-ai-assistant.md) | Confirm-first CLI + MCP setup this page hands to an assistant | ### Contextual menu Every page includes a contextual menu (top-right) for one-click AI integration: * **Copy page** copies the current page as Markdown * **Open in Claude / ChatGPT** starts a conversation with the page preloaded * **Connect to Cursor / VS Code** installs this documentation's hosted MCP server so the assistant can search the docs directly ### Agent tab in documentation Every operation page includes an **Agent** tab alongside CLI, MCP, and API tabs. The Agent tab shows the natural-language prompt in full (same titled text codeblock pattern as the examples above). Use those as guidance when building prompts or agent workflows. ### Run a governed demo with an agent [Govern your first agent](/tutorials/get-started/govern-your-first-agent) carries a prebuilt prompt that hands a full sandbox demo to an AI agent: provision, deploy Rules, then give you the questions that prove allow, redact, and deny. Agent-facing instructions live at [`/agent/tutorials/get-started/govern-your-first-agent`](/agent/tutorials/get-started/govern-your-first-agent). ## Skills Skills are multi-step workflows for agent onboarding, policy deployment, and incident investigation. They are unavailable. Use the CLI and MCP surfaces above for full platform access. ## Next steps * [MCP](/reference/mcp) reference for the full tool catalog * [Govern your first agent](/tutorials/get-started/govern-your-first-agent) for a governed sandbox demo * [Permissions](/concepts/platform/permissions) concept for the role-to-tool mapping * [Code Execution Agent](/tutorials/examples/use-cases/code-agent) to govern the assistant as a first-class Dome agent # Interactive agent Source: https://docs.domesystems.ai/tutorials/examples/interactive-agent Govern an agent that chats or collaborates with a person in the loop (coming soon) This example is coming soon. An **interactive agent** works with a person in the loop — chat, copilots, and other turn-based sessions where each request is driven by a human. The risk profile centers on what the person can reach through the agent, how identity is attributed, and what leaves the session. This example will cover how to register the agent, attach tools and models behind a Gateway, authorize per turn, and read the audit trail for a live session. Until it ships, use the related patterns below. ## Next steps * [Govern per end user](/tutorials/get-started/govern-per-end-user) when each caller should authorize as themselves * [Identity patterns](/concepts/identity-patterns) concept for standing vs delegated identity * [Multi-Adapter Tools Agent](/tutorials/examples/use-cases/tools-agent) for multiple backends behind one agent * [Knowledge Base Agent](/tutorials/examples/use-cases/knowledge-agent) for retrieval in a governed session # Noninteractive agent Source: https://docs.domesystems.ai/tutorials/examples/noninteractive-agent Govern a batch or background agent that runs without a person in the loop (coming soon) This example is coming soon. A **noninteractive agent** runs without a person driving each turn — scheduled jobs, batch pipelines, and other autonomous workloads. It usually uses [standing identity](/concepts/identity-patterns): the agent is the principal, and every call is attributed to that workload. This example will cover how to register the agent, attach the tools and models it needs, bound what it may call, and use audit to reconstruct a run after the fact. Until it ships, use the related patterns below. ## Next steps * [Identity patterns](/concepts/identity-patterns) concept for when standing identity fits * [Code Execution Agent](/tutorials/examples/use-cases/code-agent) for a governed autonomous execution pattern * [Govern your first agent](/tutorials/get-started/govern-your-first-agent) for the sandbox path end to end * [Develop](/develop) to authenticate and route runtime traffic # Code Execution Agent Source: https://docs.domesystems.ai/tutorials/examples/use-cases/code-agent Governing an agent that generates and executes code A code execution agent generates code from natural language and runs it in a sandbox. The risk profile is unlike a traditional application: the agent decides what to write and execute, the "threat actor" and "authorized user" can be the same process, and static analysis doesn't apply to code that doesn't exist yet. Governance treats every execution as a first-class, authorizable event. The gateway restricts which tools the agent can call, Cedar rules bound the surfaces it can reach, response filtering strips sensitive data before it flows back to the model, and every call lands in an immutable audit trail. ## Threat Model | Threat | Description | | -------------------------------- | -------------------------------------------------------------------------------------------------- | | **Arbitrary code execution** | The agent generates and runs malicious or unintended code that escapes the sandbox boundary | | **Data exfiltration via output** | Code output contains file paths, credentials, environment variables, or internal network addresses | | **Resource exhaustion** | Generated code consumes unbounded CPU, memory, or disk, affecting other workloads | | **Unauthorized system access** | The agent accesses filesystem, network, or database resources outside its intended scope | ## Governance Approach | Threat | Dome Capability | How It Helps | | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | Arbitrary code execution | [Register](/connect/agents#register-agent) with capabilities, [Authorize](/govern/rules) | Declare code-execution capabilities and permit only sandbox tools | | Data exfiltration via output | [Guards](/govern/guards) | Classify output fields containing paths and credentials, redact before returning to the agent | | Resource exhaustion | [Register](/connect/agents#register-agent) with capabilities, [Pass identity for delegated agents](/develop#pass-identity-for-delegated-agents) | Enforce tool-level authorization so the agent cannot invoke resource-intensive tools without the required capability | | Unauthorized system access | [Authorize](/govern/rules), [Stream Live Events](/operate/observe) | Forbid access to production and database tools, audit every tool call for anomaly detection | ## Implementation Register the code execution agent with the capability it needs: ```bash theme={"system"} dome agents register \ --name "code-exec-agent" \ --capabilities "code-execution" ``` Add the code execution sandbox as a backend with field classifications on sensitive output fields: ```bash theme={"system"} dome tool add \ --name "code-sandbox" \ --url "https://sandbox.internal:8443" \ --protocol streamable-http \ --field-classification output_paths=SENSITIVE,env_vars=SENSITIVE,stderr=PII \ --gateway Default ``` `--gateway` makes the connection reachable through a [Gateway](/connect/gateways); the agent also needs a [grant](/connect/gateways#manage-agent-access) to that Gateway. Write rules that permit the agent to call only sandbox tools and forbid access to production infrastructure: ```bash theme={"system"} dome rules validate code-exec-rules.cedar dome rules apply code-exec-rules.cedar --name "code-exec-policy" ``` See the [Policy Example](#policy-example) below for the Cedar rule content. Test the rules against historical events to verify they do not break existing workflows: ```bash theme={"system"} dome rules simulate code-exec-rules.cedar ``` Review the DecisionDiffs to confirm that only the intended changes occur. Stream audit events for real-time monitoring of code execution activity: ```bash theme={"system"} dome audit stream --agent code-exec-agent ``` Every tool call, authorization decision, and field redaction action appears in the audit stream. ## Policy Example ```cedar title="code-exec-rules.cedar" theme={"system"} // Permit the code execution agent to call sandbox tools permit( principal == Dome::Agent::"code-exec-agent", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"code-sandbox" ); // Permit the agent to discover available tools permit( principal == Dome::Agent::"code-exec-agent", action == Dome::Action::"mcp:discover", resource ); // Prevent the code execution agent from calling production-deploy. forbid( principal == Dome::Agent::"code-exec-agent", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"production-deploy" ); // Prevent the code execution agent from writing to databases. forbid( principal == Dome::Agent::"code-exec-agent", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"database-write" ); ``` Cedar evaluates all rules against every request. If any `forbid` matches, the request is denied regardless of `permit` rules. The default decision when no rule matches is deny. ## Next steps * [Authorize Access](/govern/rules) to write and deploy Cedar * [Tools](/connect/resources/tools) to attach sandboxes and catalogs * [Multi-Adapter Tools Agent](/tutorials/examples/use-cases/tools-agent) for multiple tool adapters * [Develop](/develop) to authenticate and route runtime traffic # Knowledge Base Agent Source: https://docs.domesystems.ai/tutorials/examples/use-cases/knowledge-agent Governing an agent that reads from personal and shared knowledge bases A knowledge base agent answers questions from a mix of sources — each end user's own documents (Notion, Google Drive, personal Confluence) and shared company-wide content (internal wikis, public runbooks). The same agent identity serves many users, and every read against a personal source must be scoped to the calling user's data while shared sources stay pooled. Governance binds every call to a verified end-user identity, then splits backend authentication by source type. Per-user OAuth backends route each call with the calling user's own upstream token, so per-user authorization is delegated to the upstream service. Shared backends use a single workspace credential for content the whole workspace should see. Cedar rules stay coarse-grained at the agent and backend level, field classifications redact sensitive document metadata before it reaches the model, and the audit trail captures both agent and end-user identity on every call. ## Threat Model | Threat | Description | | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | **Cross-user data leakage** | The agent uses one user's credentials — or workspace-pooled credentials — to fetch another user's documents | | **Sensitive content surfacing through model context** | Documents returned to the agent contain PII, secrets, or financial data that bleed into the model's context window or outputs | | **Unauthorized backend access** | The agent reaches knowledge bases it should not query, or attempts write operations on a read-only role | | **Missing user attribution in audit** | Audit captures the agent identity but not which end user triggered the read, breaking compliance review | ## Governance Approach | Threat | Dome Capability | How It Helps | | --------------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Cross-user data leakage | [Authenticate Identities](/develop#authenticate), [Attach Backends](/connect/resources/tools#add-tool) | `--actas-required` on the agent forces every call to carry verified user identity; per-user OAuth backends route each call with that user's upstream token, so the upstream enforces per-user data access | | Sensitive content surfacing | [Guards](/govern/guards) | Field classifications on document metadata redact PII and SECRET fields before the response reaches the agent | | Unauthorized backend access | [Authorize](/govern/rules) | Cedar rules pin the agent to specific knowledge backends and forbid write operations | | Missing user attribution | [Stream Live Events](/operate/observe), [Audit and Export](/operate/audit) | Every audit event captures both agent and act-as identities; per-user backend events (`credential.provision_link.*`, `oauth.consent.granted`) trace provisioning history per user | ## Implementation Register the agent so every request must carry a verified end-user identity: ```bash theme={"system"} dome agents register \ --name "knowledge-agent" \ --capabilities "knowledge-read" \ --actas-method oidc \ --actas-provider vp_workspace_oidc \ --actas-required ``` With `--actas-required`, the gateway rejects any tool call without a verified act-as token. The per-user OAuth flow in step 2 keys credentials off this same act-as `sub`. `vp_workspace_oidc` is a placeholder ID for a [workspace verification provider](/connect/agents/delegated#create-a-verification-provider). Create one for your IdP and pass its returned ID, or skip the provider step and inline the discovery URL on the agent with `--actas-oidc-url `. Add the personal knowledge base as a per-user OAuth backend. Each end user provisions their own upstream credential through a magic-link flow on first use — the gateway never holds a single shared credential that could span users: ```bash theme={"system"} dome tool add \ --name "notion-personal" \ --url "https://api.notion.com" \ --protocol streamable-http \ --auth-method oauth \ --credential-type per-user \ --oauth-authorize-url "https://api.notion.com/v1/oauth/authorize" \ --oauth-token-url "https://api.notion.com/v1/oauth/token" \ --oauth-client-id "$NOTION_CLIENT_ID" \ --oauth-client-secret "$NOTION_CLIENT_SECRET" \ --oauth-default-scope "read_content" \ --field-classification author_email=PII,owner_email=PII,page_content=SENSITIVE ``` On the first call from a new user, the gateway returns a `401` with a magic-link `provision_url`. The user opens it, completes Notion's OAuth consent against their own account, and the next `tools/list` reflects their newly provisioned backend. Add the shared internal wiki as a shared OAuth backend. One operator-supplied credential serves every caller — appropriate for content the whole workspace should see: ```bash theme={"system"} dome tool add \ --name "company-wiki" \ --url "https://wiki.internal:8443" \ --protocol streamable-http \ --auth-method api-key \ --credential-type shared \ --authorization "Bearer $WIKI_TOKEN" \ --field-classification author_email=PII \ --gateway Default ``` Write authorization rules that key off the agent identity and act-as presence — not per-user identity. Per-user authorization is delegated to the upstream service via the per-user OAuth credential: ```bash theme={"system"} dome rules validate knowledge-agent-rules.cedar dome rules apply knowledge-agent-rules.cedar --name "knowledge-agent-policy" ``` See the [Policy Example](#policy-example) below for the Cedar rule content. Test rules against historical events before deploying: ```bash theme={"system"} dome rules simulate knowledge-agent-rules.cedar ``` Verify that requests without an act-as token are denied for the personal backend and that write operations are universally forbidden. Stream audit events to watch both per-user provisioning health and read activity: ```bash theme={"system"} dome audit stream --agent knowledge-agent ``` Look for `credential.provision_link.issued` (a user hit the magic-link flow), `oauth.consent.granted` (a user successfully provisioned), `oauth.token.refresh_failed` (a user's token needs re-consent), and `mcp.tool_call.attempted` events to see read patterns per user. ## Policy Example ```cedar title="knowledge-agent-rules.cedar" theme={"system"} // Permit reads from personal knowledge bases only when act-as identity is present. // Per-user authorization is enforced upstream via the per-user OAuth credential. permit( principal == Dome::Agent::"knowledge-agent", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"notion-personal" ) when { principal has act_as }; // Permit reads from the shared company wiki for any authenticated request. permit( principal == Dome::Agent::"knowledge-agent", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"company-wiki" ); // Forbid any tool with SENSITIVE classifications when act-as identity is missing. forbid( principal == Dome::Agent::"knowledge-agent", action == Dome::Action::"mcp:call", resource ) when { resource.field_classifications.contains("SENSITIVE") && !(principal has act_as) }; // Forbid all write and delete operations — the knowledge agent is read-only. forbid( principal == Dome::Agent::"knowledge-agent", action == Dome::Action::"mcp:call", resource ) when { context.method == "write" || context.method == "delete" }; // Permit tool discovery so the agent sees its provisioned backends. permit( principal == Dome::Agent::"knowledge-agent", action == Dome::Action::"mcp:discover", resource ); ``` Per-user OAuth keeps Cedar rules coarse-grained. The upstream service decides which documents the calling user can see, because the gateway forwards the user's own access token. Cedar's job is to gate which backends the agent can reach and to block reads when the user identity is missing — not to enumerate per-user document permissions. ## Next steps * [Authorize Access](/govern/rules) to write and deploy Cedar * [Tools](/connect/resources/tools) to attach retrieval backends * [Code Execution Agent](/tutorials/examples/use-cases/code-agent) for sandbox execution * [Audit events](/operate/audit) to verify retrieval and denials # Multi-Adapter Tools Agent Source: https://docs.domesystems.ai/tutorials/examples/use-cases/tools-agent Governing an agent that connects to multiple external services A multi-adapter tools agent reaches several backends through the gateway — CRM, analytics, payments, internal APIs. Each backend has different sensitivity, authentication, and compliance posture, so a single compromise cascades across services. Governance treats every backend as its own authorizable surface with its own policy. Per-backend Cedar rules grant narrow, named access to the exact tools the agent needs on each service. Act-as identity propagates end-to-end, so backends authorize the real user, not the bot. Field classifications travel with each backend, and every cross-service call lands in a unified audit trail. ## Threat Model | Threat | Description | | ---------------------------------------- | -------------------------------------------------------------------------------------------------- | | **Over-privileged tool access** | The agent has broader access than it needs, reaching backends it should never call | | **Credential leakage between backends** | A vulnerability in one backend exposes credentials for another through shared agent context | | **Unauthorized cross-service data flow** | The agent reads from a sensitive backend and writes that data to a less-secured one | | **Backend configuration drift** | Backend credentials or field classifications fall out of sync with actual service security posture | ## Governance Approach | Threat | Dome Capability | How It Helps | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | Over-privileged tool access | [Authorize](/govern/rules), [Pass identity for delegated agents](/develop#pass-identity-for-delegated-agents) | Per-backend Cedar rules grant only the specific tools each agent needs | | Credential leakage between backends | [Attach Backends](/connect/resources/tools#add-tool), [Route Traffic](/develop#route-traffic) | The gateway manages credentials per-backend — agents never see backend credentials | | Unauthorized cross-service data flow | [Guards](/govern/guards), [Authenticate Identities](/develop#authenticate) | Field classifications prevent sensitive data from leaving its backend, act-as identity enforces user-level access | | Backend configuration drift | [Stream Live Events](/operate/observe), [Audit and Export](/operate/audit) | Audit trail captures every backend interaction for drift detection and compliance review | ## Implementation Register the agent and declare which service domains it accesses: ```bash theme={"system"} dome agents register \ --name "multi-tools-agent" \ --capabilities "crm-access,analytics-read,payments-read" \ --actas-method oidc \ --actas-provider vp_workspace_oidc \ --actas-required ``` Setting `--actas-required` ensures every request carries end-user identity. The gateway rejects requests without a valid act-as header. `vp_workspace_oidc` is a placeholder ID for a [workspace verification provider](/connect/agents/delegated#create-a-verification-provider). Create one for your IdP and pass its returned ID, or skip the provider step and inline the discovery URL on the agent with `--actas-oidc-url `. Add each backend with its own credentials and field classifications: ```bash theme={"system"} # Analytics backend — read-only, no sensitive fields. --gateway # makes each connection reachable; the agent also needs a grant to the Gateway # (see /connect/gateways#add-resource-memberships). dome tool add \ --name "analytics-api" \ --url "https://analytics.internal:8443" \ --protocol streamable-http \ --auth-method api-key \ --credential-type shared \ --authorization "Bearer $ANALYTICS_TOKEN" \ --gateway Default # CRM backend — contains PII, requires identity forwarding dome tool add \ --name "crm-api" \ --url "https://crm.internal:8443" \ --protocol streamable-http \ --header-actas "X-Dome-Act-As" \ --auth-method api-key \ --credential-type shared \ --authorization "Bearer $CRM_TOKEN" \ --field-classification email=PII,phone=PII,address=PII \ --gateway Default # Payment backend — highly sensitive dome tool add \ --name "payments-api" \ --url "https://payments.internal:8443" \ --protocol streamable-http \ --auth-method api-key \ --credential-type shared \ --authorization "Bearer $PAYMENTS_TOKEN" \ --field-classification card_number=SENSITIVE,account_id=SENSITIVE \ --gateway Default ``` Write authorization rules that grant different access levels per backend: ```bash theme={"system"} dome rules validate multi-tools-rules.cedar dome rules apply multi-tools-rules.cedar --name "multi-tools-policy" ``` See the [Policy Example](#policy-example) below for the Cedar rule content. Verify the rules against historical events before they take effect: ```bash theme={"system"} dome rules simulate multi-tools-rules.cedar ``` Stream audit events filtered to the agent to watch for unexpected cross-service access patterns: ```bash theme={"system"} dome audit stream --agent multi-tools-agent ``` ## Policy Example ```cedar title="multi-tools-rules.cedar" theme={"system"} // Permit the registered agent read-only access to analytics tools. permit( principal == Dome::Agent::"multi-tools-agent", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"analytics-api" ) when { context.method == "read" }; // Permit CRM access only when act-as identity is present permit( principal == Dome::Agent::"multi-tools-agent", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"crm-api" ) when { principal has act_as }; // Forbid CRM write operations unless act-as identity has the editor role forbid( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"crm-api" ) when { context.method == "write" && !(principal.act_as.roles.contains("editor")) }; // Permit the registered agent read-only access to payments. permit( principal == Dome::Agent::"multi-tools-agent", action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"payments-api" ) when { context.method == "read" }; // Forbid all write operations on payments forbid( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"payments-api" ) when { context.method == "write" }; // Permit tool discovery so the agent sees its available tools permit( principal == Dome::Agent::"multi-tools-agent", action == Dome::Action::"mcp:discover", resource ); ``` The payment backend rules demonstrate defense in depth: the `forbid` on write operations applies even when a permit matches. Layer forbid rules to create hard boundaries that no permit can bypass. ## Next steps * [Adapters](/sdks/python/adapters) for framework wrappers * [Tools](/connect/resources/tools) to attach catalogs and credentials * [Gateways](/connect/gateways) for membership and grants * [Authorize Access](/govern/rules) to permit and forbid tool calls # Build a governed app Source: https://docs.domesystems.ai/tutorials/get-started/build-a-governed-app Wire model and tool calls in one app, then click through allow, redact, and deny. Tool calls and inference are what an agent actually does. The application puts both on one path: the model names the `employee-summary` pool, may call tools through the same gateway, and every step stays governed, including allow, redact, and deny. Build a small governed HR application that runs a model + tool agent loop through Dome, then hand me the Chat and Tools walks that prove it. Use the Vite + Hono reference app at [https://github.com/dome-systems/demo-hr-desk](https://github.com/dome-systems/demo-hr-desk). First, show me this plan and ask me to confirm before running anything: 1. Confirm the sandbox from Govern + Call a model through a pool is still active (demo-hr, hr-assistant, employee-summary pool, llm rule) 2. Mint a second API key for the app, separate from my editor's key 3. Clone the demo app and wire env: token, gateway URL, and DOME\_POOL=employee-summary 4. Walk me through the generic agent flow, then the app files that implement each step (with the relevant code) 5. Run Chat and walk me through a Try asking prompt so I see model + tool steps 6. On Tools, click through allow, redact, and deny 7. Show me the same decisions in the audit trail Follow the commands at [https://docs.domesystems.ai/agent/tutorials/get-started/build-a-governed-app.md](https://docs.domesystems.ai/agent/tutorials/get-started/build-a-governed-app.md) exactly. Non-negotiable rules: * Narrate as you go. Before each step, tell me in one or two sentences what you are about to do and why it matters. Do not silently run the whole flow. * Sandbox only. Run `dome context current` and confirm the workspace name starts with `sandbox-`. If it does not, stop and ask me. Never modify a workspace you did not provision in this session. * Never print the `dome_...` agent token in chat. Write it into `.env` and confirm that file is gitignored. * `dome auth login` opens a browser. Run it, then wait for me before continuing. After a successful sign-in, run `dome context sync` so stale workspaces drop out of the local cache. * Do not weaken the rules to make a call succeed. The denial is the point of the exercise. If a call fails that should have worked, tell me instead of editing the Cedar bundle. * Keep the app small. Prefer the reference app as-is. Do not add auth UI or expand the product. * Chat runs the model via the pool and tools via MCP, both through Dome from the **backend**. The browser never holds the agent token and never calls the Gateway. * After each create step, give me a markdown link into the Dome console for that resource. Derive the base URL from `dome auth status` → `Server`. * Expect re-runs. Check for existing resources before creating them and use `--if-not-exists` where available. * Never report a step as done without showing the command output. When the app runs, walk me through Chat first (model + tool steps, View gateway call), then the three Tools outcomes and which control produced each: Cedar allowlist, response Filter, and the forbid rule. Then offer to run `dome audit query --results denied --limit 10` so I can see the denial attributed to the agent. In this tutorial, you will run a small HR application against the Gateway you already set up. The model calls `employee-summary` and may invoke tools. Dome governs every allow, redact, and deny, and records each step in the audit trail. The walkthrough uses the Vite + Hono [demo-hr-desk](https://github.com/dome-systems/demo-hr-desk) reference app. To do this, you will: Issue a second key on the existing agent identity. Clone the reference app and fill `.env`. See the generic loop, then the app files that implement it. Chat through the pool and watch model and tool steps. Click the three tool actions on the Tools tab. Match what you saw against the audit trail. ## Prerequisites For this tutorial, you will need: * [Govern your first agent](/tutorials/get-started/govern-your-first-agent) completed, with its sandbox still active. That tutorial created the `demo-hr` connection, the `redact-contact` Filter, the `hr-assistant` agent, and the tool rule bundle. * [Call a model through a pool](/tutorials/get-started/call-a-model-through-a-pool) completed. This application needs the `employee-summary` pool and the `hr-assistant-llm` rule so Chat can invoke the model. * Node.js 18 or later. * The Dome CLI, installed and signed in. Confirm you are still on the sandbox before you start: ```bash theme={"system"} dome context current ``` The workspace should read `sandbox-get-started`. If it does not, switch back: ```bash theme={"system"} dome context use sandbox-get-started ``` > This tutorial runs entirely in a sandbox. In a production workspace, minting agent credentials is a developer action, while the rules this app runs under are owned by security. ## Mint a credential for the app An agent identity can hold several credentials. Your editor already has one. The app needs its own, so you can revoke either without disturbing the other. ```bash theme={"system"} dome agents create-key hr-assistant --name service ``` The token is shown once. Copy the `Token: dome_…` value. Both keys resolve to `hr-assistant`, so both are governed by the same rules and both appear in audit under the same agent. ## Set up the project Clone the reference HR application: ```bash theme={"system"} git clone https://github.com/dome-systems/demo-hr-desk.git cd demo-hr-desk npm install cp .env.example .env ``` Fill `.env` from the CLI. The gateway URL must include the `/gateways/` segment and must **not** include `/mcp` or `/v1`. The Hono proxy appends those itself: ```bash title=".env" theme={"system"} DOME_TOKEN=dome_... DOME_GATEWAY_URL=https://GATEWAY_HOST/gateways/DEFAULT_GATEWAY_ID DOME_POOL=employee-summary ``` ```bash theme={"system"} dome context current dome gateway list ``` This tutorial uses plain HTTP so the wire shape stays visible. However, you can use any OpenAI-compatible client, AI SDK, or MCP client at the Gateway URL with the agent token. Chat completions go to `…/gateways//v1`, and tools go to `…/gateways//mcp`. ## How a governed agent works Any app that puts a model and tools behind Dome follows the same shape. **The frontend never calls Dome.** The agent token is a bearer credential for the Gateway. If it lives in the browser, anyone who opens DevTools can call tools and models as that agent. Keep the token on your backend and have the UI talk only to your server. 1. **UI → your backend.** The client posts a user message (or a direct tool click) to your server, never to the Gateway. 2. **Backend → model (pool).** The server `POST`s OpenAI-shaped chat completions to `…/gateways//v1/chat/completions`, with `model` set to the **pool name** (`employee-summary`), not a vendor model id. The agent token stays in server env. 3. **Model may request tools.** If the completion includes `tool_calls`, the server maps each short name to a qualified MCP tool and `POST`s JSON-RPC `tools/call` to `…/gateways//mcp`. 4. **Tools return through Dome.** Allow, redact, and deny happen on that MCP path before your code sees the result. Denied calls become structured errors you feed back to the model. 5. **Loop.** Tool results go into the message list. The server calls the pool again until the model returns plain text (or you hit a round limit). 6. **Optional direct tools.** A Tools UI can hit your backend's `/api/tool`, which then calls `/mcp`, with still no browser → Dome path. Useful for proving allow / redact / deny in isolation. Dome manages authorization, filtering, and audit. Your application manages the agent loop: what to ask the model, which tools to attempt, and how to show the results. ## How this application implements it [demo-hr-desk](https://github.com/dome-systems/demo-hr-desk) is the reference application for this tutorial. The sections below are a guided tour of how it implements each step above. Skip ahead to [Run the agent loop](#run-the-agent-loop) if you want to try it first. ### UI → your backend `src/App.tsx` posts conversation history to `/api/chat`. The agent token never leaves the server. ```tsx title="src/App.tsx" theme={"system"} async function sendChat(prompt: string) { // ... const messages = [...prior, { role: "user" as const, content: text }]; // ... const res = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messages }), }); const result = (await res.json()) as AgentResponse; // ... } ``` `server/index.ts` mounts the backend routes. Only these handlers (and the helpers they call) may use `DOME_TOKEN` or reach the Gateway. ```ts title="server/index.ts" theme={"system"} app.post("/api/chat", async (c) => { // ... build messages from body.messages or body.prompt const result = await runAgent({ messages, pool: body.pool, }); return c.json(result); }); ``` ### Backend → model (pool) `server/llm.ts` is the curl from [Call a model through a pool](/tutorials/get-started/call-a-model-through-a-pool) in TypeScript: Gateway + `/v1/chat/completions`, bearer agent token, `model` = pool. ```ts title="server/llm.ts" theme={"system"} export async function chatCompletion(options: { prompt?: string; messages?: ChatMessage[]; pool?: string; tools?: unknown[]; }): Promise { const base = process.env.DOME_GATEWAY_URL; const token = process.env.DOME_TOKEN; const pool = options.pool || process.env.DOME_POOL || DEFAULT_POOL; const requestBody: Record = { model: pool, messages, }; if (options.tools?.length) { requestBody.tools = options.tools; requestBody.tool_choice = "auto"; } // ... const url = `${base.replace(/\/$/, "")}/v1/chat/completions`; const res = await fetch(url, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify(requestBody), }); // ... } ``` ### Model may request tools When the completion includes `tool_calls`, `server/agent.ts` maps each short name to a qualified MCP tool and calls `server/dome.ts`. ```ts title="server/agent.ts" theme={"system"} export const TOOL_MAP: Record = { list_employees: "demo-hr/hr/list_employees", get_employee: "demo-hr/hr/get_employee", get_salary: "demo-hr/finance/get_salary", // ... }; for (const call of toolCalls) { const shortName = call.function?.name ?? ""; const mcpName = TOOL_MAP[shortName] ?? shortName; // ... parse arguments const toolTrace = await callTool(mcpName, args); // ... } ``` ### Tools return through Dome `server/dome.ts` posts JSON-RPC `tools/call` to `/mcp`. Allow, redact, and deny happen on that path before your code sees the result. ```ts title="server/dome.ts" theme={"system"} export async function callTool( name: string, args: Record = {}, ): Promise { const base = process.env.DOME_GATEWAY_URL; const token = process.env.DOME_TOKEN; const requestBody = { jsonrpc: "2.0" as const, id: 1, method: "tools/call" as const, params: { name, arguments: args }, }; // ... const url = `${base.replace(/\/$/, "")}/mcp`; const res = await fetch(url, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify(requestBody), }); // ... map body.error → denied, result → allowed / error } ``` ### Loop `server/agent.ts` runs the rounds. Tool results go into the message list. The server calls the pool again until the model returns plain text (or you hit a round limit). ```ts title="server/agent.ts" theme={"system"} export async function runAgent(options: { messages: ChatMessage[]; pool?: string; }): Promise { // ... seed system prompt + user messages for (let round = 0; round < MAX_ROUNDS; round++) { const trace = await chatCompletion({ messages: messages as ChatMessage[], pool: options.pool, tools: OPENAI_TOOLS, }); steps.push({ type: "model", trace }); // ... return early if denied / no tool_calls for (const call of toolCalls) { const shortName = call.function?.name ?? ""; const mcpName = TOOL_MAP[shortName] ?? shortName; // ... parse arguments const toolTrace = await callTool(mcpName, args); steps.push({ type: "tool", name: shortName, mcpName, arguments: args, trace: toolTrace }); messages.push({ role: "tool", tool_call_id: call.id, content: toolResultContent(toolTrace), }); } } // ... } ``` Each `steps` entry is what the Chat transcript and **View gateway call** sheet show: model requests naming `employee-summary`, tool requests naming `demo-hr/…`. ### Optional direct tools A Tools UI can hit `/api/tool` for one MCP call without the model loop. Still no browser → Dome path. Useful for proving allow, redact, and deny in isolation. ```tsx title="src/App.tsx" theme={"system"} async function runTool(action: (typeof TOOL_ACTIONS)[number]) { // ... const res = await fetch("/api/tool", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: action.name, arguments: action.args }), }); const trace = (await res.json()) as CallTrace; // ... } ``` ```ts title="server/index.ts" theme={"system"} app.post("/api/tool", async (c) => { const body = await c.req.json<{ name?: string; arguments?: Record; }>(); // ... const trace = await callTool(body.name, body.arguments ?? {}); return c.json(trace); }); ``` That is the whole app: UI → your backend → pool and/or MCP. The frontend never calls Dome. The agent token never leaves the server. ## Run the agent loop Start the application: ```bash theme={"system"} npm run dev ``` Open [http://localhost:5173](http://localhost:5173). Stay on **Chat** (the default). Use **Try asking** and start with **Who is E001 and how do I reach them?** You should see: 1. A model step that named `employee-summary` (open **View gateway call** on that step to confirm `"model": "employee-summary"`). 2. A tool step for `get_employee` with contact fields already `[REDACTED]`. 3. A final assistant reply that uses the tool result. Try **What is Alice's salary?** next. The model may attempt `get_salary`. The gateway denies it, and the transcript shows the refusal, the same Cedar rule as a direct tool click, now inside the loop. ## Show allow, redact, and deny Chat mixed allow, redact, and deny into the model loop. The Tools tab isolates each outcome with one click, so you can see which control fired without the model choosing the tools. Open **Tools** and click each button in order. Your application does not decide who may call which tool, and it does not mask fields. You already defined those controls on the Gateway. Your code only receives the allowed, redacted, or denied result. | Action | Status | What to notice | | ----------------------- | ------------------ | ------------------------------------------------------------ | | List employees | Allowed | Demo employees, including `E001`. | | Who is E001? | Allowed · redacted | Alice Johnson's record with `"email": "[REDACTED]"`. | | What is Alice's salary? | Denied | A `reason` from the rule that matched, not an empty payload. | The second result is the important one for Guard Filters. The backend returned a real address. The `redact-contact` Guard Filter replaced it on the response path before your proxy deserialized it. The page never asked for masking and cannot switch it off. Each result lists the outcome. Click **View gateway call** to open the side sheet with the outbound request and gateway response (token redacted). Why each click behaved that way: | Click | What Dome did | | ----------------------- | -------------------------------------------------------------------------------------------- | | List employees | Cedar allowed `mcp:call` on `demo-hr/hr/list_employees`. | | Who is E001? | Cedar allowed the call. The `redact-contact` Guard Filter masked `email` on the response. | | What is Alice's salary? | Cedar denied `mcp:call` on `demo-hr/finance/get_salary` because it is outside the allowlist. | These are remediation steps if you get stuck: * `400` with `select a gateway`, or an application outcome of `denied` on every call including list\_employees. The gateway URL is missing its `/gateways/` segment, or the token is wrong. Re-read both values: ```bash theme={"system"} dome context current dome gateway list ``` * The page loads but every click fails with a network error. Confirm `npm run dev` is running and that `/api/chat` and `/api/tool` are reachable. The browser talks only to the Hono proxy. Dome is never called from the client. * Every call is denied, including `list_employees`. The rule bundle from the first tutorial is missing from this workspace: ```bash theme={"system"} dome rules show --agent hr-assistant ``` * The employee record still shows a real email address. The Filter is not assigned to the response direction on this connection: ```bash theme={"system"} dome tool guards filters list demo-hr ``` * Chat fails with a model / pool error, or tools work but Chat does not. Confirm the pool tutorial landed: `employee-summary` is attached to Default, and `hr-assistant` has the `llm:invoke` rule. ```bash theme={"system"} dome model pool get employee-summary dome rules simulate --agent hr-assistant --action llm:invoke \ --resource employee-summary --resource-type llm_pool ``` `.env` must set `DOME_POOL=employee-summary`. ## Verify the results The outcomes you saw are also on the record, attributed to the agent rather than to your process. ```bash theme={"system"} dome audit query --limit 20 ``` You should see, for this run: 1. `llm.model_call.attempted` / `llm.model_call.completed` for Chat turns through `employee-summary`. 2. `mcp.tool_call.attempted` and `mcp.tool_call.completed` for `list_employees` (and any tools the model invoked). 3. `mcp.tool_result.filtered` for `get_employee`, which is the Filter reporting what it changed. 4. `access.denied` for `get_salary`, carrying the reason your UI showed. To read only the rejection: ```bash theme={"system"} dome audit query --results denied --limit 10 ``` Both credentials resolve to one identity, so nothing here distinguishes this app from your editor. That is deliberate: the rules and the record follow the agent, not the process that holds the key. When you want them separated in audit, register a second agent rather than a second key. ## Next steps You learned how to run model and tool calls from one application through the same agent and Gateway. Keep the `sandbox-get-started` workspace for the next tutorial. Continue with: * [Govern per end user](/tutorials/get-started/govern-per-end-user) to authorize from the person, not only the agent * [Develop](/develop) to authenticate and call Gateways from your application * [Guards](/govern/guards) to inspect and filter request and response content # Call a model through a pool Source: https://docs.domesystems.ai/tutorials/get-started/call-a-model-through-a-pool Route chat through a model pool with failover, authorization, and your provider key held server-side. Tool calls are one half of what an agent does. The other half is inference, and it carries its own problems: a provider key sitting in an environment variable, spend nobody notices until the invoice, and no record of which agent asked for what. A [model connection](/connect/resources/models) is one upstream LLM endpoint and its provider credential, for example Claude with your Anthropic key. A [pool](/connect/resources/models/pools) groups one or more of those connections behind a single name. The agent puts the pool name in the request's `model` field. Dome chooses which connection serves the call. That split is what lets you swap providers, add failover, or change weights later without touching the app. Add a governed LLM pool to my Dome sandbox and restrict which agent may call it. Walk me through spend quotas if my plan includes them. Then hand me the curl that proves the pool works, and show routing by adding a second model and switching which member is primary. First, show me this plan and ask me to confirm before running anything: 1. Confirm the sandbox from the earlier tutorials is still active 2. Add a model connection with my provider API key 3. Create a pool, add the connection as a member, and attach the pool to the Default gateway 4. Explain spend quotas (Pro). Set a monthly pool cap if my plan allows it, or skip cleanly on Free 5. Deploy a Cedar rule permitting inference on that pool and nothing else 6. Form the LLM ingress URL on the Default gateway and export credentials 7. Call the pool once with curl against `/v1/chat/completions`, naming `employee-summary` 8. Add a second model (same provider or other provider), put it in the pool as failover, then switch priorities and curl again. Same pool name, different member in audit 9. Show me the audit trail, and the spend against the cap if a quota was set Follow the commands at [https://docs.domesystems.ai/agent/tutorials/get-started/call-a-model-through-a-pool.md](https://docs.domesystems.ai/agent/tutorials/get-started/call-a-model-through-a-pool.md) exactly. Non-negotiable rules: * Ask me for my provider API key before you need it, and tell me which provider you expect. Do not guess, and do not look for one in my shell history, environment, or files. For the second member, ask whether I want another model on the same key or a second provider. * Never print my provider API key or the `dome_...` agent token in chat. Pass the provider key to the CLI once, and write the agent token into a `.env` file that is gitignored. * Narrate as you go. Before each step, tell me in one or two sentences what you are about to do and why it matters. Do not silently run the whole flow. * Sandbox only. Run `dome context current` and confirm the workspace name starts with `sandbox-`. If it does not, stop and ask me. * Teach the pool as the thing the client names. The connection is the backend member. Do not skip the pool and have me call the connection directly. * Authorize on `resource.pool`, not on the connection name, so adding a second member later does not require a rule change. * Cost quotas are a Pro feature. On Free, explain what they do, show the command, and continue without one if `dome model quota set` fails with a plan limit. Do not treat a missing quota as a failed tutorial. * On Pro, set the spend cap low, at five dollars per month, so the guardrail is real and cheap to test. Do not raise the cap or widen the rule to make a call succeed. * Spend sparingly: one curl to prove the primary, one curl after the priority switch. Do not loop retries. * After each create step, give me a markdown link into the Dome console for that resource. Derive the base URL from `dome auth status` → `Server`. * Never report a step as done without showing the command output. When you form the LLM ingress and show the curl, be explicit about all three things that differ from calling the provider directly: the base URL (Gateway + `/v1/chat/completions`), the `Authorization` bearer (agent token, not the provider key), and the `model` field (pool name `employee-summary`). When you switch members, the curl must stay identical. Only pool membership changes. Then offer to run `dome audit query --limit 20`, and `dome model quota list` if a quota was set. In this tutorial, you will put an LLM behind the same Gateway your tools already sit behind. The provider key stays in Dome, the agent names a pool rather than a vendor model, every call is attributed, and on Pro spend can be capped before it reaches the provider. You will also put two different models in that pool and switch which one serves traffic without changing the curl. To do this, you will: Register a provider model and hold its key in Dome. Create a pool, add the connection, and attach the pool to the gateway. Learn how pool quotas work. Set one on Pro, or skip on Free. Deploy a rule that permits this pool and nothing else. Form the Gateway `/v1/chat/completions` URL and export credentials. POST once naming `employee-summary`. Add a second model, flip priority, and curl again with the same pool name. Check the audit trail, and the spend if you set a quota. ## Prerequisites For this tutorial, you will need: * [Govern your first agent](/tutorials/get-started/govern-your-first-agent) completed, with its sandbox still active. This tutorial reuses that workspace, its Default gateway, and the `hr-assistant` agent. * An API key for OpenAI or Anthropic. Dome calls the provider on your behalf, so the spend lands on your provider account. * A second model for the routing section: another model on that same key, or a key for the other provider. * An agent token (`dome_…`) for `hr-assistant`, the key from the first tutorial, or mint another with `dome agents create-key`. Optional: a **Pro** plan if you want to set a spend [quota](/govern/quotas) on the pool. Quotas are included on Pro. Free workspaces skip that step and continue with the rest of the tutorial. Confirm the workspace before you start: ```bash theme={"system"} dome context current ``` The workspace should read `sandbox-get-started`. Switch back if it does not: ```bash theme={"system"} dome context use sandbox-get-started ``` > This tutorial runs entirely in a sandbox. In a production workspace, adding a provider connection and setting spend caps are typically finance and operator actions, not developer ones. ## Add a model connection A model connection is a named route to one provider model, holding the credential Dome uses to reach it. Agents can call a connection by name, but this tutorial puts the connection in a pool instead. Naming the pool keeps the client's `model` field stable when you later add failover, change weights, or swap the upstream provider. ```bash theme={"system"} dome model add openai-mini \ --provider openai \ --model gpt-4o-mini \ --api-key "$OPENAI_API_KEY" ``` ```bash theme={"system"} dome model add claude-sonnet \ --provider anthropic \ --model claude-3-5-sonnet-20241022 \ --api-key "$ANTHROPIC_API_KEY" ``` The rest of this tutorial uses `openai-mini`. Substitute your connection name if you chose Anthropic. `--api-key` stores the credential in Dome's secret store, which is why nothing downstream needs a copy. For providers with their own identity story, [`--use-workload-identity`](/connect/resources/models#credentials) skips the stored credential and uses the gateway's cloud-native chain instead, such as GCE metadata for Google or the AWS SDK chain for Bedrock. That avoids holding a long-lived key at all. Leave the connection off the Gateway for now. A connection that is not a pool member and not attached to a gateway is registered but unreachable. You store the credential first, then expose the model only through the pool in the next step, so the agent never learns a direct connection name to call around the pool. Confirm the connection is registered and the credential is stored: ```bash theme={"system"} dome model get openai-mini ``` ## Put it in a pool A pool is the name your client will put in the `model` field. Dome picks which member serves each request, and can try another member when the first fails. Create `employee-summary` and attach it to Default: ```bash theme={"system"} dome model pool create employee-summary \ --description "HR assistant employee summarization" \ --routing-strategy priority_weighted \ --failover-max all \ --gateway Default ``` | Setting | What it means here | | -------------------- | ------------------------------------------------------------------------------------------------------ | | `priority_weighted` | Prefer members by priority tier, then by weight within a tier. With one member, every call goes there. | | `--failover-max all` | Walk every eligible member before giving up. | Then add the connection as its only member: ```bash theme={"system"} dome model pool member add employee-summary openai-mini \ --priority 0 \ --weight 1 ``` `--priority 0` marks this member as primary. Dome tries a `--priority 1` member only if the primary fails. Even with one member, the pool still matters. The client and the rule both name `employee-summary`. After the first curl succeeds, you will add a second model and switch which member serves without changing either. Confirm the pool and its membership: ```bash theme={"system"} dome model pool get employee-summary dome model pool member list employee-summary ``` ## Cap the spend A [quota](/govern/quotas) is a dollar cap on a subject over a window. Enforcement happens before the provider is called, so an exhausted cap costs nothing. That is how you stop a runaway session from becoming an invoice surprise, and why quotas belong next to pools even when you are not setting one yet. Cost quotas are included on the **Pro** plan. Free workspaces cannot create them (`limit-max-cost-quotas` is zero). On Free, read this section and continue to [Authorize the pool](#authorize-the-pool). The rest of the tutorial does not depend on a quota. On Pro, set five dollars per month on the pool so every member shares one budget: ```bash theme={"system"} dome model quota set \ --subject pool \ --pool employee-summary \ --limit 5 \ --window monthly \ --name "sandbox cap" ``` Confirm it, along with what has been spent so far: ```bash theme={"system"} dome model quota list ``` The subject is what makes a quota useful. `--subject pool` caps everything flowing through this pool no matter which member served the call. `--subject model` caps one connection across every pool it belongs to. `--subject agent` caps one agent across every model. `--subject act-as` caps an individual end user, which is what you reach for when one person's runaway session should not exhaust the team's budget. Windows are `daily` or `monthly`, and a quota can be created with `--disabled` if you want it in place before you start enforcing it. ## Authorize the pool The Gateway attachment makes `employee-summary` reachable. It does not decide who may call it. Tool rules from earlier tutorials cover `mcp:call`, not inference. Inference is a separate action, `llm:invoke`, on `Dome::LLMModel`. Allow `hr-assistant` to call models in the `employee-summary` pool, and deny every other inference call. Scope the rule to the pool name rather than a connection like `openai-mini`, so a failover member you add later inherits the same permit. Create `hr-assistant-llm.cedar`: ```cedar title="hr-assistant-llm.cedar" theme={"system"} permit( principal is Dome::Agent, action == Dome::Action::"llm:invoke", resource is Dome::LLMModel ) when { resource.pool == "employee-summary" }; forbid( principal is Dome::Agent, action == Dome::Action::"llm:invoke", resource ) unless { resource has pool && resource.pool == "employee-summary" }; ``` `resource.pool` is how Cedar sees membership. Authorizing on `Dome::LLMModel::"openai-mini"` would lock the rule to one connection and miss any member you add later. A connection-scoped rule permits only that one model. Adding a failover member later would still be denied until you rewrite the bundle: ```cedar theme={"system"} permit( principal is Dome::Agent, action == Dome::Action::"llm:invoke", resource == Dome::LLMModel::"openai-mini" ); forbid( principal is Dome::Agent, action == Dome::Action::"llm:invoke", resource ) unless { resource == Dome::LLMModel::"openai-mini" }; ``` Deploy it as its own agent-scoped bundle: ```bash theme={"system"} dome rules apply hr-assistant-llm.cedar --agent hr-assistant --name hr-assistant-llm ``` Check the decision before you write any code. Simulation runs the same evaluator as the gateway, with no call and no spend: ```bash theme={"system"} dome rules simulate --agent hr-assistant --action llm:invoke \ --resource employee-summary --resource-type llm_pool ``` Expect `ALLOW`. The `llm_pool` resource type is how simulation addresses a pool the way a live call does: it stamps `resource.pool` so the permit above matches. Substituting a pool name you never created should return `DENY`. Dome rewrites pool-name literals to pool UUIDs when the bundle deploys, so the rule you write with `"employee-summary"` is what the gateway evaluates against the unspoofable pool id. You author names. The platform stores ids. ## Point at the LLM ingress Point curl at the Default gateway's [LLM ingress](/concepts/gateways/llm-gateway) and authenticate as the agent. That path is where OpenAI- and Anthropic-shaped model traffic enters the data plane: ``` https:///gateways//v1/chat/completions ``` The first tutorial used the same Gateway's tools ingress at `/mcp`. Model traffic shares the `/gateways/` prefix and uses `/v1/...` instead. Retrieve the three placeholders: * `AGENT_API_KEY`. The `Token: dome_…` value from [Govern your first agent](/tutorials/get-started/govern-your-first-agent). Tokens are shown once and cannot be recovered. If you did not save it, mint another: ```bash theme={"system"} dome agents create-key hr-assistant --name pool-curl ``` * `GATEWAY_HOST`. Prepend `https://` to your current Dome host, for example `https://gateway.dev.domesystems.ai`. ```bash theme={"system"} dome context current ``` * `DEFAULT_GATEWAY_ID`. The UUID of the Default gateway. ```bash theme={"system"} dome gateway list ``` The result should look similar to `https://gateway.dev.domesystems.ai/gateways/3f9a2c14-8d7e-4b1a-9c02-5e6f7a8b9c01/v1/chat/completions`. Export once. `DOME_GATEWAY_URL` stops at the Gateway. curl appends `/v1/chat/completions`: ```bash theme={"system"} export DOME_GATEWAY_URL="https://GATEWAY_HOST/gateways/DEFAULT_GATEWAY_ID" export DOME_TOKEN="AGENT_API_KEY" ``` ## Call the pool The Gateway speaks the same OpenAI chat-completions protocol. Compared with calling the provider directly, three fields change: | Field | Value | | ------------- | ---------------------------------------------------------------------- | | URL | `$DOME_GATEWAY_URL/v1/chat/completions` | | Authorization | Agent bearer token (`dome_…`), not the provider key | | `model` | `employee-summary` — the pool name, not `gpt-4o-mini` or `openai-mini` | Dome picks which member serves the call. The client never names the upstream model or connection. Call the pool once: ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/v1/chat/completions" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "model": "employee-summary", "messages": [ { "role": "user", "content": "Summarize this employee in one sentence: E001 Alice Johnson, Senior Engineer in Engineering." } ] }' ``` Expect a normal chat-completions JSON body. The assistant text is a one-line employee summary. The important proof is that the call succeeded while naming `employee-summary`, not `gpt-4o-mini` or `openai-mini`. Leave the curl exports in your shell. The next section reuses them after you change pool membership. Provider SDKs work the same way later: OpenAI clients use base URL `…/gateways//v1` and `model: "employee-summary"`. Anthropic clients omit the trailing `/v1` on the base URL because their SDK appends `/v1/messages`. This tutorial sticks to curl so the wire shape is visible without an SDK. These are remediation steps if you get stuck: * `400` with `select a gateway`. The URL is missing its `/gateways/` segment. A bare gateway host serves no inference. * `403` with `model not available in this gateway`. The pool exists but is not attached to Default, or it has no members. Confirm both: ```bash theme={"system"} dome gateway get Default dome model pool member list employee-summary ``` * `403` with a `permission_error` naming a rule. Cedar denied the call. Reproduce it without spending anything: ```bash theme={"system"} dome rules simulate --agent hr-assistant --action llm:invoke \ --resource employee-summary --resource-type llm_pool ``` * `404` with `no connection for requested model`. You passed a provider model id such as `gpt-4o-mini`, or the connection name `openai-mini`, where Dome expects the pool name. Check what this workspace exposes: ```bash theme={"system"} dome model pool list ``` * `429` with `llm: quota exceeded`. The cap is working. The message names the subject that ran out. Review it, and raise it deliberately rather than by reflex: ```bash theme={"system"} dome model quota list ``` If you never set a quota (Free plan), this error will not appear from Dome. Check your provider account instead. * `401` from the gateway. The bearer token is the provider's key, not Dome's. This endpoint authenticates the agent, and the provider credential never leaves the platform. ## Route across two members A single pool name can front more than one upstream. Add a second member for failover when the primary is down, shift traffic with weights, or promote a cheaper or stronger model without rewriting curl or Cedar. Clients and rules keep saying `employee-summary`. Membership decides who answers. In this section you add a failover member, then flip priorities so the new connection becomes primary. Failover is already set: `priority_weighted` with `--failover-max all` tries a `--priority 1` member only if the primary fails. ### Add the second connection A pool can mix providers. The same `employee-summary` name can hold OpenAI and Anthropic members side by side. Pick same provider or other provider below. Reuse the key you already stored. Match the tab to your primary connection: If your primary was `openai-mini`, add a stronger OpenAI model: ```bash theme={"system"} dome model add openai-full \ --provider openai \ --model gpt-4o \ --api-key "$OPENAI_API_KEY" dome model pool member add employee-summary openai-full \ --priority 1 \ --weight 1 ``` If your primary was `claude-sonnet`, add a lighter Anthropic model: ```bash theme={"system"} dome model add claude-haiku \ --provider anthropic \ --model claude-3-5-haiku-20241022 \ --api-key "$ANTHROPIC_API_KEY" dome model pool member add employee-summary claude-haiku \ --priority 1 \ --weight 1 ``` Put a second vendor behind the same pool name. If your primary was OpenAI: ```bash theme={"system"} dome model add claude-sonnet \ --provider anthropic \ --model claude-3-5-sonnet-20241022 \ --api-key "$ANTHROPIC_API_KEY" dome model pool member add employee-summary claude-sonnet \ --priority 1 \ --weight 1 ``` If your primary was Anthropic, add `openai-mini` the same way and attach it at `--priority 1`. Confirm both members: ```bash theme={"system"} dome model pool member list employee-summary ``` You should see priority `0` (primary) and priority `1` (failover). Healthy calls still hit priority `0`. The second member is not a second name for the app to learn. curl and Cedar keep saying `employee-summary`. ### Switch the primary Flip priorities so the second member becomes primary. The curl keeps the same `model` value, but Dome routes it to the new primary. Substitute the connection names you actually added: ```bash theme={"system"} # Example: openai-mini was primary, openai-full was failover dome model pool member update employee-summary openai-mini \ --priority 1 \ --weight 1 dome model pool member update employee-summary openai-full \ --priority 0 \ --weight 1 dome model pool member list employee-summary ``` Call again with the **same** request, still `model: "employee-summary"`: ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/v1/chat/completions" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "model": "employee-summary", "messages": [ { "role": "user", "content": "Summarize this employee in one sentence: E001 Alice Johnson, Senior Engineer in Engineering." } ] }' ``` You now have one pool name and two backends. Membership and priority decide which serves. Keep the preferred model at priority `0` and the alternate at priority `1`. With `--failover-max all`, Dome tries the backup when the primary fails. Flip priorities back if you want the original primary for [Build a governed app](/tutorials/get-started/build-a-governed-app). The application only names `employee-summary`. It does not care which member is on top. ## Verify the results Confirm the calls went through the pool and landed on `hr-assistant`, not a shared provider key. On Pro, also confirm they counted against your spend cap. Read the record: ```bash theme={"system"} dome audit query --limit 20 ``` You should see `llm.model_call.attempted` and `llm.model_call.completed` for `hr-assistant`, carrying token usage and the **member that served each call**. The first curl and the post-switch curl should name different members if you flipped priority. Denials appear as `access.denied`, the same event type a rejected tool call produces, because both are authorization decisions. If you set a quota on Pro, check the spend. The used figure moves after each call: ```bash theme={"system"} dome model quota list ``` To confirm the cap enforces rather than merely reports, lower it below what you have already spent and call again: ```bash theme={"system"} dome model quota update --limit 0.01 ``` The next request returns `429` and never reaches the provider. Restore the cap afterward with another `update`, or remove it: ```bash theme={"system"} dome model quota rm ``` Skip the quota commands on Free. There is nothing to list or update. The next tutorial wires the same pool into an application where the model and tool calls run together. ## Next steps You learned how to hold a provider key in a model connection, route through a pool with failover, authorize `llm:invoke` on the pool, and attribute spend. Keep the `sandbox-get-started` workspace for the rest of this track. Continue with: * [Build a governed app](/tutorials/get-started/build-a-governed-app) to wire model and tool calls in one application * [Pools](/connect/resources/models/pools) for routing strategies and membership * [Set Usage Limits](/govern/quotas) for spend caps on pools, models, and agents # Govern per end user Source: https://docs.domesystems.ai/tutorials/get-started/govern-per-end-user The agent inherits each user's permissions — same agent, different tool access and answers per person. A shared agent has a shared problem. If the HR application can read every employee record, then everyone who talks to it can, whatever their own access happens to be. Copying your permission model into the agent's prompt is not a fix. The model can be talked out of it. With [standing identity](/concepts/identity-patterns), the agent acts as itself — every caller gets the same Cedar answer from the agent's grants. With [delegated identity](/concepts/identity-patterns), each call carries a verified person, and Cedar authorizes on that person instead. Set up a Dome agent with **delegated identity** (`delegated-hr`) so each end user's permissions apply, then show me the same application returning different answers for Eva Martinez (HR), Alice Johnson (self), and Carol Williams (peer). Contrast this with standing identity on `hr-assistant` from earlier tutorials. Use the Vite + Hono reference app at [https://github.com/dome-systems/demo-hr-desk](https://github.com/dome-systems/demo-hr-desk). First, show me this plan and ask me to confirm before running anything: 1. Confirm the sandbox from earlier Get Started tutorials is still active 2. Explain standing (`hr-assistant`) vs delegated (`delegated-hr`), then register `delegated-hr` with act-as required 3. Deploy Cedar: directory open to everyone, get\_employee for HR or self (act\_as.sub == employee\_id), get\_salary HR-only, plus the pool llm rule for Chat 4. Simulate Eva, Alice (self on E001), and Carol (denied on E001) before any live call 5. Point the application at delegated-hr: checkout tutorial/govern-per-end-user, put token + DOME\_ACTAS\_SECRET in server .env, run it, and explain how the application signs X-Dome-Act-As 6. Walk me through Act as Eva / Alice / Carol on Tools (Who is E001? and salary). Same buttons, different outcomes 7. Show me the decisions in the audit trail, each naming the end user Follow the commands at [https://docs.domesystems.ai/agent/tutorials/get-started/govern-per-end-user.md](https://docs.domesystems.ai/agent/tutorials/get-started/govern-per-end-user.md) exactly. Non-negotiable rules: * Narrate as you go. Before each step, tell me in one or two sentences what you are about to do and why it matters. Do not silently run the whole flow. * Sandbox only. Run `dome context current` and confirm the workspace name starts with `sandbox-`. If it does not, stop and ask me. * Never print the `dome_...` agent token or the act-as signing secret in chat. Write both into `.env` and confirm it is gitignored. * The browser must never call Dome. Only the Hono backend holds `DOME_TOKEN` and `DOME_ACTAS_SECRET` and sets `X-Dome-Act-As`. Do not put either secret in client code, Vite env, or the browser. * Do not put users' names or groups in a prompt, a system message, or an if-statement that decides allow/deny in my code. The whole point is that Cedar decides, not the application. * Do not widen the rules to make Carol's denied call succeed. The difference between personas is the result I am asking for. * Use `dome rules simulate` with act-as flags and `--eval-arguments` to check identities before running any live call. * After each create step, give me a markdown link into the Dome console for that resource. Derive the base URL from `dome auth status` → `Server`. * Never report a step as done without showing the command output. Show me clearly that the runs differ only in the signed identity passed to Dome, not in the code path taken. Then offer to run `dome audit query --limit 20` so I can see each decision attributed to both the agent and the person. In this tutorial, you will switch from standing `hr-assistant` to a delegated agent, `delegated-hr`. Eva (HR), Alice (self), and Carol (peer) click the same buttons and get different answers. The app has no logic that knows the difference. To do this, you will: Configure act-as verification on `delegated-hr` and make it mandatory. Leave `hr-assistant` standing. Deploy Cedar that reads groups and compares `act_as.sub` to tool arguments. Simulate Eva, Alice, and Carol before making a live call. Keep the token and act-as secret server-side. Sign per request. See different answers from one UI, and each in audit. ## Background Earlier tutorials used **`hr-assistant`** standing. This one registers a second agent, **`delegated-hr`**, that requires act-as. Cedar reads `principal.act_as`, so Eva, Alice, and Carol can get different answers for the same tool. | | `hr-assistant` (standing) | `delegated-hr` (delegated) | | -------------------------- | ------------------------------ | ---------------------------------------------------------- | | **Who authorizes** | The agent's own grants | The verified human on `principal.act_as` | | **Credential** | Bearer token only | Bearer token + required `X-Dome-Act-As` | | **Same tool, same record** | Identical outcome for everyone | Eva allow · Alice self · Carol deny on `get_employee` E001 | | **Anonymous call** | Allowed under standing grants | Rejected (`--actas-required`) | You register a **second** agent rather than flipping `hr-assistant` to require act-as. Those settings would break earlier tutorials that call with no person attached. `delegated-hr` is the application agent that borrows each end user's identity. [Act-as](/concepts/agents/identity#act-as-identity) carries that person on each call. The boundary is enforced at the gateway, not in the model's prompt. That only works if **your backend** talks to Dome. Keep the token and signing secret on the server; the browser only says who is using the app. A frontend that called Dome directly could forge or omit act-as. ## Prerequisites For this tutorial, you will need: * [Govern your first agent](/tutorials/get-started/govern-your-first-agent) completed, with its sandbox still active. This tutorial reuses that workspace, its `demo-hr` connection, and its Default gateway. * [Call a model through a pool](/tutorials/get-started/call-a-model-through-a-pool) completed if you want **Chat** under `delegated-hr` (the `employee-summary` pool). * [Build a governed app](/tutorials/get-started/build-a-governed-app) completed, or the [demo-hr-desk](https://github.com/dome-systems/demo-hr-desk) repo cloned as below. * Node.js 18 or later. Confirm the workspace before you start: ```bash theme={"system"} dome context current ``` The workspace should read `sandbox-get-started`. Switch back if it does not: ```bash theme={"system"} dome context use sandbox-get-started ``` Clone the reference app if you do not already have it from the previous tutorial: ```bash theme={"system"} git clone https://github.com/dome-systems/demo-hr-desk.git cd demo-hr-desk npm install cp .env.example .env ``` If you already cloned it for [Build a governed app](/tutorials/get-started/build-a-governed-app), just `cd` into that directory. You will check out this tutorial's branch when you wire act-as. > This tutorial runs entirely in a sandbox. In a production workspace, the identity provider is wired once by an operator, and the rules that read its claims are owned by security. ## Register a delegated agent Register **`delegated-hr`** rather than reusing **`hr-assistant`**. Standing vs delegated is chosen [per agent](/concepts/identity-patterns). `hr-assistant` stays the standing editor/curl agent from earlier tutorials. `delegated-hr` is the purpose-built application agent: it authenticates as itself, but every governed call must present a verified end user, and Cedar authorizes on that person. This tutorial uses **HMAC** — a shared secret your backend uses to sign act-as. That is fine for a sandbox. In production, use **OIDC** instead: register with `--actas-method oidc` and forward a JWT from your identity provider so Dome verifies the person against the IdP, not a secret your service holds. Generate the signing secret first so you can save it: ```bash theme={"system"} export ACTAS_SECRET=$(openssl rand -hex 32) echo "$ACTAS_SECRET" ``` ```bash theme={"system"} dome agents register --name delegated-hr \ --actas-method hmac \ --actas-hmac-secret "$ACTAS_SECRET" \ --actas-required \ --if-not-exists ``` Two flags carry the meaning: * `--actas-method hmac` tells the gateway how to verify the identity your service presents. Shared-secret HMAC is the sandbox path here; production uses `oidc`. * `--actas-required` rejects any call arriving without an identity. Without it, act-as is accepted when present and ignored when absent, and a caller who omits it gets the agent's own permissions. Mint a credential for the application: ```bash theme={"system"} dome agents create-key delegated-hr --name delegated-hr-service ``` `--actas-allowed-group`, `--actas-allowed-email`, and `--actas-allowed-subject` add an admission-level allowlist, rejecting identities outside it before Cedar runs. That is a coarse gate for narrowing which population an agent may serve. Leave it unset here so Cedar makes every decision. ## Write rules about the person Cedar sees the end user as `principal.act_as`, a record carrying `sub`, `email`, `roles`, `groups`, and any custom `claims` the verified identity provided. Rules can read those the same way they read anything else. This policy does four things: * Anyone with a verified identity can list employees and view the org chart * Someone in the `hr` group can open any employee record and read salary * Anyone else can open only their own record (`act_as.sub` matches `employee_id`) * Calls with no act-as identity, and tools outside that set, are denied So Eva (HR) gets Alice's record and salary; Alice gets her own record only; Carol gets neither for Alice. The reference repo ships `delegated-hr.cedar`. Create it (or copy from the clone): ```cedar title="delegated-hr.cedar" theme={"system"} permit( principal is Dome::Agent, action == Dome::Action::"mcp:discover", resource ); permit( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource ) when { resource in [ Dome::MCPTool::"demo-hr/hr/list_employees", Dome::MCPTool::"demo-hr/hr/org_chart" ] }; // HR can open any employee; others only their own record (act_as.sub == employee_id). permit( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"demo-hr/hr/get_employee" ) when { principal has act_as && ( principal.act_as.groups.contains("hr") || ( resource has arguments && resource.arguments has employee_id && resource.arguments.employee_id == principal.act_as.sub ) ) }; // Payroll is HR-only. permit( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"demo-hr/finance/get_salary" ) when { principal has act_as && principal.act_as.groups.contains("hr") }; // Overrides the auto-generated agent_spec permit that opens every Default gateway tool. forbid( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"demo-hr/hr/get_employee" ) unless { principal has act_as && ( principal.act_as.groups.contains("hr") || ( resource has arguments && resource.arguments has employee_id && resource.arguments.employee_id == principal.act_as.sub ) ) }; forbid( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"demo-hr/finance/get_salary" ) unless { principal has act_as && principal.act_as.groups.contains("hr") }; forbid( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource ) unless { resource in [ Dome::MCPTool::"demo-hr/hr/list_employees", Dome::MCPTool::"demo-hr/hr/org_chart", Dome::MCPTool::"demo-hr/hr/get_employee", Dome::MCPTool::"demo-hr/finance/get_salary" ] }; ``` The `principal has act_as` guard matters. `act_as` is absent from the entity entirely when no identity was verified, and reading an absent attribute is an evaluation error rather than a false. Guard it, and an anonymous call falls through to a clean deny. Self-lookup compares `resource.arguments.employee_id` to `principal.act_as.sub`. Demo personas set `sub` to the employee id (E005 Eva, E001 Alice, E003 Carol), so Alice can open her own record while Carol cannot open Alice's. Payroll (`get_salary`) stays HR-only. If you completed [Call a model through a pool](/tutorials/get-started/call-a-model-through-a-pool), Chat still needs `llm:invoke` on `employee-summary`. Tool rules do not cover inference. Create `delegated-hr-llm.cedar` (same shape as `hr-assistant-llm.cedar`, scoped to this agent): ```cedar title="delegated-hr-llm.cedar" theme={"system"} permit( principal is Dome::Agent, action == Dome::Action::"llm:invoke", resource is Dome::LLMModel ) when { resource.pool == "employee-summary" }; forbid( principal is Dome::Agent, action == Dome::Action::"llm:invoke", resource ) unless { resource has pool && resource.pool == "employee-summary" }; ``` Deploy both files scoped to the new agent, grant gateway access, and (for Chat) permit the pool: ```bash theme={"system"} dome rules apply delegated-hr.cedar delegated-hr-llm.cedar --agent delegated-hr --name delegated-hr dome gateway access grant Default delegated-hr ``` Apply both Cedar files in one deploy so the tool rules and the pool rule stay active together. A second `dome rules apply` at the same agent scope replaces the previous bundle rather than layering beside it. ## Check the identities Simulation accepts an end-user identity, so you can test a rule that reads `act_as` without a signing secret, a service, or a live call. Pass `--eval-arguments` whenever the rule compares `employee_id`. Eva (HR) looking up Alice: ```bash theme={"system"} dome rules simulate --agent delegated-hr --action mcp:call \ --resource demo-hr/hr/get_employee --resource-type mcp_tool \ --eval-arguments '{"employee_id":"E001"}' \ --actas-sub E005 --actas-email eva@example.com --actas-groups hr ``` Expect `ALLOW`. Alice looking up herself (`sub` E001): ```bash theme={"system"} dome rules simulate --agent delegated-hr --action mcp:call \ --resource demo-hr/hr/get_employee --resource-type mcp_tool \ --eval-arguments '{"employee_id":"E001"}' \ --actas-sub E001 --actas-email alice@example.com --actas-groups engineering ``` Expect `ALLOW`. Carol looking up Alice: ```bash theme={"system"} dome rules simulate --agent delegated-hr --action mcp:call \ --resource demo-hr/hr/get_employee --resource-type mcp_tool \ --eval-arguments '{"employee_id":"E001"}' \ --actas-sub E003 --actas-email carol@example.com --actas-groups engineering ``` Expect `DENY`. Confirm salary is HR-only: ```bash theme={"system"} dome rules simulate --agent delegated-hr --action mcp:call \ --resource demo-hr/finance/get_salary --resource-type mcp_tool \ --eval-arguments '{"employee_id":"E001"}' \ --actas-sub E005 --actas-email eva@example.com --actas-groups hr dome rules simulate --agent delegated-hr --action mcp:call \ --resource demo-hr/finance/get_salary --resource-type mcp_tool \ --eval-arguments '{"employee_id":"E001"}' \ --actas-sub E001 --actas-email alice@example.com --actas-groups engineering ``` Expect `ALLOW` then `DENY`. Confirm that the directory stays open to everyone by swapping the resource for `demo-hr/hr/list_employees`, and that omitting the act-as flags entirely denies the employee lookup rather than erroring. This is the loop worth keeping. A rule that reads identity claims has more branches than a rule that reads a tool name, and simulation is where you find the branch you forgot. ## How this application implements it Act-as is a **server-side** concern. The gateway authenticates the agent with `Authorization: Bearer dome_…` and the end user with `X-Dome-Act-As`. Both headers are secrets your browser must never hold. | Layer | Holds | Talks to Dome? | | ------------------------ | ------------------------------------------- | ------------------------------------------------------------- | | Frontend (`src/App.tsx`) | Persona id only (`eva` / `alice` / `carol`) | No. Only `POST /api/chat` and `POST /api/tool` on your origin | | Backend (Hono proxy) | `DOME_TOKEN`, `DOME_ACTAS_SECRET` | Yes. Signs act-as and calls `/mcp` and `/v1/chat/completions` | Same frontend/backend split as [Build a governed app](/tutorials/get-started/build-a-governed-app#how-this-application-implements-it). With act-as the stakes are higher: if the browser held the agent token or signing secret, anyone with DevTools could impersonate anyone. Check out this tutorial's branch, which adds the Act as picker and HMAC signing, then point the **server** at `delegated-hr`: ```bash theme={"system"} cd demo-hr-desk git fetch origin git checkout tutorial/govern-per-end-user ``` Update `.env` with the **delegated-hr** token and the secret you generated above. These values are read only by the Node process, never shipped to Vite or the browser: ```bash title=".env" theme={"system"} DOME_TOKEN=dome_... DOME_GATEWAY_URL=https://GATEWAY_HOST/gateways/DEFAULT_GATEWAY_ID DOME_POOL=employee-summary DOME_ACTAS_SECRET=... ``` ```bash theme={"system"} dome context current dome gateway list ``` Do not prefix these with `VITE_`. Do not put `DOME_TOKEN` or `DOME_ACTAS_SECRET` in client bundles, public env, or browser storage. The frontend must not import them and must not `fetch` the Gateway URL. ### How the application signs The browser only sends a persona id (`eva` | `alice` | `carol`). The Hono proxy maps it to claims, signs with the server secret, and sets `X-Dome-Act-As` on the outbound Dome request. Cedar never sees a branch in `App.tsx`. Personas are fixed claims for the demo. There is still no allow/deny logic in the UI: ```ts title="server/actas.ts" theme={"system"} export const PERSONAS = { eva: { id: "eva" as const, label: "Eva Martinez", detail: "HR Director", sub: "E005", email: "eva@example.com", groups: ["hr"], }, alice: { id: "alice" as const, label: "Alice Johnson", detail: "Senior Engineer", sub: "E001", email: "alice@example.com", groups: ["engineering"], }, carol: { id: "carol" as const, label: "Carol Williams", detail: "Staff Engineer", sub: "E003", email: "carol@example.com", groups: ["engineering"], }, } as const; ``` `signHMACActAs` builds the envelope the gateway verifies: identity fields + `_ts`, HMAC-SHA256 over canonical JSON `|` timestamp, then standard base64 of the signed payload. That string is the `X-Dome-Act-As` header value. ```ts title="server/actas.ts" theme={"system"} export function signHMACActAs( secret: string, identity: ActAsIdentity, ts = Math.floor(Date.now() / 1000), ): string { if (!identity.sub && !identity.email) { throw new Error("act-as identity requires sub or email"); } const payload: Record = { _ts: ts }; if (identity.sub) payload.sub = identity.sub; if (identity.email) payload.email = identity.email; if (identity.groups?.length) payload.groups = identity.groups; // roles / claims omitted when empty const canonical = `${canonicalJson(payload)}|${ts}`; const digest = createHmac("sha256", secret).update(canonical).digest(); payload._signature = digest.toString("base64url"); return Buffer.from(canonicalJson(payload)).toString("base64"); } export function actAsHeaderValue(identity: ActAsIdentity): string | undefined { const secret = process.env.DOME_ACTAS_SECRET?.trim(); if (!secret) return undefined; return signHMACActAs(secret, identity); } ``` Tool and chat routes attach the **full** signed header on the real gateway request. `redactActAs` is only for the **View gateway call** sheet. It truncates the header in the UI trace so the signed blob is not dumped into the browser transcript. Dome still receives the unredacted value. ```ts title="server/dome.ts" theme={"system"} if (actAs) { const signed = actAsHeaderValue(actAs); if (signed) { headers["X-Dome-Act-As"] = signed; // sent to Dome displayHeaders["X-Dome-Act-As"] = redactActAs(signed); // shown in the sheet } } ``` ```ts title="server/types.ts" theme={"system"} export function redactActAs(header: string) { if (header.length <= 16) return ""; return `${header.slice(0, 12)}…(${header.length} chars)`; } ``` Start the app: ```bash theme={"system"} npm run dev ``` Open [http://localhost:5173](http://localhost:5173). The **Act as** bar appears when `DOME_ACTAS_SECRET` is set. ### Click through Eva, Alice, and Carol Stay on **Tools** for the clearest contrast: 1. Select **Eva Martinez** (HR · `hr` · E005). 2. Click **List employees**. Allowed. 3. Click **Who is E001?**. Allowed. Email arrives as `[REDACTED]` (Filter still applies). 4. Click **What is Alice's salary?**. Allowed (HR-only). 5. Select **Alice Johnson** (Engineering · E001). 6. Click **Who is E001?**. Allowed (self: `sub` matches `employee_id`). Email still redacted. 7. Click **What is Alice's salary?**. Denied. 8. Select **Carol Williams** (Engineering · E003). 9. Click **Who is E001?**. Denied. Open **View gateway call** and confirm `X-Dome-Act-As` on the request. The response carries the Cedar reason. If you want, try the same walk on **Chat** — ask **Who is E001 and how do I reach them?** as Eva, then Alice, then Carol. Same prompt, different outcomes. Model and tool steps both carry the signed identity. | Persona | List employees | Who is E001? | Alice's salary | | ----------------- | -------------- | ------------------------- | -------------- | | Eva (`hr` · E005) | Allowed | Allowed · redacted | Allowed | | Alice (E001) | Allowed | Allowed · self · redacted | Denied | | Carol (E003) | Allowed | Denied | Denied | One UI, one agent, one credential, three answers. Alice can still see herself. Carol cannot open Alice's record. Only Eva reaches payroll. For a real identity provider, configure the agent with `--actas-method oidc` and forward the JWT your application already validated as `X-Dome-Act-As`. The gateway verifies the signature against the provider's discovery document, so your service never becomes the authority on who the caller is. HMAC is the sandbox path. Refer to [Use OIDC or bound act-as](/tutorials/production/use-oidc-or-bound-act-as) (coming soon) for the production methods. These are remediation steps if you get stuck: * The Act as bar does not appear. `DOME_ACTAS_SECRET` is missing or empty. Restart `npm run dev` after writing `.env`. Confirm `/api/config` returns `"actAsEnabled": true`. * `400` with `act-as header required`. The agent is registered with `--actas-required` and the call carried no identity. Confirm the app is sending a persona and that the secret is set so the proxy signs. * `403` with `act-as verification failed`. The signature did not verify. Usually the secret in `.env` is not the one the agent holds. Rotate both to a known value: ```bash theme={"system"} dome agents update delegated-hr --actas-hmac-secret "$NEW_SECRET" ``` * `403` with `agent act-as method does not meet workspace policy`. The workspace requires a stronger verification method than this agent uses, typically `oidc` rather than `hmac`. Check the policy before weakening anything: ```bash theme={"system"} dome agents get delegated-hr ``` * Both users get the same answer. The rule is not reading the claim you think it is, or you are still on the `hr-assistant` token. Simulate both identities again and confirm `.env` uses the `delegated-hr` key: ```bash theme={"system"} dome rules simulate --agent delegated-hr --action mcp:call \ --resource demo-hr/hr/get_employee --resource-type mcp_tool \ --actas-sub alice --actas-groups engineering ``` * A `VITE_` env var or browser network tab shows calls to the Gateway / holds `dome_…`. Wrong shape. Move the token and act-as secret back to server `.env` and call Dome only from the Hono proxy. The frontend may only hit `/api/*` on your origin. * Chat fails with a pool / llm error while Tools work. Apply the pool rule to this agent: ```bash theme={"system"} dome rules apply delegated-hr.cedar delegated-hr-llm.cedar --agent delegated-hr --name delegated-hr ``` * The employee record arrives with a masked email for Eva or Alice. That is the response Filter from the first tutorial, still doing its job on this connection. Filters apply to the tool, not to the agent, so a second agent inherits them. ## Verify the results Confirm both decisions are on the record: ```bash theme={"system"} dome audit query --limit 20 ``` Each event carries the agent and the verified end user, so a denial is attributable to a person rather than to a shared service account. That is the audit property act-as buys you, and it is the one that matters during an investigation. To read only the rejection: ```bash theme={"system"} dome audit query --results denied --limit 10 ``` A rejected identity is recorded separately, as `authorization.act_as.rejected`, because a failed signature is a different problem from a denied call. The first means your service or its secret is misconfigured. The second means the rules did their job. Quotas take end users as subjects too. `dome model quota set --subject act-as --act-as E005 --limit 20` caps one person's inference spend, which is how you stop a single runaway session from consuming a team's budget. ## Clean up This is the last tutorial in the Get Started track. Delete the sandbox to remove the agents, rules, and grants together: ```bash theme={"system"} dome workspace delete sandbox-get-started ``` To keep the workspace and retire only this agent: ```bash theme={"system"} dome agents revoke-key delegated-hr delegated-hr-service ``` Rotate the act-as secret anywhere you copied it. It signs identity assertions, so treat it like the credential it is. Clear `DOME_ACTAS_SECRET` (or switch back to the `hr-assistant` token) if you return to earlier tutorials. ## Next steps You learned how to require verified end-user identity, authorize from the person, and attribute decisions to both agent and user. Continue with: * [Delegated agents](/connect/agents/delegated) to configure verification providers and Act-As * [Simulate Rules](/govern/rules/simulate) to probe Act-As decisions before deploy # Govern your first agent Source: https://docs.domesystems.ai/tutorials/get-started/govern-your-first-agent Allow employee lookups, redact contact details, and deny payroll — from Cursor, Claude, or curl. Dome is the governance layer for agentic software. It gives every agent an identity, enforces what each agent can call, redacts sensitive fields, and records every decision. Set up a governed HR assistant with Dome in a throwaway sandbox, then hand me the questions that prove allow, redact, and deny. First, show me this plan and ask me to confirm before running anything: 1. Install the Dome CLI and sign in (opens a browser), then sync contexts 2. Provision a throwaway sandbox workspace 3. Register the demo HR tool server as `demo-hr` 4. Create a JSON Filter that redacts contact fields on the response path 5. Register an hr-assistant agent and mint its API key 6. Grant Gateway access and deploy Cedar Rules allowing HR and IT tools, denying compensation and customer data 7. Write my MCP client config so the assistant can reach the governed gateway tools ingress — or skip the client and prepare curl against the same URL 8. Tell me which questions to ask (or which curls to run) and what I should expect Follow the commands at [https://docs.domesystems.ai/agent/tutorials/get-started/govern-your-first-agent.md](https://docs.domesystems.ai/agent/tutorials/get-started/govern-your-first-agent.md) exactly. Non-negotiable rules: * Narrate as you go. Before each step, tell me in one or two sentences what you are about to do and why it matters. Do not silently run the whole flow. * Sandbox only. After switching context, run `dome context current` and confirm the workspace name starts with `sandbox-`. If it does not, stop and ask me. Never modify a workspace you did not provision in this session. * Never print the `dome_...` agent token in chat. Write it directly into the MCP config (or a gitignored `.env` for curl) and confirm that file is gitignored. * `dome auth login` opens a browser. Run it, then wait for me before continuing. After a successful sign-in, run `dome context sync` so stale workspaces drop out of the local cache. * You cannot reload my MCP config. After writing it, stop, tell me to enable the hr-assistant server, and wait for my confirmation. If I prefer curl instead of an MCP client, hand me the Verify the results curl tab and skip the client reload. * Do not call the governed tools yourself to demo the outcomes. After setup, give me the AI Client prompts or the API via curl commands and expected results, then stop so I can try them. * After each create/grant/deploy step, give me a markdown link into the Dome console for that resource. Derive the base URL from `dome auth status` → `Server` (for example `https://app.dev.domesystems.ai/agents` after registering the agent, `https://app.dev.domesystems.ai/tools` after adding the tool). * Expect re-runs. Check for existing resources before creating them and use `--if-not-exists` where available. * Never report a step as done without showing the command output. Write the config for whichever client I am in: `.cursor/mcp.json` for Cursor, `claude mcp add --transport http` for Claude Code, `~/.codex/config.toml` for Codex. Remind me that `AGENT_API_KEY` is the `Token: dome_…` from `dome agents create-key`, shown once. Mint another key if I lost it. Once I confirm the server is live, or if I am verifying with curl, tell me to try these prompts (or the matching curls), with the expected result for each: 1. "Who works here?" Expect a list of demo employees with ids such as `E001`. 2. "Who is E001, and how do I reach them?" Expect Alice Johnson's record with `email` as `[REDACTED]`. The Filter strips contact fields on the way back. 3. "What is Alice's salary?" Expect Cedar to deny the call. 4. "Pull up the Acme Corp customer record." Expect a denial too. If it returns data, the Rules are broader than intended. Tell me instead of moving on. After I try them, offer to run `dome audit query --limit 20` so I can see the decisions attributed to the agent, then point me at Call a model through a pool. Keep the sandbox — later tutorials reuse it. In this tutorial, you will stand up an HR assistant that Dome governs. It can look up employees and org structure, but it cannot read compensation or customer records, and contact details come back masked. You will see those limits enforced from Cursor or Claude in the same chat UI you already use — or, if you prefer not to wire an MCP client, by calling the gateway directly with curl. To do this, you will: Create a disposable workspace for this tutorial. Register the demo HR server and attach it to Default. Create a JSON Filter and assign it to the response path. Register the assistant and create an API key. Grant gateway access and deploy authorization rules. Point Cursor, Claude, or Codex at the tools ingress — or skip ahead and verify with curl. Test allow, redact, and deny in an AI client or via curl, then inspect audit. ## Prerequisites For this tutorial, you will need: * The Dome CLI, installed and signed in. * An invitation to Dome. The platform is invitation-only. Contact [support@domesystems.ai](mailto:support@domesystems.ai) if you do not have access yet. * A role that can provision a sandbox and deploy rules: admin, operator, or equivalent. Refer to [Permissions](/concepts/platform/permissions) concept. * Optional: Cursor, Claude Code, or another MCP client that accepts a remote URL plus a bearer `Authorization` header. If you only want to exercise the governed tools, curl is enough — skip [Connect your client](#connect-your-client) after you have the gateway URL and agent key. ### Install the CLI The Dome CLI is the primary interface for managing agents, deploying rules, and operating the platform. Install it, then confirm the binary is on your `PATH`. ```bash theme={"system"} brew trust dome-systems/tap brew install dome-systems/tap/dome ``` Homebrew 6 requires you to trust a third-party tap before it loads its formulae. On older Homebrew, `brew trust` is unnecessary and harmless. Set your platform, then extract the binary onto your `PATH`: ```bash theme={"system"} VERSION=0.2.0 # latest: https://github.com/dome-systems/releases/releases/latest OS=darwin # or linux ARCH=arm64 # or amd64 curl -sSL "https://github.com/dome-systems/releases/releases/download/v${VERSION}/dome_${VERSION}_${OS}_${ARCH}.tar.gz" | tar xz sudo mv dome /usr/local/bin/dome ``` Confirm the binary is on your `PATH`: ```bash theme={"system"} dome version ``` ### Sign in Log in with your provisioned account. The CLI opens a browser-based SSO flow and stores a session token locally on success. ```bash theme={"system"} dome auth login ``` If your invitation points at a non-default environment, for example a dedicated development stack, pass the server URL explicitly: ```bash theme={"system"} dome auth login --server https://app.dev.domesystems.ai ``` Confirm the session: ```bash theme={"system"} dome auth status ``` You should see `Authenticated true` with your server and org. List the contexts available to your account. You will provision a sandbox under a tenant in the next section. ```bash theme={"system"} dome context list ``` > This tutorial runs entirely in a sandbox. In a production workspace, attaching backends is typically an operator action and deploying rules a security action. ## Prepare your environment Dome nests resources so teams can share a company account without sharing data. * An **organization** is the company boundary for billing, ownership, and invites. * A **tenant** is a hard isolation wall inside that company. Agents and data in one tenant cannot see another. * A **workspace** is where you actually work: agents, tools, rules, and audit. Refer to [Platform scope](/concepts/platform/scopes) concept. A [sandbox](/concepts/platform/scopes) is a disposable workspace with the same capabilities as production, safe to throw away. Provision one: ```bash theme={"system"} dome sandbox provision --scope=workspace --workspace-name get-started ``` The server prefixes the name, creating `sandbox-get-started`. Sync your local [contexts](/cli/session/context) and switch into it: ```bash theme={"system"} dome context sync dome context use sandbox-get-started ``` Confirm you are on the sandbox and note the gateway host. You need it when you configure your client: ```bash theme={"system"} dome context current ``` Check that the workspace reads `sandbox-get-started` before continuing. Everything after this point creates or changes resources. ## Add a tool A [tool](/connect/resources/tools) is a backend the gateway calls on an agent's behalf. The agent never sees its URL or credentials. Register the public demo server as `demo-hr` and attach it to Default, the [Gateway](/connect/gateways) your client will connect to. A tool is unreachable until it belongs to one. ```bash theme={"system"} dome tool add \ --name demo-hr \ --url https://demo-mcp.domesystems.ai/mcp \ --protocol streamable-http \ --auth-method none \ --gateway Default ``` The connection name prefixes every tool it exposes, so the demo server's ten tools become `demo-hr/it/get_incidents`, `demo-hr/hr/get_employee`, and so on. You will use those qualified names when you write rules. ## Redact contact fields The assistant needs employee directory details. It does not need anyone's email address or phone number. A [Guard](/govern/guards) inspects tool responses before they reach the agent, so you can strip those fields without asking the backend to change. Tool connections take JSON Filters, which match fields by path and act on them. Create `redact-contact.json`: ```json title="redact-contact.json" theme={"system"} { "json": { "components": [ { "fieldActions": [ { "matcher": { "path": "**.email" }, "action": "FILTER_ACTION_REDACT" }, { "matcher": { "path": "**.phone" }, "action": "FILTER_ACTION_REDACT" } ] } ] } } ``` The `**.` prefix is recursive descent: it matches the named field at any depth. That covers the top-level `email` on an employee record and the nested `primary_contact.email` on a customer record, without enumerating either path. Create the Filter from that config: ```bash theme={"system"} dome guards filters create redact-contact \ --description "Redact contact fields in tool responses" \ --config-from redact-contact.json ``` You should see `Filter created: redact-contact` with an ID and `v1`. Filters are versioned, so a later change deploys a new version and leaves this one in history. Creating a Filter does not apply it. Assign it to the response direction on the connection: ```bash theme={"system"} dome tool guards filters set demo-hr \ --direction response \ --filters redact-contact ``` Assignments are per direction. This one inspects what comes back from the tool. A `request` assignment would inspect the arguments going out. ## Create the agent Register an [agent](/concepts/agents/identity) to mint its identity, then issue a credential. ```bash theme={"system"} dome agents register --name hr-assistant --if-not-exists ``` The CLI prints `Agent registered: hr-assistant` with the agent UUID. The API key is shown once. Save it before moving on: ```bash theme={"system"} dome agents create-key hr-assistant --name cursor ``` Store the `Token: dome_…` value in a password manager or your client config directly. It is a bearer token for a live gateway, so treat it like a production credential. ## Set access rules Access in Dome is two decisions. First, may this agent reach a Gateway at all? Second, once it is there, which tools may it actually call? You will grant the first with a Gateway access grant, then tighten the second with Cedar Rules. ### Grant gateway access ```bash theme={"system"} dome gateway access grant Default hr-assistant ``` You should see confirmation that `hr-assistant` was granted access to Default. That grant does two things: 1. **Admits** the agent at the Default endpoint, so the gateway will accept its requests. 2. **Permits** every resource currently in Default, plus any resource added later. That is a useful baseline for a brand-new agent, but it is broader than this HR assistant's job. The next step narrows it. ### Deploy authorization rules For this assistant, you will deploy three rules: 1. Keep tool discovery open, so the client still lists every demo tool. 2. Name the five HR and IT tools this assistant may call. 3. Add a `forbid … unless` that blocks everything else. The last two share the same allowlist. `forbid` always wins over `permit`, so tools outside the list stay denied even when the Gateway grant would allow them. Create `hr-assistant.cedar`: ```cedar title="hr-assistant.cedar" theme={"system"} permit( principal is Dome::Agent, action == Dome::Action::"mcp:discover", resource ); permit( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource ) when { resource in [ Dome::MCPTool::"demo-hr/hr/list_employees", Dome::MCPTool::"demo-hr/hr/get_employee", Dome::MCPTool::"demo-hr/hr/org_chart", Dome::MCPTool::"demo-hr/it/list_services", Dome::MCPTool::"demo-hr/it/get_incidents" ] }; forbid( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource ) unless { resource in [ Dome::MCPTool::"demo-hr/hr/list_employees", Dome::MCPTool::"demo-hr/hr/get_employee", Dome::MCPTool::"demo-hr/hr/org_chart", Dome::MCPTool::"demo-hr/it/list_services", Dome::MCPTool::"demo-hr/it/get_incidents" ] }; ``` Deploy it as an agent-scoped bundle. Scoping to `hr-assistant` is what limits `principal is Dome::Agent` to this one agent: ```bash theme={"system"} dome rules apply hr-assistant.cedar --agent hr-assistant --name hr-assistant ``` A successful deploy prints `Bundle deployed: hr-assistant` with a version and hash. Because only the `mcp:call` rules carry an allowlist, these rules gate invocation, not discovery. The assistant still lists all ten demo tools in your client. It just cannot call the six outside its job. Keeping discovery open is what lets you watch a denial happen instead of wondering why a tool vanished. ## Connect your client Point your MCP client at the Default gateway's [tools ingress](/concepts/gateways/tools-gateway) and authenticate as the agent. That path is where MCP discovery and `tools/call` traffic enter the data plane: ``` https:///gateways//mcp ``` If you would rather not configure an MCP client, keep this URL and the agent token handy and jump to [Verify the results](#verify-the-results) — the curl tab hits the same tools ingress. Retrieve the three placeholders: * `AGENT_API_KEY`. The `Token: dome_…` value printed by `dome agents create-key` when you created the agent. Tokens are shown once and cannot be recovered. If you did not save it, mint another and use that value: ```bash theme={"system"} dome agents create-key hr-assistant --name cursor ``` * `GATEWAY_HOST`. Prepend `https://` to your current Dome host, for example `https://gateway.dev.domesystems.ai`. ```bash theme={"system"} dome context current ``` * `DEFAULT_GATEWAY_ID`. The UUID of the Default gateway. ```bash theme={"system"} dome gateway list ``` The result should look similar to `https://gateway.dev.domesystems.ai/gateways/3f9a2c14-8d7e-4b1a-9c02-5e6f7a8b9c01/mcp`. That `/mcp` path is the tools ingress; model traffic uses a different path under the same `/gateways/` prefix. Register that endpoint with your client, passing the agent token as a bearer credential. Or skip the tabs below and use the same URL and token with curl in [Verify the results](#verify-the-results). Use project `.cursor/mcp.json` or **Settings → MCP**: ```json theme={"system"} { "mcpServers": { "hr-assistant": { "url": "https://GATEWAY_HOST/gateways/DEFAULT_GATEWAY_ID/mcp", "headers": { "Authorization": "Bearer AGENT_API_KEY" } } } } ``` Gitignore this file. It holds a live gateway credential. Run this in your terminal, not inside a `claude` session: ```bash theme={"system"} claude mcp add --transport http hr-assistant \ https://GATEWAY_HOST/gateways/DEFAULT_GATEWAY_ID/mcp \ --header "Authorization: Bearer AGENT_API_KEY" ``` Every flag must come before the server name. Confirm the result with `claude mcp list`. Codex reads the token from the environment rather than the config file, so export it first: ```bash theme={"system"} export DOME_AGENT_TOKEN=AGENT_API_KEY ``` Then add the server to `~/.codex/config.toml`, or `.codex/config.toml` for a single trusted project: ```toml theme={"system"} [mcp_servers.hr-assistant] url = "https://GATEWAY_HOST/gateways/DEFAULT_GATEWAY_ID/mcp" bearer_token_env_var = "DOME_AGENT_TOKEN" ``` `bearer_token_env_var` takes the **name** of the variable, not the token itself. Codex resolves it at launch, so export it before starting a session, then verify with `/mcp`. Reload MCP. Tools such as `demo-hr/hr/list_employees` appear once the client connects. If you are verifying with curl instead, you can skip the reload and continue. These are remediation steps if you get stuck: * No tools appear after reloading. Confirm the URL ends in `/mcp` and includes the `/gateways/` segment. A bare gateway host serves nothing. Then confirm the agent has both a gateway grant and a deployed bundle: ```bash theme={"system"} dome gateway access list Default dome rules simulate --agent hr-assistant --action mcp:discover \ --resource demo-hr/hr/list_employees --resource-type mcp_tool ``` Your bundle permits `mcp:discover` outright, so a `DENY` here means the bundle did not deploy. An empty access list means the grant did not land. * Every call returns an authorization error. The token is incomplete, or it belongs to a different agent. Tokens are shown once and cannot be recovered, so mint a fresh one and replace the credential in your client: ```bash theme={"system"} dome agents create-key hr-assistant --name cursor-2 ``` * The employee record still shows a real email address. The Filter exists but is not assigned, or it is assigned to the wrong direction. Confirm the chain on the response path: ```bash theme={"system"} dome tool guards filters list demo-hr ``` Assignments are picked up on the next gateway sync, so allow a moment after setting the chain. * Tools appear but calls time out. The demo server is public and unauthenticated, so a timeout usually points at egress restrictions on your network. Confirm you can reach `https://demo-mcp.domesystems.ai/mcp` directly. ## Verify the results Confirm allow, redact, and deny without changing the chat UI. Only the tool path is governed. Use an AI client if you connected one above, or POST MCP JSON-RPC to the same tools ingress with curl. Export the same placeholders once if you are using the curl tab: ```bash theme={"system"} export DOME_GATEWAY_URL="https://GATEWAY_HOST/gateways/DEFAULT_GATEWAY_ID" export DOME_TOKEN="AGENT_API_KEY" ``` Ask the assistant the prompts below. 1. **Who works here?** The call is allowed. You receive the demo employee directory, including ids such as `E001`: ```json theme={"system"} { "id": "E001", "name": "Alice Johnson", "department": "Engineering", "title": "Senior Engineer" } ``` 2. **Who is E001, and how do I reach them?** The call is allowed, and the Filter masks the contact field on the way back: ```json theme={"system"} { "id": "E001", "name": "Alice Johnson", "department": "Engineering", "title": "Senior Engineer", "email": "[REDACTED]" } ``` The assistant learns who the person is and loses the ability to contact them directly. The backend still returned the real address. The Filter replaced it before the agent saw it. 3. **What is Alice's salary?** The call is denied. `demo-hr/finance/get_salary` is not in the allowlist, so the `forbid` applies. The gateway returns a JSON-RPC error rather than data: ```json theme={"system"} { "error": { "code": -32001, "message": "" } } ``` Your assistant paraphrases this in its own words, so the exact wording in chat varies. The audit trail below is the authoritative record. 4. **Pull up the Acme Corp customer record.** Also denied, and you never named this tool in the rule. That is the difference between an allowlist you maintain by hand and a boundary that holds by default. Post JSON-RPC `tools/call` to the Gateway MCP endpoint. Substitute the exports above, or paste the URL and bearer token inline. 1. **List employees**, allowed: ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/mcp" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "demo-hr/hr/list_employees", "arguments": {} } }' ``` Expect demo employees in `result`, including `E001`. 2. **Get E001**, allowed, contact redacted: ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/mcp" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "demo-hr/hr/get_employee", "arguments": { "employee_id": "E001" } } }' ``` Expect `"email": "[REDACTED]"` in the returned record. The backend still returned the real address. The Filter replaced it before your client saw it. 3. **Get salary**, denied: ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/mcp" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "demo-hr/finance/get_salary", "arguments": { "employee_id": "E001" } } }' ``` Expect a JSON-RPC `error` with code `-32001` and a reason from the authorization decision, not a salary payload. 4. **Get customer**, denied, and never named in the allowlist: ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/mcp" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": { "name": "demo-hr/sales/get_customer", "arguments": { "name": "Acme Corp" } } }' ``` Expect the same class of denial. That is the difference between an allowlist you maintain by hand and a boundary that holds by default. ### Inspect the audit trail Every allow, filter, and deny is attributable to the agent: ```bash theme={"system"} dome audit query --limit 20 ``` You should see `mcp.tool_call.attempted`, `mcp.tool_call.completed`, `mcp.tool_result.filtered`, and `access.denied`, each carrying the `hr-assistant` agent ID. To read only the rejections, filter by result: ```bash theme={"system"} dome audit query --results denied --limit 10 ``` ### Test rule changes without a client Simulation runs the same evaluator as the gateway with no side effects, so you can check a rule before shipping it without a client reload or a real call: ```bash theme={"system"} dome rules simulate --agent hr-assistant --action mcp:call \ --resource demo-hr/hr/get_employee --resource-type mcp_tool ``` Expect `ALLOW`. Swap the resource for `demo-hr/finance/get_salary` or `demo-hr/sales/get_customer` and expect `DENY` for both. This is the loop to use when you tighten the rule later. ## Next steps You learned how to register an agent, attach a tool through a Gateway, deploy Rules and a Guard, and verify allow, redact, and deny. Keep the `sandbox-get-started` workspace for the rest of this track. Continue with: * [Call a model through a pool](/tutorials/get-started/call-a-model-through-a-pool) to put an LLM behind the same Gateway * [Agents](/connect/agents) to manage identities, keys, and lifecycle * [Authorize Access](/govern/rules) to write and deploy Cedar Rules # Tutorials Source: https://docs.domesystems.ai/tutorials/index Hands-on tutorials for building with Dome ## Get Started Govern an agent end to end in a sandbox. Allow employee lookups, redact contact details, and deny payroll — from Cursor, Claude, or curl. Route chat through a model pool with failover, authorization, and your provider key held server-side. Wire model and tool calls in one app, then click through allow, redact, and deny. The agent inherits each user's permissions — same agent, different tool access and answers per person. ## Role Learn what developer, security, operator, and platform admin each own on Dome — guided sandbox walks for each persona. Register an agent, call tools through a Gateway, and confirm allow and deny in audit. Author and simulate Cedar, redact sensitive fields, prove denials, and export evidence. Stand up the substrate: attach tools, expose a Gateway, grant access, and watch audit. Set who may administer Dome, check platform health, and export an audit snapshot. ## Production Diagnose a live denial, put an existing LLM client under Dome, deploy a working agent, or use OIDC or bound act-as. Gateway admission, Cedar, and audit. Repoint an LLM client at Dome without rewriting logic. Docker, Render, Fly, and Lambda — coming soon. Replace sandbox HMAC act-as with an IdP JWT or a bound enrollment identity — coming soon. ## Examples Working examples you can adapt: Person in the loop — coming soon. Batch or background — coming soon. ### Use cases Governed code execution. Multiple tool adapters behind one agent. Retrieval over a knowledge base. # Adopt an existing app Source: https://docs.domesystems.ai/tutorials/production/adopt-an-existing-app Move an LLM app you already run onto the governed path, with a parity check and a one-variable rollback You already have an app calling a provider directly. The provider key is in its environment, spend shows up on an invoice, and nothing records which part of your system asked for what. Dome speaks the providers' own wire protocols, so adoption is a configuration change rather than a rewrite. The work is proving parity and keeping a way back. Move my existing LLM app onto Dome without rewriting its logic, and keep a rollback. First, inspect my repository and show me this plan with your findings, then ask me to confirm before changing anything: 1. Every place my code constructs a provider client or names a model, and which provider SDKs I use 2. Any API surface I use that Dome does not proxy, so I know the risks before I start 3. A Dome model connection mirroring my current provider and model 4. The three configuration values that change, wired so I can revert by changing one variable 5. A parity check: the same prompt through both paths, compared 6. A rule and a spend cap once parity holds Follow the commands at [https://docs.domesystems.ai/agent/tutorials/production/adopt-an-existing-app.md](https://docs.domesystems.ai/agent/tutorials/production/adopt-an-existing-app.md) exactly. Non-negotiable rules: * Read before you write. Show me what you found in my code and wait for confirmation before editing anything. * Change configuration, not logic. Do not restructure my code, swap my SDK, or "improve" my prompts while you are here. If my code hardcodes a base URL or a key, tell me and propose the smallest change that makes it configurable. * Do not touch production. Work on a local or development environment only. Ask me which one before you start. * Keep the rollback real. The switch back must be a single environment variable, and you must show me it works before we go further. * Never print or commit my provider key or the `dome_...` agent token. Confirm the env file is gitignored. * Sandbox first. Provision a sandbox workspace for the parity check rather than using a real workspace, unless I tell you otherwise. * Do not skip the parity check. Run the same prompt through both paths and show me both outputs before recommending the switch. * Never report a step as done without showing the command output. Tell me plainly if any part of my app cannot move yet, and why. Then show me the spend and the audit trail for the calls the governed path made. In this tutorial, you will repoint an app that already calls a provider at Dome, verify it behaves the same, then add the governance that was the reason for moving. Your application logic does not change. To do this, you will: Confirm your API surface is supported before you start. Create a connection that matches what you call today. Change three values, keeping a one-variable rollback. Compare the same request through both paths. Apply the rule and the cap you moved for. ## Prerequisites For this tutorial, you will need: * An app that calls OpenAI or Anthropic through their official SDK or plain HTTP. * The Dome CLI, installed and signed in. * The provider API key your app uses today. * A workspace you can experiment in. Provision a sandbox if you do not have one: ```bash theme={"system"} dome sandbox provision --scope=workspace --workspace-name adopt dome context sync dome context use sandbox-adopt ``` > Adopting an existing app usually spans two roles. A developer changes the configuration, while the rule and the spend cap belong to security and finance. Both halves are shown here. ## Check what Dome proxies Establish this before you change anything, because it determines whether your app can move today. | Surface | Status | | -------------------------------- | ------------------------------------- | | `POST /v1/chat/completions` | Proxied | | `POST /v1/messages` | Proxied | | `POST /v1/messages/count_tokens` | Proxied | | `POST /v1/embeddings` | Proxied | | `GET /v1/models` | Proxied | | `POST /v1/responses` | Not yet. Returns `501` | | `POST /v1/moderations` | Not yet. Returns `501` | | Provider-specific endpoints | Through `POST /v1/passthrough/` | If your app is built on the OpenAI Responses API, it cannot move yet. Find out now rather than midway through a migration: ```bash theme={"system"} grep -rn "responses.create\|/v1/responses\|moderations" --include="*.py" --include="*.ts" --include="*.js" . ``` Also find every place a client is constructed or a model is named. Those are the only lines this tutorial touches: ```bash theme={"system"} grep -rn "OpenAI(\|Anthropic(\|base_url\|baseURL\|api_key\|apiKey" --include="*.py" --include="*.ts" --include="*.js" . ``` A base URL or key that is hardcoded rather than read from the environment is the one code change worth making. Make it configurable, and the rest of this is deployment work. ## Mirror your model in Dome Create a connection that resolves to exactly what your app calls today. Name it after the model id your code already passes, and your application's model string keeps working unchanged: ```bash theme={"system"} dome model add gpt-4o \ --provider openai \ --model gpt-4o \ --api-key "$OPENAI_API_KEY" \ --gateway Default ``` ```bash theme={"system"} dome model add claude-3-5-sonnet-20241022 \ --provider anthropic \ --model claude-3-5-sonnet-20241022 \ --api-key "$ANTHROPIC_API_KEY" \ --gateway Default ``` Naming the connection after the upstream model is a deliberate migration convenience, not the end state. Dome resolves a connection name to a provider model, so a name of your own choosing lets you change providers later without touching the app. Adopt the model id first, rename once you are stable. The provider key now lives in Dome. Remove it from your app's environment at the end of this tutorial, not yet. You still need it for the parity check. Register an agent for the app and mint its credential: ```bash theme={"system"} dome agents register --name checkout-service --if-not-exists dome agents create-key checkout-service --name production dome gateway access grant Default checkout-service ``` ## Switch one environment Three values change, and nothing else: | Value | From | To | | -------- | ---------------------- | --------------------------------------------------------- | | Base URL | The provider's default | `https:///gateways//v1` | | API key | Your provider key | The Dome agent token, `dome_…` | | Model | A provider model id | Your Dome connection name | Naming the connection after the model handled the third. Wire the first two so that reverting is a single variable: ```python title="llm_client.py" theme={"system"} import os from openai import OpenAI def build_client() -> OpenAI: """Route through Dome when configured; otherwise call the provider directly.""" gateway_url = os.getenv("DOME_GATEWAY_URL") if gateway_url: return OpenAI(base_url=f"{gateway_url}/v1", api_key=os.environ["DOME_TOKEN"]) return OpenAI(api_key=os.environ["OPENAI_API_KEY"]) ``` Unsetting `DOME_GATEWAY_URL` returns the app to the provider. That is the rollback, and it needs no deploy of new code. Collect the endpoint values: ```bash theme={"system"} dome context current dome gateway list ``` Smoke-test the endpoint before you point the app at it. This lists the models the gateway exposes to you and confirms the URL and credential are right: ```bash theme={"system"} curl -s https://GATEWAY_HOST/gateways/DEFAULT_GATEWAY_ID/v1/models \ -H "Authorization: Bearer AGENT_API_KEY" ``` Anthropic clients set `base_url` to the Gateway **without** the `/v1` suffix, because their SDK appends `/v1/messages` itself. Anthropic routes also accept the credential in `x-api-key` when no `Authorization` header is present, so an app that only sets `x-api-key` works unchanged. ## Verify parity Run the same prompt through both paths and compare. Use a deterministic request so the comparison means something: ```python title="parity_check.py" theme={"system"} import os from openai import OpenAI PROMPT = "Reply with exactly the word: ready" def ask(client: OpenAI) -> str: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": PROMPT}], temperature=0, ) return response.choices[0].message.content direct = OpenAI(api_key=os.environ["OPENAI_API_KEY"]) governed = OpenAI( base_url=os.environ["DOME_GATEWAY_URL"] + "/v1", api_key=os.environ["DOME_TOKEN"], ) print("direct: ", ask(direct)) print("governed:", ask(governed)) ``` Both lines should match. Check three things beyond the text itself: that response objects carry the same shape your code already destructures, that streaming still streams if you use it, and that your latency budget still holds with a proxy hop in the path. Then confirm the governed call was recorded, which the direct call was not: ```bash theme={"system"} dome audit query --limit 10 ``` You should see `llm.model_call.attempted` and `llm.model_call.completed` attributed to `checkout-service`, with token usage. That record is the thing you did not have before. These are remediation steps if you get stuck: * `400` with `select a gateway`. The base URL is missing its `/gateways/` segment. For OpenAI clients it must also end in `/v1`. * `404` with `no connection for requested model`. The model string in your code does not match a connection name in this workspace. Compare them: ```bash theme={"system"} dome model list ``` * `403` with `model not available in this gateway`. The connection is not attached to Default. Check membership with `dome gateway get Default`. * `403` with `agent is not granted access to this gateway`. The agent has no admission grant. Note that `GET /v1/models` skips this check, so a working smoke test does not prove the grant exists: ```bash theme={"system"} dome gateway access grant Default checkout-service ``` * `401` from the gateway. Your app is still sending the provider key. This endpoint authenticates the agent. * `429` with `llm: quota exceeded`. A spend cap fired. The message names the subject that ran out. * `501` on a request that worked before. You are calling a surface Dome does not proxy yet, such as Responses or Moderations. Keep those calls on the direct path until they are supported. * Requests succeed but nothing appears in audit. The app is still on the direct path. Confirm `DOME_GATEWAY_URL` is set in the environment the process actually reads. ## Add the governance Parity holding is the midpoint, not the finish. Nothing is governed yet: the agent may call anything in the gateway and spend without limit. Deploy a rule naming what this app may invoke. Create `checkout-service.cedar`: ```cedar title="checkout-service.cedar" theme={"system"} permit( principal is Dome::Agent, action == Dome::Action::"llm:invoke", resource == Dome::LLMModel::"gpt-4o" ); forbid( principal is Dome::Agent, action == Dome::Action::"llm:invoke", resource ) unless { resource == Dome::LLMModel::"gpt-4o" }; ``` ```bash theme={"system"} dome rules apply checkout-service.cedar --agent checkout-service --name checkout-service ``` Then cap the spend. Set the limit from what this app actually costs, which you now have a record of: ```bash theme={"system"} dome model quota set \ --subject agent \ --agent checkout-service \ --limit 200 \ --window monthly \ --name "checkout service" ``` Confirm both before you rely on them: ```bash theme={"system"} dome rules simulate --agent checkout-service --action llm:invoke \ --resource gpt-4o --resource-type llm_model dome model quota list ``` Now remove the provider key from your app's environment. Until you do, the app can still bypass everything you just built by falling back to the direct path. Keep the fallback branch in the code if you want, but point it at nothing. A rollback path that silently restores ungoverned access is worse than no rollback path, because it works. ## Verify the results Four things should now be true, and each has a command that proves it: 1. The app reaches the provider only through Dome. Its environment no longer holds a provider key. 2. Calls are attributed. `dome audit query --limit 10` names `checkout-service`. 3. Only the intended model is reachable. Simulating any other connection name returns `DENY`. 4. Spend is capped. `dome model quota list` shows the limit and what has been used against it. Roll out the way you would any configuration change: one environment, then a share of production traffic, then the rest. Because the switch is an environment variable, staged rollout and rollback use the mechanism you already have. ## Next steps In this tutorial, you: * Confirmed your [API surface](/concepts/gateways/llm-gateway) was proxied before starting, rather than discovering a gap midway. * Moved a provider credential into [Dome](/connect/resources/models) and out of your app's environment. * Changed three configuration values and no application logic, keeping a one-variable rollback. * Proved parity before adding constraints. * Applied a [rule](/govern/rules) and a [quota](/govern/quotas) sized from real usage. Continue with: Diagnose the first rejection your rollout produces. The agent inherits each user's permissions — same agent, different tool access and answers per person. Failover and routing across providers once you are behind the gateway. Endpoints, pipeline stages, and error shapes in full. # Debug a tool call denial Source: https://docs.domesystems.ai/tutorials/production/debug-a-denial Read a rejected call back to the gate that produced it, using the error, the audit trail, and simulation An agent reports that a tool call failed. The message it repeats to you came from a model paraphrasing an error, so it is not evidence. Dome recorded the actual decision, and several different gates could have produced it. The skill worth having is narrowing quickly: which gate answered, and what would change the answer. Teach me to diagnose Dome denials by planting three different failures in my sandbox and walking me through each one. First, show me this plan and ask me to confirm before running anything: 1. Confirm the sandbox from the first tutorial is still active 2. Reproduce a Cedar denial and show me how to attribute it to a rule 3. Reproduce a Gateway access denial and show me how it differs from Cedar authorization 4. Reproduce a malformed endpoint error and show me how it differs from an authorization error 5. Show me the audit evidence and the simulation for each 6. Restore everything you changed Follow the commands at [https://docs.domesystems.ai/agent/tutorials/production/debug-a-denial.md](https://docs.domesystems.ai/agent/tutorials/production/debug-a-denial.md) exactly. Non-negotiable rules: * Narrate as you go. For each failure, tell me what you are breaking, what error I should expect, and which gate produced it. Do not silently run the whole flow. * Sandbox only. Run `dome context current` and confirm the workspace name starts with `sandbox-`. If it does not, stop and ask me. Never break anything in a workspace you did not provision in this session. * Restore every change you make, in the same session, and prove it with the command output. Do not leave the sandbox in a broken state. * Diagnose from evidence, not from guessing. For each failure, show me the audit event and the simulation result, not just the client-side error. * Do not fix a denial by widening the rules. Show me what the rule says, then restore the original state. * Never print the `dome_...` agent token in chat. * Never report a step as done without showing the command output. For each of the three failures, tell me the one command I would have run first if I did not know the cause in advance. Then give me a short summary I can keep: which error message points at which gate. In this tutorial, you will plant three failures at three different layers and diagnose each from the outside. By the end you will be able to read an error message and know which command to run first. To do this, you will: Understand what runs before what. Attribute a rejection to the rule that caused it. Find the missing Gateway grant that rejects invocation before Cedar evaluates the resource. Separate transport problems from authorization problems. Recognize the events that are not denials. ## Prerequisites For this tutorial, you will need: * [Govern your first agent](/tutorials/get-started/govern-your-first-agent) completed, with its sandbox still active. You will use its `hr-assistant` agent, its rule bundle, and its `demo-hr` connection. * The Dome CLI, installed and signed in. Confirm the workspace, and stay in the sandbox. You are about to break things deliberately: ```bash theme={"system"} dome context current ``` ## Learn the order of the gates A call passes several independent checks before it reaches a backend. They run in a fixed order, and the first one to reject ends the call, so the gate that answered tells you where to look. | Order | Gate | What it decides | Typical signal | | ----- | ------------------- | ---------------------------------------------- | -------------------------------------------------------------- | | 1 | Authentication | Is this a valid, active credential? | `401`, or `403` for a suspended or revoked agent | | 2 | Act-as verification | Is the end-user identity present and provable? | `400 act-as header required`, `403 act-as verification failed` | | 3 | Endpoint selection | Did the caller name a Gateway? | `400 select a gateway` | | 4 | Gateway membership | Is this resource in that Gateway? | `tool not available in this gateway` | | 5 | Gateway admission | Is this agent granted access to that Gateway? | `agent is not granted access to this gateway` | | 6 | Quota | Is there budget left? | `429 llm: quota exceeded` | | 7 | Rules | Does Cedar permit it? | `denied by rule: ` | Two properties of this ladder cause most confusion. Quota sits **above** rules. A permit that looks correct still fails if the spend cap is exhausted, so a rule you just deployed is not always the thing to change. Discovery behaves differently from invocation. `tools/list` remains membership- and Cedar-filtered and does not prove that the agent has a Gateway access grant. The grant is enforced when the agent invokes a resource, so use an actual call as the access check. ## Diagnose a rule denial Start with the denial you already have. The `hr-assistant` rules permit five tools and forbid the rest, so payroll is rejected. Ask the assistant in your MCP client: ``` What is Alice's salary? ``` Your client shows whatever the model made of the error. The underlying response is a JSON-RPC error with code `-32001`, carrying `dome.authorization_denied` in its `data.type`, and a message that is Dome's recorded reason rather than the model's paraphrase. Get the authoritative record: ```bash theme={"system"} dome audit query --results denied --limit 5 ``` The `access.denied` event names the agent, the resource, and the reason. A reason of `denied by rule: ` means an explicit `forbid` matched. A reason of `denied by rule` with no identifier, or `no rule bundle loaded`, means nothing permitted the call rather than something forbidding it, which is a different fix. Confirm the decision without a client in the loop. Simulation runs the same evaluator with no side effects: ```bash theme={"system"} dome rules simulate --agent hr-assistant --action mcp:call \ --resource demo-hr/finance/get_salary --resource-type mcp_tool ``` Expect `DENY` with the reason and the determining rule. Add `--json` for the full response, including the scope trace showing which scope contributed the decision. Then read what the agent is actually running under, which is not always what you think you deployed: ```bash theme={"system"} dome rules show --agent hr-assistant ``` That resolves the whole hierarchy. To see only the agent-scoped bundle, use `dome agents get-policies hr-assistant`. Simulation takes the same identity flags the gateway would supply. `--actas-sub`, `--actas-email`, `--actas-groups`, and `--actas-roles` let you reproduce a denial that only affects one person, and `--eval-arguments` supplies tool arguments for rules that inspect them. ## Diagnose a Gateway access denial Now revoke the agent's Gateway grant: ```bash theme={"system"} dome gateway access revoke Default hr-assistant ``` Reload MCP in your client, then ask it to call `demo-hr/hr/list_employees`. The call fails with `agent is not granted access to this gateway` before the resource's Cedar rule is evaluated. Discovery can still list the tool, so a successful `tools/list` is not an access test. This is the case where simulation misleads you if you ask it the wrong question. Cedar still permits discovery, so a bundle check comes back clean: ```bash theme={"system"} dome rules simulate --agent hr-assistant --action mcp:discover \ --resource demo-hr/hr/list_employees --resource-type mcp_tool ``` That returns `ALLOW` while invocation is rejected, because Gateway admission runs before resource authorization. The permit is real but is never reached. The command to run first for a missing Gateway grant: ```bash theme={"system"} dome gateway access list Default ``` An empty result, or a list without `hr-assistant`, is your answer. Restore it: ```bash theme={"system"} dome gateway access grant Default hr-assistant ``` Retry the tool call and confirm it succeeds. The lesson generalizes: when invocation reports a missing Gateway grant, inspect Gateway access before changing resource rules. Membership is the neighboring failure. A tool that exists but belongs to no Gateway is unreachable through that endpoint. This command shows which resources a Gateway actually carries: ```bash theme={"system"} dome gateway get Default ``` ## Diagnose a malformed endpoint The third class of failure is not authorization at all, and it is worth being able to rule out in seconds. Call the gateway without naming a Gateway: ```bash theme={"system"} curl -i -X POST https://GATEWAY_HOST/mcp \ -H "Authorization: Bearer AGENT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` You get `400` with `select a gateway: use /gateways/{id}/mcp`. Nothing was evaluated and nothing appears in audit, because the request never identified a gateway to be evaluated against. The same applies to `/v1` for inference. Two related mistakes produce different errors, which is how you tell them apart: * A URL missing the `/mcp` suffix reaches no handler. * A valid URL with a wrong or unauthenticated token returns `401` from authentication, ahead of every authorization gate. The rule of thumb: **`400` and `401` are your problem, `403` and `-32001` are the policy's answer.** Only the second kind is worth taking to your security team. Match the message you have to the gate that produced it: * `select a gateway`. The URL omits the `/gateways/` segment. Nothing was evaluated. * `401 invalid or expired token`. The credential is wrong, malformed, or rotated. Mint a new key rather than editing rules. * `403 agent is suspended` or `agent is revoked`. Lifecycle state, not policy. Check the agent record with `dome agents get`. * `act-as header required`. The agent requires a verified end-user identity and the call arrived without one. * `act-as verification failed`. An identity was presented but did not verify. Usually a mismatched HMAC secret or an expired token. * `tool not available in this gateway`. Membership. The tool exists but is not in this gateway, or the gateway is inactive. * `agent is not granted access to this gateway`. Admission on invocation. Discovery may still list the resource and is not proof that a grant exists. * `llm: quota exceeded`. A spend cap. The message names the subject that ran out. Check `dome model quota list`. * `denied by rule: `. An explicit `forbid` matched. Read it with `dome rules show --agent `. * `no rules for workspace` or `no rule bundle loaded`. Nothing was deployed, so the default deny applied. Deploy a bundle. * `policy stale` or `gateway policy stale, fail closed`. The gateway's policy copy aged past its tolerance and it is failing closed. This is an infrastructure signal, not a rule problem. ## Tell denials from redactions Two event types look like failures in a log and are not. A Filter that rewrites a field records `mcp.tool_result.filtered`, and a Filter that blocks an argument on the way out records `mcp.tool_request.filtered`. In both cases the call was authorized. Something was changed, not refused. This distinction matters when an agent reports missing data. If the field arrived as `[REDACTED]`, no denial occurred and the rules are not the place to look: ```bash theme={"system"} dome tool guards filters list demo-hr ``` Filters attach to the connection, so every agent reaching that tool inherits them regardless of its own rules. For the full picture of a single request, follow its activity chain. One user action can span many events, and the chain shows them in order: ```bash theme={"system"} dome audit chains --limit 10 dome audit chain ``` To read one event in full, including its payload: ```bash theme={"system"} dome audit get ``` ## Verify the results Confirm you left the sandbox as you found it. The grant should be back: ```bash theme={"system"} dome gateway access list Default ``` The five permitted tools should still allow, and payroll should still deny: ```bash theme={"system"} dome rules simulate --agent hr-assistant --action mcp:call \ --resource demo-hr/hr/list_employees --resource-type mcp_tool dome rules simulate --agent hr-assistant --action mcp:call \ --resource demo-hr/finance/get_salary --resource-type mcp_tool ``` Expect `ALLOW` then `DENY`. `dome rules simulate` exits non-zero on a denial, which makes it usable as a check in CI: assert that the calls you intend to be impossible stay impossible. ## Next steps In this tutorial, you: * Learned the order the [gates](/concepts/architecture/dataplane) run in, and why quota sits above rules. * Attributed a denial to its rule using the [audit trail](/operate/audit) rather than the client's paraphrase. * Found a missing Gateway grant from its explicit invocation denial. * Separated transport errors from policy answers. * Distinguished a [Guard](/govern/guards) rewriting a field from a rule refusing a call. Continue with: Every flag, including identity and argument evaluation. Event envelope, filters, chains, and export formats. Move an app you already run onto the governed path. Live streams, metrics, and OpenTelemetry export. # Deploy your agent Source: https://docs.domesystems.ai/tutorials/production/deploy-your-agent Docker, Render, Fly, and Lambda This tutorial is coming soon. # Use OIDC or bound act-as Source: https://docs.domesystems.ai/tutorials/production/use-oidc-or-bound-act-as Replace sandbox HMAC act-as with an IdP JWT or a bound enrollment identity (coming soon) This tutorial is coming soon. [Govern per end user](/tutorials/get-started/govern-per-end-user) teaches the pattern with HMAC: your **backend** holds the agent token, signs `X-Dome-Act-As`, and calls Dome. The frontend never sees either secret. Production usually should not mint identities with a shared HMAC secret. This tutorial will cover the two stronger verification methods: | Method | When to use | What the backend sends | | --------- | ----------------------------------- | -------------------------------------------------------------------------------- | | **OIDC** | End users sign in through your IdP | The end-user JWT as `X-Dome-Act-As`. Dome verifies it against discovery metadata | | **Bound** | The agent is enrolled to one person | No client act-as header. The platform injects the enrollment identity | Until this tutorial ships, use the HMAC sandbox path and the delegated-agent reference below. ## Next steps Until this tutorial ships, use the HMAC path and the delegated-agent reference: * [Govern per end user](/tutorials/get-started/govern-per-end-user) for the HMAC sandbox path * [Delegated agents](/connect/agents/delegated) for OIDC providers, workspace policy, and methods * [Pass identity for delegated agents](/develop#pass-identity-for-delegated-agents) in application code * [Adopt an existing app](/tutorials/production/adopt-an-existing-app) to move a running app onto the governed path # Developer Source: https://docs.domesystems.ai/tutorials/role/developer Register an agent, call tools through a Gateway, and confirm allow and deny in audit. On Dome, a developer owns the agent as a workload: you register it, mint its credentials, point it at a Gateway, and verify that governed calls succeed or fail for the reasons you expect. Operators usually attach shared backends. Security usually owns the baseline Rules. In this sandbox you finish the loop yourself so you can see the full path. Run the Dome Developer role tutorial in a throwaway sandbox: register an agent, attach demo-hr, deploy a minimal Cedar allowlist, then hand me curl or Python calls that prove allow and deny, plus audit. First, show me this plan and ask me to confirm before running anything: 1. Confirm CLI auth, provision sandbox-role-developer, switch into it 2. Register role-dev-agent and mint an API key (do not print the token) 3. Attach demo-hr to Default and grant the agent gateway access 4. Deploy a Cedar allowlist (directory tools only; deny salary) 5. Hand me Verify tabs (curl and Python) for list\_employees (allow) and get\_salary (deny) 6. Show audit for those decisions, then offer clean up Follow the commands at [https://docs.domesystems.ai/agent/tutorials/role/developer.md](https://docs.domesystems.ai/agent/tutorials/role/developer.md) exactly. Non-negotiable rules: * Narrate as you go. Before each step, tell me in one or two sentences what you are about to do and why it matters. * Sandbox only. After switching context, run `dome context current` and confirm the workspace name starts with `sandbox-`. If it does not, stop and ask me. * Never print the `dome_...` agent token in chat. Write it into a gitignored `.env` for the verify step. * Do not call the governed tools yourself to demo outcomes. After setup, hand me the curl/Python commands and expected results, then stop. * After each create/grant/deploy step, give me a markdown link into the Dome console. Derive the base URL from `dome auth status` → `Server`. * Never report a step as done without showing the command output. In this tutorial, you will register an agent, attach the demo HR tools behind Default, deploy a narrow Cedar allowlist, and prove allow vs deny from curl or Python — then see both decisions in audit. To do this, you will: Create a disposable workspace for this role walk. Mint identity and an API key for the workload. Put `demo-hr` on Default and admit the agent. Allow directory tools; deny compensation. Call the gateway with curl or Python. Read the decisions attributed to your agent. ## Prerequisites For this tutorial, you will need: * The [Dome CLI](/install) installed and authenticated (`dome auth login`, then `dome auth status`) * A role that can provision a sandbox and deploy rules in that sandbox (admin, operator, or equivalent — refer to [Permissions](/concepts/platform/permissions) concept) > This tutorial runs entirely in a sandbox. In a production workspace, attaching backends is typically an [operator](/tutorials/role/operator) action and deploying rules a [security](/tutorials/role/security) action. A developer still needs enough Connect and Govern context to integrate and debug. ## Provision a sandbox A [sandbox](/concepts/platform/scopes) is a disposable workspace with the same capabilities as production. Provision one for this role track: ```bash theme={"system"} dome sandbox provision --scope=workspace --workspace-name role-developer ``` The server prefixes the name, creating `sandbox-role-developer`. Sync contexts and switch into it: ```bash theme={"system"} dome context sync dome context use sandbox-role-developer dome context current ``` Confirm the workspace reads `sandbox-role-developer` before continuing. ## Register an agent An [agent](/concepts/agents/identity) is the workload identity every governed call is attributed to. Register one, then mint a credential the gateway will accept as `Authorization: Bearer`: ```bash theme={"system"} dome agents register --name role-dev-agent --if-not-exists dome agents create-key role-dev-agent --name service ``` Save the `Token: dome_…` value once into a gitignored `.env`. Tokens are shown once and cannot be recovered — mint another key if you lose it. ## Attach tools and grant access A [tool](/connect/resources/tools) is a backend the gateway calls on the agent's behalf. The agent never sees its URL. Register the public demo HR server and attach it to Default: ```bash theme={"system"} dome tool add \ --name demo-hr \ --url https://demo-mcp.domesystems.ai/mcp \ --protocol streamable-http \ --auth-method none \ --gateway Default ``` The connection name prefixes every tool (`demo-hr/hr/list_employees`, and so on). Admit the agent at Default so the gateway accepts its requests: ```bash theme={"system"} dome gateway access grant Default role-dev-agent ``` That grant admits the agent and permits resources currently in Default. The next step narrows what the agent may actually call. ## Deploy authorization rules Cedar decides every `mcp:call`. This allowlist keeps directory tools open and blocks compensation (and everything else) with `forbid … unless`: ```cedar title="role-dev-agent.cedar" theme={"system"} permit( principal is Dome::Agent, action == Dome::Action::"mcp:discover", resource ); permit( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource ) when { resource in [ Dome::MCPTool::"demo-hr/hr/list_employees", Dome::MCPTool::"demo-hr/hr/get_employee", Dome::MCPTool::"demo-hr/hr/org_chart" ] }; forbid( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource ) unless { resource in [ Dome::MCPTool::"demo-hr/hr/list_employees", Dome::MCPTool::"demo-hr/hr/get_employee", Dome::MCPTool::"demo-hr/hr/org_chart" ] }; ``` Deploy it scoped to this agent: ```bash theme={"system"} dome rules apply role-dev-agent.cedar --agent role-dev-agent --name role-dev-agent ``` Expect `Bundle deployed: role-dev-agent`. Optionally simulate before a live call: ```bash theme={"system"} dome rules simulate --agent role-dev-agent --action mcp:call \ --resource demo-hr/hr/list_employees --resource-type mcp_tool dome rules simulate --agent role-dev-agent --action mcp:call \ --resource demo-hr/finance/get_salary --resource-type mcp_tool ``` Expect `ALLOW`, then `DENY`. ## Verify allow and deny Build the tools ingress URL from your sandbox gateway host and Default's id: ```bash theme={"system"} dome context current dome gateway list ``` Export once: ```bash theme={"system"} export DOME_GATEWAY_URL="https://GATEWAY_HOST/gateways/DEFAULT_GATEWAY_ID" export DOME_TOKEN="dome_..." ``` Post MCP JSON-RPC `tools/call` to `$DOME_GATEWAY_URL/mcp`. Use curl or Python — same payloads, same outcomes. **List employees** — allowed: ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/mcp" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "demo-hr/hr/list_employees", "arguments": {} } }' ``` Expect demo employees in `result`, including `E001`. **Get salary** — denied: ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/mcp" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "demo-hr/finance/get_salary", "arguments": { "employee_id": "E001" } } }' ``` Expect a JSON-RPC error (authorization deny), not payroll data. Requires `httpx` (`pip install httpx`). Same gateway URL and token as above: ```python title="verify_calls.py" theme={"system"} import json import os import httpx url = os.environ["DOME_GATEWAY_URL"].rstrip("/") + "/mcp" token = os.environ["DOME_TOKEN"] def call(name: str, arguments: dict | None = None) -> None: response = httpx.post( url, headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", }, json={ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": name, "arguments": arguments or {}}, }, timeout=30.0, ) print(json.dumps(response.json(), indent=2)) # Allowed — directory call("demo-hr/hr/list_employees") # Denied — compensation call("demo-hr/finance/get_salary", {"employee_id": "E001"}) ``` ```bash theme={"system"} python verify_calls.py ``` Expect employees in the first response and an authorization error in the second. ## Confirm in audit Every decision is attributed to the agent. List recent events: ```bash theme={"system"} dome audit query --limit 20 ``` Focus on denials: ```bash theme={"system"} dome audit query --results denied --limit 10 ``` You should see the allowed `list_employees` call and the denied `get_salary` call under `role-dev-agent`. ## Clean up Delete the sandbox to remove the agent, rules, and grants together: ```bash theme={"system"} dome workspace delete sandbox-role-developer ``` ## Next steps You learned how to register an agent, call tools through a Gateway, and confirm allow and deny in audit. Continue with: * [Security](/tutorials/role/security) to author, simulate, and tighten Rules and Guards * [Operator](/tutorials/role/operator) to attach backends and expose Gateways * [Govern your first agent](/tutorials/get-started/govern-your-first-agent) for the full Get Started path with Filters and an MCP client # Operator Source: https://docs.domesystems.ai/tutorials/role/operator Stand up a workspace substrate: attach tools, expose a Gateway, grant access, and watch audit. On Dome, an operator owns the shared substrate: workspaces, tool and model connections, Gateways, grants, and the live signals that show the platform is healthy. You make backends reachable under Dome, group them behind named endpoints, grant the right agents access at the edge, and watch traffic. Developers integrate agents into that substrate. Security tightens what those agents may do once they are on it. Run the Dome Operator role tutorial in a throwaway sandbox: attach demo-hr to Default, register a throwaway agent for a smoke grant, deploy a tiny Cedar permit for list\_employees, then hand me one curl or Python call that proves the substrate works, plus audit. First, show me this plan and ask me to confirm before running anything: 1. Confirm CLI auth, provision sandbox-role-operator, switch into it 2. Attach demo-hr to Default 3. Register role-ops-agent (scaffolding only), mint a key (do not print it), grant Default 4. Deploy a minimal Cedar permit for discover + list\_employees 5. Hand me one Verify call (curl and Python) for list\_employees 6. Show audit query (and mention audit stream), then offer clean up Follow the commands at [https://docs.domesystems.ai/agent/tutorials/role/operator.md](https://docs.domesystems.ai/agent/tutorials/role/operator.md) exactly. Non-negotiable rules: * Narrate as you go. Emphasize substrate (backends, gateway, grants) — not a full developer or security walkthrough. * Sandbox only. Confirm `dome context current` starts with `sandbox-`. * Never print the agent token. Write it to a gitignored `.env`. * Do not call governed tools yourself to demo. Hand me the verify commands. * Link the console after create/grant/deploy. Derive base URL from `dome auth status` → `Server`. * Never report a step as done without command output. In this tutorial, you will provision a sandbox, attach `demo-hr` to Default, grant a throwaway agent at the edge, and prove the substrate with one allowed call — then see it in audit. To do this, you will: Create the disposable workspace you will operate in. Make `demo-hr` a governed target on the Gateway. Admit a throwaway agent so you can prove the edge path. Permit discovery and one directory tool so the smoke call succeeds. Call once with curl or Python, then check audit. ## Prerequisites For this tutorial, you will need: * The [Dome CLI](/install) installed and authenticated * A role that can provision a sandbox and attach tools (admin, operator, or equivalent — refer to [Permissions](/concepts/platform/permissions) concept) > This tutorial runs entirely in a sandbox. The throwaway agent and tiny Cedar rule exist only so you can prove backends and grants work — not to replace the [Developer](/tutorials/role/developer) or [Security](/tutorials/role/security) tutorials. ## Provision a sandbox ```bash theme={"system"} dome sandbox provision --scope=workspace --workspace-name role-operator dome context sync dome context use sandbox-role-operator dome context current ``` Confirm the workspace reads `sandbox-role-operator`. ## Attach a tool to Default A tool is unreachable until it belongs to a [Gateway](/connect/gateways). Register the public demo HR server on Default: ```bash theme={"system"} dome tool add \ --name demo-hr \ --url https://demo-mcp.domesystems.ai/mcp \ --protocol streamable-http \ --auth-method none \ --gateway Default ``` Confirm Default lists the connection: ```bash theme={"system"} dome gateway get Default ``` ## Grant a smoke-test agent Without an agent grant, nothing can call through the edge — even with a healthy backend. Register a disposable agent and admit it: ```bash theme={"system"} dome agents register --name role-ops-agent --if-not-exists dome agents create-key role-ops-agent --name smoke dome gateway access grant Default role-ops-agent ``` Save the token to a gitignored `.env`. Treat this agent as scaffolding for the smoke test, not a production workload identity. ## Deploy a minimal allow Gateway grants admit the agent; Cedar still decides each call. Deploy the smallest permit that lets one directory tool succeed: ```cedar title="role-ops-agent.cedar" theme={"system"} permit( principal is Dome::Agent, action == Dome::Action::"mcp:discover", resource ); permit( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"demo-hr/hr/list_employees" ); ``` ```bash theme={"system"} dome rules apply role-ops-agent.cedar --agent role-ops-agent --name role-ops-agent ``` ## Smoke-test the substrate ```bash theme={"system"} dome context current dome gateway list ``` ```bash theme={"system"} export DOME_GATEWAY_URL="https://GATEWAY_HOST/gateways/DEFAULT_GATEWAY_ID" export DOME_TOKEN="dome_..." ``` One allowed call proves backend membership, the grant, and Cedar together. ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/mcp" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "demo-hr/hr/list_employees", "arguments": {} } }' ``` Expect demo employees in `result`. Requires `httpx` (`pip install httpx`): ```python title="smoke_ops.py" theme={"system"} import json import os import httpx url = os.environ["DOME_GATEWAY_URL"].rstrip("/") + "/mcp" token = os.environ["DOME_TOKEN"] response = httpx.post( url, headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", }, json={ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "demo-hr/hr/list_employees", "arguments": {}, }, }, timeout=30.0, ) print(json.dumps(response.json(), indent=2)) ``` ```bash theme={"system"} python smoke_ops.py ``` Confirm the call landed in audit. Tail live events in a second terminal if you want the operator view: ```bash theme={"system"} dome audit query --limit 10 # Optional live tail: # dome audit stream ``` ## Clean up ```bash theme={"system"} dome workspace delete sandbox-role-operator ``` ## Next steps You learned how to attach tools to a Gateway, grant access at the edge, and prove the substrate with a smoke call. Continue with: * [Developer](/tutorials/role/developer) to register real agent workloads and verify allow/deny * [Security](/tutorials/role/security) to tighten Rules, Filters, and evidence export * [Stream Live Events](/operate/observe) for day-to-day triage # Platform Admin Source: https://docs.domesystems.ai/tutorials/role/platform-admin Set who may administer Dome, check platform health, and export an audit snapshot. On Dome, a platform admin owns tenancy boundaries, workspace membership, who may change shared systems, and whether the estate stays operable and auditable. You decide who can administer Dome. You do not usually write every Cedar rule or attach every backend — operators and security run day-to-day Connect and Govern work inside the boundaries you set. Run the Dome Platform Admin role tutorial in a throwaway sandbox: list members, optionally assign a role if I give you a real teammate email, verify platform access and Gateway inventory, and export today's audit snapshot. First, show me this plan and ask me to confirm before running anything: 1. Confirm CLI auth, provision sandbox-role-platform-admin, switch into it 2. List workspace members 3. If I provide a real teammate email, set their role; otherwise skip members set 4. Run `dome auth status` and `dome gateway list` 5. Export today's audit (empty file is OK — we're proving the pipeline) 6. Offer clean up Follow the commands at [https://docs.domesystems.ai/agent/tutorials/role/platform-admin.md](https://docs.domesystems.ai/agent/tutorials/role/platform-admin.md) exactly. Non-negotiable rules: * Narrate as you go. Stay in the admin lane — no Cedar authoring, no backend attachment. * Sandbox only. Confirm `dome context current` starts with `sandbox-`. * Do not invent teammate emails. Ask me for a real address or skip role assignment. * Never report a step as done without showing the command output. * Link the console after membership changes. Derive base URL from `dome auth status` → `Server`. In this tutorial, you will provision a disposable workspace, inspect membership, optionally assign a role, verify platform access and Gateway inventory, and export an audit snapshot. To do this, you will: Create a workspace you can safely administer and delete. List members; set a role only if a real teammate email is available. Confirm authentication and inspect the workspace's Gateways. Prove the compliance export pipeline runs. ## Prerequisites For this tutorial, you will need: * The [Dome CLI](/install) installed and authenticated * Permission to provision a sandbox and manage workspace members (workspace `admin`, or a higher role that cascades — refer to [Permissions](/concepts/platform/permissions) concept) > This tutorial stays in the Manage lane. For backends and Gateways refer to [Operator](/tutorials/role/operator). For Cedar and day-to-day audit investigation refer to [Security](/tutorials/role/security). ## Provision a sandbox ```bash theme={"system"} dome sandbox provision --scope=workspace --workspace-name role-platform-admin dome context sync dome context use sandbox-role-platform-admin dome context current ``` Confirm the workspace reads `sandbox-role-platform-admin`. ## Inspect and assign membership List who is already on the workspace: ```bash theme={"system"} dome workspace members list ``` Each member holds one workspace role: `admin`, `operator`, `security`, or `viewer` (refer to [Permissions](/concepts/platform/permissions) concept). Assign a role only when you have a real teammate email that already exists in your org: ```bash theme={"system"} dome workspace members set --user teammate@example.com --role operator ``` If you do not have a second user handy, skip this command. The rest of the tutorial still exercises health and export. ## Check platform access Confirm the CLI can reach the platform, then list the public Gateways in the active workspace: ```bash theme={"system"} dome auth status dome gateway list ``` Pair that inventory with a quick glance at recent audit activity: ```bash theme={"system"} dome audit query --limit 10 ``` An empty result is fine on a brand-new sandbox. ## Export an audit snapshot Platform admins prove the export pipeline works even when there is little traffic. Export today's events as JSON Lines: ```bash theme={"system"} dome audit export \ --since "$(date -u +%Y-%m-%dT00:00:00Z)" \ --format jsonl > platform-audit.jsonl ``` A zero-line file still means the command succeeded — you verified the path. Use `--format ocsf` or `cef` when a downstream SIEM expects those shapes. ## Clean up ```bash theme={"system"} dome workspace delete sandbox-role-platform-admin ``` ## Next steps You learned how to inspect membership, check gateway health, and export an audit snapshot. Continue with: * [Operator](/tutorials/role/operator) to attach backends and expose Gateways * [Security](/tutorials/role/security) for Rules, Filters, and evidence packages * [Permissions](/concepts/platform/permissions) concept for the full role-to-capability map # Security Source: https://docs.domesystems.ai/tutorials/role/security Author and simulate Cedar, redact sensitive fields, prove denials, and export audit evidence. On Dome, security owns what each agent may call, what content may leave a backend, and whether the audit trail can prove it. You author and simulate Cedar Rules, apply Guards and Filters, investigate denials, and export evidence. Operators make backends reachable. Developers integrate agents. In this sandbox you stand up just enough substrate to exercise the Govern path end to end. Run the Dome Security role tutorial in a throwaway sandbox: attach demo-hr, apply a contact redaction Filter, deploy restrictive Cedar, then hand me curl or Python calls that prove allow, redact, and deny, plus audit export. First, show me this plan and ask me to confirm before running anything: 1. Confirm CLI auth, provision sandbox-role-security, switch into it 2. Register role-sec-agent, mint a key (do not print the token), attach demo-hr, grant Default 3. Create and assign redact-contact Filter on demo-hr responses 4. Write, validate, simulate, and deploy Cedar (directory allow; salary/customer deny) 5. Hand me Verify tabs (curl and Python) for list\_employees, get\_employee (redacted), get\_salary (deny) 6. Show audit query and export today's events, then offer clean up Follow the commands at [https://docs.domesystems.ai/agent/tutorials/role/security.md](https://docs.domesystems.ai/agent/tutorials/role/security.md) exactly. Non-negotiable rules: * Narrate as you go. Before each step, tell me in one or two sentences what you are about to do and why it matters. * Sandbox only. After switching context, run `dome context current` and confirm the workspace name starts with `sandbox-`. If it does not, stop and ask me. * Never print the `dome_...` agent token in chat. Write it into a gitignored `.env`. * Do not call the governed tools yourself to demo outcomes. After setup, hand me the curl/Python commands and expected results, then stop. * After each create/grant/deploy step, give me a markdown link into the Dome console. Derive the base URL from `dome auth status` → `Server`. * Never report a step as done without showing the command output. In this tutorial, you will deploy a restrictive Cedar allowlist, redact contact fields on the response path, prove allow / redact / deny from curl or Python, and export audit evidence. To do this, you will: Create a disposable workspace for this role walk. Register an agent, attach `demo-hr`, and grant Default. Create a Filter and assign it to tool responses. Allow directory tools; deny compensation and customers. Call the gateway with curl or Python. Query denials and export today's audit trail. ## Prerequisites For this tutorial, you will need: * The [Dome CLI](/install) installed and authenticated (`dome auth login`, then `dome auth status`) * A role that can provision a sandbox and deploy rules (admin, security, or equivalent — refer to [Permissions](/concepts/platform/permissions) concept) > This tutorial runs entirely in a sandbox. In a production workspace, attaching backends is typically an [operator](/tutorials/role/operator) action. Here you attach `demo-hr` yourself so you can finish the Govern loop. ## Provision a sandbox ```bash theme={"system"} dome sandbox provision --scope=workspace --workspace-name role-security dome context sync dome context use sandbox-role-security dome context current ``` Confirm the workspace reads `sandbox-role-security` before continuing. ## Stand up minimal substrate You need an agent identity and a reachable tool so Rules and Filters have something to govern. ```bash theme={"system"} dome agents register --name role-sec-agent --if-not-exists dome agents create-key role-sec-agent --name service ``` Save the token to a gitignored `.env` — do not leave it in chat history. ```bash theme={"system"} dome tool add \ --name demo-hr \ --url https://demo-mcp.domesystems.ai/mcp \ --protocol streamable-http \ --auth-method none \ --gateway Default dome gateway access grant Default role-sec-agent ``` ## Redact contact fields A [Guard](/govern/guards) Filter strips sensitive fields from tool responses before the agent sees them. Create `redact-contact.json`: ```json title="redact-contact.json" theme={"system"} { "json": { "components": [ { "fieldActions": [ { "matcher": { "path": "**.email" }, "action": "FILTER_ACTION_REDACT" }, { "matcher": { "path": "**.phone" }, "action": "FILTER_ACTION_REDACT" } ] } ] } } ``` Create the Filter and assign it to the response direction on `demo-hr`: ```bash theme={"system"} dome guards filters create redact-contact \ --description "Redact contact fields in tool responses" \ --config-from redact-contact.json dome tool guards filters set demo-hr \ --direction response \ --filters redact-contact ``` ## Author, simulate, and deploy Rules This allowlist keeps directory tools open. Everything else — including salary and customer records — is denied by `forbid … unless`: ```cedar title="role-sec-agent.cedar" theme={"system"} permit( principal is Dome::Agent, action == Dome::Action::"mcp:discover", resource ); permit( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource ) when { resource in [ Dome::MCPTool::"demo-hr/hr/list_employees", Dome::MCPTool::"demo-hr/hr/get_employee", Dome::MCPTool::"demo-hr/hr/org_chart" ] }; forbid( principal is Dome::Agent, action == Dome::Action::"mcp:call", resource ) unless { resource in [ Dome::MCPTool::"demo-hr/hr/list_employees", Dome::MCPTool::"demo-hr/hr/get_employee", Dome::MCPTool::"demo-hr/hr/org_chart" ] }; ``` Validate and simulate **before** deploying so you know the allow and deny paths: ```bash theme={"system"} dome rules validate role-sec-agent.cedar dome rules simulate --agent role-sec-agent --action mcp:call \ --resource demo-hr/hr/list_employees --resource-type mcp_tool dome rules simulate --agent role-sec-agent --action mcp:call \ --resource demo-hr/finance/get_salary --resource-type mcp_tool ``` Expect `ALLOW`, then `DENY`. Deploy when those match: ```bash theme={"system"} dome rules apply role-sec-agent.cedar --agent role-sec-agent --name role-sec-agent ``` ## Verify allow, redact, and deny ```bash theme={"system"} dome context current dome gateway list ``` ```bash theme={"system"} export DOME_GATEWAY_URL="https://GATEWAY_HOST/gateways/DEFAULT_GATEWAY_ID" export DOME_TOKEN="dome_..." ``` **List employees** — allowed: ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/mcp" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "demo-hr/hr/list_employees", "arguments": {} } }' ``` **Get E001** — allowed, email redacted: ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/mcp" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "demo-hr/hr/get_employee", "arguments": { "employee_id": "E001" } } }' ``` Expect `"email": "[REDACTED]"`. **Get salary** — denied: ```bash theme={"system"} curl -sS -X POST "$DOME_GATEWAY_URL/mcp" \ -H "Authorization: Bearer $DOME_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "demo-hr/finance/get_salary", "arguments": { "employee_id": "E001" } } }' ``` Requires `httpx` (`pip install httpx`): ```python title="verify_security.py" theme={"system"} import json import os import httpx url = os.environ["DOME_GATEWAY_URL"].rstrip("/") + "/mcp" token = os.environ["DOME_TOKEN"] def call(name: str, arguments: dict | None = None) -> None: response = httpx.post( url, headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", }, json={ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": name, "arguments": arguments or {}}, }, timeout=30.0, ) print(json.dumps(response.json(), indent=2)) call("demo-hr/hr/list_employees") call("demo-hr/hr/get_employee", {"employee_id": "E001"}) call("demo-hr/finance/get_salary", {"employee_id": "E001"}) ``` ```bash theme={"system"} python verify_security.py ``` Expect allow, redacted email, then deny. ## Investigate and export evidence ```bash theme={"system"} dome audit query --limit 20 dome audit query --results denied --limit 10 ``` Export today's trail as JSON Lines for a compliance package or SIEM: ```bash theme={"system"} dome audit export \ --since "$(date -u +%Y-%m-%dT00:00:00Z)" \ --format jsonl > security-audit.jsonl ``` ## Clean up ```bash theme={"system"} dome workspace delete sandbox-role-security ``` ## Next steps You learned how to author and simulate Cedar, redact sensitive fields, prove denials, and export audit evidence. Continue with: * [Developer](/tutorials/role/developer) to register agents and verify from the workload side * [Operator](/tutorials/role/operator) to attach backends and expose Gateways * [Simulate Rules](/govern/rules/simulate) for deeper pre-deploy checks # What is Dome? Source: https://docs.domesystems.ai/what-is-dome How Dome governs agent access to tools and models Dome is the governance layer between AI agents and the tools and models they use. It gives each agent an identity, controls what it can reach and do, protects credentials and sensitive data, and records activity. ## How does Dome work? An agent sends each request to a Gateway, a named endpoint that exposes a selected group of tools and models. Dome identifies the agent, checks Gateway access, evaluates authorization rules and quotas, applies request Guards, calls the resource with protected backend credentials, applies response Guards, and records what happened. AI agents access tools and models through Dome, which connects, governs, operates, and manages the request path Refer to [Architecture](/concepts/architecture) concept for the component model and the stage-by-stage request pipeline. Dome's pillars support this flow: 1. **[Connect](/connect).** Configure agents, tools, models, and Gateways so workloads can reach resources under governance. 2. **[Govern](/govern).** Control which resources agents can reach and what actions they may perform. Set quotas, protect credentials, and apply Guards. 3. **[Operate](/operate).** Investigate audit events, stream live events, and subscribe to events. **[Manage](/manage)** and **[Develop](/develop)** support those pillars. Manage administers the platform around them (access, environments, integrations, settings, and export). Develop wires an agent into product code: authenticate, call tools and models through a Gateway, and handle denials. ## Next steps Once you know what Dome is, pick a path in: * [Install](/install) the CLI and select a workspace * [Quickstart](/quickstart) to register an agent and verify governed traffic * [Personas](/personas) to identify your role and find relevant docs * [Architecture](/concepts/architecture) concept for the component model and request path * [Develop](/develop) when you are ready to wire the application