{
  "access": "public",
  "type": "reference",
  "format": "markdown",
  "title": "Policy & Security",
  "chunked": true,
  "url": "https://library.datagrout.ai/policy-security",
  "summary": "**Control what agents can do, and mask what they can see**",
  "content_markdown": "# Policy and Security\n\n**Control what agents can do, and mask what they can see**\n\nDataGrout enforces policy on every tool call. Semantic Guards validate requests before execution. Dynamic Redaction masks sensitive data after execution. Policies cascade from server defaults through integration overrides, and restrictions are monotonic -- a child policy can tighten constraints but never loosen them.\n\n---\n\n## Semantic Guards\n\nSemantic Guards validate every tool call before it reaches the upstream integration. If a call violates policy, it is rejected with a clear explanation. No data leaves DataGrout until the guard passes.\n\n### Side Effect Controls\n\nRestrict the maximum side effect level for a server or integration.\n\n| Level | Allows |\n|-------|--------|\n| **None** | Read-only queries. No state changes. |\n| **Read** | Read operations. Identical to None for most integrations. |\n| **Write** | Create and update operations. |\n| **Delete** | All operations including destructive ones. |\n\nSet the level in your server's policy settings. Any tool call that exceeds the configured level is blocked.\n\n### Destructive Operation Blocking\n\nA separate toggle that blocks delete, drop, purge, and truncate operations regardless of the side effect level. Enable this as an additional safeguard even when write operations are allowed.\n\n### Scope Verification\n\nGuards verify that a tool call targets only the integrations and data scopes available to the requesting agent. Calls that attempt to access integrations outside the server's configured set are rejected.\n\n---\n\n## Dynamic Redaction\n\nAfter a tool executes, Dynamic Redaction scans the response and masks sensitive data before it reaches the agent. Redaction is applied server-side -- the agent never sees the original values.\n\n### PII Auto-Detection\n\nWhen PII controls are enabled, DataGrout automatically detects and masks:\n\n- Email addresses\n- Phone numbers\n- Social Security numbers\n- Credit card numbers\n- Physical addresses\n- Dates of birth\n\n### Redaction Strategies\n\nYou choose how each field type is masked.\n\n| Strategy | Behavior | Example |\n|----------|----------|---------|\n| **scramble** | Replace with random characters of the same length | `john@acme.com` -> `xkqp@mwvz.org` |\n| **mask_email** | Preserve domain, mask local part | `john@acme.com` -> `j***@acme.com` |\n| **mask_phone** | Show last 4 digits | `(555) 867-5309` -> `***-***-5309` |\n| **mask_all** | Replace entire value with asterisks | `123-45-6789` -> `***********` |\n| **apron** | Keep N characters on each end, mask the middle | `4111111111111111` -> `4111********1111` |\n| **fixed_length** | Replace with a fixed number of masking characters | `sensitive` -> `********` |\n\n### Field-Level Rules\n\nDefine redaction rules per field name. For example, you can apply `mask_email` to all fields named `email` or `PrimaryEmailAddr`, and `apron` with 4 visible characters to fields named `credit_card`.\n\n---\n\n## Cadence — Intelligent Loop Detection\n\nMCP clients and autonomous agents can fire the same tool call multiple times — due to retries, network issues, or agent loops. Cadence goes beyond simple time-window deduplication by understanding the *consequence* of each tool call and detecting whether the world state changed between identical calls.\n\n### How It Works\n\nCadence operates on two layers:\n\n**Layer 1 — Consequence-Aware Session Tracking.** Every tool call is hashed into an idempotency key (tool name + canonical JSON args) scoped to the MCP session. A monotonic state sequence counter increments on every tool completion. If the sequence hasn't advanced between two identical calls, nothing in the session changed — it's a loop. If another tool ran in between, it's a legitimate pattern and the call proceeds.\n\n**Layer 2 — Time-Window Backstop.** The traditional debounce mechanism remains as a secondary defense. Tool calls are hashed by server identity, tool name, and argument content. After execution, the outcome (success or error) is recorded. When an identical call arrives within the configured window, it is blocked immediately.\n\n### Consequence Tiers\n\nEvery tool is automatically classified by its declared side-effect metadata — no per-tool configuration needed:\n\n| Tier | Criteria | Behavior |\n|------|----------|----------|\n| **Read** | `side_effect: none` or `read` | Allow up to 3 identical calls without state change. Warn at 4, block at 6. |\n| **Write** | `side_effect: write`, non-destructive | Allow 1 execution. Return a confirmation gate on the 2nd identical call. |\n| **Destructive** | `destructive: true` or `side_effect: delete` | Hard block after 1 execution per idempotency key per session. |\n\nWrite-tier confirmation gates return `isError: false` with structured guidance so agents can adapt their behavior rather than interpreting the response as a failure.\n\n### Auditing\n\nWhen a call is blocked by the time-window backstop, the `debounce_attempts` counter on the original execution record is incremented. Both layers include the original `receipt_id` in the response for traceability.\n\n### Configuration\n\n- **Cadence session tracking is always active** — no configuration needed. Tiers are derived from existing tool metadata.\n- **Time-window backstop** is configurable per server in the Interaction Settings tab:\n  - Enabled by default with a success rule at 60 seconds.\n  - Add multiple rules per response type (success/error), adjustable from 5 to 300 seconds each.\n  - Can be disabled entirely for workflows that intentionally repeat identical calls.\n\n---\n\n## Policy Cascade\n\nPolicies flow through a strict hierarchy:\n\n```\nUser Account Settings\n  └─ Server-Level Defaults\n       └─ Integration-Level Overrides\n```\n\nAt each level, you can tighten restrictions but never loosen them. If the server policy sets side effects to \"Read\", an integration override cannot escalate to \"Write\". It can only restrict further to \"None\".\n\nThis means:\n\n- A permissive server policy lets you customize per integration.\n- A restrictive server policy locks down every integration beneath it.\n- You never need to worry about an override accidentally opening a hole.\n\n---\n\n## How to Configure\n\n### Server-Level Policies\n\n1. Navigate to **Settings** and click the gear icon on your server.\n2. Open the **Policy** section.\n3. Set:\n   - Side effect level (None / Read / Write / Delete)\n   - Destructive operations toggle (on/off)\n   - PII detection toggle (on/off)\n   - Field redaction rules\n\nThese become the defaults for every integration on this server.\n\n### Integration-Level Overrides\n\n1. Open an integration's detail page.\n2. Navigate to the **Interaction Settings** tab.\n3. Toggle **Override server defaults**.\n4. Configure tighter restrictions for this specific integration.\n\nFor example, your server might allow Write operations, but you restrict a particular integration to Read-only because it connects to a production database.\n\n---\n\n## Examples\n\n### Read-Only Agent\n\nLock an entire server to read-only access:\n\n- Side effects: **None**\n- Destructive operations: **Off**\n\nEvery tool call that attempts to create, update, or delete data is blocked.\n\n### PII-Safe Reporting\n\nAllow data access but mask personal information:\n\n- Side effects: **Read**\n- PII detection: **On**\n- Email fields: `mask_email`\n- Phone fields: `mask_phone`\n- SSN fields: `mask_all`\n\nAgents can query data freely. All personally identifiable information is masked before it reaches the agent.\n\n### Mixed Policy\n\nAllow writes to your CRM but restrict billing to read-only:\n\n- Server default: **Write**\n- CRM integration: inherits **Write** (no override)\n- Billing integration: override to **Read**\n\n---\n\n## Schema Compatibility\n\nDataGrout automatically sanitizes tool schemas for cross-platform compatibility. When tools from external MCP servers use advanced JSON Schema keywords, these can cause errors on LLM APIs that only support a subset of the specification.\n\n### Unsupported Keyword Stripping\n\nKeywords like `exclusiveMinimum`, `exclusiveMaximum`, `const`, `$comment`, `deprecated`, `readOnly`, and others are automatically removed from tool schemas before they are served to clients. This prevents errors from APIs (such as Gemini) that reject these keywords.\n\n### Structural Fixes\n\n- **Type arrays** (`\"type\": [\"string\", \"object\"]`) are simplified to accept any type\n- **`oneOf`/`anyOf`** blocks are flattened into merged properties\n- **Array schemas** missing `items` get a default `items` added\n\n### Tool Name Deduplication\n\nWhen using OpenAI-compatible tool names, names are truncated to 64 characters. If two tools produce the same name after truncation, DataGrout appends a numeric suffix (`_2`, `_3`, etc.) to ensure uniqueness, preventing \"duplicate tool name\" errors from clients.\n\n---\n\n## Composite Billing\n\nWhen a tool call uses gateway parameters like `refract` or `chart`, multiple\ncredits charges occur independently:\n\n- **Gateway base** (1 credit) — charged for every tool call\n- **Refraction** (10 credits new / 1 credit reuse) — charged by `prism.refract`\n- **Chart** (10 credits new / 1 credit reuse) — charged by `prism.chart`\n- **CTC minting** (10 credits) — charged when a Cognitive Trust Certificate is\n  produced\n\nEach tool charges its own credits and issues its own receipt. The gateway\ncollects all inner receipts and presents a **composite receipt** in the\nresponse metadata with a per-component breakdown:\n\n```json\n{\n  \"composite\": true,\n  \"total_charged\": 21,\n  \"total_estimated\": 21,\n  \"total_savings\": 0,\n  \"components\": [\n    {\"source\": \"gateway\", \"charged\": 1, \"estimated\": 1},\n    {\"source\": \"prism.refract\", \"charged\": 10, \"estimated\": 10, \"savings\": 0},\n    {\"source\": \"prism.chart\", \"charged\": 10, \"estimated\": 10, \"savings\": 0}\n  ]\n}\n```\n\nOn **skill reuse**, the savings are visible per-component:\n\n```json\n{\n  \"source\": \"prism.refract\",\n  \"charged\": 1,\n  \"estimated\": 10,\n  \"savings\": 9\n}\n```\n\nTool prices are defined in a centralized pricing map (`DataGrout.Pricing`)\ndesigned for future migration to a database-backed admin panel.\n\n---\n\n## Cryptographic Signing (DG CA)\n\nDataGrout operates its own Certificate Authority (DG CA) that provides two signing layers:\n\n### Ed25519 Signatures\n\nAll trust artifacts produced by DataGrout are signed with **Ed25519** using a platform-wide signing keypair. Signed artifacts include:\n\n- **Cognitive Trust Certificates** — every CTC is signed over its immutable fields (id, plan hash, assurances, timestamp, and child certificate IDs). Including child CTC IDs in the parent signature makes certificate chains tamper-evident — altering or inserting a sub-certificate invalidates the parent.\n- **Rule packs** — governance policy bundles distributed by the Arbiter are signed before deployment. Nodes verify the signature before applying policy changes.\n- **Machine-client JWTs** — authentication tokens issued to automated clients (Conduit SDK, Substrate) are Ed25519-signed JWTs.\n\nIn production, the Ed25519 private key is injected via environment and never written to disk. In development, an unsigned fallback (SHA-256 digest) is used so the system runs without secrets.\n\n### ECDSA P-256 (X.509 Identity)\n\nA separate ECDSA P-256 key is used exclusively for X.509 certificate signing. Substrate clients generate a key pair locally and send their public key to the DG CA, which returns a signed client certificate for mutual TLS. In production, the P-256 private key lives inside an **AWS KMS HSM** (FIPS 140-2 Level 2) and never leaves the hardware boundary.\n\n### Signature Verification\n\nCTC signatures can be verified by anyone with the DG CA public key, which is served at `GET /ca.pem`. The CTC viewer displays verification status for each certificate, and the public key is embedded in the Conduit SDK for offline verification.\n\n---\n\n## Certificate Chains\n\nWhen multiple inner tools produce Cognitive Trust Certificates, the gateway\ncollects them into a **certificate chain**. Each CTC is independently signed\nwith Ed25519 and verifiable. The chain appears in `_meta.datagrout`:\n\n- `ctc` — the primary certificate (typically from refraction)\n- `certificate_chain` — all certificates in order, each with `id`,\n  `viewer_url`, `assurances`, and `guarantees`\n\nBecause each parent CTC's signature covers its child certificate IDs, the\nchain is tamper-evident end-to-end. Modifying any certificate in the chain\ninvalidates every ancestor's signature.\n\nChart rendering now produces its own CTC with deterministic assurances (same\ninput always produces the same SVG output, sandboxed, no side effects).\n\nThe chain can be toggled via the **Include CTC details** checkbox in\nInteraction Settings.\n\n---\n\n## Related\n\n- [Interaction Settings](interaction-settings) -- Full configuration reference\n- [Cognitive Trust Certificates](cognitive-trust-certificates) -- Policy compliance verification in workflows\n- [Core Concepts: Policies](core-concepts) -- Policy overview\n"
}