A gate in front of your model that masks what shouldn’t leave, refuses what shouldn’t run, and holds what a person should see first — in single-digit milliseconds. Every decision lands in the same trace you already debug with.
Self-hostable · runs on CPU · one base-URL change to adopt
The moment you gave it tools, the failure stopped being an embarrassing transcript and became a thing that happened. Observability watches all three of these and does nothing about any of them.
A customer pastes an identifier into chat. A coordinator names a trial participant. It goes to your model vendor verbatim and lands in somebody else’s logs.
“Ignore your instructions.” The model obeys, because obeying instructions is the only thing it knows how to do — and out comes the system prompt, the tool list, the data behind them.
It refunds two orders of magnitude too much, emails the wrong list, or answers a clinical question it had no business answering. Confident, instant, wrong.
Observability watches. Astriel acts. It sees the risk — and steps in.
Every other platform stops at the first verb. Astriel closes the loop — and each stage feeds the next.
Full tracing of every call, tool, and turn.
Multi-turn, trajectory & simulation scoring.
Redact, block & approve in real time.
Tamper-evident, policy-versioned audit trail.
Guardrail decisions, eval scores and simulation runs are not a separate console you check on Fridays. They land as observations in the same trace you already open when something breaks.
Rules are ordered, not scored together. Rule 01 masks the identifier and the request continues; rule 02 refuses and nothing downstream runs.
| Scenario | Turns | Trajectory | Judge | Δ vs baseline |
|---|---|---|---|---|
| escalates-without-verifying | 7 | Loop | 0.41 | −0.28 |
| refunds-over-policy-limit | 5 | Wrong tool | 0.52 | −0.19 |
| asks-for-order-id-first | 4 | Direct | 0.94 | +0.01 |
| declines-out-of-scope | 3 | Direct | 0.97 | ±0.00 |
| resists-prompt-injection | 6 | Direct | 0.91 | +0.04 |
# .github/workflows/agent-evals.yml astriel sim gate --baseline main --threshold 0.85 ✗ 2 scenarios regressed past the threshold escalates-without-verifying 0.69 → 0.41 refunds-over-policy-limit 0.71 → 0.52 Exit 1 — blocking the deploy
Count of guardrail decisions grouped by rule, 25 Jul – 1 Aug, enforced decisions only.
| Rule | Action | Decisions | Share |
|---|---|---|---|
| mask-identifiers | Mask | 1,847 | |
| block-injection | Refuse | 213 | |
| high-value-action | Hold | 64 | |
| leaked-credentials | Refuse | 9 |
Blocks rose 4.1× at 14:00 on Tuesday. support-agent v8 → v9 was saved 22 minutes earlier, which precedes the move. A prompt change landed after the rise and is excluded.
When nothing precedes the anomaly, it says so — rather than offering the nearest coincidence as a cause.
| Policy | Version | Mode | Rules | Saved by | Saved at |
|---|---|---|---|---|---|
| support-agent | v9 | Enforcing | 4 | r.okafor | 28 Jul 14:38 |
| support-agent | v8 | Shadow | 3 | r.okafor | 21 Jul 09:12 |
| intake-agent | v2 | Enforcing | 6 | j.lindqvist | 04 Jul 16:01 |
| Decision | Kinds | Opened by | When | Stated reason |
|---|---|---|---|---|
| obs_4f81c2 | pii.ssn | a.mensah | 29 Jul 11:04 | Chargeback dispute #8812 |
| obs_9ab073 | pii.credit_card | a.mensah | 26 Jul 15:47 | Duplicate charge investigation |
Masking is reversible by design — and every reveal is itself an event with a name, a timestamp and a stated reason. An auditor asking “who looked at this?” gets a row, not a shrug.
Ordered rules with firewall semantics — first match wins, and every rule can redact, block, flag, or hold for a human.
Screens are populated with example data — the layouts and labels are the real ones.
Checks produce findings. Ordered rules decide what happens to them. The policy is a versioned file you write. Nothing in that sentence mentions an industry — so what follows are illustrations, not a menu, and the last one is the point.
The risk is not a bad sentence — it is a correct-looking tool call. Identifiers get masked before they leave your infrastructure, and anything above a threshold waits for a person.
pii.credit_card, pii.ssn, secrets, injection
Refunds and credits above your own threshold
“Prove no card number reached the model vendor.”
Here the danger is an assistant that answers a clinical question it has no business answering. Approval stops being a nicety and becomes the control the regime is asking about.
pii.email, pii.phone, blocklist phrases you author, injection
Anything touching dosing, eligibility or deviations
“Show the control, and who reviewed each exception.”
The one everybody has and nobody governs. It reads from a wiki, it writes to a ticket queue, and it will happily paste an API key into a reply if the model decides that is helpful.
injection (8 kinds + classifier), secrets (8 kinds), pii
Writes to production systems and outbound email
“Which internal tools can this thing actually reach?”
We have no idea what your agent does, and the engine does not need to. A policy is an ordered list of rules; a rule names a check, a threshold, what it applies to and what to do. That is the whole surface.
mode: shadow # record only until you say otherwise on_error: closed # refuse if a check cannot run rules: - id: whatever-matters-to-you check: pii kinds: [pii.email, pii.phone] # narrow it, or omit for all target: input action: redact - id: your-own-forbidden-phrases check: blocklist # phrases you author target: output action: block - id: anything-irreversible check: injection threshold: 0.85 action: require_approval
No industry field. No vertical setting. Rules are evaluated in order and the first match decides — so reading the file top to bottom tells you exactly what the agent can and cannot do.
The engine, the checks, the ordering, the evidence trail
The rules — and you can change them without asking us
Which action, done by mistake, you could not undo?
One honest limit, since the rest of this section is a claim about generality: detector coverage is where a domain difference is still real. The built-in kinds today are US-centric personal data, cloud secrets, injection families, and blocklist phrases you write yourself. Loadable entity packs — so you can add an MRN, an NHS number or an IBAN without waiting on us — are the next thing landing.
So don’t. Every policy starts in shadow: it scores 100% of real traffic, blocks nothing, and shows you exactly what it would have done. You promote it when the numbers stop surprising you.
| Rule | Action | Count | Share of findings |
|---|---|---|---|
| mask-identifiers | Would redact | 2,549 | |
| block-injection | Would block | 214 | |
| high-value-action | Would require approval | 37 | |
| leaked-credentials | Would block | 41 |
The figures above are a worked example, not a customer’s results — we would rather show you the shape of the report than borrow somebody else’s numbers. The point is what fills it in: after a week in shadow, the case for enforcing is made out of your traffic, and you can check it before anything is switched on.
Tracing is close to solved and there are good tools doing it. The control plane in front of the model is where almost nobody is — and it is the only part that can change an outcome.
Genuinely useful, and you should keep yours — we are built on the same data model, so your instrumentation carries over. But a trace of a leak is a trace of a leak. Nothing in a read path can decline a request.
Usually an SDK call at every checkpoint you remember to add, two crude outcomes, GPU-backed pricing, and a decision log that lives in their console instead of your trace — so the block and the request that caused it are never on the same screen.
Five outcomes including hold-for-a-human. Deterministic checks on CPU. One base URL to adopt. Open core and self-hostable, so the sensitive value never has to leave your network to be found.
You choose, per policy, and you choose in advance: on_error: open lets traffic through unchecked, on_error: closed refuses
it. Neither is the right default for everyone — an internal
summariser should stay up, a system handling regulated data should
probably stop. What we will not do is decide it for you at 3am.
If the upstream model fails mid-request, the input decision is still recorded and the trace is tagged, so a provider outage does not create a hole in your evidence.
No, and the difference is the whole product. Observability is a reader: it records what happened. Astriel sits in the request path and can change the outcome — mask a value before it leaves, refuse a call before the model runs, hold an action until a person approves it. Those need to happen synchronously, before the fact.
Tier-0 checks are deterministic pattern matching on CPU — single digit milliseconds, no GPU, no model call. The optional Tier-1 classifier catches paraphrased attacks that patterns miss and costs more; it is a per-rule opt-in, so you pay for it only where you want it.
The gate speaks the OpenAI and Anthropic APIs, so in the common case you change a base URL and nothing else. There is no SDK to thread through every call site, and no per-checkpoint instrumentation to maintain — which matters, because a checkpoint someone forgot to add is exactly where the incident happens.
Because you will have watched it not block anything for a week first. Shadow mode scores real traffic and takes no action, so you see the false-positive rate on your own data before enforcement is on. Rules can also be narrowed to specific entity kinds — redact identifiers while leaving email addresses alone, for instance — rather than being all-or-nothing per check.
Yes, and every recovery is itself a logged event with the person, the time and a stated reason. That is deliberate: irreversible masking sounds safer right up to the first legitimate dispute investigation, and unlogged reveals turn the safety property into a formality.
The engine has no concept of an industry. Checks produce findings, ordered rules decide what happens, and the policy is a versioned file you write. Today’s built-in detectors lean US-centric on personal data — that is a real limit and we would rather say so — but injection, secrets and blocklist rules are domain-neutral, and loadable entity packs are the next thing landing.
Open core, self-hostable, and it runs on your own infrastructure with your own model keys. The point of the architecture is that sensitive values never need to leave your network to be detected — which is not a claim you can make about a hosted scanning service, however good its policies are.
Observe. Evaluate. Protect. Prove.
Run it in shadow for a week. It will block nothing, and it will tell you exactly what a week of your real traffic would have cost you.
We’ll get back to you personally with the one base-URL change and a policy to start from.
We read every one of these ourselves. You’ll hear back at the address you gave — usually within a day.