Authentication & Modes

The API runs in one of three degradation modes depending on which services are available:

ModeServicesBehaviour
FullOPA + Neo4j + Anthropic APIComplete pipeline — real policy evaluation, graph writes, Claude responses
PartialOPA onlyPolicy decisions work; graph writes fall back to file receipts
SimulationNoneSandbox policy fallback; receipts stored as JSON files; Claude chat uses pre-baked responses

No authentication is required on any endpoint. Set ANTHROPIC_API_KEY as an environment variable to enable Claude-powered endpoints (/api/claude/run, /api/intent/explore).

Core Decision Engine

POST /api/decision/evaluate

Gate a request through OPA policy. If allowed, mints a TrustAtom™ (Ed25519-signed, SHA-256 hashed receipt) and writes to Neo4j. If denied, returns the policy reasoning with no TrustAtom.

Request body
{
  "env": "SANDBOX",
  "tenant_id": "demo-tenant",
  "decision_id": "dec_001",
  "principal_id": "human_tester",
  "agent_id": "agent_gatekeeper",
  "action": "READ_EVIDENCE",
  "resource_id": "neo4j:trustgraph",
  "request_category": "GRAPH_READ"
}
Response — ALLOW
{
  "input": { "action": "READ_EVIDENCE", "request_category": "GRAPH_READ", "..." },
  "decision": {
    "decision": "ALLOW",
    "reasons": ["action_in_safe_categories"],
    "policy_version": "sandbox-fallback-v1"
  },
  "trustatom": {
    "id": "ta_c7f2a1b3",
    "evidence_hash": "8a4f2e...d901c3",
    "signature_b64": "XkL9bQ...72NjYw==",
    "minted_at": "2026-02-26T13:56:40Z"
  },
  "neo4j": { "written": true },
  "timing": { "gate_ms": 12, "sign_ms": 3, "graph_ms": 8, "total_ms": 23 }
}
Response — DENY
{
  "input": { "action": "EXPORT_INTEGRATION", "..." },
  "decision": {
    "decision": "DENY",
    "reasons": ["action_not_in_safe_categories"],
    "policy_version": "sandbox-fallback-v1"
  },
  "trustatom": null,
  "neo4j": { "written": false }
}
POST /api/trustatom/verify

Verify the Ed25519 signature and SHA-256 evidence hash of a TrustAtom receipt. Can be called by auditors independently — no knowledge of the private key required.

Request body
{
  "evidence_hash": "8a4f2e...d901c3",
  "signature_b64": "XkL9bQ...72NjYw==",
  "receipt_payload": { "..." }
}
Response
{
  "ok": true,
  "signature": { "valid": true, "algorithm": "Ed25519" },
  "evidence_hash": {
    "match": true,
    "expected": "8a4f2e...d901c3",
    "computed": "8a4f2e...d901c3"
  }
}

Intent Layer (A²C CoEx)

POST /api/intent/parse

Parse a natural language query into a structured IntentPacket. Uses weighted phrase matching (no LLM required).

Intent types
query_trust_graph explore_policy audit_compliance visualize_lineage compare_decisions request_approval
Request body
{ "text": "Show me denied decisions from the last 7 days" }
Response
{
  "ok": true,
  "intent": {
    "intent_type": "query_trust_graph",
    "confidence": 0.85,
    "text": "Show me denied decisions from the last 7 days",
    "scope_constraints": { "time_range": "7d", "decision_type": "DENY" },
    "clarifications_needed": []
  }
}
POST /api/intent/explore

A²C Collaborative Exploration — Claude investigates trust decisions and explains policy reasoning in natural language. Supports streaming (SSE) and full-response modes.

