Skip to content

API Reference ​

NASO exposes an asynchronous RESTful API built on FastAPI. All responses follow standard HTTP status codes and return JSON payloads.

Authentication ​

Four routes answer without credentials, and no others:

RouteWhy it is open
POST /auth/loginhow a client bootstraps a session
POST /auth/logouttakes an optional bearer and clears the cookie either way; an anonymous caller gets 200 and nothing else. It removes credentials, it never returns any
GET /system/statusorchestrators and load balancers hold no credentials, and both are written to say nothing an anonymous caller could use
GET /system/health

Everything else — all 46 remaining operations, GET /ai/health included — requires authentication. This is not a claim from inspection: backend/tests/test_tenant_isolation.py walks the OpenAPI schema and issues an anonymous request to every documented route, failing if anything outside that table answers, or if a guarded route rejects with anything other than 401/403 (a 422 would mean the request was parsed before it was authorised).

The interactive docs are not behind auth

/api/docs, /api/redoc and /api/openapi.json are served to anyone who can reach the port. They expose the full route inventory and every request schema — no data, but a complete map. That is a reasonable default for development and a poor one on an exposed deployment; pass docs_url=None, redoc_url=None, openapi_url=None to FastAPI() in backend/app/main.py, or block the three paths at the reverse proxy.

Log in to obtain a token:

bash
curl -X POST http://localhost:8000/auth/login \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "username=admin@naso.example.com&password=your_password"

Response:

json
{
  "access_token": "eyJhbGciOiJFZERTQSIs...",
  "token_type": "bearer"
}

Avoid a .local address

email-validator rejects the special-use TLDs local, test, localhost, invalid, arpa and onion. An admin provisioned under one of them authenticates fine and then fails response validation on /users/me. backend/init_db.py defaults to admin@naso.example.com for this reason.

There are two ways to present the token, and they behave differently:

Authorization: Bearer <access_token> — for API clients. Nothing else is required.

Authorization: Bearer <access_token>

Cookies — for browsers. The same login response also sets naso_access_token (httpOnly, so JavaScript cannot read it) and a naso_csrf companion that it deliberately can. A cookie-authenticated request that changes state must echo that companion back:

X-Naso-CSRF: <value of the naso_csrf cookie>

Without it the request is rejected with 403. Safe methods are exempt, and so is /auth/login itself. See Security Model.

Endpoints ​

Intelligence Stream ​

MethodEndpointDescription
GET/leaks/List all leak records for the current tenant
GET/leaks/{id}/screenshotRetrieve forensic screenshot (binary)
GET/leaks/export/dossierExport full PDF dossier
GET/leaks/recon/darkweb?q=<query>Execute dark web probe via Ahmia

Identity Management ​

MethodEndpointDescription
GET/identities/List monitored identities
POST/identities/Register new identity
GET/identities/{id}/insightsDeep forensic identity profile
PATCH/identities/{id}/protectToggle VIP protection
POST/identities/mergeTrigger batch auto-merge
GET/identities/graphForce-graph topology data

AI Co-Analyst ​

MethodEndpointDescription
GET/ai/healthCheck local LLM availability
POST/ai/chatStream SSE chat with tool calling
GET/ai/plansList investigation plans
POST/ai/plansCreate investigation plan
PATCH/ai/plans/{id}Update plan metadata
DELETE/ai/plans/{id}Delete investigation plan
POST/ai/plans/{id}/tasksAdd task to plan
PATCH/ai/plans/{id}/tasks/{taskId}Update task status

System & Compliance ​

MethodEndpointDescription
GET/system/statusLive database health check with latency
GET/system/healthComposite readiness across every backing service
GET/system/auditRetrieve the tamper-evident audit log — ?limit= (≤200) &offset=
GET/system/audit/verifyVerify the hash chain; admins may pass ?tenant_id=
GET/users/meThe authenticated operator — how the SPA restores a session
PUT/users/meUpdate operator profile

Both health endpoints are unauthenticated, because whatever probes them has no credentials. They answer different questions:

  • /system/status asks can the API reach its database — one probe, always 200, with "status": "operational" or "degraded" in the body. This is what the container HEALTHCHECK gates on.
  • /system/health asks which of the five backing services is up — database, Redis, Elasticsearch, MinIO and RabbitMQ, probed concurrently under a 3s timeout each. It answers 200 when everything configured is reachable and 503 when anything is not, so a load balancer can act on the status line alone. Each component reports ok, degraded, or disabled — the last for an optional service this deployment never configured, which is not a fault.

Neither returns hostnames, versions, or exception text. An unauthenticated endpoint that names the host that refused a connection is a free network map; the detail goes to the log instead.

Token claims ​

Access tokens are Ed25519-signed (EdDSA) and carry iss, aud, nbf, iat, exp, jti and sub. All seven are required on decode, and iss/aud are matched against JWT_ISSUER / JWT_AUDIENCE, so a token minted for another deployment sharing the same key pair is rejected rather than honoured. JWT_LEEWAY_SECONDS (default 30) absorbs clock skew on the time-based claims. Revocation is by jti against the Redis blacklist.

Security Headers ​

The API implements strict security policies:

  • CORS: Restricted origin policies.
  • CSRF: Double-submit cookie — cookie-authenticated mutating requests must echo the naso_csrf cookie back in an X-Naso-CSRF header.
  • TrustedHost: Drops requests with malformed Host headers.
  • Container Hardening: API runs as a non-root user (uid 10001) with no-new-privileges, cap_drop: ALL, and a read-only filesystem.

Interactive Documentation ​

When the backend is running, interactive OpenAPI documentation is available at:

  • Swagger UI: http://localhost:8000/api/docs
  • ReDoc: http://localhost:8000/api/redoc