Skip to content

SpiceDB-Backed Enforcement

ReBAC + taint tracking for IPI defense

The SpiceDB engine is an optional enforcement backend that combines relationship-based access control (ReBAC) via SpiceDB with runtime taint tracking. Use it when you need role-based tool access and automatic permission degradation as the agent reads from untrusted data.

Overview

Enable with policy_engine="pde" when creating JanusAgent:

agent = JanusAgent(
    model="openai/gpt-4o",
    policy_engine="pde",
    agent_role="coding_agent",
)

The engine does not load static JSON policies. Access is governed by the SpiceDB schema and bootstrap relationships, plus a per-session taint level.

ReBAC Layer

SpiceDB stores a permission graph in the Zanzibar model. Each tool has its own object type (e.g. tool_view_file, tool_run_tests). ACL edges grant roles the can_invoke relation on tools. Agents are members of roles. At tool-call time, SpiceDB is queried: does this agent's role have invoke permission on this tool?

Bootstrap assigns the coding_agent to roles readonly, developer, and executor. Tools are mapped to roles by tier (see below).

Taint Tracking

A session taint level starts at 0 and only increases. When the agent reads from a data source, call agent.update_taint(risk) with the source's risk level. Higher taint restricts which tools remain accessible.

Risk levels map to taint values:

Risk Taint
low 10
medium 40
high 70
critical 90

Example: after reading a web page (critical risk), call agent.update_taint(90). Tools with TOOL_TAINT_LIMIT below 90 are then blocked.

Two-Step Check

Before a tool executes:

  1. Taint gate (Python): If current_taint > TOOL_TAINT_LIMIT[tool_name], block.
  2. SpiceDB ACL gate: Query CheckPermission for invoke on tool_<name> for the agent. If no ACL edge exists, block.

Both must pass for the tool to run.

Tier Structure

Tools are grouped by risk. Tier 4 tools have no ACL edges and are always denied.

Tier Tools Taint limit (approx)
0 (read-only) view_file, grep, code_search, git_diff, git_log 90
1 (disk writes) edit_file, create_file, delete_file 50–70
2 (local exec) run_tests, run_script, pip_install 30–40
3 (remote git) git_commit, git_push, git_clone 20–60
4 (dangerous) bash_terminal, http_request, read_secret, write_secret, deploy 5–10, no ACL

How to Run

  1. Start SpiceDB (Docker):

bash cd demos && docker compose up -d && cd ..

  1. Use the engine via the demo scenario or Janus: Demo 5 bootstraps the demo schema and relationships when you run it (web app or uv run python -m examples.run demo5_taint_cascade --protected). The PDE code lives in Policy-Discovery-Engine/policy_engine/; janus.policy.pde_enforcer.PDEEnforcer wraps GraphInterceptor and uses a sync gRPC client so it works inside an asyncio event loop (e.g. uvicorn).

  2. Call agent.update_taint(risk) (or the scenario’s taint callback) when the agent reads from risky sources (e.g. after fetch_url or reading untrusted files).

  3. Run the example tests: uv run pytest tests/test_examples/ -v.

Layout

The SpiceDB engine lives under Policy-Discovery-Engine/policy_engine/ (enforcement, schema, bootstrap). The Janus adapter is janus/policy/pde_enforcer.py, which imports GraphInterceptor from the PDE and uses a sync gRPC client so enforcement works under uvicorn. The default engine remains the JSON Schema enforcer; PDE is optional.