Requires: ANTHROPIC_API_KEY. Without it, the console falls back to simulation mode with pre-baked responses.
Request body — streaming
{ "text": "Why was EXPORT_INTEGRATION denied?", "output_format": "stream" }
Response — SSE stream
data: {"chunk": "Looking at decision dec_0x4f21..."}
data: {"chunk": " The policy gate evaluated your request..."}
data: [DONE]
Request body — full
{ "text": "Why was EXPORT_INTEGRATION denied?", "output_format": "full" }
Response — full
{ "ok": true, "response": "The EXPORT_INTEGRATION action was denied because..." }
POST /api/decision/explain

Generate a policy explanation for a specific decision by ID. Returns human-readable reasoning plus compliance framework alignment.

Request body
{
  "decision_id": "dec_001",
  "mode": "why_allowed"
}

Modes: why_allowed · why_denied · constraints · similar_decisions

Response
{
  "ok": true,
  "explanation": {
    "summary": "READ_EVIDENCE was allowed under sandbox-fallback-v1 because the action falls within GRAPH_READ safe categories.",
    "policy_rules": ["action_in_safe_categories", "request_category_allowed"],
    "compliance_alignment": {
      "nist_csf": ["PR.AC-1: Access control enforced"],
      "soc2": ["CC6.1: Logical access controls"]
    }
  }
}

Graph Queries (Neo4j)

These endpoints query the Neo4j evidence graph. If Neo4j is unavailable, they return file-based fallback receipts or empty arrays.

GET /api/graph/stats

Dashboard counts — total decisions, allow/deny split, TrustAtom count, principal and agent counts.

Response
{
  "ok": true,
  "decision_count": 127,
  "allowed_count": 119,
  "denied_count": 8,
  "trustatom_count": 119,
  "principal_count": 5,
  "agent_count": 3
}
GET /api/graph/decisions/recent

Recent decisions with TrustAtom IDs, ordered by timestamp descending. Query param: limit (default 20, max 100).

GET /api/graph/decisions/denied

Denied decisions with policy reasoning. Query param: limit (default 20, max 100).

GET /api/graph/decision/{decision_id}

Full lineage for a single decision: Decision node + linked TrustAtom + PolicyEvaluation + DecisionTrace + Principal + Agent.

Advanced Analytics (DCT-Aware)

GET /api/graph/decisions/range

Time-range filtered decisions with DCT metadata. Defaults to last 24 hours if no range given.

Query params: since_ms · until_ms · limit (default 100)

Response
{
  "ok": true,
  "decisions": [{
    "decision_id": "dec_001",
    "action": "READ_EVIDENCE",
    "decision": "ALLOW",
    "timestamp_ms": 1740488200000,
    "dct_env": "SANDBOX",
    "dct_risk_score": 0.05,
    "dct_compliance_tags": "NIST:ID,NIST:DE,SOC2:CC6.1"
  }],
  "count": 1,
  "range": { "since_ms": 1740401800000, "until_ms": 1740488200000 }
}
GET /api/graph/analytics/agents

Per-agent decision statistics: total, allow count, deny count, deny rate. Query param: limit (default 20).

Response
{
  "ok": true,
  "agents": [{
    "agent_id": "agent_gatekeeper",
    "total": 50,
    "allowed": 42,
    "denied": 8,
    "deny_rate": 0.16
  }]
}
GET /api/graph/analytics/high-risk

Decisions with DCT risk score above threshold — anomaly surface for security review. Query params: threshold (default 0.7) · limit (default 50).

Response
{
  "ok": true,
  "decisions": [{
    "decision_id": "dec_003",
    "action": "DEPLOY",
    "dct_risk_score": 0.9,
    "decision": "DENY"
  }],
  "threshold": 0.7
}
GET /api/graph/analytics/compliance

Which compliance framework tags are most covered by decisions — shows audit coverage distribution.

Response
{
  "ok": true,
  "coverage": [
    { "tag": "NIST:ID", "count": 45 },
    { "tag": "SOC2:CC6.1", "count": 38 },
    { "tag": "MITRE:T1078", "count": 12 }
  ]
}
GET /api/graph/decision/{decision_id}/chain

DCT parent→child chain traversal. Follows dct_parent_decision_id links to reconstruct the full causal chain of decisions.

