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                                          │
              │                                                            │
   ┌────────────────┐   HTTPS_PROXY     ┌──────────────────────┐           │
   │  agent host    │ ────────────────► │  collector (LAN)     │           │
   │  laptop /      │  http://lan:8080  │                      │           │
   │  container /   │   + mitm CA       │  mitmproxy :8080     │           │
   │  pod           │                   │  PDP :8000 (loopback)│           │
   └────────────────┘                   │  PAC :9443           │           │
              │                          └──────┬───────────────┘           │
              │                                 │ decisions only            │
              │                                 │ (no raw payload)          │
              └─────────────────────────────────┼───────────────────────────┘

                                                ▼  HTTPS outbound
                                       ┌─────────────────────┐
                                       │   kilasec.com       │
                                       │                     │
                                       │  Caddy (TLS, route) │
                                       │  cloud.app :8000    │
                                       │   /api/v1/ingest/*  │
                                       │   /api/audit /me…   │
                                       │                     │
                                       │  SQLite (per-tenant)│
                                       └─────────────────────┘

                                                │  HTTPS, session cookie

                                       ┌─────────────────────┐
                                       │  admin browser      │
                                       │  /app/ (React SPA)  │
                                       └─────────────────────┘

Collector (on-prem)

  • mitmproxy on :8080 — terminates TLS for agent traffic. The first time a client connects, mitmproxy mints a per-connection cert signed by a CA the collector generated on first run. That CA must be trusted on every agent host that goes through the proxy.
  • PDP (Policy Decision Point) on 127.0.0.1:8000 — a small FastAPI app that loads policies.yaml and answers POST /evaluate with {verdict, matched_rule, reason}. Bound to loopback only — never exposed to the LAN.
  • PAC server on :9443 — serves the proxy auto-config to DHCP clients. Cert signed by the collector's own mitmproxy 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). One-way: cloud does not call into the collector.

Cloud (kilasec.com)

  • Caddy — TLS termination, request routing. Routes /api/* to localhost:8000, /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, dhcp_leases). 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, and now dest_ip:dest_port since 369f3d2)
  • Method + URL (path only; no query strings with secrets)
  • For redact: the names of redacted fields (body.card_number), never the values
  • Token counts + estimated cost (when collector emits usage)

Never crosses:

  • Authorization / x-api-key / Bearer values — scrubbed in _classify_request before the event is shipped.
  • Request bodies (with the exception of fields the policy author explicitly chose to ship for audit).
  • mitmproxy CA private key — the collector generates and keeps it. Cloud never sees it.
  • DHCP lease MAC addresses, unless the customer opts into the lease feeder.

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. For the original content, you must SSH into the collector.

→ 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, stored on the cloud VMThe "default" credential before collectors are enrolled. Carried by the legacy ingest path.
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 cookie (kilasec_session=)Browser, set by /invite/{code}All SPA → API calls. Cookie value equals the tenant token; httpOnly so JS can't read it.

Cloud get_tenant_id() resolves any of these to the same tenant_id, in order: cookie → Authorization: Bearerx-api-token. Cookie wins so upstream-proxy-injected headers can't override a real session.

Identity model

Names for sources are derived from the network, not from a database we maintain. Priority (strongest first):

  1. DHCP-pushed identity (vendor option)
  2. DHCP lease feed (the customer's DHCP server POSTs leases to /api/v1/leases)
  3. Reverse DNS at the collector
  4. mDNS / Bonjour on the LAN
  5. x-agentfw-agent request header (SDK opt-in)
  6. AGENTFW_AGENT env var
  7. Manual admin override (saved in cloud identities table)

The display name is resolved at query time, so renames apply retroactively. Audit rows store the raw facts (source_ip, mac, 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 (yet). Each tenant's policies.yaml lives on their collectors. 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.