API reference
The kilasec cloud backend (cloud.app) speaks JSON over HTTPS. This page is the high-level map; each endpoint group has its own page with shapes and examples.
Base URL
https://kilasec.com/apiCaddy strips the /api prefix and proxies to cloud.app on localhost:8000. In dev, Vite proxies /api → http://localhost:8000 so the same URLs work locally.
Authentication
Three credential mechanisms; cloud-side get_tenant_id() accepts any of them, cookie first.
Authorization: Bearer agt_<tenant-or-collector-token>
# or
X-Api-Token: agt_<tenant-or-collector-token>
# or (browser)
Cookie: kilasec_session=agt_<tenant-token>All endpoints (except /healthz and /invite/{code}) require a tenant. The credential resolves to a tenant_id and every read / write is scoped to that tenant — no cross-tenant data leaks possible.
Endpoint groups
| Group | Page | Purpose |
|---|---|---|
| Ingest | /reference/ingest | Collector → cloud event/usage/agent uploads |
| Audit & stats | /reference/audit | Dashboard reads — events, stats, vocabulary |
| Policy | /reference/policy | Read policy rules (write paths still live on the collector) |
| Collectors | /reference/collectors | Enroll, list, restart, rebuild, revoke |
| Identities | /reference/identities | Manual name overrides for the Agents page |
| DHCP leases | /reference/leases | Customer-fed lease snapshots |
Quick reference (the bits everyone needs)
Health
curl https://kilasec.com/api/healthz{ "ok": true, "service": "agentfw-cloud", "tenants": 1, "version": "1" }No auth required. Returns 200 if the cloud backend is reachable.
Recent decisions
curl -H "Authorization: Bearer $TOKEN" \
"https://kilasec.com/api/audit?limit=10"Returns the most recent N decision events for this tenant, newest last in the array (the UI reverses to newest-first). See the audit endpoint page for the full event shape.
Live tail (SSE)
curl -N -H "Authorization: Bearer $TOKEN" \
https://kilasec.com/api/eventsServer-Sent Events stream. Each data: line is one decision JSON, identical in shape to the /audit rows. Caddy is configured with flush_interval -1 so the stream isn't buffered.
Push events (from a collector)
curl -X POST -H "Authorization: Bearer $COLLECTOR_TOKEN" \
-H "Content-Type: application/json" \
--data '{"events":[{...}]}' \
https://kilasec.com/api/v1/ingest/events{ "ok": true, "ingested": 1 }The collector's uplink.py is what calls this in production. For synthetic testing, see Generate test traffic.
Errors
Standard HTTP. All errors come back as JSON {"detail": "..."}:
| Status | Meaning |
|---|---|
| 400 | malformed body (FastAPI Pydantic validation) |
| 401 | no credential, or credential not recognised |
| 403 | CSRF middleware blocked a state-changing call without the X-Kilasec-CSRF header |
| 404 | tenant-scoped resource doesn't exist for this tenant |
| 409 | duplicate (e.g. POST /admin/tenants on an existing id) |
| 5xx | bug; report at github.com/blox-24/agentfw |
CSRF
State-changing requests from the browser must carry X-Kilasec-CSRF: 1. The SPA does this automatically; if you're testing with curl from your own browser session cookie, add the header manually.
Collector tokens are exempt — they hit /v1/ingest/* with Authorization: Bearer only and bypass CSRF (which is correct: CSRF is a same-origin browser concern, not a server-to-server one).
Rate limits
There's a soft per-tenant rate limit on the ingest endpoints (currently 200 req/s, burst 500) via the slowapi middleware. Collector clients should batch — POST events arrays of up to 500 at a time, not one HTTP call per decision.
Backward compatibility
API versions live in the path: /v1/…. New fields are additive. Renames go through a deprecation cycle of at least one minor version with both names valid; check the changelog before upgrading.