Designed and built by Vladimir Kamenev vlad@degenito.ai·github.com/DeGenitoAI/portier
Portier.
AI-SecOps  ·  Hospitality

Portier

A doorkeeper for your AI agents.

Your concierge and guest-service agents read untrusted guest text — booking notes, chats. Portier inspects every message before the agent acts and returns a deterministic ALLOW · REDACT · BLOCK decision in code, mapped to OWASP LLM Top 10 (2025), the OWASP Agentic Top 10, and NIST AI RMF.

DETERMINISTIC — a hard guarantee (code decides) PROBABILISTIC — a signal, never the decision
01

Live inspection

Pick a scenario — or paste your own note and try to get something past the firewall. The six-layer trace streams in live; deterministic code makes the call.

portier · /inspectstream: SSE
deterministic replay — safe to try anything
Press Inspect to watch the trace stream in.
▢ awaiting decision…

Try it yourself

Press Try ▸ on any example to load it into the box above and watch the six-layer trace stream, or Copy it and edit your own. It all runs in safe DEMO mode — paste anything you like. Each card shows the expected verdict and why the deterministic boundary reaches it.

02

The inspection pipeline

Probabilistic layers produce evidence first; deterministic code makes and enforces the decision. If a probabilistic layer is wrong, the verdict does not change — the boundary is code.

■ deterministic = hard guarantee○ probabilistic = signal only▦ policy engine = the boundary
03

The agent registry

Least privilege is real, not cosmetic: each specialist sees only its slice of tools, and none of them can act — they read and classify. The decision is made by code that has no model at all.

AgentModel roleScoped tools (least privilege)
Inspectorsupervisor — delegates & assembles a typed verdict; never decidesdelegate-only · no data tools
InjectionAnalystprompt-injection / jailbreak intentvector_signaturesread-only
DataClassifiersensitive-access & state-change intentclassification_ctxread-only
FrameworkMapperOWASP / NIST mappingknowledge_baseread-only
PolicyEnginethe security boundary — pure codeno LLM · no tools
04

The defense, layer by layer

Six ordered layers, each tagged by what it can promise. Built from the 2025–2026 state of the art — every layer cited to its primary source.

A Probabilistic signals

Spotlightingprob

Untrusted content is wrapped & datamarked so the agent treats it as opaque data — not instructions. Microsoft.

Embedding signalprob

sqlite-vec KNN against known injection signatures. A vote, never a verdict — shown as audit evidence. OWASP LLM08.

Quarantined inspectorprob

Scoped PydanticAI specialists read & classify only — the dual-LLM / CaMeL pattern. DeepMind.

B Deterministic guarantees

PII / PCI scandet

Microsoft Presidio detects guest PII and card data; redaction is applied in code. OWASP LLM02.

Policy enginedet

Meta's Rule of Two + egress allowlist → the ALLOW/REDACT/BLOCK decision. Pure function, exhaustively tested. OWASP LLM06.

Output restrictionsdet

Strip hidden Unicode tag chars; defang markdown images/links — closes exfiltration channels without the model. SOTA.

05

Deployed on Cloudflare

A Worker at the edge fronts a Python container — no servers to manage, scales to zero. The LLM & graph backends are optional (LIVE mode); DEMO runs with neither.

Edge · Worker

Gateway

Serves this console, relays SSE, sets security headers, and rate-limits /inspect. Holds no security logic.

HTTPS ▸
Container · FastAPI

Inspection engine

The six-layer pipeline + the deterministic policy boundary. PydanticAI · Azure/Claudesqlite-vecPresidioNeo4jrate-limitaudit log

── solid = request path┄┄ dashed = optional (LIVE only)backend: DEMO ⇄ LIVE via PORTIER_MODE
06

The safety boundary is code, not a prompt

2025–2026 consensus: prompt injection can't be solved at the model layer. So the decision is made by a pure function — even a fully jailbroken specialist cannot change the verdict.

decide() · the only place a Decision is created
ALLOW — clean pass REDACT — PII/PCI present BLOCK — rule-of-two / egress / critical
# the security boundary — pure Python, no LLM, exhaustively tested
def decide(signals) -> Decision:
    rule_of_two = (signals.untrusted_input
                 + signals.sensitive_access
                 + signals.state_change) >= 3
    egress_bad  = any(t not in ALLOWLIST for t in signals.egress_targets)
    if rule_of_two or egress_bad or signals.has_critical:
        return Decision(BLOCK, ...)   # code decides — never the model
    if signals.pii_entities:
        return Decision(REDACT, ...)
    return Decision(ALLOW, ...)
07

Everything used to build it

A current stack — PydanticAI agents, the edge platform, and a verified security posture, every choice deliberate.

08

How it was built

From a job spec to a deployed, hardened system — research-first, spec-driven, verified in the real app.