Technical Architecture

Zero-trust authorization, engineered for machine-speed decisions.

Sub-millisecond authorization with cryptographic proof, designed from first principles for the AI-agent era. Every decision is a signed artifact — not a logged event, not a token claim, but a cryptographically verifiable permit.

<1ms
p99 target
Ed25519
Signatures
Merkle tree
Audit log
Zero shared
Secrets model

Authorization Pipeline

Every decision traverses a verifiable pipeline.

The hot path is designed for latency, not flexibility. Policy loading, key rotation, and tenant configuration happen on the control plane; the data plane executes a fixed pipeline with no round-trips to external systems.

client
0ms
Agent SDK

Constructs permit payload, signs with tenant key, attaches nonce + TTL.

TypeScript / Rust WASM
validation
~0.2ms
Edge Gateway

Verifies Ed25519 signature, checks nonce replay cache, validates TTL window.

Rust · Redis (nonce store)
decision
~0.6ms
Policy Engine

Evaluates compiled decision tree against permit fields, rate limits, and conditions.

Rust bytecode VM · FFI to Node.js
Merkle
~0.8ms
Audit Log

Appends decision leaf to append-only Merkle tree. Async — does not block response.

Postgres (partitioned) · SHA-256
verifiable
<1ms
Signed Response

Returns permit + gateway signature. Any party can verify offline using public key.

Ed25519 · CBOR envelope

Cumulative latency budget

0ms
0.2ms
0.6ms
0.8ms
<1ms

Cryptographic Signing

Ed25519 signing flow.

Permits are cryptographically bound to the issuing agent identity. A permit without a valid signature is rejected before touching the policy engine — no database lookups, no network calls.

Key Hierarchy

Root Key (HSM)
  └─ Tenant Key
      └─ Agent Signing Key (Ed25519, rotated daily)
          └─ Permit Signature

