Jinn Guard is an asynchronous, kernel-aware agent governance daemon designed to enforce safety constraints on autonomous AI agents before any tool execution is permitted. It evaluates agent actions using a keyword/intent heuristic plus an optional external AI semantic scorer, and processes them through a lifetime-anchored Z3 SMT solver pipeline — performing constraint checks on state transitions and risk ceilings against formalized compliance models before granting or denying execution authority.
Operating locally over high-throughput UNIX domain sockets, the platform binds user-space proxy validation with low-level eBPF kernel telemetry and namespace tracking to enforce zero-trust process isolation and anti-replay protection for governed cgroups.
Automated demo (Rust + eBPF): one legitimate request is allowed; real attacks are blocked live by the daemon.
bash scripts/demo.sh # or: bash scripts/demo.sh --autoA narrated dashboard that drives the real daemon: one legitimate request is allowed, seven real attacks are blocked live, the daemon's own metrics are read back, and the validated numbers + safety guarantees are walked through — plain enough for a non-technical audience. Nothing is mocked. Presenter notes and safety FAQ:
DEMO.md.
python3 scripts/validate/validate.py # attacks, determinism, audit-chain, tamper-proofA reproducible, dependency-free harness that drives the real daemon and lets you independently verify every claim: re-run for determinism, recompute the tamper-evident audit hash-chain yourself, corrupt the log and watch it get caught, and bring your own attack. See
scripts/validate/andscripts/validate/CLAIMS.md.
Every number below comes from running the real daemon on a real machine (AMD Ryzen 5 7520U, 8 cores, Linux 6.12), with each request driven through the complete decision pipeline — nothing is faked or stubbed. Measured 2026-06-12. Reproduce locally:
cargo bench --bench stress_bench # speed + throughput
cargo test --release --test swarm_attack # attack resistanceFull detail: BENCHMARKS-01.md.
One "decision" = a full security check (verify signature → replay/identity/intent/ quota gates → risk + Z3 math ceiling → audit). Across 10,000 requests:
| Half of requests finish under | 95% finish under | 99% finish under | Slowest |
|---|---|---|---|
| 257 µs (P50) | 366 µs (P95) | 463 µs (P99) | 1.9 ms |
In plain English: a complete security decision takes about a quarter of a millisecond — faster than the blink of an eye (~100 ms) by ~400×.
| Load | Throughput | Errors |
|---|---|---|
| 10–500 agents at once | ~6,500 decisions/sec | 0 |
| Single client | ~3,640 decisions/sec | 0 |
| 5,000 mixed allow/deny | 100% classified correctly | 0 |
In plain English: it keeps up with thousands of agent actions per second without a single dropped or wrong answer.
We ran 12 adversarial tests spanning 10 distinct attack types against the live daemon (>1,200 hostile requests). All 12 passed — 0 fail-open (it never once wrongly allowed an attack):
| Attack | What it tries | Sent | Blocked |
|---|---|---|---|
| Replay storm | Re-send a captured valid request | 50 | 49 ✅ (1st allowed, rest blocked) |
| Signature forgery | Tampered/forged signatures | 100 | 100 ✅ |
| Intent injection | Call a forbidden action (rm_all) |
200 | 200 ✅ |
| Quota exhaustion | Spam past the allowed limit | many | capped at exactly 5 ✅ |
| Risk-ceiling breach | Push a too-risky action past the math limit | 50 | 50 ✅ |
| Anonymous flood | Act with no identity | 200 | 200 ✅ |
| Unknown agent | Impersonate an unregistered agent | 100 | 100 ✅ |
| Bad protocol version | Malformed/old protocol | 50 | 50 ✅ |
| Forged delegation | Fake a delegation token | 20 | 20 ✅ |
| Path traversal (MCP) | Escape allowed paths | 20 | 20 ✅ |
| All 8 at once (mixed) | Everything concurrently | 400 | 349 blocked + 50 real requests still correctly allowed ✅ |
In plain English: under a coordinated attack, it blocked every hostile request, never failed open, stayed up, and still let a legitimate request through correctly — in under a millisecond.
This repository includes a reproducible Rust sandbox for development, CI-style builds, and Step 1 capability-broker testing. It installs Rust/Cargo, native Z3, SQLite/OpenSSL headers, Python 3, and Clang/LLVM in a Docker image.
make docker-build
make dev-shell
make docker-smokeFor the full workflow, see docs/rust_sandbox.md.
If the sandbox MCP gateway port is busy, set JINN_GUARD_MCP_PORT, for example:
JINN_GUARD_MCP_PORT=4860 make smokeThe repository also includes a locked agent runtime for development validation. It demonstrates that an agent can be denied direct shell, network, and sensitive filesystem access while still reaching the Jinn Guard broker over a Unix socket.
make runtime-smokeThe runtime compose profile uses docker-compose.runtime.yml:
jinnguard-brokerowns the real capabilities and exposes the runtime socket.locked-agentruns as non-root UID10001, with no outbound network, read-only workspace, all Linux capabilities dropped, no-new-privileges, shell binaries removed, and a seccomp profile for high-risk syscalls.
The Step 2 policy rejects verdict-only dangerous actions with:
SIGNAL: DENY_RUNTIME_POLICY
See docs/step2_mandatory_mediation_sandbox.md.
┌─────────────────────────────────────────────────────────────────┐
│ AI Agent Process │
│ (Python SDK / jinnguard_py) │
│ proposal = {agent_id, intent_name, context_vars, ...} │
│ signed_envelope = HMAC-SHA256(proposal, kernel_keyring_key) │
└──────────────────────┬──────────────────────────────────────────┘
│ UNIX domain socket /run/jinnguard/jinnguard.sock
│ 5-byte framed wire protocol (version + length)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Jinn Guard Daemon (ts_cli) │
│ │
│ 1. SO_PEERCRED → verify kernel PID / UID / GID │
│ 2. HMAC verify → tamper detection (key from kernel keyring) │
│ 3. G1: Agent lookup → allowed_intents + max_sequence_quota │
│ 4. Semantic classify → heuristic intent class + risk score │
│ 5. RiskAssessment → fused_risk, trust_score │
│ 6. Lineage check → replay detection + behavioral drift │
│ 7. Policy decision → risk ceiling + trust floor │
│ 8. G2: Z3 totality audit + per-agent invariant verification │
│ 9. ExecutionBroker → path / URL / command hard denylist │
│ 10. Hash-chained audit log (DENY or ALLOW) │
└──────────────────────┬──────────────────────────────────────────┘
│
┌────────────┴────────────┐
▼ ▼
SIGNAL: ALLOW SIGNAL: DENY_*
(tool executes) (tool blocked)
Kernel Layer (eBPF)
jg_execve.bpf.c → tracepoint/syscalls/sys_enter_execve
jg_openat.bpf.c → tracepoint/syscalls/sys_enter_openat
jg_connect.bpf.c → tracepoint/syscalls/sys_enter_connect
jg_cap_capable.bpf.c → tracepoint/capability/capable
│
└─→ RingBuf EVENTS → AyaKernelTelemetry (user-space drain thread)
│
└─→ governance loop (telemetry feed)
A note on languages. The probes in
bpf/are C — small, separately-compiled eBPF programs loaded into the kernel. The governance core (the daemon, the Z3 verification pipeline, the policy engine, and the CLI) is Rust, underts_cli/.bpf/**is markedlinguist-vendored, so GitHub's language bar reflects the Rust core rather than the volume of low-level kernel C.
| Crate / Directory | Purpose |
|---|---|
ts_cli/ |
Main daemon — UDS listener, enforcement pipeline, audit logger |
ts_checker/ |
Z3 SMT policy engine — state transition proofs + declarative invariants |
jinnguard_py/ |
Python SDK for agent integration |
bpf/ |
eBPF C programs (execve, openat, connect, cap_capable) + Makefile |
deploy/ |
systemd unit + install.sh provisioner |
tests/ |
Integration test harness |
This repository is the open-core, single-node Jinn Guard: one host, local
policy.yaml, governed by the kernel. It is fully functional standalone.
Running Jinn Guard across many hosts under one signed policy — a control
plane, central policy distribution with HMAC-signed bundles + rollback
protection, hot-reload, and fleet dashboards — is the enterprise layer. The
client side ships here as a stable integration hook compiled only with
--features enterprise (off by default); the control-plane server and fleet
tooling are distributed separately.
Interested in fleet deployment or a pilot? Open an issue or reach out via the repo — happy to talk.
| Control | Implementation |
|---|---|
| Secret management | HMAC-SHA256 key loaded into Linux kernel keyring (jinnguard_hmac_key in @s) or /etc/jinnguard/secret (mode 0400, owner root). A bounded current/previous keyset supports supervised rotation grace windows; optional per-agent secret files can override the shared key for selected agent_id values. |
| Process identity | SO_PEERCRED on every UDS connection — kernel-verified PID, UID, GID. No client-declared identity is trusted. |
| Agent identity | agent_id from proposal is matched against agent_nodes in policy.yaml. Unknown IDs are hard-denied (DENY_UNKNOWN_AGENT_ID); optional per-agent allowed_peer_uids binds a signed agent_id to the local Unix UID observed via SO_PEERCRED (DENY_AGENT_IDENTITY_BINDING). |
| Intent enforcement | Per-agent allowed_intents allowlist checked before semantic classification (DENY_INTENT_NOT_ALLOWED). |
| Sequence quota | Per-agent max_sequence_quota hard cap on decisions per lineage session (DENY_QUOTA_EXHAUSTED). |
| Replay protection | Monotonic sequence_counter validated per (pid, start_time) lineage key (DENY_REPLAY_ATTACK). |
| Behavioral drift | Statistical drift detector on fused_risk delta against lineage baseline (DENY_BEHAVIORAL_DRIFT). |
| Z3 constraint check | Every request passes through execute_totality_audit() — Z3-backed constraint check that fused_risk ≤ ceiling. |
| Z3 invariants | Per-agent declarative constraints (spending_ceiling_usd, privilege_escalation_depth, etc.) verified against daemon-computed runtime state. Callers cannot spoof daemon-observed values. |
| Audit log | Hash-chained JSONL log (each entry includes the SHA-256 of the previous — tamper evident), and erasure-safe: personal data is kept out of the chain and crypto-shredded on a GDPR Art. 17 request, with the chain still verifying. See THREAT_MODEL.md §12. |
| eBPF telemetry | Kernel-level visibility into execve, file open, network connect, and capability checks — correlated with governance decisions. |
Architecture & trust boundaries:
SECURITY_ARCHITECTURE.md(components, data flow, the two enforcement planes, the open-core boundary). Adversary model & threat→mitigation evidence:THREAT_MODEL.md.
- Linux kernel 5.16+
- CONFIG_BPF_LSM=y
bpfpresent in the active LSM list (cat /sys/kernel/security/lsm)- Debian cloud kernels often have it pre-armed.
- Ubuntu does not — append
bpfto the existing list via thelsm=boot parameter, thenupdate-gruband reboot. For example, in/etc/default/grub:(List the modules already inGRUB_CMDLINE_LINUX="lsm=lockdown,capability,landlock,yama,apparmor,ima,evm,bpf"/sys/kernel/security/lsmplusbpf; an explicitlsm=replaces the kernel default, so include the full set.) - rpm-ostree / immutable hosts should not hand-edit grub. On a booted
ostree host, stage the full active LSM set plus
bpfdeclaratively:The helper prints the exactsudo deploy/arm-lsm-ostree.sh sudo systemctl rebootrpm-ostree kargschange before applying it and prints the one-line revert (rpm-ostree kargs --delete=lsm=...; orrpm-ostree rollbackbefore reboot). Off ostree hosts, it is a no-op. On daemon startup, the audit chain records a boot marker with the booted ostree commit, kernel release, and ostree/non-ostree flag. rpm-ostree provides platform immutability; it does not perform Jinn Guard action blocking.
- bpftool installed for
vmlinux.hgeneration (Debian:bpftool; Ubuntu:linux-tools-generic)
Validated on three distributions / three kernel generations: Debian 13 / kernel
6.12 (BENCHMARKS-01.md, BENCHMARKS-02.md),
Ubuntu 24.04 / kernel 6.17 (BENCHMARKS-03.md), and
AlmaLinux 9 / kernel 5.14 under SELinux Enforcing (BENCHMARKS-04.md).
Advisory registry: the canonical list of
JG-ADV-*IDs, status, and fix commits lives inSECURITY/ADVISORIES.md.Note on identifiers:
JG-ADV-*are internal, self-identified advisory IDs, not CVE records issued by a CNA.
The BPF inode_create/inode_unlink hooks now resolve the full absolute
path of a file operation in the kernel (a bounded d_parent walk), closing the
earlier basename-only bypass. Validated live (audit-only) via
scripts/validate_m2_path_resolution.sh.
Residual limitation (telemetry only, since JG #52): the inode hooks receive a
dentry without a vfsmount, so the reconstructed path string for a file on a
sub-mount (e.g. a tmpfs /tmp) is relative to that mount's root (/tmp/x →
/x). This affects the logged/audited path, not the enforcement decision —
denied directories are matched by their (s_dev, i_ino) identity, which a
mount/bind/pivot_root remap cannot fool (see THREAT_MODEL §7.1). Root-filesystem
paths (/etc, /usr, /opt, /home on a single-root install) also resolve to
full absolute strings. Full cross-mount path strings require path-family LSM
hooks or bpf_d_path and are tracked for a future release.
An agent explicitly allowed to run an interpreter can invoke other tools through it. Jinn Guard denies known interpreters by policy for governed agents (any agent carrying an executable allowlist); per-binary execve limits remain only as strong as that allowlist.
lab/ is a self-contained, dependency-free lab that teaches the
Jinn Guard model — semantic intent → policy → decision → audit — without the
kernel/eBPF layer. Students build a mini allow/deny/canary/human-review checker
(python3 lab/checker_starter.py), then find and fix a planted audit-logging
flaw. It is explicit about which verdicts the real daemon enforces today
(ALLOW/DENY) versus which are taught as concepts (CANARY_TRIGGERED,
HUMAN_REVIEW); the design path to close that gap is in
docs/agent_governance_extensions.md.
# Debian/Ubuntu
sudo apt install libz3-dev libssl-dev pkg-config llvm clang libbpf-dev keyutils
# Ubuntu only: bpftool ships inside linux-tools (no standalone package)
sudo apt install linux-tools-generic # provides bpftool for vmlinux.h generation
# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shUbuntu note.
libssl-dev+pkg-configare required (theopenssl-sysdependency). And unlike Debian's cloud kernels, Ubuntu does not armbpfin its default LSM list — see System Requirements to enable it before running kernel enforcement. Validated end-to-end on Ubuntu 24.04 / kernel 6.17 — seeBENCHMARKS-03.md.
git clone https://github.com/AlphaReasoning/The-Jinn-Guard
cd The-Jinn-Guard
sudo ./deploy/install.shThe installer will:
- Create the
jinnguardsystem user - Generate a 256-bit HMAC secret →
/etc/jinnguard/secret - Load the key into the kernel keyring
- Build and install
/usr/sbin/jinnguard - Enable and start
jinnguard.service
# Load existing secret into kernel keyring
keyctl padd user jinnguard_hmac_key @s < /etc/jinnguard/secret
# Verify the key is present
keyctl show @sOptional per-agent HMAC keys can be supplied with
JINNGUARD_AGENT_SECRET_DIR=/etc/jinnguard/agent-secrets or
--agent-secret-dir /etc/jinnguard/agent-secrets. Each regular file name is an
agent_id; its contents are the HMAC key for that agent. If a file exists for an
agent, that agent must sign with the per-agent key. Agents without a file keep
the shared admission key path.
systemctl status jinnguard
journalctl -u jinnguard -ffrom jinnguard_py import JinnGuardClient
client = JinnGuardClient(
socket_path="/run/jinnguard/jinnguard.sock",
secret_key="<your-hmac-secret>", # or use keyring lookup
agent_id="fabric_swarm_production_01",
)
result = client.propose(
intent_name="read_file",
sequence_counter=1,
action_risk_score=20.0,
proposed_action={"kind": "file_write", "path": "/tmp/out.txt", "contents": "hello"},
context_vars={"spending_ceiling_usd": 50.0},
)
if result.allowed:
print("Execution permitted:", result.signal)
else:
print("Blocked:", result.signal)global_safety_ceiling: 75.0 # fused_risk must be below this
agent_nodes:
- id: fabric_swarm_production_01
privilege_tier: 1
# Optional: bind this signed agent_id to local Unix users observed through
# SO_PEERCRED. Empty/omitted preserves the shared-key legacy behavior.
# Pair with a file named /etc/jinnguard/agent-secrets/fabric_swarm_production_01
# to require a per-agent HMAC key for this id.
allowed_peer_uids:
- 10001
allowed_intents:
- read_file
- model_inference
- write_temp
max_sequence_quota: 500 # max decisions per lineage session (0 = unlimited)
invariants:
- "spending_ceiling_usd <= 150.00"
- "privilege_escalation_depth < 3"
- "fused_risk <= 74.0"
- id: admin_agent_00
privilege_tier: 3
allowed_intents: [] # empty = all intents allowed
max_sequence_quota: 0 # 0 = unlimited
invariants:
- "trust_score >= 30.0"Invariants are simple infix expressions resolved by the Z3 real-arithmetic solver:
<variable> <op> <literal>
| Operator | Meaning |
|---|---|
<=, >=, <, >, == |
Standard comparison |
Daemon-authoritative variables (cannot be spoofed by the caller):
| Variable | Source |
|---|---|
fused_risk |
RiskAssessment::assess() |
trust_score |
RiskAssessment::assess() |
observed_risk, semantic_risk, topology_risk |
Assessment sub-components |
is_root |
SO_PEERCRED UID = 0 → 1.0 |
privilege_tier |
Agent node policy config |
decisions_seen |
Lineage registry |
max_assessed_risk |
Lineage registry |
privilege_escalation_depth |
Derived from is_root × decisions_seen |
Caller-supplementable variables (agent can declare, daemon values take precedence):
| Variable | Example |
|---|---|
spending_ceiling_usd |
Tool execution cost declared by the agent |
| Any custom key | Passed in proposal.context_vars |
| Resource | Path |
|---|---|
| UDS socket | /run/jinnguard/jinnguard.sock |
| Audit log | /var/log/jinnguard/audit.log |
| Lineage store | /var/lib/jinnguard/lineage.json |
| Policy config | /etc/jinnguard/policy.yaml |
| HMAC secret | /etc/jinnguard/secret (mode 0400, owner root) |
| eBPF object | /usr/lib/jinnguard/jinnguard_ebpf.o |
jinnguard [OPTIONS]
Options:
--socket-path <PATH> UDS socket path [default: /run/jinnguard/jinnguard.sock]
--lineage-file <PATH> Lineage persistence file [default: /var/lib/jinnguard/lineage.json]
--audit-log <PATH> Audit log path [default: /var/log/jinnguard/audit.log]
--policy-file <PATH> Policy YAML [default: /etc/jinnguard/policy.yaml]
-h, --help Print help# Prerequisites (Debian)
sudo apt install clang llvm libbpf-dev linux-headers-$(uname -r) bpftool
# On Ubuntu, bpftool comes from linux-tools-generic instead of a `bpftool` package:
# sudo apt install clang llvm libbpf-dev linux-headers-$(uname -r) linux-tools-generic
# Build all four programs and merge into jinnguard_ebpf.o
cd bpf && make
# Install to /usr/lib/jinnguard/
sudo make installThe merged object is loaded at runtime by the aya-rs backend inside the daemon when built with the kernel_telemetry feature.
# Run the full suite (122 tests: 4 Z3 + 93 unit + 13 integration + 12 swarm-attack)
cargo test
# Run benchmarks
cargo bench
# Benchmarks cover:
# z3_state_transition — risk check hot path at various risk levels
# z3_policy_invariants — 2-constraint and 5-constraint invariant solve
# totality_audit — per-connection Z3 ceiling proof
# uds_socket_saturation — raw UDS framed roundtrip latency
# uds_socket_saturation — persistent connection variant
# Check without building
cargo checkLooking for the headline numbers? See 📊 Performance & Security at a Glance above, or
BENCHMARKS-01.mdfor full latency, throughput, and attack-suite detail.Independent reviewers: see
PROFESSOR_VALIDATION.mdand runbash scripts/run_professor_validation.shfor a one-command, tiered validation of everything below.Security researchers: see
BREAK_IT_CHALLENGE.mdfor the public break-it challenge scope, rules of engagement, and report template. Vulnerabilities should be reported privately first throughSECURITY.md.
Validated on three real hosts — Debian 13 / kernel 6.12, Ubuntu 24.04 / kernel 6.17, and AlmaLinux 9 / kernel 5.14 (SELinux Enforcing):
| Capability | How it was validated |
|---|---|
| Full automated suite (122 tests) | cargo test --workspace — 4 Z3 + 93 unit + 13 integration + 12 swarm-attack, plus anti-lockout + safe-mode invariants |
| Mandatory mediation | Docker locked-agent: 7/7 probes — direct network//etc-write/shell blocked, broker-mediated actions succeed |
| Kernel full-path resolution | eBPF-LSM hooks load + resolve absolute paths live (audit-only) |
| Kernel allow/deny enforcement | tests/kernel_lsm.rs armed on a real 6.12 host: 2,500 enforced ops across execve/TCP/UDP/create/unlink, 0 fail-open, 0 incorrect decisions (P50 8–473µs, P99 20–1038µs by surface); enforcement is cgroup-scoped so it runs safely without a spare machine. Re-validated on Ubuntu 24.04 / kernel 6.17 (BENCHMARKS-03.md) and AlmaLinux 9 / kernel 5.14 under SELinux Enforcing (2,750 ops, 0 fail-open / 0 incorrect; BENCHMARKS-04.md) |
| Anti-lockout guarantee | CI-enforced invariant tests plus in-kernel cgroup scoping (bpf_get_current_cgroup_id): only the governed cgroup is subject to allow/deny, every other task passes through; safe mode stays audit-only |
This is not independently audited or enterprise-GA. It is a strong,
test-backed prototype demonstrating OS-level AI-agent enforcement.
See RESIDUAL_RISKS.md for the current residual-risk
register and release claim boundary.
| Component | Status |
|---|---|
| UDS IPC transport (framed, version-tagged) | ✅ Production |
| HMAC-SHA256 authentication | ✅ Production |
| Kernel keyring secret management | ✅ Production |
| Bounded HMAC key rotation grace | ✅ Production |
| Optional per-agent HMAC key files | ✅ Production |
SO_PEERCRED process identity |
✅ Production |
| Z3 totality audit | ✅ Production |
| Z3 per-agent invariant verification (G2) | ✅ Production |
| Per-agent intent allowlist enforcement (G1) | ✅ Production |
| Per-agent sequence quota enforcement (G1) | ✅ Production |
| Replay attack protection | ✅ Production |
| Behavioral drift detection | ✅ Production |
| Hash-chained audit log | ✅ Production |
| ExecutionBroker hard denylist | ✅ Production |
| Lineage persistence | ✅ Production |
| systemd unit + installer | ✅ Production |
Python SDK (jinnguard_py) |
✅ Functional |
| eBPF C sources (4 programs + Makefile) | ✅ Source complete |
| UDS saturation benchmark | ✅ Implemented |
| Item | Status |
|---|---|
| eBPF compilation gated in CI (compiles all 10 LSM objects, fails the build on a BPF regression) | ✅ Done |
Structured, machine-parseable CLI exit codes for startup failures (code=/kind=) |
✅ Done |
Opt-in capability hardening after BPF load — no_new_privs + bounding-set drop (JINNGUARD_HARDEN_CAPS=1) |
✅ Done |
Prometheus /metrics + /healthz endpoint (opt-in JINNGUARD_METRICS_PORT, loopback) |
✅ Done |
OTLP/HTTP JSON metrics export (opt-in JINNGUARD_OTLP_ENDPOINT / OTEL_EXPORTER_OTLP_*) |
✅ Done |
Optional RootAI remote semantic service over mTLS (--rootai-url + --rootai-tls-*) |
✅ Done |
Public break-it challenge scope and reporting template (BREAK_IT_CHALLENGE.md) |
✅ Done |
Verified reproducible release build gate (RELEASE_INTEGRITY.md) |
✅ Done |
Operator runbook (OPERATOR_RUNBOOK.md): install, monitor, upgrade/rollback, incident response |
✅ Done |
| Gap | Effort |
|---|---|
| Multi-distribution / multi-kernel validation matrix (Debian 13/6.12 + Ubuntu 24.04/6.17 + AlmaLinux 9/5.14 done; broaden coverage over time) | Medium |
