Service: secure-ai-policy-engine.service
Binary: /usr/libexec/secure-ai/policy-engine
Port: 8500 (loopback only)
Language: Go
Unified policy decision point for the entire SecAI OS appliance. Replaces scattered enforcement logic across tool-firewall, airlock, registry, and agent with a single, auditable decision API. Every allow/deny/ask decision emits a structured evidence object for full traceability.
Designed as a stepping stone toward full OPA/Rego: the current implementation uses the existing YAML policy files directly, but the decision API contract is identical to what an embedded OPA engine would expose. The upgrade path is to swap the evaluation internals without changing any caller.
+----------------+ +----------------+ +----------------+
| Tool Firewall | --> | | <-- | Agent |
| (8475) | | Policy Engine | | (8476) |
+----------------+ | (8500) | +----------------+
| |
+----------------+ | /api/v1/decide| +----------------+
| Airlock | --> | | <-- | Registry |
| (8490) | +-------+--------+ | (8470) |
+----------------+ | +----------------+
v
+---------------------+
| Decision Evidence |
| (audit log + API) |
+---------------------+
| Domain | Subject | What It Decides |
|---|---|---|
tool_access |
tool name | Allow/deny a tool invocation based on allowlist, denylist, path constraints |
path_access |
file path | Allow/deny agent read/write based on workspace scope |
egress |
destination URL | Allow/deny outbound network based on airlock state and destination allowlist |
agent_risk |
action name | Allow/deny/ask for agent actions based on risk classification |
sensitivity |
sensitivity level | Allow/deny based on sensitivity ceiling enforcement |
model_promotion |
model name | Allow/deny model format based on allowed/denied format lists |
Every response includes a DecisionEvidence object:
{
"timestamp": "2026-03-13T12:00:00Z",
"domain": "tool_access",
"policy_digest": "sha256:f1b3cb15d629...",
"rule_id": "tools.allow.filesystem.read",
"input_hash": "a1b2c3d4e5f6",
"eval_time_us": 42
}- policy_digest — SHA-256 of loaded policy files. Changes when policy is reloaded.
- rule_id — which specific rule matched the decision.
- input_hash — truncated SHA-256 of the request for correlation.
- eval_time_us — evaluation latency in microseconds.
- Policy file:
/etc/secure-ai/policy/policy.yaml(main policy) - Agent policy:
/etc/secure-ai/policy/agent.yaml(agent-specific rules) - Audit log:
/var/lib/secure-ai/logs/policy-engine-audit.jsonl
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /health |
None | Liveness check with policy digest |
| POST | /api/v1/decide |
None | Evaluate a policy decision |
| GET | /api/v1/stats |
None | Decision statistics (allow/deny/ask counts) |
| GET | /api/v1/digest |
None | Current policy digest for cache invalidation |
| POST | /api/v1/reload |
Token | Hot-reload policy files without restart |
DynamicUser=yes-- no persistent userPrivateNetwork=yes-- Unix socket communication onlyProtectSystem=strict-- read-only rootPrivateDevices=yes-- no device accessMemoryMax=128M-- minimal footprint- Per-service seccomp profile
- Landlock filesystem restrictions
The policy engine is designed for a clean upgrade to embedded OPA:
- Replace
evaluate()internals withrego.New()+rego.PrepareForEval() - Convert
policy.yaml→ Rego data bundle - Convert
agent.yaml→ Rego data bundle - Write Rego rules for each domain (tool_access.rego, etc.)
- The HTTP API contract and evidence format stay identical
- Tool Firewall -- current tool-level enforcement (caller)
- Agent -- agent policy engine (caller)
- Architecture -- system design overview
- Policy Schema -- YAML policy file reference