Guardrails
A site’s guardrails are the rules Sill evaluates against every incoming mandate before the merchant’s processor is ever called. Each rule belongs to a category, carries typed parameters, and resolves to one of three actions: allow (explicit exemption), reject, or escalate to a human reviewer. Rules are grouped into an ordered, versioned policy; exactly one policy is active per site at any moment. The dashboard’s Guardrails view is where an operator authors, dry-runs, and publishes that policy.
How a mandate meets the rules
Section titled “How a mandate meets the rules”Every signed mandate that reaches Sill’s edge is evaluated against the site’s active policy in order. The first rule that matches decides the outcome and short-circuits the rest — the mandate is approved, rejected, or escalated to the human-in-the-loop queue. Each evaluation is written to the audit envelope as a signed, Merkle-chained record.
Most rules only gate the Transactional path — a real signed mandate. A subset also evaluates against Discovery-class traffic, but only for bridged-connector tool calls (not plain identification-only visits), and only in an advisory capacity: the verdict is recorded in the audit trail but never blocks the call. The policy engine page documents exactly which rules carry this dual scope.
flowchart LR M[Signed mandate] --> V[Verifier] V -->|reject| R[Rejected] V -->|pass| P[Policy engine] P -->|match: allow| A[Approve] P -->|match: reject| R P -->|match: escalate| Q[HITL queue] P -->|no match| A A --> AUD[Signed audit record] R --> AUD Q --> AUD
Guardrails configured here run inside the policy stage. A handful of rules are enforced outside it — see Enforcement layers below.
Rule categories
Section titled “Rule categories”The Guardrails view groups rules into seven categories, each with its own filter chip and count in the header strip. Adding a rule is a two-pass picker: choose a category, then a specific rule, then fill in its typed parameters.
| Category | What it gates | Example rules |
|---|---|---|
| Agent identity | Who can call | Allowlisted agents only; require valid IntentMandate; mandate replay protection; geofence; mandate validity window cap; delegation-chain policy |
| Rate limits | How fast | Per-agent calls per minute; per-IP calls per hour; aggregate cap across agents; failed-auth lockout; MCP session rate limit |
| Spend caps | How much | Max per transaction (per-currency); daily cap per user; cross-agent organization cap; cart total ≤ Intent ceiling; currency must match Intent; cart immutability after authorization |
| Scope | What actions | HITL on destructive actions; per-customer data scoping; skill manifest integrity; mandate body size limit; emergency kill switch |
| Dark patterns | Manipulative buying signals | No urgency manipulation; no drip pricing; subscription requires explicit consent |
| Prompt injection | Adversarial inputs | Instruction-override detection; Unicode tag block; credential-leak detection |
| Custom DSL | Merchant-authored predicate | A single rule whose body is an expression in Sill’s policy DSL |
Many rules cite a standard — AP2, OWASP API Top 10, OWASP Agentic ASI, OWASP MCP, UK CMA / DMCC, MITRE ATLAS — to make the intent of the guardrail explicit. Where a rule’s enforcement is delegated to a layer outside the policy evaluator, the dashboard renders an inline pill (ENFORCED AT VERIFIER, ENFORCED AT EDGE, etc.) so the operator sees that the rule is always-on and not merchant-configurable.
The Guardrails view, with the category filter strip across the top and one column of rule cards below.
Adding and configuring a rule
Section titled “Adding and configuring a rule”The Add rule modal is a two-pass picker. The first pass shows the seven categories; the second shows the rules in the chosen category, with each entry tagged either with its parameter form or with a COMING SOON badge. Rules whose handler has not yet shipped cannot be added — the dashboard preserves the roadmap entry so an operator can see what is coming but never publish an unenforceable rule.
Once a rule is selected, its typed parameter form swaps in. Common shapes:
- Allowlist — a list of registered
agent_ids (Agent identity → Allowlisted agents only). - Per-currency cap — one cap per ISO-4217 currency, with no FX conversion (Spend → Max per transaction; Scope → HITL on destructive actions).
- Country allow/deny — an allow-list or deny-list of ISO country codes (Agent identity → Geofence).
- Whole-second window — a maximum validity window (Agent identity → Mandate validity window cap).
- Byte limit — a maximum mandate body size (Scope → Mandate body size limit).
- Toggle-only — for emergency kill switch and similar always-or-never controls.
- Custom DSL — a free-form expression in Sill’s policy DSL.
Each rule also carries an on-match action of allow, reject, or escalate. The Guardrails view’s parameter hints reflect the selected action — a “max amount cap” rule set to allow reads as an explicit exemption that skips later rules, not a rejection.
Starter templates
Section titled “Starter templates”Authoring a policy rule-by-rule is one path to a first policy. When a site’s draft is empty, the Guardrails view offers a faster one: Load a starter template drops a curated, pre-reviewed rule set straight into the draft in a single click. Every template ships as a reviewed, versioned artifact — the same policy DSL review discipline that gates any other change to the rule catalog — so loading one is never a shortcut around what a rule set is allowed to do.
The picker only appears when there is genuinely nothing it could silently overwrite: the draft has zero rules, and the active policy is either absent or the untouched auto-seeded baseline. A site with any merchant-authored rules — including a template loaded and then edited — never sees the button; the only way back to it is to clear the draft down to zero rules.
| Template | Rules | Adds beyond the baseline | Best for |
|---|---|---|---|
| E-commerce baseline | 8 | Nothing — this is the baseline. The same param-free rule set Sill auto-seeds when a site first connects a payment rail (rate limits, cart/currency sanity checks, prompt-injection guards), promoted to a selectable starting point. | Any merchant who wants a working policy immediately and plans to layer on caps, allowlists, or review rules as they observe live traffic. |
| Conservative / manual review | 12 | A per-transaction spend ceiling; unbounded human review on refunds and cancellations (any amount above a small auto-approve threshold escalates — never auto-rejected); a tighter mandate validity window; a smaller mandate body-size cap. | Merchants who want a hard backstop on runaway spend and a human in the loop on every meaningful refund or cancellation. |
| Low friction / high volume | 10 | Site-wide protection against distributed scraping across many agent identities, and per-customer data scoping (BOLA protection) — deliberately without a spend ceiling, human review, or validity tightening. | Merchants who want abuse resistance and cross-customer data protection with fewer controls than Conservative — trading some review friction for throughput at scale. |
The starter-template picker: one card per template, each showing its rule-count and a plain-language summary of what it adds.
A few properties hold for every template, not just one:
- One-time load, then it’s yours. There is no back-link to the template and no auto-resync — editing the loaded rules, or a later template version shipping, never retroactively changes a draft or policy that already loaded it.
- Loading touches the draft only. The active policy is untouched until an explicit Publish — the same two-step draft-then-publish flow, and the same confirm-replace gate, as any other rule change (see Draft, active, and the fingerprint below).
- The safety bar is the same as hand-authored rules. Every template’s composition is verified, at server boot, to approve a clean signed mandate — a template can never ship a rule set that would fail-closed on a legitimate first order. A malformed template fails the dyno’s startup, not a merchant’s traffic.
- The audit trail records where a policy came from. Publishing a template-loaded draft writes
policy_seeded_from_templateto the audit log, with the template’s slug, version, and the published rule count — visible alongside every other publish action.
Draft, active, and the fingerprint
Section titled “Draft, active, and the fingerprint”Editing a rule produces a draft for the site. The active policy keeps running against live traffic unchanged; the draft is what the next publish will activate. The view computes a stable fingerprint over the current rule set (rulesFingerprint(rules)) and compares it with the active policy to classify each row as unchanged, edited, added, or removed. Drafts auto-save to the server with a short debounce so a tab refresh does not lose work.
When a draft is published, Sill bumps the policy version label, writes the new active policy, and records the change in the audit envelope. Auto-seeded baselines carry a recognisable label so the dashboard and audit trail can distinguish them from a merchant-authored publish.
Dry-run (shadow evaluation)
Section titled “Dry-run (shadow evaluation)”Dry-run is a staging mode for the live policy. While dry-run is on, the active policy continues to enforce on live traffic exactly as before; in parallel, Sill evaluates the operator’s draft rules against the same incoming mandates and records every decision the draft would have made into a 7-day shadow log. Nothing is blocked, escalated, or auto-approved by the draft — only logged.
sequenceDiagram participant Op as Operator participant Dash as Guardrails view participant Edge as Policy engine participant Shadow as Shadow log (7d) Op->>Dash: Enable dry-run Op->>Dash: Edit a rule (e.g. tighten r05 cap) Dash->>Edge: Auto-save draft (debounced) Note over Edge: Active policy keeps enforcing<br/>against live mandates. Edge-->>Shadow: "Would have blocked" for each<br/>draft-vs-active divergence Op->>Dash: Review shadow log (REJECT / ESCALATE / INDETERMINATE) Op->>Dash: Publish (or revert) Dash->>Edge: Bump version, swap active = draft
The DRY-RUN RESULTS card is collapsed by default beneath the rules grid and surfaces:
- Summary header — total,
REJECTcount,ESCALATEcount, over the 7-day window. - By rule histogram — which draft rules are noisiest in the loaded shadow buffer.
- Per-row “why” sentence — a categorical, plain-text explanation derived from the catalog + the operator’s configured parameters. The sentence is never built from the inbound mandate.
- INDETERMINATE pill — for rate-limit rules whose shadow verdict cannot be a definitive statement (rate state is not evaluated in shadow). The pill makes the limit honest rather than asserting false certainty.
- View mandate — opens the underlying mandate inline. Buyer reveal is not auto-opened.
- Draft fingerprint changed — surfaced when the draft has been edited since the panel was opened, so the operator knows the prior shadow rows are stale.
Dry-run is enabled with explicit confirmation and disabled with a quiet toast — it is a per-operator UI mode, not a server-side enforcement switch.
Dry-run mode: the banner sits above the rules grid; the DRY-RUN RESULTS card sits below and lists every draft-vs-active divergence over the last seven days.
QuickTest
Section titled “QuickTest”QuickTest lets an operator paste a sample mandate body into the Guardrails view and see exactly which rules match, in which order, and with which outcome — without enforcing anything. Rules enforced outside the policy evaluator (verifier, webhook, response, dashboard, edge, origin) are intentionally not testable from QuickTest, since they never run in the evaluator path.
Publishing
Section titled “Publishing”When the draft is ready, Publish bumps the version, writes the new active policy server-side, and records the change in the audit log. The next mandate that arrives is evaluated against the new policy. If a rule fails the server’s publish gate (an unsupported type, a malformed parameter, or a non-policy-configurable rule that should never have reached the draft), the publish is rejected with a rule_{i}_{reason} code and the active policy is left untouched.
Enforcement layers
Section titled “Enforcement layers”Most rules are enforced inside the policy evaluator on the edge. A small set is enforced elsewhere in the pipeline; they appear in the Guardrails view for visibility but cannot be edited or disabled.
| Layer | When it runs | Examples |
|---|---|---|
verifier | Before policy evaluation | Mandate replay protection (r11); failed-auth lockout (r24); cart immutability after authorization (r16) |
policy | The evaluator stage — merchant-configurable | The bulk of the rule catalog |
origin | Configured in the policy; enforced right before the charge (needs a running spend total the edge cannot compute) | Daily spend cap per user (r06); cross-agent organization cap (r31) |
webhook | The inbound-webhook handler (HMAC-SHA256 against the rotating secret) | Webhook signature verification (r27) |
response | A post-response hook on the agent’s outbound payload | Outbound sanitization |
dashboard | Gated on a human dashboard-user’s role | Refund authorization window (r26, roadmap) |
edge | Always-on ingress guard, before any mandate exists | MCP session rate limit (r30) |
The dashboard surfaces a tooltip on every non-policy rule explaining where it runs and why it cannot be tested from QuickTest.
Organization-wide spend caps
Section titled “Organization-wide spend caps”Two spend caps act at the charge rather than the edge, because each needs a running total Sill keeps across every payment rail:
- Daily cap per user (
r06) — how much a single user’s agents can spend over a rolling 24 hours. - Cross-agent organization cap (
r31) — the combined spend of every agent acting for one organization over a rolling window. A per-transaction or per-user cap won’t stop a fleet of agents under the same organization from adding up to a large total; this rule does, with one budget that covers every rail — Stripe today, and any cart added later.
Because they act at the charge, neither can be exercised by a shadow run. The Red Team lists them separately under Settlement-layer coverage — marked active when configured, or as a recommended gap you can turn on in one click. Enforcement is visible in the audit log: a blocked charge is recorded with its reason, and no money moves.
Frequently asked
Section titled “Frequently asked”Does turning on dry-run weaken my active policy? No. The active policy continues to enforce exactly as before. Dry-run evaluates draft changes in parallel and logs what they would have done. The dashboard’s enable-dry-run modal is explicit about this.
Can I run two policies side by side on the same site? No. Each site has exactly one active policy. Dry-run gives you a staged-but-not-enforcing second view of the draft against the same live mandates.
What happens if a rule’s parameters are invalid at publish time? The publish is rejected with a rule_{i}_{reason} code; the active policy is unchanged. The dashboard surfaces the error inline so the operator can correct it.
Are shadow-log rows signed and exportable? Shadow rows are dashboard-only operator artifacts bounded to a 7-day window and a per-fetch cap. The signed, exportable record is the audit envelope written by the active policy. See Audit log and export.
Why is a rate-limit row in the shadow log labelled INDETERMINATE? Rate-limit state (r03, r04, r13, r17) is not evaluated in shadow, so a “would-have-blocked” verdict on those rules is not a definitive statement that enforcement would block. The pill makes the limit honest rather than overclaiming.
How do I test the organization spend cap (r31)? It’s enforced at the charge, not in the evaluator, so it doesn’t run in QuickTest or dry-run. Confirm it’s on from the Red Team’s Settlement-layer coverage section, and see real cap rejections in the audit log.
If I load a starter template and then edit the rules, does it still count as that template? For loading purposes, no — the empty-state picker only reappears when the draft is empty again. For the audit trail, the original publish is still recorded as seeded from that template’s slug and version; later publishes from further edits are recorded as ordinary publishes, same as any hand-authored change.
Will Sill silently update my policy if a starter template changes later? No. Loading is one-time: there is no back-link from a published policy to the template it came from, and no mechanism re-applies a newer template version to a site that already loaded an older one. A template update only affects sites that load it after the update ships.
See also
Section titled “See also”- Policy engine — the DSL and evaluation semantics.
- Signed mandates — what each rule sees.
- Human-in-the-loop — what happens on
escalate. - Audit log and export — the signed record of every decision.
- Verify a signature — third-party verification recipe.
- OWASP API Security Top 10 — referenced by the rate-limit and scope categories.
- OWASP Top 10 for LLM Applications — referenced by the prompt-injection category.
- OWASP Top 10 for Agentic Applications — referenced by the agent-identity category (ASI03 / ASI07).
- MITRE ATLAS — referenced by skill-manifest integrity (
AML.T0011.002). - NIST AI Risk Management Framework — Measure / Manage support via the audit envelope.