▶
Watch the walkthrough
A narrated 1:47 walkthrough of the live demo — signing in as admin, the risk dashboard, tamper-evidence verification, anomaly feeds and the dual-control approval flow. Audio commentary included.
01
Overview
Chokepoint is a full-stack security product built to answer one question: how do you let people — and increasingly, AI agents — perform high-impact actions without giving any single actor enough authority to abuse it?
It is a live, installable web application with real role-based authentication, a working two-person (dual-control) approval workflow, a SHA-256 hash-chained and HMAC-signed tamper-evident audit log, and explainable anomaly detection. It is framed around the OWASP Agentic AI failure class ASI03 — Identity & Privilege Abuse.
Where the substance is
lib/); the UI is the proof it actually runs. A Vitest suite tests the security properties themselves, not just page rendering.02
The problem
High-impact operations — wiping devices, changing access, moving money, reconfiguring production — share a dangerous property: a single account with enough privilege can cause irreversible damage, whether through malice, a stolen credential, or a compromised automation.
The problem gets harder with AI agents. Agents act fast, autonomously, and with borrowed human authority — exactly the conditions that make identity and privilege abuse so damaging. Chokepoint treats both humans and automated agents as named identities that must pass the same gates.
The design targets four goals simultaneously:
- No single actor can complete a sensitive action alone (separation of duties).
- Every action is attributable to an identity and cannot be silently erased (tamper-evident audit).
- Every action is authorized against a least-privilege policy before it runs (default-deny).
- Suspicious behavior is surfaced in human-readable form (explainable detection).
03
Threat model
The threat model assumes an attacker may hold a valid account (insider or stolen credential), may control an automated agent, and may attempt to alter the audit trail after acting. It treats the application's own authorization logic as the primary line of defense rather than trusting the network perimeter.
Assets
The ability to perform high-impact actions; the integrity and completeness of the audit record; session credentials.
Actors
Over-privileged users, stolen/reused credentials, malicious insiders, and compromised or over-permitted AI agents.
Abuse cases
Self-approval of destructive actions, role escalation, out-of-hours privilege use, brute force, and post-hoc log tampering.
Trust boundary
Anything reaching a route handler is untrusted until authenticated, authorized, and — for sensitive actions — approved.
04
Architecture
A Next.js (App Router) TypeScript application. Authentication issues a signed session cookie; a single policy engine authorizes every action; sensitive actions require dual-control approval; everything is written to a hash-chained, HMAC-signed ledger; and an anomaly layer scores risk for the dashboard.
Diagram reflects the module structure documented in the repository. Charts and diagrams are dependency-free so the strict CSP is untouched.
Key modules
| Module | Responsibility |
|---|---|
| lib/crypto.ts | PBKDF2-SHA256 password hashing with per-user salt, HMAC-SHA256 signatures, SHA-256 hashing, and constant-time comparison. |
| lib/ledger.ts | Append-only, hash-chained, HMAC-signed event log plus verifyChain() that detects alteration, deletion, and reordering. |
| lib/authz.ts | RBAC policy matrix, a single can() authorization gate on every action, and dual-control (distinct + authorized approver) enforcement. |
| lib/anomaly.ts | Explainable risk scoring and severity classification for suspicious signals. |
| lib/session.ts | Signed, HttpOnly, SameSite=Strict, expiring session cookie (Secure flag set when the connection is actually HTTPS). |
| lib/store.ts | In-memory store with seed/demo data and derived dashboard metrics (risk trend, severity breakdown, activity). |
05
Security controls
Each control is implemented in code and exercised by tests. Click any stage in the interactive flow below to inspect the principle — or read the full control inventory.
Request path · control plane
Actor
IdentityA human operator or an automated/AI agent initiates a request. Every actor — person or machine — is a named identity with a role, because access control begins with knowing who is acting.
If this control failed
Anonymous or shared accounts make every later control un-auditable: you cannot enforce least privilege or attribute actions you cannot tie to an identity.
Interactive request flow — click a stage to inspect the security principle.
Least privilege & RBAC▶
Four roles — viewer, auditor, operator, admin — each with only the permissions they need. Every action passes through one policy gate.
Enforced: Policy matrix in lib/authz.ts; default-deny.
Separation of duties▶
A person can never approve their own privileged request. The approver must be a distinct, authorized identity.
Enforced: Dual-control checks in lib/authz.ts, covered by authorization tests.
Tamper-evident audit trail▶
Every event is SHA-256 hash-chained and HMAC-signed. Edit, delete, reorder, or re-sign with the wrong key and verification fails.
Enforced: lib/ledger.ts + verifyChain(); proven by ledger tests.
Credential hygiene▶
PBKDF2-SHA256 with a per-user salt and timing-safe comparison — no plaintext or fast hashes.
Enforced: lib/crypto.ts; crypto tests.
Session hygiene▶
Sessions live in an HttpOnly, SameSite=Strict, signed, expiring cookie. Secure is set behind real HTTPS so it also runs on a local preview.
Enforced: lib/session.ts.
Explainable anomaly detection▶
Failed logins, after-hours privilege use, unknown sources, privilege escalation, and automation are surfaced with human-readable reasons and severity.
Enforced: lib/anomaly.ts; anomaly tests assert signals fire on the right conditions.
Transport & app hardening▶
Strict Content Security Policy and security headers; dependency-free charts so the CSP stays tight and no new transitive risk is added.
Enforced: Response headers; npm audit reports 0 vulnerabilities.
Human and AI actors▶
Access control treats people and automated/AI agents as named identities, addressing identity & privilege abuse in agentic systems (OWASP ASI03).
Enforced: Actor model in the policy and ledger layers.
06
Auth & approval flows
Authentication
Credentials are verified against PBKDF2-SHA256 hashes with a per-user salt using timing-safe comparison. On success, a signed, HttpOnly, SameSite=Strict, expiring session cookie is issued. The Secure flag is set only when the connection is genuinely HTTPS (via x-forwarded-proto), so it is enforced behind TLS in production yet still works on a local HTTP preview.
Authorization & dual-control
Every action passes through a single can() policy gate against a role matrix (viewer / auditor / operator / admin), defaulting to deny. High-impact actions additionally require approval from a second, distinct, authorized identity — the requester can never approve their own request.
07
Audit ledger & integrity
The ledger is append-only. Each entry stores a hash of the previous entry plus its own payload, forming a chain, and each entry is also signed with an HMAC keyed by a server secret. Verification recomputes the chain and signatures from start to finish.
// entry[i] integrity binding (conceptual)
entry[i].prevHash = SHA256(entry[i−1])
entry[i].hash = SHA256(prevHash + payload + ts)
entry[i].hmac = HMAC(key, hash)
// verifyChain(): recompute every link & signature
Tampering breaks verification in a detectable way:
- Alter a payload → its hash no longer matches, and every later link fails.
- Delete an entry → the chain has a gap; prevHash references break.
- Reorder entries → prevHash links no longer line up.
- Re-sign with the wrong key → HMAC verification fails (the server secret is unknown to the attacker).
08
Attack & failure scenarios
Rather than assert the controls work, the project reasons about how they fail and verifies the outcome. Selected scenarios:
An operator edits or deletes an audit event to cover an action
Attack · Tamper with a ledger entry's payload, remove an entry, or reorder events.
Outcome · verifyChain() recomputes hashes and HMACs and fails at the affected position — alteration, deletion, and reordering are all detectable.
Someone re-signs the log with a different key after tampering
Attack · Attempt to forge valid HMAC signatures without the server's secret.
Outcome · Without the HMAC key, forged signatures fail verification; the wrong-key case is explicitly tested.
A user approves their own destructive request
Attack · Requester tries to self-approve to satisfy the two-person rule alone.
Outcome · Authorization rejects it: approver must be distinct and authorized. Self-approval is blocked in code and in tests.
A lower-privileged role attempts an admin action
Attack · Viewer/operator calls an endpoint or action outside their role.
Outcome · The default-deny policy gate denies the action and records the attempt (an anomaly signal), rather than silently allowing it.
Credential guessing / brute force
Attack · Repeated failed logins, or timing-based user enumeration.
Outcome · Salted PBKDF2 hashing and constant-time comparison remove timing leaks; failed logins feed anomaly detection for risk scoring.
An automated agent over-uses privilege
Attack · An AI/automated identity performs high-impact actions or works outside expected patterns.
Outcome · Agents are named identities subject to the same RBAC and approval gates; automation and unusual patterns are flagged as anomaly signals.
09
Testing
The suite (npm test, Vitest — 26 tests across crypto, ledger, authz, and anomaly) tests the security properties rather than just rendering.
tests/ledger.test.ts—The audit chain detects altered payloads, deleted entries, reordered entries, and re-signed (wrong-key) entries.
tests/authz.test.ts—The policy matrix and dual-control rules — distinct-approver and authorized-approver — behave correctly.
tests/crypto.test.ts—PBKDF2 is salted and timing-safe, and HMAC keyed signatures verify as expected.
tests/anomaly.test.ts—Risk signals fire on exactly the intended conditions.
Dependency posture
npm audit 0 vulnerabilities, and dashboard charts are hand-built SVG to avoid adding dependencies that would loosen the Content Security Policy.10
Results & takeaways
Chokepoint demonstrates that the controls are not just described — they run and they are verified: a policy gate that defaults to deny, an approval rule that blocks self-approval, and a ledger that provably detects tampering. It is installable as a PWA (with a Capacitor path to native) and deploys to Vercel, so a reviewer can be inside the console within a minute using a one-click demo account.
The engineering lesson that carried over from the earlier reset lab: security claims are only as strong as the tests that try to break them. Designing the controls and the failure cases together is what makes the difference between a demo and something defensible.
11
Limitations
Honest scope
- Demo data and seeded accounts are held in an in-memory store; this is a productized demonstration, not a hardened multi-tenant production service.
- Seed/demo credentials are intentionally simple so the app runs out of the box — production requires setting CHOKEPOINT_SECRET and CHOKEPOINT_SESSION_SECRET and issuing real credentials.
- There is no production identity provider / SSO integration, no organizational directory sync, and no enterprise key management for HMAC keys in the demo build.
- Anomaly detection uses explainable rule- and score-based signals, not a trained ML model; it is transparent rather than exhaustive.
- Scale, high-availability, and operational concerns (backups, key rotation, distributed verification) are out of scope for the demonstration.
12
Future work
- Persistent, durable storage for the ledger with externalized key management and key rotation.
- SSO / OIDC integration and stronger MFA for production identities.
- External, append-only log backends (e.g. WORM storage or a SIEM) and independent chain verification / anchoring.
- Richer policy as code and configurable approval thresholds per action risk.
- Hardened agent identity model for AI actors — scoped tokens, per-agent policies, and tighter rate/capability limits.