v3.0.0 — Runtime Risk Engine. Targets three runtime-security gaps highlighted at RSA 2026: (1) agent self-modification of policy, (2) silent permission drift, (3) MCP intent-aware inspection. End-to-end demo arc shippable in 10 minutes against a fresh deployment.
Project status. Early-stage, single-maintainer project. The code is functional and CI-gated, but it has not had an independent security audit or penetration test, has no production deployments or reference customers yet, and all compliance references describe control mappings and design intent — not third-party certification or accreditation. Evaluate accordingly.
An integrated runtime risk engine for AI agents.
OAuth tells you who called a tool. TokenDNA aims to tell you whether the call is what it claims to be, whether the agent is allowed to make it, and what happens to your blast radius if it turns out it isn't.
| Gap (RSA 2026) | Detection | Severity |
|---|---|---|
| Agent self-modification of governing policy (CrowdStrike F50 incident) | POLICY_SCOPE_MODIFICATION in trust graph + policy_guard BLOCK |
CRITICAL |
| Silent permission drift — scope grows without attestation | PERMISSION_WEIGHT_DRIFT in trust graph + permission_drift alert |
HIGH |
| MCP intent-aware tool inspection — chain pattern detection | mcp_inspector bounded-gap subsequence matcher with confidence scoring |
CRITICAL |
UIS event → trust_graph → anomaly fires
│ │
▼ ▼
blast_radius policy_guard
(live signals (BLOCK)
on the blast) │
│ ▼
│ policy_advisor
│ (tightening
│ suggestion)
▼ │
intent_correlation operator approves
(chain to MITRE) in dashboard
Every state-changing operation in every security module emits an AuditEvent
into a tamper-evident hash-chained log, intended to provide SOC 2-relevant audit evidence.
DEV_MODE=true uvicorn api:app --port 8000 &
python scripts/demo_runtime_risk_engine.pyWalks through baseline → drift → self-modification → MCP chain → deception trip → blast radius (with live signals attached) → policy_guard verdict → policy_advisor recommendation → operator approval. Idempotent, replay-safe.
ent.blast_radius, ent.intent_correlation, ent.enforcement_plane (policy_guard + policy_advisor + cert_dashboard), ent.behavioral_dna (permission_drift + agent_lifecycle), ent.mcp_gateway (mcp_inspector + mcp_gateway). Defined in modules/product/commercial_tiers.py; gated via require_feature("ent.<key>").
Underlying platform — v2.2.0 hardening: RBAC, immutable audit log, security headers middleware, HMAC-SHA256 IP fingerprinting, secrets manager backend, CIS Docker hardening, Postgres + Helm + Grafana + SAML/SCIM all implemented.
Zero-Trust token integrity and session behavioral analytics.
TokenDNA's identity primitives originated as a stolen-JWT detector that built a behavioral "DNA" fingerprint for each user — device, IP, geolocation, ASN, browser, OS — and flagged anomalies like impossible travel, session branching, Tor/VPN usage, and known-malicious IPs. Every request is scored and responded to adaptively: allow, step-up MFA, block, or auto-revoke. Those primitives are the foundation the Runtime Risk Engine sits on top of.
Client
│ Authorization: Bearer <jwt>
│ DPoP: <dpop-proof>
▼
Cloudflare Worker (edge/index.js)
├─ RS256 JWT verification (JWKS endpoint)
├─ JWT revocation check (KV)
├─ Full RFC 9449 DPoP validation
└─ ML risk pre-check → auto-revoke if score says so
│
▼ (proxied)
TokenDNA API (FastAPI / Python)
├─ GeoIP lookup (ip-api or MaxMind)
├─ Threat intel (Tor, datacenter ASN, VPN, AbuseIPDB)
├─ DNA fingerprint (SHA-256 device + IP hash)
├─ Adaptive ML model (per-user Redis profile)
├─ Session graph (impossible travel, branching)
├─ Unified scoring (ALLOW / STEP_UP / BLOCK / REVOKE)
├─ Risk-adaptive responses (200 / 202 / 403 / 401)
└─ Async event logging → ClickHouse
│
├─ Redis (baselines, profiles, graph, revocation list, rate limits)
└─ ClickHouse (immutable event log, 90-day TTL)
| Tier | HTTP | Meaning | Action |
|---|---|---|---|
| ALLOW | 200 | Normal session | Pass through |
| STEP_UP | 202 | Elevated risk | Dispatch MFA challenge |
| BLOCK | 403 | High risk | Reject + alert |
| REVOKE | 401 | Critical risk | Revoke token + alert |
# 1. Clone and configure
cp .env.example .env
# Edit .env — set OIDC_ISSUER, OIDC_AUDIENCE, and any webhook URLs
# 2. Start full stack
docker compose up -d
# 3. Check health
curl http://localhost:8000/For local development without an OIDC provider:
DEV_MODE=true docker compose up -d
# JWT verification is bypassed and a synthetic dev tenant is injected.All settings are read from environment variables (see .env.example).
| Variable | Default | Description |
|---|---|---|
DEV_MODE |
false |
Disable JWT verification (dev only) |
DEV_TENANT_ROLE |
owner |
Synthetic tenant role used only when DEV_MODE=true |
TOKENDNA_COMPLIANCE_PROFILE |
commercial |
Deployment gate profile: commercial, cmmc_l2, fedramp_high, dod_il4, dod_il5, or dod_il6 |
OIDC_ISSUER |
— | OIDC provider base URL |
OIDC_AUDIENCE |
— | Expected aud claim in JWTs |
TOKENDNA_OIDC_TENANT_CLAIM |
org_id,tenant_id,tid,organization |
JWT claim used to resolve the tenant |
TOKENDNA_OIDC_ROLE_CLAIM |
tokendna_role,role |
JWT claim used for direct TokenDNA role mapping |
TOKENDNA_OIDC_GROUPS_CLAIM |
roles,groups |
JWT claim used for group-based role mapping |
TOKENDNA_OIDC_GROUP_ROLE_MAP_JSON |
— | JSON map from IdP groups to owner, admin, analyst, or readonly |
TOKENDNA_OIDC_ALLOW_SUB_TENANT_FALLBACK |
false in production |
Allow sub/client_id tenant fallback outside hardened production mode |
TOKENDNA_SCIM_GROUP_ROLE_MAP_JSON |
— | JSON map from SCIM groups to TokenDNA roles |
REDIS_HOST |
redis |
Redis hostname |
CLICKHOUSE_HOST |
clickhouse |
ClickHouse hostname |
GEOIP_PROVIDER |
ip-api |
ip-api or maxmind |
ABUSEIPDB_API_KEY |
— | AbuseIPDB lookup key (optional) |
SCORE_THRESHOLD_ALLOW |
70 |
Score above → ALLOW |
SCORE_THRESHOLD_STEP_UP |
50 |
Score above → STEP_UP |
SCORE_THRESHOLD_BLOCK |
30 |
Score above → BLOCK |
SCORE_THRESHOLD_REVOKE |
15 |
Score at/below → REVOKE |
EDGE_DECISION_SLO_MS |
5 |
Runtime enforcement target latency in ms |
EDGE_SLO_VIOLATION_ACTION |
allow |
SLO breach action: allow, step_up, or block |
NETWORK_INTEL_MIN_OBSERVATIONS |
2 |
Anti-poisoning minimum observations before full confidence |
NETWORK_INTEL_ANTI_POISONING_MIN_TENANTS |
2 |
Minimum tenant corroboration threshold for runtime penalty |
NETWORK_INTEL_DECAY_DAYS |
30 |
Stale intelligence decay window for cleanup |
ATTESTATION_KEY_BACKEND |
software |
software, hsm, or aws_kms signer backend |
ATTESTATION_CA_KEY_ID |
tokendna-ca-default |
Active CA key id used for certificate issuance |
ATTESTATION_ACTIVE_KEY_ID |
— | Explicit active key selection override |
ATTESTATION_KEYRING_JSON |
— | JSON keyring for rotation lifecycle automation |
TOKENDNA_DB_BACKEND |
sqlite |
Data backend selector: sqlite or postgres |
TOKENDNA_PG_DSN |
— | PostgreSQL DSN used when backend is postgres |
TOKENDNA_DB_DUAL_WRITE |
false |
When true, SQLite writes are mirrored to Postgres for migration |
AUDIT_BACKEND |
file |
Comma-separated audit sinks: file, redis, siem |
AUDIT_LOG_PATH |
audit.jsonl |
Hash-chained audit log path when file audit is enabled |
IMPOSSIBLE_TRAVEL_SPEED_KMH |
900 |
km/h threshold |
BRANCHING_THRESHOLD |
3 |
Distinct devices before flagging |
SIEM_WEBHOOK_URL |
— | HTTPS webhook for SIEM events |
SLACK_WEBHOOK_URL |
— | Slack incoming webhook |
| Method | Path | Role | Description |
|---|---|---|---|
GET |
/ |
None | Health check |
GET |
/secure |
READONLY+ | Main integrity check — validate token DNA |
GET |
/profile/{uid} |
ANALYST+ | Inspect user behavioral profile |
DELETE |
/profile/{uid} |
ADMIN+ | Reset user profile baseline |
POST |
/revoke |
ANALYST+ | Manually revoke token by jti |
GET |
/api/sessions |
ANALYST+ | Active session risk profiles |
GET |
/api/cloud-findings |
ANALYST+ | Cloud scan findings with severity summary |
GET |
/api/audit |
OWNER only | Tail the immutable audit log |
GET |
/admin/tenants |
ADMIN+ | List tenants |
POST |
/admin/tenants |
OWNER only | Create a new tenant |
GET |
/docs |
Dev only | Swagger UI (disabled in production) |
TokenDNA now publishes machine-readable identity artifacts to speed ecosystem adoption:
GET /api/schema/bundle— consolidated UIS + attestation schema bundle.GET /api/schema/uis.json— UIS JSON schema artifact.GET /api/schema/attestation.json— attestation JSON schema artifact.GET /api/schema/artifacts— catalog of all schema artifacts.GET /api/schema/artifacts/{name}— fetch a specific artifact by name.POST /api/schema/publish— generate and return publish-ready schema artifacts.
Wrapper endpoints for SDK-style integrations:
POST /api/oss/sdk/normalize— request/response wrapped UIS normalization.POST /api/oss/sdk/attest— request/response wrapped attestation creation.
These wrappers intentionally return stable metadata fields (sdk_version,
schema_version, generated_at) so third-party SDKs can map them directly.
The edge worker (edge/) runs at the CDN layer and handles DPoP proof-of-possession (RFC 9449) before any request reaches the backend.
cd edge
npm install -g wrangler
wrangler login
# Create KV namespace
wrangler kv:namespace create TOKEN_CACHE
# Set secrets (never in wrangler.toml)
wrangler secret put JWKS_URL
wrangler secret put BACKEND_API
# Deploy
wrangler deployEdit edge/wrangler.toml to replace placeholder KV namespace IDs and domain routes.
Each user has a Redis-backed profile of their known behavioral characteristics. The ML scorer computes a 0–100 match score against that profile:
| Signal | Weight |
|---|---|
| Device fingerprint | 30 pts |
| Country match | 25 pts |
| IP match | 15 pts |
| ASN match | 15 pts |
| OS family | 5 pts |
| Browser family | 5 pts |
| Mobile/desktop flip | 5 pts |
Penalties are then applied for threat signals (Tor exit = −40, impossible travel = −50, etc.) to produce the final score.
All session events are stored in tokendna.sessions with a 90-day TTL:
SELECT user_id, country, tier, final_score, is_tor, impossible_travel, timestamp
FROM tokendna.sessions
WHERE user_id = 'abc123'
ORDER BY timestamp DESC
LIMIT 20;TokenDNA is designed toward FedRAMP High and DoD IL4+ alignment; IL5/IL6 deployment profiles are on the roadmap and gated behind the customer-managed federal build. These are design targets, not accreditations — no control has been assessed or authorized by a third party. The implemented posture is tracked control-by-control in compliance/dod/control_matrix.json. Key controls in v2.2.0:
| Control Family | Implementation |
|---|---|
| AU-2 / AU-3 / AU-9 — Audit | Immutable hash-chained JSONL audit log; HMAC-SHA256 tamper detection; os.fsync() write hardening |
| AC-3 / AC-6 — Access Control | 4-tier RBAC (OWNER / ADMIN / ANALYST / READONLY); require_role() FastAPI dependency |
| SC-8 / SC-28 — Transmission / Data | HMAC-SHA256 IP/UA fingerprinting prevents rainbow-table reversal; HSTS + full security headers middleware |
| IA-5 — Credential Management | AWS Secrets Manager and HashiCorp Vault backend; FIPS 140-2 endpoint support |
| CM-7 — Least Functionality | CIS Docker Benchmark hardening; seccomp syscall allowlist; non-root container user (UID 10001) |
| SI-2 — Flaw Remediation | GitHub Actions CI: CodeQL, pip-audit, TruffleHog, Trivy on every PR; Dependabot weekly scans |
| SC-5 — Denial of Service Protection | Per-tenant rate limiting via Redis; 1MB body size hard limit; header size enforcement |
Open gaps being tracked toward full accreditation: mTLS service mesh, database encryption at rest, CAC/PIV authentication, full ABAC. See SECURITY.md for the complete posture.
All community contributions are welcome. Every PR must be approved by the repository owner before merge. Read CONTRIBUTING.md for the security checklist and responsible disclosure process.
Business Source License 1.1 (BUSL-1.1). See LICENSE. Free for non-competing use; converts to Apache 2.0 four years from first public release.