eBPF-native, developer-first Web Application and API Firewall for Kubernetes. kernel shield · userspace engine · control plane
Trishula protects Kubernetes traffic in three planes. The kernel shield runs
verifier-checked eBPF programs. The userspace engine runs the detection
stack: OWASP CRS, CEL rules, OpenAPI positive security, bot fingerprints and
the ban table. The control plane compiles WAFPolicy CRDs into signed policy
bundles. Every verdict is a correlated OpenTelemetry record.
Status: the 2026.01-rc3 component set sits merged on main. The socket-plane compiler pathway lands its first class (route marking) with a machine-checked verifier gate. The lab
gates run green on a live cluster. The bench harness measures the
§17 walls in CI. The gaps carry their own
list on this page.
Each slice landed as a watched-RED then GREEN leaf PR per AGENTS.md.
| Component | Where | What exists | Evidence |
|---|---|---|---|
| Shield loaders | internal/shield/ |
CO-RE XDP + TC objects (arm64, amd64); Go loaders; fail-closed TierRefusal; tier measure at boot. |
#80 tier run · #10 ban E2E |
| Kernel C + wire | bpf/ |
shield_xdp.c (ACL, verdict cache, ban table), flow_tc.c (ringbuf); wire layouts test-pinned. |
banwire tests in-package |
| Ingest | internal/engine/ingest/ |
ringbuf drain → decode → per-connection assembly; kernel-free. | TR-04d merge |
| Detection ladder | internal/engine/ladder/ |
S0–S5 wiring + verdict vocabulary; HTTP/1 and HTTP/2/gRPC parse. | TR-05 merge |
| OTel telemetry | internal/engine/otel/ |
span + metric + log record, correlated by request id; the trishula.ban record and the verdict.dos.sig signature attribute (TR-195). |
#11 evidence · #250 tests in-package |
| Bot fingerprints | internal/engine/botdef/ |
JA4 + header-plane detector; golden vectors. | #99 |
| Positive security | internal/engine/positive/ |
OpenAPI 3.1-subset body gate; fail-closed. | #13 evidence |
| CRS evaluator | internal/crs/ · test/crs-differential/ |
embedded Coraza reference + CI-blocking parity differential. | #7 parity 19/19 |
| ScoredWindowBan | internal/engine/rateban/ |
§13.3 ban with clock seam + evidence; kernel publish. | #10 in-VM E2E |
| Rate algorithms | internal/engine/rateban/ |
token bucket, multi-window sliding, EWMA per §13.2; CRD-selected; Retry-After on every throttle. | #20 RED/GREEN tables in-package |
| Behavioural DoS detectors | internal/engine/rateban/ · bpf/flow_tc.c |
slowloris shape over the kernel's flow-window features + the h2 rapid-reset ratio; kernel counts and stamps only; the detector path scores, never enforces. | #31 RED/GREEN tables in-package + the ingest4d kernel gate |
| Shield DoS runner | cmd/shield/ · internal/shield/ |
TR-195: the pinned flow_stats sweep → the detector cues → the scored path → the enforcer publish; the cue, the evidence row and the kernel row state the same signature. |
#249 tests in-package; in-VM acceptance #253 |
| Operator | internal/operator/ · api/v1alpha1/ |
WAFPolicy CRD → bundle compile (fail closed) → publish. |
#76 M9 evidence |
| Manual ban rules | internal/operator/ · cmd/engine/ |
TR-195: a CEL rule action: ban stops the request with 403 + X-Trishula-Decision: ban + the owning rule; the rule emits the manual-rule would-ban record with the signature. |
#248 tests in-package · #252 consult-signal tests |
| Route-class fast path | internal/fastpath/ · bpf/ |
TR-40b: the route class compiles through the census into the template, loads through the verifier under route-gate.yml, and the split reads back. A tracked evaluation; no runtime commitment. |
census record §9 |
| Engine + consult | cmd/engine/ |
transparent proxy; bundle consult; hot reload; one verdict-signal emission per consult decision (the ban decisions carry the signature). | #6 hop evidence · #252 consult-signal tests |
| DX1 lab | lab/dx1/ · lab/demo.sh |
idempotent bring-up; timed apply→effect; hop verification. | Proof record |
Budgets read off §17.1.
Wall numbers read from the last recorded per-row CI run. The run:
40147859961,
head 05affd9, the rc3 content with the verbose bench step (#230).
The numbers re-read at each release. Each row's wall carries the receipt comment this read posted.
| Cell | Budget p99 | Last build p99 | Verdict |
|---|---|---|---|
| idle-get | 600 µs | 865.0 µs | unmet (1.44x) on this read — runner noise; the quiet rig reads 0.30x |
| attack-sqli-small | 1500 µs | 1029.0 µs | holds (0.69x) |
| attack-xss-small | 1500 µs | 1078.9 µs | holds (0.72x) — variance class, #147 |
| ladder-json-1k | 600 µs | 825.7 µs | unmet (1.38x) on this read — runner noise; the quiet rig reads 0.64x |
| ladder-json-8k | 600 µs | 862.1 µs | unmet (1.44x) — variance class, #147 |
| ladder-json-64k | 1200 µs | 1096.6 µs | holds (0.91x) |
| ladder-form-dense-1k | 600 µs | 15395.2 µs | pinned open — the §17.1 |
| ladder-form-dense-8k | 600 µs | 135633.1 µs | pinned open — the §17.1 |
| steady-json-8k (the M2 e2e cell) | 1500 µs | 433.7 µs | holds (0.29x) |
The list pairs each not-shipped feature with its tracker.
- Inline
< 10 mswalls on the lab plane — the CI harness measures engine-only cells today. - Verdicts on live ringbuf traffic inside cluster pods — a later engine slice.
- HTTP/3 request-path parse — the posture evaluation record publishes (TR-29); the parse is a later engine slice.
- Socket-plane: more policy classes after route marking, and the kTLS posture — the first class lands as a tracked evaluation (#52).
- trishulactl full CLI surface — the skeleton exists; the surface is a later phase.
- OpenAPI promotion gates — learn mode and drift telemetry ship (TR-18); the full gate ladder rides later slices (#18).
- ML scoring beyond v0 — the ONNX in-band scorer ships (TR-21); the adaptive tier rides later slices (#21).
- Wire-out OTLP export — the triad verifies in-memory today.
- The DPU-class port — the evaluation record publishes (TR-32); the port itself rides a later phase (#32). Multi-arch and OpenShift notes ship (TR-33); the rows leave this list.
The board is the Trishula Roadmap.
Layout (the reference implementation)
api/v1alpha1/ CRD types (WAFPolicy, BanPolicy, …)
cmd/ engine, operator, shield, trishula, trishulactl, attackdemo, cuegen
bpf/ shield_xdp.c, flow_tc.c (CO-RE, bpf2go)
cue/ CUE definitions — the declarative layer (TR-46), the golden-diff source
internal/ engine (ingest, ladder, cel, http1, http2, otel, botdef, positive, rateban), operator, shield, cuegen
manifests/ install manifests (the CRD only; controller-gen-owned)
deployment/ derived manifests (the dx1 lab tree, written by cmd/cuegen — never hand-edited)
lab/ demo.sh · dx1/ · demo-attack.sh · verify-xdp-chain.sh · tier-gate.sh
test/ crs-differential · ban-conformance · banenforce · tier · bench
docs/ spec, paper, academy
just demo # or: bash lab/demo.shjust alone lists every lab lane; the runner installs with
brew install just. The
prereq gate = docker OR a reachable kubernetes cluster; the gate runs
inside the scripts (lab/prereq.sh). The script brings the DX1 lab up on
the cluster your kubeconfig points at — a local kind or OrbStack, or a
cloud Kubernetes. Needs go, kubectl, helm and one of the two
target surfaces. It builds the engine and operator binaries, applies
the policy and runs the timed apply→effect scenario. The
exit banner reads DEMO-OK or DEMO-FAIL. The verbatim recorded output sits
on the issue record above.
bash lab/dx1/dx1-gate.shThe gate asserts: clone → first enforced rule in ≤ 15 min. The nightly job keeps the timing asserted.
go test ./...The suite gates the bench walls, the CRS parity differential (19/19 corpus cases), the ban conformance set, the shield tier gate and the docs guards.
lab/bench-vm.sh runs the §17.2 rig in the dev VM. Start from a VM copy of the tree:
orb run -m trishula-build-dev -u root bash /mnt/mac/<path-to-clone>/lab/bench-vm.shThe script prints four sections: the new CI shape, the old-shape control, the walls with the prefilter on and the control with it off. The VM is the A/B surface. Hosted CI stays the calibration surface.
The verbatim lab/demo.sh output for the rc1 exit (2026-10-10) sits on the recorded
run at main ed9b271. The hop, M9 and parity evidence trails link from the
rows above.
The shield measures what the hook sees (#80). On OrbStack-family pod-veths the attach altitude is not the effective altitude.
| Tier | Meaning | Enforcement posture |
|---|---|---|
full |
every packet at both altitudes (XDP + TC) | full payload visibility at TC |
first_packet |
first packet only (OrbStack pod-veth posture) | ban/ACL on new connections only; sampled flows |
none |
no packets visible | loader refuses and fails loudly (TierRefusal) |
MIT — see LICENSE.