Root key lives exclusively in an HSM (AWS CloudHSM or on-prem PKCS#11). Never exported, never touches application memory.

Tenant keys are derived per-tenant via HKDF-SHA256 and sealed in the HSM.

Agent signing keys are Ed25519 ephemeral keys rotated on a 24h schedule. Rotation is zero-downtime — the public key is published to the gateway before the old key expires.

Sign + Verify Flow

// Agent side — permit construction
const permit = {
  agent:      "billing-ai",
  action:     "payment.create",
  resource:   "stripe:customer_xyz",
  amount:     2450_00,          // cents
  nonce:      randomBytes(16),  // prevents replay
  issued_at:  Date.now(),
  expires_at: Date.now() + 30_000  // 30-second TTL
};

// Canonical JSON ensures byte-identical serialization
const signature = ed25519.sign(
  canonicalize(permit),  // RFC 8785 JCS
  tenantSigningKey       // 32-byte private key
);

// ─── Gateway side ───────────────────────────────────
const valid = ed25519.verify(
  signature,
  canonicalize(permit),
  tenantPublicKey  // distributed via control plane
);

if (!valid)                   reject(401, "invalid_signature");
if (Date.now() > permit.expires_at) reject(401, "permit_expired");
if (nonceSeen(permit.nonce))  reject(401, "replay_detected");

Why Ed25519

  • Deterministic signatures

    Same input always produces the same signature — simplifies testing and auditability. No k-nonce, no PRNG dependency.

  • ~100µs sign time

    On commodity x86-64 hardware (single core). 10× faster than ECDSA P-256, 100× faster than RSA-2048 at equivalent security.

  • 64-byte signatures

    Versus 256+ bytes for RSA-2048. Matters at wire level: 20K req/s × 200 bytes saved = ~4 MB/s less bandwidth per gateway.

  • No entropy at sign time

    Eliminates a class of entropy exhaustion bugs common in containerized runtimes with /dev/urandom pressure.

  • Post-quantum roadmap

    Migration path to SLH-DSA (FIPS 205, stateless hash-based) planned. Signature format includes algorithm field for negotiated upgrade.

Audit Log

Merkle-tree audit log — tamper-evident by construction.

Every authorization decision appends a leaf to an append-only Merkle tree. The tree root is published hourly as a signed checkpoint. Enterprise customers can anchor roots to Bitcoin or Ethereum for third-party verifiability.

8-leaf tree · proof path for L5★ highlighted in orange

RootH(0-3)H(4-7)H(0-1)H(2-3)H(4-5)H(6-7)L0L1L2L3L4L5★L6L7
Target leaf / proof pathRequired sibling hashes

Proof size

O(log n)

3 hashes for 8 leaves; 20 for 1M decisions

Verification

O(log n)

Hash operations only, no DB query

Anchoring interval

1 hour

Configurable; enterprise can go 1-min

On-chain anchoring

Optional

Bitcoin OP_RETURN or Ethereum calldata

Inclusion proof verification

function verifyInclusion(
  leaf:  Hash,
  proof: Hash[],
  root:  Hash,
  index: number
): boolean {
  let current = leaf;
  for (const sibling of proof) {
    current = (index & 1)
      ? sha256(concat(sibling, current))  // sibling is left child
      : sha256(concat(current, sibling)); // sibling is right child
    index >>= 1;
  }
  return current === root;
}

// Example: prove leaf[5] exists in a tree of 8 leaves
// proof = [leaf[4], H(leaf[6]||leaf[7]), H(H01 || H23)]
// 3 hashes suffice for O(log 8) proof

Tamper detection

Modifying any leaf changes its hash. That changes its parent hash, which changes the grandparent, all the way to the root. Since roots are published and externally anchored, retroactive tampering is detectable even without access to the original leaf data.

Leaf schema

{
  decision_id: "01HXXXXX...",  // ULID
  permit_hash: "sha256(...)",
  outcome:     "allow" | "deny" | "review",
  policy_id:   "billing-spend-limit",
  timestamp:   1713600000000,
  agent:       "billing-ai",
  gateway_id:  "gw-us-east-1a"
}

Policy Engine

Policies compile to a bytecode VM — not interpreted at runtime.

Treating policy evaluation as an interpreter problem is why most authz systems top out at ~5ms. We compile policy JSON to a flat instruction set at deploy time. The hot path executes a bytecode VM — no JSON parsing, no tree traversal.

Policy document (JSON)

{
  "id": "billing-agent-spending-limit",
  "version": 3,
  "match": {
    "agent.role": "billing",
    "action":     "payment.create"
  },
  "rules": [
    {
      "condition": "amount <= 5000_00",
      "effect":    "allow"
    },
    {
      "condition": "amount <= 50000_00 AND business_hours",
      "effect":    "review"
    },
    {
      "condition": "default",
      "effect":    "deny"
    }
  ],
  "rate_limits": {
    "per_minute": 10,
    "per_hour":   100,
    "burst":      15
  }
}

Condition types

Amount-based

amount <= N

Time-based

business_hours, cron()

Rate-based

per_minute, per_hour

Relational

agent.org == resource.org

Composite

AND, OR, NOT

Custom fn

call(fn_id, ...args)

Execution pipeline

  1. 01

    Compile on deploy

    Policy JSON → AST → flat bytecode at policy publish time. Compilation errors surface before any request is served.

  2. 02

    Bundle to gateway

    Compiled bytecode is signed (Ed25519), versioned, and pushed to all edge gateways. Gateways validate the bundle signature before loading.

  3. 03

    VM execution (~50µs)

    Written in Rust, exposed via FFI. The VM is a simple stack machine: LOAD, CMP, AND, OR, JMP, EFFECT. No heap allocation in the hot path.

  4. 04

    Conflict resolution

    When multiple policies match, the most specific match wins (longest-prefix on match fields). Ties resolved by policy priority field.

Performance target

Policy evaluation budget: 40µs target, 60µs current (beta). Bottleneck is the FFI boundary between Rust VM and Node.js worker — replacing with Wasm module is on the roadmap.

Deployment Architecture

Data plane lives in your VPC. Control plane lives in ours.

The data plane executes authorization decisions. It is stateless, horizontally scalable, and has zero runtime dependencies on the control plane. If PermitNetworks goes down, your decisions keep working.

Data Plane — your infrastructure

Edge Gateway

Docker container, ~80MB image. Runs in your VPC, k8s, or VM. mTLS listener on :8443.

Policy cache

In-process LRU cache (512MB default) holding compiled bytecode bundles. Refreshed on push from control plane.

Nonce store

Local Redis (or Redis Cluster) for replay prevention. TTL matches permit max-TTL (default 60s).

Public key ring

Rotated Ed25519 public keys, delivered via signed JWT from control plane. No private key material leaves control plane.

Availability guarantee

Last-cached policy bundle survives indefinitely. Gateway continues making decisions for the subset of agents whose public keys and policies are cached. Graceful degradation: unknown agents are denied by default.

Control Plane — PermitNetworks managed

Policy store

Postgres with row-level tenant isolation. Policies versioned with immutable history. Signed at publish time.

Audit log

Append-only, partitioned by tenant and date. Merkle tree computed continuously. No deletes, no updates.

Key management

HSM-backed (AWS CloudHSM or customer-provided PKCS#11). Agent key rotation is automated, zero-downtime.

Admin API + UI

Policy CRUD, agent registration, audit log queries, metrics. SOC 2 Type II in progress.

Isolation model

Control plane is multi-tenant but all data is logically isolated at the Postgres row level with tenant_id enforced at the RLS layer. Cross-tenant queries are structurally impossible in the data model.

Communication pattern

Agent SDK
── permit req ──▶
Edge Gateway (your VPC)
▲ policy sync (async, push)▼ no decision traffic
Control Plane

Decision traffic never leaves your VPC. The control plane only pushes policy bundles and public keys; it never receives permit payloads.

Threat Model

Security properties with cryptographic backing.

Each security property is grounded in a cryptographic primitive or protocol — not a policy claim, not a runtime check that can be bypassed.

ThreatMitigationCryptographic Guarantee
Replay attacksNonce + expiry window (default 30s TTL). Nonces stored in local Redis, checked O(1).TTL-bounded signatures — replayed permit rejected after expiry or nonce reuse
Forged permitsEd25519 signature verification on every request. No signature → immediate reject, no policy eval.EUF-CMA unforgeability — computationally infeasible to forge without private key
Policy tamperingPolicy bundles are Ed25519-signed at publish time. Gateway rejects bundles with invalid or missing signature.Integrity guarantee — tampered bundle detected before loading
Log tamperingAppend-only Merkle tree. Tree root published hourly and optionally anchored on-chain.Tamper-evidence — retroactive modification changes root hash, detectable by any observer
Insider threatKey hierarchy ensures no single engineer has access to tenant signing keys. HSM operations are logged.Accountability — every key operation is attributable to a principal
Network MITMmTLS required for agent → gateway and gateway → control plane. Certificate pinning on SDK.Confidentiality + authenticity — eavesdropping yields ciphertext; active injection is detected
Timing attacksEd25519 verify uses constant-time comparison (Rust dalek crate, formally verified).Side-channel resistance — execution time does not leak key material

Performance

Honest numbers — target vs. current beta.

We publish both target and current metrics because that gap tells you more about our engineering priorities than either number alone. Bottlenecks are known and tracked.

MetricTargetCurrent (Beta)Status
p50 decision latency0.3ms0.5msIn progress
p99 decision latency1.0ms1.4msIn progress
p999 decision latency3.0ms4.2msIn progress
Throughput per gateway50K req/s20K req/sIn progress
Signature verification80µs80µsAt target
Policy evaluation40µs60µsIn progress
Nonce lookup (Redis)50µs120µsIn progress
Audit log appendasyncasyncAt target

Nonce lookup bottleneck

Current 120µs is dominated by a single-region Redis setup. Moving to a local in-process nonce cache (Bloom filter for recent nonces, Redis for overflow) targets 20µs.

Throughput ceiling

20K req/s limit is single-gateway. Horizontal scaling is linear — tested to 200K req/s across 10 gateways. Client-side sharding by agent prefix.

Policy eval gap

60µs → 40µs target: replacing the Rust FFI boundary with a Wasm module eliminates marshaling overhead. ETA: Q3 2025.

Open Questions

Problems we haven't solved yet.

Technical buyers should know what's unresolved. These are the hardest open problems in our design. If you have opinions, we want to hear them.

Cryptography

Post-quantum signature migration

SLH-DSA (FIPS 205, stateless hash-based) vs. ML-DSA (FIPS 204, lattice-based). SLH-DSA has larger signatures (~8KB) but stronger security assumptions. ML-DSA signs faster but relies on module-lattice hardness. We haven't decided. We're watching NIST's guidance on hybrid schemes.

Distributed systems

Cross-region audit log consistency

Multi-region Merkle trees require a consensus protocol for the tree root. We currently replicate audit events async (eventual consistency, <200ms lag). Strong consistency across regions requires 1 RTT of coordination — that conflicts with our latency budget. Active area of research.

Policy semantics

Policy conflict resolution

When two policies match the same request with different effects, our current rule is 'most specific match wins'. Edge cases exist: what if two policies have equal specificity but different effects? Deny-overrides is safe but creates policy authoring friction. Allow-overrides is flexible but risky. We haven't published a formal semantics document yet.

Resilience

Retry storms without replay vulnerability

A client that retries a failed request must use the same permit (same nonce, same signature) or generate a new one. Same permit: valid if within TTL, but nonce is spent on first gateway to process it. New permit: requires another signing round-trip. We're designing a 'permit bundle' that pre-mints N permits for use in retry scenarios — but bundle revocation is an open problem.

Get Started

Questions about the architecture?

Our engineering team is available for technical deep-dives. We're happy to walk through the implementation, discuss tradeoffs, or review your agent architecture.