Skip to main content
A Policy is a single rule your organization wants enforced on governed calls. Policies live in the EgisAI dashboard — operators create, edit, and target them there. The SDK fetches the active set, caches it locally, and refreshes it continuously without requiring a redeploy of your application.

Where policies live

Policies are configured in the EgisAI dashboard. The dashboard is the source of truth; the SDK consumes the published configuration. You do not embed policy documents in your repository. The SDK works without any extra files.

Categories of rules

Exact rule shape and ordering are managed in the product. The SDK consumes the published configuration and applies rules in a fixed order designed to keep sensitive content local-first (see How it works).

PII rules can carry patterns you define

The built-in PII types cover shapes that are the same at every company — an IBAN is an IBAN whether you are a bank or a bakery. The shapes that actually leak often aren’t: EMP-004182, an internal case reference, a customer number that means nothing outside your systems but is exactly what an auditor asks about. A pii_scan rule accepts your own patterns alongside the checkbox grid, under Your own patterns on the policy form:
From there they behave like any other type. They mask shape-preservingly (EMP-004182 becomes ###-######), they appear in the audit record with a count and mask shape and never the value, and they run everywhere the built-in types run — SDK, gateway, and egress node. What they are not is a detector with a false-positive rate. The built-in types are backed by checksums (Luhn, mod-97, Verhoeff) or a named-entity model. A pattern is backed by whatever you wrote, which is the right trade for EMP-\d{6} and the wrong one for “names of our customers” — that is what the person_name type and intent rules are for. Patterns are checked when you save. One that could backtrack catastrophically is refused with an explanation rather than accepted and left to slow every agent in the org hours later.

Intent rules can target tool calls

semantic_guard rules accept a targets list in the dashboard. The default is ["text"], which means the rule reviews the prompt or response text only. Including "tool_calls" extends the rule to ask the intent judge whether each individual tool dispatch matches the operator’s described intent — useful when an agent decides to call a destructive tool you didn’t enumerate by name (deny_tool_call is pattern-based and needs an exact name). Privacy contract: tool arguments are PII-label-redacted before they reach the judge, so categories like SSN or email are replaced with <SSN> / <EMAIL> placeholders before any external network egress. Intent classification still works because the judge cares about the verb / noun shape, not the exact identifier value.

Prompt-injection rules watch what your agent reads

injection_scan exists for one problem: your agent reads text nobody on your team wrote — a fetched web page, a support ticket, a PDF, a tool result — and that text contains instructions addressed to the model. The model can’t tell the difference, so the instruction runs with your agent’s credentials. Six shapes are scored: Detection is deterministic — compiled patterns and two character-class counts, no model call and no network — so it runs in phase 1 on every call with the rest of the local checks. Two settings matter:
  • action is flag by default. The finding lands on the audit row and the call proceeds. Start here: nobody can predict what their own tool results look like, and a rule that starts by refusing traffic gets switched off before it teaches anyone anything. Switch to block once you’ve watched a week of real traffic.
  • phase should be both or response. The request side catches a user typing an override; the response side catches the document that types it for them, which is the attack this is named for.
Deliberate limits: an injection paraphrased in fluent Spanish, written as a poem, or carried in a novel encoding will pass. This is a cheap, high-precision first filter that runs on every call — semantic_guard sits behind it for the cases only a language model can judge.

Directory rules run on the server, not in your process

The Directory category (identity_guard) is the one exception to “the SDK enforces everything”. It answers a question about live state — does this person still work here? — and the answer changes while your process is running. A cached copy would enforce Friday’s directory on Monday and still report a green check, which is worse for an audit than plainly not covering that path. So these rules are enforced where Egis holds a live database connection:
  • The gateway (/v1/chat/completions) — model calls.
  • The MCP proxy (/v1/mcp/{server}) — tool calls.
Both read the caller from the X-Egis-End-User header, which the SDK already sends when you call set_context(end_user_id=...). If your app passes an email address it is matched against the directory directly; an opaque id is matched through the identity sync’s own bridge. Agents that call providers directly through the in-process SDK are not covered by a directory rule, and GET /v1/sdk/policies withholds it rather than shipping a rule that would silently do nothing. Route those agents through the gateway if you need this control on them.

Policy targeting

Operators can scope a policy to:
  • All agents in the org — applies everywhere.
  • Specific agents — applies only to the listed agents.
  • Agent groups — applies to a named set of agents managed in the dashboard.
The SDK resolves these scopes at evaluation time based on the active agent identity (see Agents). You don’t need to do anything in code to opt into a scope — the dashboard manages it.

Policy phases

Each policy also carries a phase that selects which side of a call it fires on: Phase is chosen in the dashboard when an operator creates or edits a rule, and the dashboard restricts the available phases to the combinations the rule type actually supports. The legacy spellings pre_model / post_model are still accepted on every write path and normalize to request / response. See Two-phase governance for the full matrix and how to choose the right phase for a rule.

Surface scoping

Orthogonal to phase, a policy can carry an applies_to list that scopes it to specific call surfaces: model, tool, and/or mcp. Empty or omitted means “all surfaces” — the behavior every policy had before surface scoping existed. Requires SDK ≥ 0.32.0; older SDKs ignore the field and apply the rule everywhere (the safe direction).

Policy refresh

Once active, the SDK keeps your local cache fresh in two ways:
  1. Live updates — when the live update channel is reachable, policy changes propagate to your process within seconds.
  2. Polling — at the interval set by refresh_interval_seconds (default 10), the SDK polls for updated configuration. This is the fallback for environments that block long-lived connections.
You can disable live updates with enable_sse=False; the SDK then relies on polling alone.

Inspecting policies from your code

The SDK exposes its public types so you can integrate policy decisions outside the patched call paths if you need to. The most useful primitives are:
See the API reference for each type’s fields.
These primitives are deliberately pure-Python. They evaluate locally and do no I/O, so you can use them inside your own code paths to apply consistent policy semantics to text outside the patched provider SDKs.

Empty policy sets

If your organization has no enabled policies, every call passes through. The startup banner makes this visible:
This is intentional — the SDK doesn’t apply any opinions of its own. Behavior is entirely operator-defined.

What’s next

Verdicts

The three outcomes a policy can produce.

API reference

Detailed reference for PolicyRule, PolicyContext, and PolicyDecision.