# Agent Safety & Guardrails

How ows-coda prevents the AI agent from causing harm — data fabrication, unauthorized access, resource abuse, and information leakage.

## Threat model

The primary adversary is the AI model itself — it may hallucinate data, call inappropriate tools, leak internal details, or misinterpret user queries. The system must be safe even when the model behaves unexpectedly.

| Threat                   | Impact                                              | Mitigation                                                         |
| ------------------------ | --------------------------------------------------- | ------------------------------------------------------------------ |
| Data fabrication         | User makes decisions based on fake numbers          | Read-only tools, no data synthesis by the agent                    |
| Unauthorized data access | User sees another user's data                       | Per-request auth, identity-scoped connections                      |
| Prompt injection         | Malicious input manipulates agent behavior          | Downstream error sanitization, no dynamic code execution in server |
| Resource abuse           | Excessive API calls or Bedrock token consumption    | Rate limits, round caps, thinking budgets                          |
| Information leakage      | Internal URLs, tokens, or stack traces exposed      | Error sanitization in HTTP client                                  |
| Tool misuse              | Agent calls tools with harmful or nonsensical input | Schema validation, read-only enforcement                           |

---

## Read-only enforcement

All tools in the standard catalog are **read-only**. The agent cannot modify accounts, contracts, revenue, or ledger data. The only exceptions are:

- `submit_adjustment_batch` — creates adjustment entries (marked `destructiveHint: true` in MCP, requires user confirmation)
- `execute_code` — runs sandboxed JavaScript (isolated V8, no host access — see [Sandbox Security](sandbox-security.md))

Tool handlers return `{ data, error }` — never modify external state. The `safeGet`/`safePost` HTTP client wrappers enforce this by only calling downstream endpoints that are documented as safe.

---

## Auth and identity scoping

Every request flows through the auth middleware chain:

1. **JWT validation** — Auth0 token verified against JWKS endpoint
2. **Identity extraction** — Orchard identity UUID from the JWT claim
3. **Identity forwarding** — auth headers forwarded to every downstream service call
4. **Snowflake identity scoping** — session variables carry the user's identity for row-level filtering

The agent never sees data outside the authenticated user's access scope. Downstream services enforce their own authorization — Coda forwards credentials, it does not bypass them.

---

## Error sanitization

The HTTP client (`services/http-client.ts`) sanitizes all downstream errors before they reach the model:

- **401/403** — mapped to `"Access denied"` (no internal details)
- **404** — mapped to `"Resource not found"`
- **500** — mapped to `"An error occurred while fetching data"`
- Internal URLs, tokens, and stack traces are **never** included in tool results
- Slow requests (>3s) are logged at `warn` level but not exposed to the model

This prevents the model from leaking internal infrastructure details in its responses.

---

## Rate limiting and resource caps

| Control                 | Limit                  | Purpose                                           |
| ----------------------- | ---------------------- | ------------------------------------------------- |
| API rate limit          | 100 req/min            | Prevent abuse of all endpoints                    |
| Stream rate limit       | 10 req/min             | Protect Bedrock API costs                         |
| Max conversation rounds | 15                     | Prevent infinite tool-use loops                   |
| Thinking token budget   | Per-intent (1024-2048) | Control Bedrock extended thinking costs           |
| History cap             | 20 turns               | Limit context window size                         |
| Attachment limit        | 2 files, 25 MB each    | Prevent large payload abuse                       |
| MCP concurrency         | 5 in-flight calls      | Prevent tool call flooding via MCP                |
| Sandbox timeout         | 30s (dual timeout)     | Prevent CPU/async exhaustion in code execution    |
| Sandbox data requests   | 20 per execution       | Prevent request amplification from sandboxed code |
| Sandbox result size     | 5 MB                   | Prevent memory flooding from sandboxed code       |

---

## Tool availability gating

Tools declare their own runtime availability via `enabled()` predicates. When backing infrastructure is unavailable (Snowflake pool not configured, GraphQL gateway down), the tool is automatically removed from the model's tool list. The model cannot call tools whose infrastructure is offline.

This is enforced at two points:

1. **Surfacing time** — disabled tools are excluded from the tool config sent to Claude
2. **Execution time** — if a stale conversation references a now-disabled tool, the registry rejects the call

---

## Downstream communication safety

All tool handler HTTP calls go through `safeGet`/`safePost` wrappers that:

- Forward auth headers (JWT, cookies, identity headers) from the original request
- Use `AbortSignal.timeout()` with a 30s default
- Reject responses with 4xx/5xx status codes with sanitized messages
- Never expose the raw HTTP response body to the model on failure

---

## Sandbox isolation

For code execution via the runner service, the `@coda/sandbox` package provides 5 tiers of defense:

1. **V8 isolate memory isolation** — separate heap, no shared objects
2. **Blocked APIs** — no `process`, `require`, `fetch`, `fs`, or any Node.js built-ins
3. **Dual timeout** — V8 interrupt for sync loops + wall-clock dispose for async hangs
4. **Bridge limits** — max data requests, max data bytes, max result size, max status events
5. **Resource caps** — memory limit per isolate, max concurrent isolates

See [Sandbox Security](sandbox-security.md) for the full threat model and defense-in-depth analysis.

---

## Known gaps

These are documented risks with planned mitigations (tracked in [todos.md](../todos.md)):

| Gap                             | Risk                                                    | Planned mitigation                                   |
| ------------------------------- | ------------------------------------------------------- | ---------------------------------------------------- |
| No data fabrication detection   | Agent may present numbers not from tool results         | Post-generation validation against tool result chain |
| Server-side abort on disconnect | Bedrock charges accrue after browser navigates away     | Wire `AbortSignal` through orchestrator (P0)         |
| Chat ownership check missing    | User B could stream to user A's chat ID                 | Add identity verification in stream handler (P0)     |
| No per-tool permission checks   | All authenticated users can call all available tools    | Platform service integration (COD-109)               |
| MCP layer has no rate limiting  | MCP clients can flood the Express rate limit downstream | Token bucket throttler in MCP process                |

---

## See also

- [Sandbox Security](sandbox-security.md) — V8 isolate threat model and defense tiers
- [Security Model](security.md) — platform service security architecture
- [Threat Model](threat-model.md) — STRIDE analysis of the platform service
- [Compliance Matrix](compliance-matrix.md) — OWASP Top 10 mitigations
