diff --git a/developer-guide/protect/observability.mdx b/developer-guide/protect/observability.mdx index 68829c9..50fcd49 100644 --- a/developer-guide/protect/observability.mdx +++ b/developer-guide/protect/observability.mdx @@ -5,6 +5,8 @@ description: 'Monitor Dome Guardrails with OpenTelemetry, tracing, and logging.' Dome is OpenTelemetry-compliant and integrates with popular observability platforms. This guide covers tracing, metrics, and logging setup. +If you use [Trust Runtime](/developer-guide/protect/trust-runtime), its structured audit events complement Dome telemetry with Agent identity, tool access, attestation, downgrade, and enforcement-heartbeat decisions. Every event includes the configured `agent_id`; cryptographic identity fields are available only when the runtime is attested. + ## Quick Setup Install the [OpenTelemetry](https://opentelemetry.io/) support version of Dome using pip or your package manager. @@ -15,7 +17,7 @@ Install the [OpenTelemetry](https://opentelemetry.io/) support version of Dome u [Weave](https://weave-docs.wandb.ai/) is W&B's toolkit for tracing LLM applications. You can use it with Dome to trace all inputs and outputs by wrapping Dome with Weave. -In your Weave dashboard, you'll see: +In your Weave dashboard, you will see: - All `guard_input` and `guard_output` calls - Individual Guard execution within each Guardrail - Detector-level details for each Guard @@ -26,7 +28,7 @@ In your Weave dashboard, you'll see: ## OpenTelemetry Integration -Dome works with any OpenTelemetry-compliant platform (Jaeger, Uptrace, Signoz, etc.). +Dome works with OpenTelemetry-compliant platforms such as Jaeger, Uptrace, Signoz, etc. ### Basic Setup @@ -95,7 +97,7 @@ Dome logs to Python loggers under `vijil.dome`. You can add standard handlers to |-------|---------------| | `DEBUG` | Initialization details, parameters | | `INFO` | Inputs and outputs from each component | -| `WARNING` | Missing config, context window exceeded, timeouts | +| `WARNING` | Missing configuration, context window exceeded, timeouts | | `ERROR` | Handled exceptions during execution | | `CRITICAL` | Initialization failures, unhandled exceptions | @@ -132,6 +134,9 @@ You can set up alerts for anomalies like high block rates or high latency direct ## Next Steps + + Capture identity and policy decisions + Guard configuration options diff --git a/developer-guide/protect/overview.mdx b/developer-guide/protect/overview.mdx index 1b8cdfb..617ad78 100644 --- a/developer-guide/protect/overview.mdx +++ b/developer-guide/protect/overview.mdx @@ -1,150 +1,176 @@ --- title: 'Protection Overview' -description: 'Guard your agent at runtime with Dome Guardrails.' +description: 'Protect Agent inputs, outputs, and tool use with Dome.' --- -**TL;DR:** Dome wraps your [Agent](/owner-guide/register-agents/what-is-an-agent) with configurable [Guardrails](/concepts/defense/guardrail) that intercept every input and output, blocking attacks before they reach your agent or your users. Configure which [Guards](/concepts/defense/guard) run via your registered Vijil Agent, a TOML file, or a Python dict, then choose early-exit (fast) or parallel execution (thorough) mode. +**TL;DR:** Dome scans [Agent](/owner-guide/register-agents/what-is-an-agent) inputs and outputs with configurable Guardrails. Call `Dome` directly or use an integration to apply the resulting decision. Dome marks flagged content for enforcement in enforce mode, while shadow mode reports detected violations without marking them for enforcement. -Evaluation catches vulnerabilities you know to test for. But attackers will try things you did not anticipate like new prompt injection techniques, novel encoding tricks, social engineering patterns that emerge after your last evaluation. +Evaluation catches vulnerabilities you know to test for. Attackers will also try techniques you did not expect, such as new prompt injections, encoding tricks, and social engineering patterns that emerge after your last evaluation. -Dome is Vijil's runtime protection system. It intercepts every input and output, applies configurable Guardrails, and blocks attacks before they reach your agent or your users. When Diamond identifies vulnerabilities you cannot immediately fix, Dome provides defense-in-depth while you remediate. +Dome protects Agents at runtime. Its content Guardrails scan data before and after Agent execution. Direct `Dome` calls return a `ScanResult`; your application or framework integration decides how to apply the result, such as stopping execution or returning blocked or sanitized content. + +For tool-using Agents, [Trust Runtime](/developer-guide/protect/trust-runtime) adds trust controls when you configure them. These controls include SPIFFE workload identity, Console or local constraints, tool-level mandatory access control, signed tool manifest support, tool attestation, structured audit events, and optional enforcement heartbeats. Use `secure_agent()` with LangGraph, Google ADK, or Strands, or use `TrustRuntime` directly for a custom integration. + +## Choose a Protection Interface + +| Interface | Use It When | Protection | +|-----------|-------------|------------| +| `Dome` | You need to scan Agent inputs and outputs | Content Guardrails | +| `secure_agent()` | You use LangGraph, Google ADK, or Strands | Content Guardrails plus configured identity, framework-dependent tool access control, attestation, and audit events | +| `TrustRuntime` | You need direct control or a custom integration | Individual methods for each configured trust enforcement point | ## How Dome Works -Dome wraps your agent with configurable Guardrails: +A Guardrail contains one or more Guards, and each Guard invokes one or more Detectors. Input and output Guardrails run on opposite sides of Agent execution: ```mermaid actions={false} %%{init: {'theme':'base', 'themeVariables': {'fontFamily':'Futura Medium, Futura, sans-serif','fontSize':'13px'}, 'flowchart': {'nodeSpacing':25,'rankSpacing':40,'padding':6}}}%% flowchart LR - subgraph Guardrails[Guardrails] - GuardIn[Guard] - DetectorIn[Detector] - ModelIn[Model] - ActionIn[Action] - ActionOut[Action] - ModelOut[Model] - DetectorOut[Detector] - GuardOut[Guard] + Request[Request] --> InputGuard + + subgraph InputGuardrail[Input Guardrail] + direction LR + InputGuard[Guard] --> InputDetector[Detector] end - Agent((Trusted Agent)) + InputDetector --> Agent((Agent)) + Agent --> OutputGuard - GuardIn --> DetectorIn --> ModelIn --> ActionIn --> Agent - Agent --> ActionOut --> ModelOut --> DetectorOut --> GuardOut + subgraph OutputGuardrail[Output Guardrail] + direction LR + OutputGuard[Guard] --> OutputDetector[Detector] + end - classDef guard fill:#DE1616,stroke:#2B0C0C,color:#FFFFFF,stroke-width:1px; - classDef redLine fill:#FFFFFF,stroke:#DE1616,color:#DE1616,stroke-width:1.5px; - classDef agent fill:#0247A9,stroke:#DE1616,color:#FFFFFF,stroke-width:3px; - classDef railbox fill:none,stroke:#DE1616,color:#DE1616,stroke-width:1px; + OutputDetector --> Response[Response] - class GuardIn,GuardOut guard; - class DetectorIn,DetectorOut,ModelIn,ModelOut,ActionIn,ActionOut redLine; + classDef endpoint fill:#FFFFFF,stroke:#2B0C0C,color:#2B0C0C,stroke-width:1.5px; + classDef guard fill:#DE1616,stroke:#2B0C0C,color:#FFFFFF,stroke-width:1px; + classDef detector fill:#FFFFFF,stroke:#DE1616,color:#DE1616,stroke-width:1.5px; + classDef agent fill:#0247A9,stroke:#2B0C0C,color:#FFFFFF,stroke-width:1.5px; + classDef railbox fill:none,stroke:#DE1616,color:#DE1616,stroke-width:1px; + + class Request,Response endpoint; + class InputGuard,OutputGuard guard; + class InputDetector,OutputDetector detector; class Agent agent; - class Guardrails railbox; + class InputGuardrail,OutputGuardrail railbox; ``` | Component | Purpose | |-----------|---------| -| **[Guardrail](/concepts/defense/guardrail)** | Pipeline of Guards (input or output) | -| **[Guard](/concepts/defense/guard)** | Group of Detectors of one type | +| **[Guardrail](/concepts/defense/guardrail)** | Input or output pipeline containing Guards | +| **[Guard](/concepts/defense/guard)** | Group of Detectors for one protection category | | **[Detector](/concepts/defense/detector)** | Individual detection method | ## Protection Types -### Security Guards - -Detect and block adversarial attacks: - -| Detector | What It Catches | -|----------|-----------------| -| `prompt-injection-mbert` | Injected instructions in user input | -| `prompt-injection-deberta-v3-base` | Advanced injection attempts | -| `encoding-heuristics` | Base64, Unicode, and encoding attacks | -| `security-embeddings` | Semantic similarity to known attacks | - -### Moderation Guards +Dome supports six Guard categories. The following table shows representative registered Detectors rather than the complete catalog: -Filter harmful and inappropriate content: +| Guard Category | Purpose | Representative Detectors | +|----------------|---------|--------------------------| +| **Security** | Detect prompt injection, jailbreaks, and obfuscated payloads | `prompt-injection-mbert`, `encoding-heuristics` | +| **Moderation** | Detect harmful content and match configured keyword lists | `moderation-deberta`, `moderation-flashtext` | +| **Privacy** | Detect PII and credentials | `privacy-presidio`, `detect-secrets` | +| **Integrity** | Check factual consistency and hallucinations | `hhem-hallucination`, `fact-check-roberta` | +| **Generic** | Apply a configurable LLM classifier | `generic-llm` | +| **Policy** | Check content against configured policy sections | `policy-sections` | -| Detector | What It Catches | -|----------|-----------------| -| `moderation-flashtext` | Fast keyword-based toxicity | -| `moderation-deberta` | Neural toxicity classification | -| `moderations-oai-api` | OpenAI Moderation API | -| `moderation-llamaguard` | Llama Guard safety model | + +See [Detection Methods](/developer-guide/protect/detection-methods) for the full Detector catalog and configuration parameters. + -### Privacy Guards +## Default Guardrails -Prevent exposure of sensitive data: +`Dome()` creates input and output Guardrails with the following default checks: -| Detector | What It Catches | -|----------|-----------------| -| `privacy-presidio` | PII (names, emails, SSN, etc.) | -| `detect-secrets` | API keys, passwords, credentials | +| Direction | Default Checks | +|-----------|----------------| +| **Input** | Encoding heuristics, prompt injection detection, moderation | +| **Output** | Moderation, PII detection | -## Quick Start - -You can protect your agents with default Guards. The default configuration includes: -- **Input**: Prompt injection detection, encoding heuristics, moderation -- **Output**: Moderation, PII detection +The default prompt injection and moderation models require the `local` extra when you run them locally, and Presidio PII detection requires the `pii` extra. You can instead set `DOME_INFERENCE_URL` to route supported Detectors, including the default model and PII Detectors, to an inference service. ## Configuration Sources -Dome accepts configuration from three sources: +Choose a configuration source that matches how you operate Dome: | Source | How to Use | Best For | -|--------|-----------|----------| -| **Registered Agent** | Pull from your Vijil Console agent definition | Production: keeps configuration in sync with your policy | -| **TOML file** | Reference a local `.toml` file at initialization | Version-controlled configuration in CI/CD | -| **Python dict** | Pass a `dict` at runtime | Dynamic configuration, testing, prototyping | +|--------|------------|----------| +| **Built-in defaults** | Initialize `Dome()` without a configuration | Initial setup and evaluation | +| **Local configuration** | Pass a Python dictionary or TOML path to `Dome` | Version-controlled or dynamic configuration | +| **Registered Agent** | Use `Dome.create_from_vijil_agent()` | Console-managed Agent configuration | +| **Evaluation recommendation** | Use `Dome.create_from_vijil_evaluation()` | Guardrails derived from evaluation results | +| **S3** | Use `Dome.create_from_s3()` with the `s3` extra | Remotely managed configuration in AWS | -For the full list of configuration options, see [Configure Guardrails](/developer-guide/protect/configuring-guardrails). + +For the available settings, see [Configure Guardrails](/developer-guide/protect/configuring-guardrails). + ## Scan Results -Every scan returns a result object with the following fields: +Every input or output scan returns a `ScanResult` with these key members: -| Field | Type | Description | -|-------|------|-------------| -| `is_safe` | Boolean | `True` if content passed all Guards | -| `flagged` | Boolean | `True` if any Guard flagged the content | -| `response_string` | String | Original content if safe; fallback message if blocked | -| `exec_time` | Float | Scan duration in milliseconds | -| `trace` | Dict | Per-Guard and per-Detector execution details | +| Member | Type | Description | +|--------|------|-------------| +| `flagged` | `bool` | Whether any Guard flagged the content | +| `enforced` | `bool` | Whether Dome marks a flagged result for enforcement | +| `response_string` | `str` | Original, blocked, or sanitized content returned by the Guardrail | +| `detection_score` | `float` | Highest triggered detection score, clamped between `0.0` and `1.0` | +| `triggered_methods` | `list[str]` | Detectors that flagged the content | +| `errored_methods` | `list[str]` | Detectors or Guard tasks that returned errors or timed out | +| `trace` | `dict` | Per-Guard and per-Detector execution details | +| `exec_time` | `float` | Total scan duration in seconds | +| `is_safe()` | Method returning `bool` | Returns `True` when `flagged` is `False`, regardless of enforcement mode | ## Framework Integrations -Dome is designed to be integrable with popular frameworks and runtimes. You can see the specific framework developer guides for integration patterns during the developer access phase. +You can call `Dome.guard_input()` and `Dome.guard_output()` from any Agent framework. For the complete trust stack, `secure_agent()` provides built-in adapters for LangGraph, Google ADK, and Strands. + +Tool enforcement depends on the integration points exposed by each framework. LangGraph tool access control is best-effort because the compiled graph must expose tool functions that the adapter can discover and wrap. LangGraph checks streaming output only after it yields the chunks, so an output Guard cannot retract delivered chunks. + + +See [Trust Runtime](/developer-guide/protect/trust-runtime) for integration examples and constraints. + + +## Execution and Failure Options -## Performance Options +Early exit and parallel execution are independent options at both the Guardrail and Guard levels: -| Mode | Behavior | Best For | -|------|----------|----------| -| **Early exit** (default) | Stops after the first Guard flags content | Rejecting clearly malicious input with minimal latency | -| **Complete execution** | Runs all Guards regardless of flags | Comprehensive audit trails, maximum detection coverage | -| **Parallel execution** | Runs Guards concurrently | High-throughput production environments | +| Option | Behavior | +|--------|----------| +| **Early exit** | Stops after the first configured Guard or Detector flags content | +| **Parallel execution** | Runs configured Guards or Detectors concurrently and can reduce scan wall-clock latency | -See [Configure Guardrails](/developer-guide/protect/configuring-guardrails) for the configuration options for each mode. +By default, Guards and Guardrails use early exit and do not use parallel execution. Disable early exit to run every configured check. You can also combine early exit with parallel execution; Dome cancels pending checks after one flags content. + +When configuration omits `on-error` settings, `Dome(enforce=True)` defaults to `fail_closed`, while `Dome(enforce=False)` defaults to `fail_open`. Explicit Guardrail-level or Guard-level `on-error` values override the inherited default. Failed methods remain available in `errored_methods` for inspection. + + +See [Configure Guardrails](/developer-guide/protect/configuring-guardrails) for the settings at each level. + - - The programmatic protection capabilities are currently in private preview and subject to change. - ## Next Steps + + Add identity, tool access control, attestation, and audit events + - Detailed Guard configuration options + Configure Guards, Detectors, execution, and failure behavior - Runtime patterns and best practices + Apply Guardrails at runtime + + + Review built-in Detectors and their parameters Build your own detection methods - Monitoring and tracing setup + Track Guardrail decisions and traces diff --git a/developer-guide/protect/trust-runtime.mdx b/developer-guide/protect/trust-runtime.mdx new file mode 100644 index 0000000..10a078c --- /dev/null +++ b/developer-guide/protect/trust-runtime.mdx @@ -0,0 +1,419 @@ +--- +title: 'Trust Runtime' +description: 'Add identity, content Guards, tool access control, attestation, and audit events to your agent.' +--- + +Trust Runtime adds in-process security enforcement to an AI agent. It combines agent identity, local or Console-sourced constraints, Dome content Guards, tool-level mandatory access control, signed tool manifests, tool attestation, structured audit events, and optional enforcement heartbeats. + +You can add the complete trust stack to a supported agent framework with `secure_agent()`. Use `TrustRuntime` directly when you need to control each enforcement point or integrate another framework. + +## Choose the Right Interface + +| Interface | Use It When | Protection | +|-----------|-------------|------------| +| `Dome` | You only need to scan agent inputs and outputs | Content Guardrails | +| `secure_agent()` | You use LangGraph, Google ADK, or Strands | Identity, content Guards, framework-dependent tool access control, attestation, and audit events | +| `TrustRuntime` | You need a custom integration or direct control of enforcement | The same trust capabilities through individual methods | + +The Controls Engine is a separate policy-as-data interface. Use Trust Runtime when you need identity-aware enforcement and framework integration in addition to content checks. Tool decisions become identity-bound when the runtime has an attested SPIFFE identity. + +## Install Trust Runtime + +Install the core trust dependencies when you use `TrustRuntime` directly: + +```bash +pip install "vijil-dome[trust]" +``` + +Install the framework adapters when you use `secure_agent()` with LangGraph, Google ADK, or Strands: + +```bash +pip install "vijil-dome[trust-adapters]" +``` + +The `trust` and `trust-adapters` extras include SPIFFE identity support. If you only need SPIFFE identity with an otherwise minimal installation, install the standalone identity extra: + +```bash +pip install "vijil-dome[identity]" +``` + +## Protect an Agent With `secure_agent()` + +Pass your agent object, its registered `agent_id`, and a constraint source to `secure_agent()`. The following examples use the local `constraints` dictionary from [Configure Constraints](#configure-constraints). Start in `warn` mode while you validate the integration: + +```python +from vijil_dome import secure_agent + +secured_agent = secure_agent( + agent, + agent_id="travel-agent", + constraints=constraints, + mode="warn", +) +``` + +`secure_agent()` detects the framework from the agent object and installs the appropriate adapter. Change the mode to `enforce` when you are ready to block violations: + +```python +secured_agent = secure_agent( + agent, + agent_id="travel-agent", + constraints=constraints, + mode="enforce", +) +``` + + +An `agent_id` identifies the registered Agent and its configuration. It is not cryptographic proof of the running workload's identity. SPIFFE attestation provides that proof when it is available. + + +You can provide an authenticated Console client instead of explicit constraints. If you omit both `client` and `constraints`, Trust Runtime creates a minimal local configuration with no content Guards and no tool permissions. Content scanning is skipped, and an enforced policy denies tools that are not explicitly permitted. + +### Integrate LangGraph + +Pass either a LangGraph `StateGraph` or compiled graph. `secure_agent()` returns a `SecureGraph` that applies Trust Runtime around graph execution: + +```python +from vijil_dome import secure_agent + +app = secure_agent( + graph, + agent_id="travel-agent", + constraints=constraints, + mode="warn", +) + +result = app.invoke({"messages": messages}) +``` + +Use the returned object instead of calling `graph.compile()` separately. The wrapper applies input and output Guards around graph execution. Tool access control is best-effort for LangGraph: the compiled graph must expose tool functions that the adapter can discover and wrap. Verify tool enforcement in your graph before relying on it. + +### Integrate Google ADK + +Pass a Google ADK `Agent`. The adapter injects callbacks and returns the same Agent: + +```python +from vijil_dome import secure_agent + +agent = secure_agent( + agent, + agent_id="travel-agent", + constraints=constraints, + mode="warn", +) +``` + +### Integrate Strands + +Pass a Strands `Agent`. The adapter adds a trust hook provider and returns the same Agent: + +```python +from vijil_dome import secure_agent + +agent = secure_agent( + agent, + agent_id="travel-agent", + constraints=constraints, + mode="warn", +) +``` + +For an unsupported object type, `secure_agent()` raises `TypeError`. Use `TrustRuntime` directly or register a custom adapter when automatic framework integration is not available. + +## Configure Constraints + +Trust Runtime resolves constraints in this order: + +1. The `constraints` argument, supplied as an `AgentConstraints` object or dictionary. +2. The Console, when you provide a client and do not provide explicit constraints. +3. A minimal local configuration with no content Guards or tool permissions. + +Explicit constraints are useful for local development and tests: + +```python +from datetime import UTC, datetime + +from vijil_dome import secure_agent + +constraints = { + "agent_id": "travel-agent", + "tool_permissions": [ + { + "name": "search_flights", + "identity": "spiffe://example.com/tools/flights/v1", + "endpoint": "mcp+tls://flights.internal:8443", + }, + ], + "dome_config": { + "input_guards": ["security_guard"], + "output_guards": ["moderation_guard"], + "guards": { + "security_guard": { + "type": "security", + "methods": ["encoding-heuristics"], + }, + "moderation_guard": { + "type": "moderation", + "methods": ["moderation-flashtext"], + }, + }, + }, + "organization": { + "required_input_guards": [], + "required_output_guards": [], + "denied_tools": ["get_api_credentials"], + }, + "enforcement_mode": "warn", + "updated_at": datetime.now(tz=UTC), +} + +agent = secure_agent( + agent, + agent_id="travel-agent", + constraints=constraints, + mode="warn", +) +``` + + +The effective enforcement mode is the stronger of the local `mode` and the mode in the resolved constraints. A local `mode="warn"` cannot downgrade Console-mandated or constraint-mandated `enforce` behavior. + + +If you provide a client, Trust Runtime uses its API key as the development identity when a token is present. With an authenticated SPIFFE identity and an mTLS client, the runtime requests constraints by SPIFFE ID. Otherwise, it requests constraints by `agent_id`. + + +If you omit both `client` and `constraints`, the minimal local configuration does not provide useful production protection. Supply one of these sources before you enable enforcement for an Agent. + + +## Use `TrustRuntime` Directly + +Create a `TrustRuntime` when you need to place each check in your own execution flow: + +```python +from vijil_dome import TrustRuntime + +runtime = TrustRuntime( + agent_id="travel-agent", + constraints=constraints, + mode="enforce", +) +``` + +### Guard Inputs and Outputs + +Run content through the configured Dome Guards before and after agent execution: + +```python +input_result = runtime.guard_input(user_query) +if input_result.flagged and input_result.enforced: + return input_result.guarded_response + +response = run_agent(user_query) + +output_result = runtime.guard_output(response) +if output_result.flagged and output_result.enforced: + return output_result.guarded_response + +return response +``` + +An enforcement result reports whether content was `flagged`, whether the decision was `enforced`, a detection `score`, the optional `guarded_response`, execution time, and per-Guard trace information. + +Use the asynchronous methods when your execution flow is asynchronous: + +```python +input_result = await runtime.aguard_input(user_query) +output_result = await runtime.aguard_output(response) +``` + +### Check Tool Calls + +Call `check_tool_call()` before executing a tool: + +```python +args = {"origin": "SFO", "destination": "NRT"} +decision = runtime.check_tool_call("search_flights", args) + +if decision.permitted: + result = search_flights(**args) +elif decision.enforced: + raise PermissionError(decision.error) +``` + +The decision includes the policy result, whether the caller identity was verified, and whether a denial is enforced. A tool can be denied because it is blocked by organization constraints, missing from the Agent's permissions, restricted to other actions, or evaluated against an incompatible identity policy. + +### Guard Tool Responses + +Run a string returned by a tool through the output Guards before sending it to the model: + +```python +tool_result = search_flights(**args) +guard_result = runtime.guard_tool_response("search_flights", tool_result) + +if guard_result.flagged and guard_result.enforced: + tool_result = guard_result.guarded_response +``` + +### Wrap Tools + +Use `wrap_tool()` or `wrap_tools()` to apply access checks and output Guards automatically: + +```python +safe_search = runtime.wrap_tool(search_flights) + +safe_tools = runtime.wrap_tools([ + search_flights, + book_hotel, +]) +``` + +In `enforce` mode, a denied wrapped tool raises `PermissionError`. In `warn` mode, the runtime logs the denial and calls the tool. If a wrapped tool returns a string, Trust Runtime also applies the configured output Guards. + +## Understand Identity + +Trust Runtime resolves identity through these paths: + +| Identity Path | When It Is Used | Attested | +|---------------|-----------------|----------| +| API key | A supplied Console client contains an API token | No | +| SPIFFE X.509 SVID | The runtime can connect to the configured SPIRE agent socket | Yes | +| Unattested fallback | Neither an API key nor a SPIFFE identity is available | No | + +The default SPIRE agent socket is `/run/spire/sockets/agent.sock`. Set `spire_socket` when your SPIRE Workload API uses another location: + +```python +runtime = TrustRuntime( + agent_id="travel-agent", + constraints=constraints, + spire_socket="/var/run/spire/agent.sock", + mode="enforce", +) +``` + +An unattested runtime emits an `identity_unattested` audit event. Whether an unattested Agent can call tools depends on the resolved `unattested_tool_policy` and enforcement mode. + +## Verify Tool Manifests + +A tool manifest declares the tools an Agent expects to call. Each entry associates a tool name with its endpoint and expected SPIFFE identity. The manifest signature protects this inventory from undetected modification. + +Verify the manifest signature against a trusted public key before you pass the manifest to `TrustRuntime`. The runtime loads the manifest and uses it for endpoint attestation; `attest()` does not verify the manifest signature. + +Pass a `Path` or a loaded `ToolManifest` to the runtime: + +```python +from pathlib import Path + +from vijil_dome import TrustRuntime + +runtime = TrustRuntime( + agent_id="travel-agent", + constraints=constraints, + manifest=Path("manifest.json"), + mode="enforce", +) + +attestation = runtime.attest() +if not attestation.all_verified: + for tool in attestation.tools: + if not tool.verified: + print(tool.tool_name, tool.error) +``` + +`attest()` connects to each declared endpoint, reads the SPIFFE ID from the peer certificate, and compares it with the expected identity. When the Agent has an X.509 SVID, the connection uses mTLS. Use `attest_async()` in an asynchronous application. + +If no manifest is configured, attestation succeeds with an empty tool list. If a manifest is configured but the Agent is not attested, each tool verification fails because the runtime cannot establish an identity-bound verification path. + +Framework adapters collect and expose the attestation result, but they do not stop startup when `all_verified` is `False`. If your deployment requires verified tool identities, inspect the result before the Agent accepts traffic. + + +The Dome repository contains the manifest model and client-side signing and verification code. Availability of the Console signing endpoints and packaged Manifest CLI still requires confirmation against the current Console release. A dedicated Manifest CLI reference is forthcoming. + + +## Capture Audit Events + +Trust Runtime emits an `AuditEvent` for security-relevant decisions. By default, it sends events to Python logging. Pass an `audit_sink` to route events to your telemetry pipeline: + +```python +from vijil_dome import TrustRuntime + + +def send_audit_event(event): + telemetry_client.emit(event.model_dump(mode="json")) + + +runtime = TrustRuntime( + agent_id="travel-agent", + constraints=constraints, + audit_sink=send_audit_event, + mode="enforce", +) +``` + +Events contain an `event_type`, configured `agent_id`, timestamp, and event-specific attributes. The runtime emits events for content Guard decisions, tool access checks, attestation results, identity or mTLS downgrade conditions, disabled Guards, and enforcement-mode downgrade attempts. Cryptographic identity fields are available only when the runtime has an attested identity. + +See [Observability](/developer-guide/protect/observability) for Dome tracing and metrics. Trust Runtime audit events complement those signals with identity and policy decisions. + +## Emit Enforcement Heartbeats + +Set `heartbeat_interval` to emit an enforcement heartbeat on a schedule: + +```python +runtime = TrustRuntime( + agent_id="travel-agent", + constraints=constraints, + mode="enforce", + heartbeat_interval=60.0, +) +``` + +The constructor starts one daemon thread for the runtime. It emits immediately, then once per interval. Each heartbeat reports the configured mode, whether Guards were constructed, whether configured Detectors are reachable, and whether the Agent has an attested SPIFFE identity. + +The default heartbeat is unsigned. Pass a configured `BeaconSigner` when a downstream system must verify its origin. You can also manage the scheduler directly: + +```python +runtime.start_heartbeat(60.0) +health = runtime.heartbeat_health() +runtime.stop_heartbeat() +``` + +`start_heartbeat()` requires a positive interval and does nothing if a scheduler is already running. `stop_heartbeat()` is safe to call more than once. + +## Handle Failure Behavior + +| Condition | `warn` Behavior | `enforce` Behavior | +|-----------|-----------------|--------------------| +| Content is flagged | Record the result without blocking | Return or substitute the guarded response | +| Tool policy denies a wrapped call | Log the denial and call the tool | Raise `PermissionError` before execution | +| Dome cannot initialize | Log the error, disable Guards, and emit an audit event | Raise `RuntimeError` during initialization | +| Agent identity is unattested | Emit an audit event; tool behavior follows `unattested_tool_policy` | Apply the same policy and enforce any denial | +| Tool identity does not match the manifest | Return a failed attestation status | Return a failed attestation status | + +The following failures do not depend on the enforcement mode: + +- A mode other than `warn` or `enforce` raises `ValueError`. +- An unsupported object passed to `secure_agent()` raises `TypeError`. +- A non-positive heartbeat interval raises `ValueError`. +- An invalid manifest file fails while the runtime loads and validates it. +- Direct heartbeat emission propagates signer or audit-sink errors; scheduled emission logs the error and continues the scheduler. + +Your application decides whether a failed `attest()` result prevents startup. Check `all_verified` before accepting traffic when tool identity is a deployment requirement. + +## Next Steps + + + + Compare Dome content Guardrails with the full Trust Runtime. + + + Select and configure the content Guards used by Trust Runtime. + + + Review the built-in Detectors available to Dome Guards. + + + Configure tracing, metrics, and logging for Dome. + + + Create, sign, and verify tool manifests from the command line. + + diff --git a/developer-guide/protect/using-guardrails.mdx b/developer-guide/protect/using-guardrails.mdx index 97337d0..474c9d9 100644 --- a/developer-guide/protect/using-guardrails.mdx +++ b/developer-guide/protect/using-guardrails.mdx @@ -25,6 +25,9 @@ You will learn how to perform: ## Next Steps + + Protect tool calls with identity, access control, and attestation + Detailed Guard configuration diff --git a/docs.json b/docs.json index 8965428..4a2e12a 100644 --- a/docs.json +++ b/docs.json @@ -214,6 +214,7 @@ "group": "Protect Agents", "pages": [ "developer-guide/protect/overview", + "developer-guide/protect/trust-runtime", "developer-guide/protect/configuring-guardrails", "developer-guide/protect/using-guardrails", "developer-guide/protect/custom-detectors",