Response
{
  "ok": true,
  "root": "dec_parent_001",
  "chain": [
    { "decision_id": "dec_parent_001", "action": "GRAPH_READ", "decision": "ALLOW" },
    { "decision_id": "dec_child_002", "action": "CODE_GENERATION", "decision": "ALLOW" }
  ],
  "depth": 2
}

DCT — Decision Context Token

TrustAtom responses include 5 DCT extension fields that provide richer provenance and enable advanced graph traversal.

FieldTypeDescription
dct_envstringSANDBOX · STAGING · PRODUCTION
dct_parent_decision_idstringLinks to parent decision in causal chain (empty if root)
dct_compliance_tagsstringComma-separated tags e.g. NIST:PR,SOC2:CC6.1,MITRE:T1078
dct_risk_scorefloat0.0–1.0 auto-computed risk assessment (see table below)
dct_ttl_msintTime-to-live for constrained decisions. 0 = permanent
Risk score mapping by action
ActionRisk ScoreCategory
READ_EVIDENCE0.05Safe — read-only
GRAPH_READ0.10Safe — read-only
CODE_GENERATION0.30Moderate — creates artifacts
GRAPH_WRITE0.40Moderate — mutates graph
EXPORT_INTEGRATION0.85High — data egress
DEPLOY0.90Critical — production impact
MODIFY_POLICY0.95Critical — changes trust rules

Claude Code Agent

POST /api/claude/run

Autonomous code generation — gates the task through OPA policy, calls the Claude API, and mints a TrustAtom for the generated code artifact.

Requires: ANTHROPIC_API_KEY
Request body
{
  "task": "Write a function to validate email addresses",
  "constraints": { "language": "python", "max_lines": 50 },
  "mutations": 3,
  "env": "SANDBOX",
  "tenant_id": "demo-tenant",
  "principal_id": "human_dev",
  "agent_id": "claude_code_agent"
}
Response
{
  "status": "OK",
  "envelope": { "code": "def validate_email(email: str)...", "tests": "..." },
  "gate_decision": { "decision": "ALLOW", "policy_version": "sandbox-fallback-v1" },
  "trustatom": { "id": "ta_...", "evidence_hash": "...", "signature_b64": "..." }
}

Telemetry & Scenarios

POST /api/telemetry/import

Map raw event telemetry to NICE/MITRE skill types for workforce competency tracking.

Request body
{
  "events": [
    { "event_type": "port_scan" },
    { "event_type": "phishing_email" },
    { "event_type": "lateral_movement" }
  ]
}
Response
{ "ok": true, "mapped": [ "..." ], "unmapped_count": 0 }
GET /api/scenarios/list

List all available test scenarios from the /app/scenarios directory.

POST /api/scenarios/run_all

Execute all scenarios against the policy engine and return aggregate results.

Demo

GET /demo/run

Run both a DENY and an ALLOW evaluation in one call — mints a TrustAtom for the ALLOW case. Useful for testing the full pipeline without building a request body.

Response
{
  "deny": { "input": { "..." }, "decision": { "decision": "DENY", "..." } },
  "allow": {
    "input": { "..." },
    "decision": { "decision": "ALLOW", "..." },
    "trustatom": { "id": "ta_...", "signature_b64": "...", "..." }
  }
}
GET /health

Health check. Returns service status and degradation mode of each dependency.

Response
{ "ok": true, "service": "cwn-app", "version": "v4", "neo4j": "ok", "opa": "ok" }

Error Handling

All endpoints return structured errors with a consistent shape.

{ "detail": "Error description", "status_code": 400 }
200 Success 400 Bad request 404 Not found 500 Internal error
CodeWhen
400Missing or invalid request fields
404Decision ID or resource not found
500OPA timeout, Neo4j unavailable, signing failure

CORS: Configured via FastAPI CORSMiddleware. Default allowed origins: ["*"] (development). Tighten allow_origins in main.py before production deployment.