Skip to content

Architecture ​

kilasec is a network-layer firewall for AI traffic. It sits between the agents that want to make API calls and the providers that serve them, decides what's allowed, and forwards the decision (not the payload) to a cloud control plane.

The two halves ​

  CUSTOMER NETWORK
  ─────────────────────────────────────────────────────────────────────────

  agent host / laptop / container / pod
  ┌──────────────────────────────────────┐
  │ Kilasec Connect (endpoints)           │  two lanes, ONE policy engine:
  │   • proxy lane    loopback :3128      │──┐ mitm, tenant CA on device
  │   • gateway lane  loopback :3129      │──┤ CA-free, 1 upstream TLS conn
  └──────────────────────────────────────┘  │
  browsers / OS-proxied apps ─────────────────┤ HTTPS_PROXY = lan:8080 (CONNECT)
                                               ▼
       ┌─ COLLECTOR (one container, on-prem) ──────────────────────────
       │
       │  kilasec-dp (Rust)  :8080   ← CONNECT + TLS-terminate,
       │      forge per-host leaf from tenant CA   (this IS the mitm)
       │      classify → evaluate → enforce, in-process
       │      device-TLS hop  :8443
       │          │ unix socket: events + slow path
       │          ▼
       │  mgmt plane  127.0.0.1:8000   (loopback, bearer-auth'd #527)
       │      /evaluate: approval holds · cloud who-is · token verify
       │      · uplink · rule/policy/scope CRUD
       │  PAC over TLS  :9443
       └──────────────────────────────────────┬───────────────────────
                                               │ decisions only — no raw payload
  ─────────────────────────────────────────────┼─────────────────────────────
                                               ▼  HTTPS outbound (uplink, one-way)
                                    ┌────────────────────────────┐
                                    │  kilasec.com  (CLOUD)      │
                                    │  Caddy (TLS, route)        │
                                    │  cloud.app :8000  FastAPI  │
                                    │    /api/v1/ingest/*        │
                                    │    /api/audit /me …        │
                                    │  SQLite (per-tenant)       │
                                    └─────────────┬──────────────┘
                                                  ▲  HTTPS, httpOnly cookie
                                    ┌─────────────┴──────────────┐
                                    │  admin browser             │
                                    │  /app/ (React SPA)         │
                                    └────────────────────────────┘

On the ports: HTTPS_PROXY = lan:8080 is the proxy address (http-scheme, as every forward proxy is named). The agent's actual traffic to the provider is HTTPS, tunneled via CONNECT and TLS-terminated by kilasec-dp — nothing runs in plaintext on the wire. The cloud uplink and the admin browser are both TLS.

On "mitm": the collector still man-in-the-middles every in-scope TLS flow (forge a per-host leaf from the tenant CA, terminate, inspect, re-originate) — that is the product. What changed is the implementation: the old mitmproxy (Python) was replaced by kilasec-dp (compiled Rust) with the policy engine ported to the kilasec-policy crate. There is no mitmproxy process anymore; the name survives only in the on-disk CA filename (mitmproxy-ca.pem, kept for compat) and legacy comments.

The collector is one container (entrypoint.py supervises the processes):

  • kilasec-dp (Rust) on :8080 — the data path. Terminates TLS for agent traffic by forging a per-host leaf cert signed by the tenant CA (set up on first run), then classifies the flow, resolves identity, and evaluates the rulebase in-process via the kilasec-policy crate and enforces the verdict itself. That CA must be trusted on every agent host that goes through the proxy lane. Also binds a device-TLS hop on :8443 for Kilasec Connect endpoints. (This replaced the old mitmproxy addon — see DESIGN-collector-dataplane-rearch.md.)
  • Management plane on 127.0.0.1:8000 — a FastAPI app (agentfw.proxy:app, uvicorn). It is the dp's slow path: POST /evaluate for the three things the dp can't decide locally (approval holds, cloud who-is, agent-token verify — once per session), plus the dp's event-socket consumer, the cost.json feed, rule/policy/scope CRUD, and the cloud uplink. Bound to loopback only, and bearer-auth'd whenever a cloud/mgmt token is set (AGENTFW_MGMT_TOKEN/AGENTFW_CLOUD_TOKEN; /healthz + PAC exempt; AGENTFW_MGMT_AUTH=0 restores open behavior).
  • PAC server (TLS) on :9443 — serves the proxy auto-config. Cert signed by the collector's own tenant CA so the customer never needs a separate certificate authority.
  • Cloud uplink — POSTs decisions to /api/v1/ingest/events (and /api/v1/ingest/usage, /api/v1/ingest/agents) and pulls policy/scope/pricing (guarding a last-known-good). One-way: the cloud never calls into the collector.

Two lanes onto the collector (devices running Kilasec Connect) ​

On endpoint devices, the Kilasec Connect client gives traffic two lanes into the same collector and the same policy engine — which lane a request takes changes how it's carried, never whether it's inspected:

Proxy lane (mitm)AI-gateway lane (CA-free)
Who uses itBrowsers, desktop apps, anything steered by the OS proxyAI CLIs / SDKs that honor ANTHROPIC_BASE_URL / OPENAI_BASE_URL
TransportHTTPS via local proxy :3128; TLS re-terminated with the tenant CAPlain HTTP on loopback :3129 to the local gateway, which holds ONE upstream TLS connection
CA required on deviceYes (tenant inspection CA)No — no forged certs, nothing in any trust store
Typical overhead~2 extra TLS handshakes per fresh connection~single-digit ms

The gateway is an optimization, not the enforcement boundary: a tool that misses the gateway environment (a shell opened before the agent, a GUI app) falls back to the proxy lane and is inspected identically. Connect's shell hook self-heals stale terminals onto the gateway at their next prompt, and the menu-bar app surfaces when recent AI traffic took the slow lane.

Cloud (kilasec.com) ​

  • Caddy — TLS termination, request routing. Routes /api/* to localhost:8000, /auth/invite/* for onboarding, /install for the collector installer, /app/* for the SPA, root for marketing.
  • cloud.app on :8000 — FastAPI. Receives ingest, serves the read API, handles invite redemption and session cookies.
  • SQLite — per-tenant tables (events, usage_records, agent_profiles, tenants, collectors, invites, identities, …). Tenant isolation is enforced at every endpoint via Depends(get_tenant_id).
  • SPA — React app at /app/. Cookie-authed, talks to the same-origin API via fetch("/api/…").

What crosses the on-prem / cloud boundary ​

This boundary matters more than any other architectural detail.

Crosses (collector → cloud):

  • Verdict (allow / deny / redact / require_approval / log)
  • Matched rule name + reason
  • Agent identity (name, optional hostname, principal_type, source IP)
  • Destination (host / tool / model)
  • Method + URL
  • For redact: the paths that were masked, never the values
  • Workload attribution (process / container / pod) when the eBPF host sensor is running
  • Token counts + estimated cost (when collector emits usage)

Never crosses:

  • Authorization / x-api-key / Bearer values — scrubbed by the collector before the event is shipped (only a fingerprint may remain).
  • Request bodies (prompt/tool content is replaced with a size stub unless the operator explicitly opts in via AGENTFW_SHIP_PROMPT_CONTENT).
  • The TLS-inspection CA private key — held tenant-side, not reconstructable from what the cloud stores.

This means the cloud UI cannot reconstruct the raw conversation between an agent and a provider. It can only tell you that a request happened, what kind, to where, with what verdict.

→ See On-prem / cloud boundary for the full list and why it's drawn where it's drawn.

Auth model ​

Two distinct credential types. They are not interchangeable.

TokenWho holds itUsed for
Tenant API token (agt_…)One per tenant; created and rotated by an admin on API Access, stored only as a hashScripts, SDKs, connectors and the host sensor. Scoped to read / policy / connector; CA, collector and device lifecycle stay admin-session only. Expiring per-assistant keys for Connect Claude are the same format.
Per-collector token (agt_collector_…)Each collector, in /etc/kilasec/collector.envAll ingest after enrollment. Lets you revoke one collector without rotating the others.
Session cookieBrowser, set when a user signs in via /auth/invite/{code}All SPA → API calls. httpOnly so JS can't read it.

Cloud get_tenant_id() resolves any of these to the same tenant_id, checking a token first (Authorization: Bearer, then X-Api-Token) and falling back to the session cookie when no token is present.

Identity model ​

A source's name is derived by walking a chain of signals, strongest first:

  1. Verified agent token — the kilasec_agent package sends a minted token the collector verifies against the cloud. The only authenticated name in the chain.
  2. Manual admin override (saved in the cloud identities table)
  3. Directory/DHCP-pushed identity
  4. Reverse DNS at the collector
  5. mDNS / Bonjour on the LAN
  6. x-agentfw-agent request header (self-declared)
  7. Parsed User-Agent (claude-cli, openai-python, browser, …)
  8. AGENTFW_AGENT env var (default unknown_agent)

The display name is resolved at query time, so renames apply retroactively. Audit rows store the raw facts (source_ip, hostname at event time) immutably.

→ Full details: Identity model.

What the system does not do ​

  • It doesn't terminate connections at the cloud — only at the on-prem collector. The cloud sees post-fact decisions, never live traffic.
  • It doesn't run the policy engine in the cloud. Each tenant's policy is authored in the cloud and pulled by their collectors, but evaluation happens on the collector, inline. Cloud only knows what got matched.
  • It doesn't hold customer secrets. Provider API keys stay on the customer's host; only fingerprints / decisions / redaction trails reach the cloud.

Those are deliberate. They let kilasec ship a security product without becoming a new high-value target on the customer's network.

Documentation for kilasec — the AI Agent Firewall.