> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sec0.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Scoped Policies

> Per-agent policy configuration for node-level compliance enforcement.

Agent-scoped policies let you apply different compliance and enforcement settings to individual agents (identified by `nodeId`) within the same workspace. When no agent-scoped policy exists, the middleware treats the agent as having **no enforcement** (allow-all). Having the SDK present is not sufficient without an active policy.

## How It Works

The middleware resolves policies using a scope hierarchy:

1. **Base policy**: workspace-level policy (no `nodeId`), applies to all agents by default.
2. **Agent policy**: node-specific policy keyed by `nodeId`, overrides the base policy for that agent.

When the middleware receives a tool call, it resolves the effective `nodeId` from the runtime agent state header (`x-agent-state`) and fetches the corresponding policy based on the configured scope.

## Configuration

### Remote Policy Source (Dynamic)

For production environments, configure the middleware to fetch policies dynamically per `nodeId`:

```typescript theme={null}
sec0SecurityMiddleware({
  policy: {
    source: "control-plane",
    scope: "auto",           // "base" | "agent" | "auto"
    nodeId: "ehr-agent",     // default nodeId when runtime header is missing
    fallbackToBase: true,    // fall back to base policy if agent policy is empty
    refreshTtlMs: 0,         // 0 = fetch every call; >0 = cache for N ms
  },
  controlPlaneUrl: process.env.SEC0_URL,
  auth: { apiKey: process.env.SEC0_API_KEY },
  // ... other middleware options
});
```

<Note>
  These examples focus on policy-source fields. Keep your base middleware options (`signer`, `otel`, `sec0`, and other required fields) from [Quickstart](/docs/quickstart).
</Note>

### Local Policy (Static)

For local development or simple setups, pass the policy YAML directly. Note that static policies apply uniformly. Agent scoping requires a remote source that resolves per-`nodeId`.

```typescript theme={null}
import { parsePolicyYaml } from "sec0-sdk/policy";

const policyYaml = `tenant: my-app\n...`;

sec0SecurityMiddleware({
  policy: parsePolicyYaml(policyYaml),
  // ... other middleware options
});
```

## Policy Source Options

| Key              | Type                             | Required | Description                                                          |
| ---------------- | -------------------------------- | -------- | -------------------------------------------------------------------- |
| `source`         | `"control-plane"`                | Yes      | Indicates remote policy fetch                                        |
| `scope`          | `"base"`, `"agent"`, or `"auto"` | No       | Which scope to fetch (default `"auto"`)                              |
| `nodeId`         | string                           | No       | Default nodeId when runtime nodeId is missing                        |
| `fallbackToBase` | boolean                          | No       | Fall back to base policy when agent policy is empty (default `true`) |
| `refreshTtlMs`   | number                           | No       | Cache TTL in ms; `0` fetches on every tool invocation (default `0`)  |
| `level`          | `"gateway"` or `"middleware"`    | No       | Policy tier to fetch (default `"middleware"`)                        |

## Scope Behavior

| Scope     | Runtime nodeId present                     | Runtime nodeId absent                               |
| --------- | ------------------------------------------ | --------------------------------------------------- |
| `"base"`  | Ignores nodeId; always fetches base policy | Fetches base policy                                 |
| `"agent"` | Fetches agent policy for runtime nodeId    | Uses configured `nodeId`; throws if both missing    |
| `"auto"`  | Fetches agent policy for runtime nodeId    | Falls back to configured `nodeId`, then base policy |

## How nodeId Is Resolved

The middleware determines the effective `nodeId` at runtime using this priority:

1. **Runtime header**: extracted from the `x-agent-state` header on the incoming tool call (set by upstream agent decorators)
2. **Configured default**: the `nodeId` field in the policy source config
3. **None**: falls back to base policy (or throws in `"agent"` scope)

```typescript theme={null}
// The agent decorator sets nodeId automatically via agent state propagation
class EhrAgent {
  @sec0.agent()
  async run(ctx: any, input: any, manager: AgentManager) {
    // manager.agent carries the nodeId from sec0.config.yaml hop entry
    const headers = manager.getAgentStateHeaders();
    // headers include x-agent-state with encoded nodeId
  }
}
```

## Example: Different Policies Per Agent

A common pattern is giving different agents different compliance rules. For example, an EHR agent may need HIPAA compliance packs while an order agent only needs basic security rules.

```yaml theme={null}
# sec0.config.yaml - each agent gets its own nodeId
app:
  hops:
    EhrAgent.run:
      type: agent
      nodeId: ehr-agent        # fetches policy scoped to "ehr-agent"
      agentName: ehr-agent
      agentVersion: "1.0.0"
    OrderAgent.run:
      type: agent
      nodeId: order-agent      # fetches policy scoped to "order-agent"
      agentName: order-agent
      agentVersion: "1.0.0"
```

```typescript theme={null}
// Middleware configured with auto scope - each call fetches
// the policy matching the calling agent's nodeId
sec0SecurityMiddleware({
  policy: {
    source: "control-plane",
    scope: "auto",
    fallbackToBase: true,
  },
  controlPlaneUrl: process.env.SEC0_URL,
  auth: { apiKey: process.env.SEC0_API_KEY },
  // ...
});
```

With this setup:

* Tool calls from `EhrAgent` are enforced with the `ehr-agent` policy (e.g., HIPAA compliance packs)
* Tool calls from `OrderAgent` are enforced with the `order-agent` policy (e.g., basic security rules)
* If either agent has no scoped policy, it falls back to the base workspace policy

<Warning>
  Having `sec0-sdk/middleware` in the runtime is **not sufficient** for enforcement. If the scoped policy for a `nodeId` is empty and `fallbackToBase` falls back to an empty base policy, enforcement is effectively off for that agent.
</Warning>
