Policy engine for attribute-based access control (ABAC), tamper-evident audit logging, and FedRAMP-aligned access decisions — built for systems where every request is untrusted until proven otherwise.
Written in Go because access control engines sit in the hot path of every API call and can't afford garbage collection pauses or runtime overhead.
flowchart TB
subgraph Client["Incoming Request"]
REQ[HTTP Request + JWT]
end
subgraph Middleware["Enforcement Layer"]
MW[HTTP Middleware] --> EXTRACT[Identity Extraction]
EXTRACT --> CONTEXT[Request Context Assembly]
end
subgraph Engine["Policy Engine"]
CONTEXT --> EVAL[ABAC Evaluator]
EVAL --> CACHE[Policy Cache]
EVAL --> RULES[Policy Rules - YAML]
EVAL -->|Match| ALLOW[Allow]
EVAL -->|No Match| DENY[Deny - Default]
end
subgraph Audit["Audit Trail"]
ALLOW --> LOG[Hash-Chained Logger]
DENY --> LOG
LOG --> STORE[Tamper-Evident Store]
STORE --> EXPORT[3PAO Export]
end
REQ --> MW
style DENY fill:#e74c3c,color:#fff
style ALLOW fill:#27ae60,color:#fff
style CACHE fill:#3498db,color:#fff
Every request follows the same path regardless of outcome. There are no shortcuts for "trusted" callers - the system treats internal and external requests identically per NIST 800-207.
- Extract - JWT claims, client certificate attributes, source IP, and request metadata are pulled into a typed identity context
- Assemble - Request context combines identity, target resource, action, and environment (time, network zone, device posture)
- Evaluate - ABAC engine matches context against policy rules with AND/OR composition and priority ordering
- Decide - No matching policy = deny. This is non-negotiable.
- Log - Every decision is hash-chained to the previous entry. Tampering with any log entry invalidates all subsequent entries.
cmd/zta/- CLI and server entry pointpkg/policy/- ABAC policy evaluation engine with composable conditionspkg/identity/- JWT claims extraction and identity contextpkg/audit/- Tamper-evident hash-chained audit loggingpkg/middleware/- Drop-in HTTP enforcement middlewarepolicies/- YAML policy definitions (default deny + sample rules)
- Fine-grained decisions on user, resource, action, and environment attributes
- Policy composition with AND/OR logic, priority ordering
- Default deny - no policy match = deny (fail closed)
- Sub-millisecond evaluation (< 0.5ms p99)
- Hash-chained entries detect modification or deletion
- Every allow/deny logged with full request context
- Exportable for external verification by auditors
- Aligned with FedRAMP AC-2, AC-6, AU-2 controls
- Drop-in for Go HTTP servers
- Zero-allocation hot path for cache hits
- Automatic decision logging
| Decision | Choice | Alternative Considered | Rationale |
|---|---|---|---|
| Language | Go | Rust, Java | Sub-ms latency needed in hot path. Go gives us the performance floor without Rust's compile-time complexity. Java's GC pauses are unacceptable for inline policy evaluation. |
| Policy format | YAML | OPA/Rego, Cedar | YAML is auditor-readable. FedRAMP assessors need to review policies directly - Rego requires specialized knowledge that slows audit cycles. |
| Default stance | Deny | Allow with exceptions | Fail-closed is the only defensible default for federal systems. An outage is preferable to an unauthorized access. |
| Audit storage | Hash-chained append-only | Database with checksums | Hash chaining gives cryptographic tamper evidence without external dependencies. A modified entry invalidates all subsequent hashes - auditors can verify with a single traversal. |
| Caching | In-process LRU | Redis/external cache | Policy evaluation must not depend on network calls. Adding Redis creates a failure mode where cache unavailability could cascade into auth failures across services. |
| Identity model | JWT claims | Session lookup | Stateless identity avoids a central session store that becomes a single point of failure and a scaling bottleneck. |
This engine directly addresses the following NIST 800-53 controls required for FedRAMP High authorization:
| Control Family | Controls | Implementation |
|---|---|---|
| Access Control | AC-2, AC-3, AC-6 | ABAC engine enforces least privilege per-request. No standing access. |
| Audit | AU-2, AU-3, AU-6, AU-9 | Hash-chained audit trail with full request context. Tamper-evident by design. |
| Identification | IA-2, IA-4, IA-5 | JWT-based identity with claims validation. Supports MFA-derived claims. |
| System Integrity | SI-4, SI-7 | Audit chain integrity verification. Policy checksum validation on load. |
flowchart LR
subgraph Current["Single Node - Current"]
A[Policy Engine] --> B[In-Process Cache]
A --> C[Local Audit Log]
end
subgraph Distributed["Multi-Node - Production"]
D[Policy Engine x N] --> E[Shared Policy Store]
D --> F[Distributed Audit Sink]
F --> G[Central Audit Aggregator]
E --> H[Git-backed Policy Repo]
end
Current -.->|"Scale path"| Distributed
Current scope: Single-node deployment suitable for sidecar or gateway patterns handling 10K+ decisions/sec.
Production scale path:
- Policy distribution: Git-backed policy repo with webhook-triggered cache invalidation across nodes
- Audit aggregation: Local buffering with async flush to central audit store (Kafka or append-only S3)
- Horizontal scaling: Stateless engine nodes behind load balancer. No shared state required for policy evaluation.
- Multi-region: Policy repo replication with eventual consistency (acceptable because policy changes are infrequent and auditable)
make build
./bin/zta evaluate # Run sample policy evaluation
./bin/zta serve # Start enforcement server on :8443- Default deny - no matching policy means no access
- Least privilege - policies grant minimum necessary access
- Separation of duty - policy authors can't evaluate their own policies
- Auditability - every decision traceable to a policy and subject
Built from experience leading product and engineering at the US Department of the Treasury, where zero-trust isn't marketing - it's NIST 800-207 compliance. Informed by military service where "need to know" is the foundation of every access decision.
MIT