vMCP tool count · 17 → 19June 26, 2026
Tool-Count Corrections — MCP Server Now Documented as 19 Tools
The MCP server has grown to 19 tools with the two device-flow onboarding tools (vorim_onboard_start, vorim_onboard_check), but several surfaces still said 17. Corrected the count and tool lists to 19 across the docs, marketing pages, README, the public /.well-known/mcp.json manifest, and the dataroom so everything matches what the server actually exposes.
fixedCorrected the MCP tool count from a stale 17 to the accurate 19 across vorim.ai/docs, the Integrations, Features, Home, and Blog pages, and the published @vorim/mcp-server and @vorim/openclaw-skill READMEs (the onboarding tools were added but never reflected in the count).
fixed/.well-known/mcp.json manifest updated to enumerate all 19 tools (added vorim_onboard_start and vorim_onboard_check), so agent-capable browsers see the full set the server exposes. Manifest version bumped.
fixedREADME tool tables now list all 19 tools across seven categories (added an Onboarding category); dataroom and investor materials updated from 13/17 to 19.
vAgent-led onboarding · device flowJune 23, 2026
Let Your Agent Set Up Vorim — Device-Authorization Onboarding
An AI coding agent can now set up Vorim for you: it obtains a scoped API key by polling after you approve once in the browser — no key pasting, no unauthenticated key minting. Available in the TypeScript and Python SDKs, the MCP server, and a new onboarding card on the site.
addedAgent-led onboarding via the OAuth 2.0 Device Authorization Grant (RFC 8628): an AI coding agent (Claude, Cursor, etc.) can obtain a scoped Vorim API key by polling, after a human approves once in the browser — no key pasting. POST /v1/auth/device starts the flow and returns a short user code + an activation URL; the human approves at /activate; the agent polls POST /v1/auth/device/token and receives the key.
addedSecurity by design: the unauthenticated start endpoint creates only a pending claim, never a credential. Keys are minted only at the redeeming poll, after a signed-in, email-verified, owner/admin human consents, and are server-clamped to a strict scope ceiling (agent registration + audit only) — never key-minting, billing, or wildcard scopes. Only hashes of the device and user codes are stored, codes are single-use and short-lived, and the key is returned exactly once.
addedOne-call setup in the SDKs: deviceLogin() in @vorim/sdk (TypeScript) and device_login() in vorim (Python) run the approve-and-poll loop for you and, with registerAgent: true, also register a first agent identity with the new key — returning a fully-wired { api_key, agent } in a single call. Both fail closed: they raise on denial, expiry, or timeout and never return a partial credential.
addedMCP onboarding tools: @vorim/mcp-server adds vorim_onboard_start and vorim_onboard_check so a Claude/Cursor user can say “set up Vorim” and the agent drives the device flow natively — no API key required to begin.
addedA new “Set it up your way” onboarding card on the homepage and demo page: do it yourself in the dashboard, or copy one line into your coding agent and let it handle setup while you approve the key in your browser.
vTrust scoring v2 · signed attestationsJune 19, 2026
Trust Scoring, Rebuilt — Explainable, Anti-Gaming, and Cryptographically Verifiable
The agent trust score has been redesigned from the ground up on established trust, reputation, and risk-scoring principles, and it now explains itself. The old additive point bonuses (raw success rate, flat 30-day window) are replaced by a model that ranks an agent's reliability with a Wilson score lower bound — so a 3-for-3 agent can't look as proven as a 300-for-300 one, and farming easy successes no longer buys trust — weights recent behaviour with a 7-day half-life so a trusted agent that turns rogue loses standing fast, and anchors trust to verified, paying provenance because behavioural signal alone can't stop Sybil agents. Every score now ships a coarse public band, an evidence-confidence indicator, and a platform-signed attestation a counterparty can verify offline, plus ranked plain-language reason codes for the agent's owner. The score still returns the same 0-100 number, so every existing gate (A2A / UCP / ACP min_trust_score, runtime policy thresholds) keeps working unchanged. Built on a primary-sourced research pass; 529 tests pass and it is live in production.
changedReliability is now scored with a Wilson score lower bound over recency-decayed outcomes, not a raw success ratio. A perfect-but-tiny record (3/3) ranks well below a proven one (300/300), so volume of easy successes is no longer directly purchasable into trust — the classic reputation-gaming exploit.
changedRecent behaviour dominates: outcomes are weighted with a 7-day half-life, so an agent that starts failing or probing loses trust quickly and can't coast on old clean history. Trust is fast to lose and slow to gain by design — the direct defence against a trusted agent going rogue.
changedTrust is anchored to verifiable provenance (a verified, paying organisation) on top of the cryptographic registration every agent already has. Behavioural signal alone cannot establish trust, in line with the formal result that Sybil attacks need a scarce/verifiable root, not just good behaviour.
addedPublic verification now returns a coarse trust_band (untrusted / building / established / trusted) and a confidence indicator (new / developing / established) that tells a counterparty how much history the score rests on — useful signal that can't be gamed, since more evidence is exactly what you want.
addedSigned trust attestations: GET /v1/trust/verify now includes a platform Ed25519 signature over the agent id, band, confidence, and a short freshness window. A counterparty fetches the public keys from the new GET /v1/trust/keys endpoint, matches the key id, and verifies the attestation offline — tampering (e.g. upgrading the band) is rejected, and the signed payload deliberately excludes the raw weights and gate thresholds.
addedReason codes: the authenticated, owner-only GET /v1/agents/:id/trust endpoint (and the dashboard agent-detail view) explains why an agent has its score with ranked, plain-language factors — strengths and weaknesses — without exposing magnitudes or distance-to-threshold. The embeddable trust badge now shows the human-readable band.
improvedTiered disclosure throughout: the public surface stays coarse (band, confidence, positive signals only); the agent's owner sees the full ranked reason codes; the exact weights and thresholds are never exposed on any surface. This follows how mature credit, fraud, and identity-risk systems disclose factors without disclosing the formula.
v@vorim/sdk 3.7.2 · vorim 3.9.2 · @vorim/verify 0.4.1 · @vorim/mcp-server 1.1.11June 19, 2026
Deep Security Audit — 16 Fixes, Fail-Closed Runtime Control, and Live-Verified Delegation Limits
A multi-agent find → adversarially-verify → fix sweep across authentication, the API workflows, both SDKs, every framework integration, the MCP server, and the offline verifier surfaced 16 confirmed issues (4 high, 8 medium, 4 low) — every one fixed, with ten further candidates refuted as false positives before any change was made. The headline items: the framework runtime gate now fails closed on a control-plane outage instead of silently allowing the action, the offline verifier enforces a delegation's max sub-delegation depth (so a delegate capped at "may not re-delegate" can no longer mint a child link that still verifies), the A2A scope check authorises against the live server rather than the self-reported Agent Card, and an OAuth resilience fallback that had never actually run in the production build was repaired. The server-side fixes are already live; the SDK, verifier, and MCP fixes ship in this release. All suites pass — 491 TypeScript tests, 199 Python tests — and the deploy was smoke-tested end to end.
changedSecurity (Runtime Control): the framework integration gate now fails closed when the policy control plane is unreachable. A transport-failure "fallback" decision was being treated as an allow verdict, so an outage silently let gated tool calls through in the strongest enforcement mode. The gate now denies on a control-plane outage by default (onTransportFailure: 'deny') in both SDKs — set 'allow' to restore the previous fail-open behaviour. This is a behaviour change for anyone running useRuntimeControl during an outage.
fixedSecurity (delegation): the offline verifier (@vorim/verify 0.4.1) now enforces max_chain_depth attenuation, mirroring the server's create-time invariant. Previously a delegate that the chain root capped at depth 0 ("you may not sub-delegate") could mint a self-signed child link that passed signature, continuity, and scope-subset checks and still reported delegation_valid — silently widening an attenuation the signed token was meant to carry. A link that exceeds its parent's remaining depth now fails the chain.
fixedSecurity (A2A): @vorim/a2a verify_agent now authorises required scopes against the live, server-side permission store rather than the scopes self-reported on the incoming Agent Card. A forged card could name a real, healthy agent and list scopes it was never granted; the gate now verifies each scope against Vorim and fails closed if it can't.
fixedSecurity (OAuth resilience): the signed-state fallback that keeps OAuth sign-in working during a Redis outage loaded its config with a CommonJS require() that throws in the platform's ESM build — so the fallback had in fact never executed, and a Redis outage would 500 every OAuth login. It now uses the static import and works as intended.
fixedHardening (API): the Stripe webhook now busts the cached org plan on upgrade, cancellation, and subscription change (not just on payment failure), so a revoked entitlement can no longer linger for up to 30 seconds; GET /runtime/settings is now owner/admin-only (it returned the org's Slack escalation webhook — a bearer secret — to any member); the public trust endpoint no longer reports an expired ephemeral agent as verified before the cleanup sweep flips it; and a free or downgraded org can no longer create webhook alert rules, which are a paid feature.
fixedSDK parity and durability: the OpenAI, Anthropic, LangChain, and LlamaIndex TypeScript integrations now fail closed on a permission-check transport error instead of crashing the host tool loop (matching the Python SDKs); a malformed (non-JSON) success response from the control plane now fails closed in both SDKs rather than diverging; and the LangChain tool-catalogue hash now converts a Zod tool schema to JSON Schema so it agrees with the Python SDK for cross-SDK replay verification.
fixedIntegrations and MCP: the UCP and Stripe ACP commerce middleware now document the confused-deputy risk of trusting the agent-id header and accept an authenticate hook to prove the caller controls the named agent; the MCP server validates VORIM_BASE_URL (https, or localhost for dev) so a tampered base URL can't exfiltrate the API key to another origin; and MCP audit events now preserve a legitimate latency_ms of 0.
v@vorim/verify 0.4.0 · @vorim/a2a 1.1.0 · @vorim/cli 1.0.5 · platform hardeningJune 18, 2026
Deep Security Audit — 17 Fixes, Holder-of-Key for A2A, and a Clean Dependency Tree
A find → adversarially-verify → fix → re-verify sweep across authentication, the core API workflows, both SDKs, every framework integration, the MCP server, and the offline verifier surfaced 17 confirmed issues — every one fixed, with the re-verification pass catching three of the first-pass fixes as incomplete and correcting them before release. The headline items: the attenuated-delegation invariant is now enforced against an agent's real granted permissions (not its descriptive capabilities), the Runtime Control idempotency key can no longer replay an allow verdict onto a different action, and @vorim/a2a gained Ed25519 holder-of-key proof-of-possession so a peer can no longer impersonate a high-trust agent by copying its id. Alongside the audit, the platform's dependency tree was cleaned of known advisories (nodemailer, vitest, and the Anthropic SDK's legacy fetch path among them). All suites pass — 491 TypeScript tests, 199 Python tests — and the build is green.
fixedSecurity (delegation): a signed, offline-verifiable delegation link is now derived from the delegator's actual granted permission scopes, not its free-form capabilities array. This closes a path where an agents:write holder could mint a delegation for a scope the agent was never granted, and the offline verifier (@vorim/verify) now also rejects spliced chains where one link's delegator is not the previous link's delegate.
fixedSecurity (Runtime Control): a caller-supplied idempotency_key can no longer reuse a cached allow verdict for a different action — the decision-reuse lookup now binds to the action type, target, and payload hash, so a benign approval cannot be replayed onto a deny- or escalate-worthy action.
fixedSecurity (A2A): @vorim/a2a 1.1.0 adds Ed25519 holder-of-key proof-of-possession. A passing trust score only proved an agent id existed and was trusted — not that the caller controlled it. The middleware now verifies a signed challenge against a key fetched from a trusted, id-bound source (never the incoming Agent Card or the proof itself) and fails closed by default. This is a breaking change — set allowUnverifiedIdentity: true or wire extractProof + resolvePublicKey.
fixedSecurity (Python integrations): the Python AutoGen and Google ADK runtime gate now forwards the tool payload to the policy engine, matching the TypeScript SDK — previously a payload-conditioned deny or escalate rule silently failed open in Python.
improvedOffline verifier (@vorim/verify 0.4.0): recomputes each bundle agent's key fingerprint from its public key and fails the bundle on a mismatch (catching a swapped key that keeps the original fingerprint), and adds a --strict mode that fails on unsigned or unknown-agent events so a CI consumer keying off the exit code can't accept an injected event.
fixedHardening: an org role change now bumps the user's token version so a demotion takes effect immediately instead of lingering for the access token's lifetime; the Stripe webhook handler claims-then-runs and releases its idempotency claim on failure so a transient error can't permanently drop a subscription change; the per-delegation OAuth-token rate cap is now enforced; an invite can no longer resurrect membership in a soft-deleted org; POST /auth/refresh is now rate-limited; and public-form fields are escaped and length-bounded before they reach outbound notification email.
fixedCLI (@vorim/cli 1.0.5): vorim init no longer writes the secret API key into a committable vorim.json — it writes the key to .env only and adds it to .gitignore, with a warning never to commit it.
changedDependencies: upgraded nodemailer (8 → 9), the Anthropic SDK (which dropped its legacy node-fetch path), vitest (2 → 4), fast-xml-parser (4 → 5), and concurrently (9 → 10), clearing the corresponding security advisories across the platform and both internal services. Verified with a full typecheck, the complete test suite, and a production smoke test of sign-in, token refresh, and the email-sending path.
vSDK 3.7.0 · Python 3.9.0 · Google UCP + security hardeningJune 17, 2026
Google UCP Integration, Plus an Audit-Driven Security Pass
Vorim now plugs into Google's Universal Commerce Protocol (UCP), the open standard for agentic commerce announced at NRF 2026. The integration verifies an agent's identity, transact permission, and trust score before a UCP checkout, and records the full commerce lifecycle — including UCP's real checkout-session statuses and the AP2 mandate's signed checkout_mandate — as offline-verifiable audit events. It ships in both SDKs (TypeScript and Python) and mirrors the Stripe ACP integration. Alongside it, a three-agent adversarial audit found and fixed a real SSRF in the OAuth credential-delegation path, locked down API-key scope creation, and added several auth hardening fixes. All package suites pass (@vorim/sdk 147, vorim 195, API 274).
addedGoogle UCP integration (@vorim/sdk/integrations/ucp and vorim.ucp): createVorimUCP / VorimUCP verify an agent's identity, agent:transact scope, and trust score before a UCP checkout (authorizePurchase / authorize_purchase), with Express middleware and a require_transact decorator to gate a checkout route, plus signed audit of the cart/order/fulfilment/cancellation lifecycle.
addedUCP spec fidelity (version 2026-04-08): logCheckoutSession maps UCP's real completed / incomplete / requires_escalation / error statuses to the audit result (requires_escalation hands off to Runtime Control for human approval), and logAp2Mandate captures the ap2_mandate extension's signed checkout_mandate JWT so UCP's payment proof is linked to a Vorim-signed, independently-verifiable record.
fixedSecurity: the OAuth credential-delegation token exchange did the outbound request with a raw fetch of a customer-supplied provider URL. It now routes through the SSRF-safe fetch (HTTPS-only, DNS-pinned, no redirects) and validates provider URLs at registration, closing a server-side request forgery path to internal infrastructure.
fixedSecurity: API-key creation now restricts scopes to the known set, so an org-wide '*' master scope can no longer be self-minted, and a mistyped or namespace-wildcard scope (which never authorised anything) is rejected up front.
fixedHardening: pinned the JWT verification algorithm to HS256 across all verification paths, parameterised the audit stats time-window query, and made the password-reset token single-use atomic.
vSDK 3.6.1 · Python 3.8.2 · MCP 1.1.9 · 8 new integrationsJune 11, 2026
Integrate With Everything — Eight New Connectors, Plus a Human-in-the-Loop Fix
Vorim now wraps the tool boundary in eight more places: the Vercel AI SDK and LangGraph (TypeScript), AutoGen / AG2 and Google ADK (Python), a SIEM forwarder that ships your signed audit trail to Splunk, Elasticsearch, or any OpenTelemetry collector, and three no-code connectors — n8n, Zapier, and Make. Every one threads the same value: an identity-bound permission or Runtime Control check on the way in, and a signed audit event on the way out. Alongside the connectors, an adversarial pre-release sweep caught and fixed a real human-in-the-loop bug — an escalate verdict (an action waiting on a human) was being treated as an outright denial in the framework wrappers — and hardened the SIEM forwarder against malformed timestamps. All package suites pass (@vorim/sdk 135, vorim 185).
addedVercel AI SDK integration (@vorim/sdk/integrations/vercel-ai): wrapVercelTool / wrapVercelTools gate every tool you pass to generateText / streamText and record a signed audit event, with optional Runtime Control and decision_id linkage.
addedLangGraph integration (@vorim/sdk/integrations/langgraph): wrapGraphTool / wrapGraphTools bring the same gate-and-audit boundary to LangGraph node tools, tagged framework "langgraph" in the audit trail.
addedAutoGen / AG2 integration (vorim.integrations.autogen) and Google ADK integration (vorim.integrations.google_adk) for Python: the vorim_guard / vorim_tool decorators wrap a tool function so each call is checked and audited, preserving the function signature your framework introspects.
addedSIEM forwarder (@vorim/sdk/integrations/siem): createSiemForwarder maps each audit event to an OpenTelemetry LogRecord and ships it to an OTLP/HTTP collector, a Splunk HEC endpoint, or an Elasticsearch bulk endpoint — making Vorim a feed into the tools your SOC already runs, not a silo. No heavy OpenTelemetry dependency required.
addedNo-code connectors: an n8n community node (n8n-nodes-vorim), a Zapier app, and a Make app — each exposing Check Permission, Runtime Decision, Emit Audit Event, and Verify Trust so you can put a Vorim check or a signed audit event into any automation without writing code.
fixedHuman-in-the-loop escalations now resolve correctly. When Runtime Control returns escalate (an action paused for a human), the Vercel, OpenAI, and Anthropic wrappers — and the AutoGen / Google ADK decorators — were treating it as a denial and hard-failing the call. They now resolve the escalation via a configurable onEscalation policy: wait (default, poll for the human verdict and fail closed on timeout), deny, or allow. The default never silently proceeds on an unresolved escalation.
fixedThe SIEM forwarder no longer crashes on a malformed event timestamp. A bad value previously threw on the OpenTelemetry path (taking down the whole batch) and produced null or invalid timestamps on the Splunk and Elasticsearch paths; all three now fall back to the current time and emit a valid record.
fixedLangChain audit durability: with asyncAudit set to false, the integration now actually awaits the audit write before the tool pipeline continues (it was fire-and-forget in both branches), so a process that exits immediately after a tool call cannot lose the event.
improvedThe n8n, Zapier, and Make connectors now document the API-key scope each action needs (runtime:decide, audit:write, audit:read), and the n8n and Zapier connection tests validate the key itself instead of an unauthenticated health check — so a wrongly-scoped or revoked key is caught when you connect, not at the first run. The n8n Emit Audit Event action also exposes decision_id and other optional fields for full audit-to-decision linkage.
vSDK 3.6.0 · MCP 1.1.8 · Python 3.8.1June 11, 2026
SDK & MCP Reliability — Contract Fixes from a Full Adversarial Sweep
A deep, multi-agent test sweep across both SDKs, the MCP server, the verifier, and every framework integration — backed by live end-to-end runs against production — surfaced a batch of contract bugs that we fixed and verified. The headline correctness fix: the SDKs' canonical-form hashing now agrees with the verifier on objects containing undefined-valued fields, so a signed event can never canonicalize one way in the SDK and another in @vorim/verify. Both SDKs' list_agents now return the documented { agents, meta } shape (pagination metadata was being dropped), the MCP server validates tool inputs against the real API enums up front, and the framework integrations gained an opt-in path to gate tool calls through Runtime Control with full decision_id linkage. Every confirmed finding was fixed before release; all package suites pass (@vorim/sdk 108, vorim 163, @vorim/verify 50, MCP builds + live stdio smoke test).
fixedCanonical-form parity: @vorim/sdk's JCS canonicalization now drops undefined-valued object fields, matching @vorim/verify and @vorim/shared-types. Previously the SDK threw on { a: 1, b: undefined } while the verifier silently dropped b — a cross-module divergence that could make a validly-signed event fail verification. Added a regression test for nested undefined fields.
fixedlistAgents() / list_agents() in both SDKs now return { agents, meta } as documented. They were returning the raw agent array and silently dropping pagination meta (total / total_pages), so callers reading .agents got undefined and could not paginate. Added _request_envelope helpers that preserve the full { data, meta } envelope; corrected the Python test that had mocked a shape the API never returns.
fixedTS SDK listAgentDelegations() now matches its AgentDelegationRecord type. The API returned raw snake_case rows (public_chain_id, delegator_public_id, …) while the SDK typed camelCase, so every field read undefined. The endpoint now returns camelCase consistent with the sibling delegation-chain endpoint and the dashboard.
fixedMCP server tool inputs are now validated against the real API enums: emit_event event_type (8 values) and result (success/denied/error), update_agent status, and the permission scope on check/grant/revoke. Invalid values used to pass MCP validation and then 400 at the API with an opaque error. update_agent also drops the capabilities field the update API silently ignored, and list_agents preserves pagination meta.
fixedStripe Agentic Commerce integration emits a valid audit-event shape — event_type "api_request" and a result enum — instead of the bogus "agent_action" / "outcome" fields it was sending under an as-any cast, which the API rejected or stored malformed.
fixedOpenAI integration: asyncAudit=false now actually awaits the audit write before the tool pipeline continues. Both branches were previously identical fire-and-forget, so the flag did nothing.
addedOpt-in Runtime Control gating in the OpenAI and Anthropic integrations: set useRuntimeControl: true and each tool call is evaluated through beforeAction() against your live policy rules, with the returned decisionId linked onto every audit event's decision_id. Default off preserves the fast permission-check behavior on all plans. (Runtime Control gating requires the Growth+ runtime_control feature.)
improvedPython test reliability: the framework-integration tests now force their peer-dependency mocks (instead of setdefault, which no-ops when a real but incomplete dependency is installed), so the full Python suite collects and passes in any environment — 163 tests green.
v3.12.0June 8, 2026
Runtime Control — Gate, Modify, and Escalate Agent Actions Before They Happen
Runtime Control moves Vorim from after-the-fact audit to in-the-moment decisions. An agent calls beforeAction() before it performs a gated action; Vorim returns allow / deny / modify / escalate based on the agent's permissions, trust score, and your policy rules — fast enough to sit in the action's critical path. modify rules can mask or redact PII in the payload (mask_fields / redact_keys / redact_pii). escalate rules pause the action for a human to approve or deny, with Slack + email notifications. Every decision is correlated to the post-action audit event via decision_id, so the audit trail now records both "what the agent said it would do" and "what it actually did." Available in both SDKs (@vorim/sdk 3.5.0, vorim 3.8.0), a new /runtime dashboard, and a full policy-rule API. Built and verified over four weeks with four independent adversarial audit passes; every confirmed finding fixed before release.
addedbeforeAction() / before_action() in both SDKs (@vorim/sdk 3.5.0, vorim 3.8.0): call it before a gated agent action to get a typed decision (allow / deny / modify / escalate / fallback). Deny throws VorimDeniedError by default — the verdict is in the response body, not the HTTP status, so a denial can't be mistaken for success. Includes waitForDecisionResolution() to poll an escalation to its human verdict, and a runtimeFailOpen option so a control-plane blip never blocks your agent.
addedPolicy rules: a /v1/runtime/rules API and a visual rule editor in the new /runtime dashboard. Rules match on action type, target pattern, agent trust score, PII presence, time window, or specific agents — and drive a deny / modify / escalate verdict. First match by priority wins.
addedmodify verdict: a rule can sanitise the action payload before it proceeds — mask_fields (replace a value with ***), redact_keys (drop a key), or redact_pii (mask detected emails / SSNs / Luhn-valid card numbers in every string value). The SDK hands your agent the sanitised payload to send in place of the original.
addedEscalation workflow: an escalate rule pauses the action and surfaces it in the dashboard escalation queue for an operator to approve or deny. Operators are notified via a Slack incoming webhook and/or email (configurable per organization). The SDK polls for the human verdict and translates it (approved → allow, denied → deny).
addeddecision_id linkage: emit() now carries the decision_id from beforeAction(), correlating each runtime decision with the audit event the action produced — the highest-leverage diff for a security investigation ("the engine said allow; here is what the agent actually did").
addedDecision metering: a /v1/runtime/usage endpoint and a dashboard meter show monthly runtime-decision usage against your plan (Growth: 1,000,000 / month; Enterprise: unlimited). Enforcement fails open — a metering error never blocks a production agent.
improvedEscalation notifications send through a single SSRF-safe outbound path (HTTPS-only, resolved-IP pinned, redirects refused) shared with audit webhooks, and all agent-supplied fields are escaped before they reach Slack or email — a crafted action target can't inject links or mentions into your operators' channel.
improvedPermission, trust, and rate-limit caches now degrade gracefully when Redis is unavailable: a cache error falls through to the database (for reads) or fails open (for the limiter) instead of turning a Redis blip into a 500 or a silently-bypassed permission check. The decision hot path is benchmarked to stay within its low-latency budget under load.
changedSchema: policy_rules.transform column + verdict CHECK constraints added (migration scripts/migrations/2026-06-08-runtime-control-week2-4.sql). Runtime Control is a Growth+ feature, gated by API-key scope runtime:decide.
changedTests: 418 unit + 7 real-database integration tests passing. Four adversarial audit rounds (per-week + a final holistic pass) across the full change; all confirmed findings — including the SSRF and notification-injection issues above — fixed before this release.
v3.11.7June 5, 2026
Security Hardening Round 9 — Eight Findings (Three Are 3.11.6 Regressions)
Round 8 adversarial workflow against the 3.11.6 stack confirmed 8 actionable findings: 4 HIGH (three of them regressions in 3.11.6 patches I shipped earlier today), 2 MEDIUM, 2 LOW. The HIGHs include a logout rate limit that collapsed every user into a single global bucket (the key extracted bytes from the deterministic HS256 JWT header rather than the per-user payload), a tombstone format that overflowed VARCHAR(320) for long original emails, unbanUser that left the rewritten email in place after the ban was lifted (bricking the user), and OAuth login that hard-failed whenever Redis was down (no fallback path). Cumulative 288 findings closed across 8 rounds.
fixedRL-001 / AUTH-3.11.6-A (HIGH): logoutRateLimit key is now derived from the JWT payload section (between the two dots), not the first 25 bytes of the Authorization header. The HS256 header serialises to the constant string "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", so the prior code returned the SAME 25 bytes for every authenticated user — collapsing all logouts into ONE global 30/min bucket. With ~1 logout/sec across all customers we would 429 every fresh logout. The payload section encodes sub/orgId/email/exp so it is per-user-unique without needing to decode
fixedUNBAN_LEAVES_TOMBSTONED_EMAIL (HIGH): admin.unbanUser now restores the original email from a new pre_tombstone_email column, invalidates the token_version Redis cache, and returns 409 EMAIL_TAKEN if the address has been re-registered while the user was banned. Previously unbanUser only set deleted_at=NULL — leaving the tombstoned email "banned-<uuid>@vorim.invalid" in place. The user could log in (deleted_at filter cleared) but every "what is my email" surface would show the invalid address, and any password-reset flow that emails the user would silently no-op
fixedEMAIL_TOMBSTONE_LENGTH_OVERFLOW (HIGH, blocker for above): the 3.11.6 tombstone form `deleted-<uuid>+<original>@vorim.invalid` overflowed the VARCHAR(320) column for original emails ≥ 275 chars — the bcrypt-acceptable max. The UPDATE would throw 22001 and the soft-delete would fail entirely, leaving the user undeleted but the request 500ing. Now the tombstone is fixed-form `banned-<uuid>@vorim.invalid` / `deleted-<uuid>@vorim.invalid` / `removed-<uuid>@vorim.invalid` (~60 chars max) and the original lives in pre_tombstone_email for restore. Migration scripts/migrations/2026-06-05-users-pre-tombstone-email.sql applied
fixedAUTH-3.11.6-D (HIGH): OAuth login no longer hard-fails when Redis is unavailable. The primary path still uses Redis (random 64-char hex token + 10-min TTL + single-use DEL); when Redis is down we mint a signed fallback token `<payload>.<HMAC-SHA256(JWT_SECRET, payload)>` that encodes provider + PKCE verifier + expiry in the token itself. CSRF protection is preserved (attacker still needs a server-signed token); single-use semantics are sacrificed in the fallback only. Without this, every OAuth login attempt failed during any Redis outage — making Redis a single point of failure for sign-in
fixedAUTH-3.11.6-B (MEDIUM): POST /v1/auth/reset-password now enforces the same complexity rule as register (≥1 digit or ≥1 non-letter, 8-128 chars). Without parity, an attacker who triggered a password reset could downgrade the user's password below the floor — setting "passwordpassword" if the original was "Password1"
fixedWH-003-A1 (MEDIUM): PATCH /v1/webhooks/:id now re-runs the same SSRF + https-only validation that POST runs on channel updates. Without this, an attacker could create a benign rule, wait past initial review, then PATCH the URL to http://169.254.169.254/... — bypassing the registration-time guard entirely
fixedRL-002 (LOW): PATCH /v1/webhooks/:id and DELETE /v1/webhooks/:id now share the webhookCrudRateLimit (20/min per key) with POST. Otherwise an attacker could create + delete in tight loops, using PATCH/DELETE as unrate-limited DNS reconnaissance surface
fixedAUTH-SUB-1 (LOW): requireAuth now rejects JWTs whose sub claim is not a UUID, returning 401 UNAUTHORIZED. Previously a forged token with sub="anything" would pass jwt.verify and only fail later when Postgres returned 22P02 invalid_text_representation on the SELECT — surfacing as 500 errors that leaked the schema in error_handler logs
fixedAUTH-EPOCH-TTL-1 (LOW): invalidateTokenVersionCache now refreshes the tv-epoch TTL only when INCR returns 1 (key was just created), not on every call. The prior implementation issued EXPIRE on every invalidate — turning the bounded "currently active users" set into an unbounded "any user that ever logged out" set with ever-extended TTLs. The bootstrap SET in the cache-write Lua already attaches the TTL, so first-INCR coverage is sufficient
changedVersions: server-only patch + web. No SDK / MCP / verify bumps this round
changedTests: 160/160 API passing. Smoke tests confirm authed-call → 200, logout → 200, reused-after-logout → 401 TOKEN_REVOKED (token_version cache busted), forged-sub JWT → 401 (not 500), weak reset-password → 400 with explicit error messages
v3.11.6June 5, 2026
Security Hardening Round 8 — Six HIGH Findings (One Is a 3.11.5 Regression)
Round 7 adversarial workflow against the 3.11.5 stack confirmed 39 findings. The HIGH-severity bugs include a regression in 3.11.5's Lua CAS fix (snapshot-read failures defaulted to "0" which guaranteed CAS-fails for every user with a non-zero epoch, mass-401ing legitimate sessions during routine Redis blips), a wrong undici lookup signature on webhook delivery (every pinned-IP webhook delivery threw ERR_INVALID_IP_ADDRESS), missing deleted_at filters on the API-key owner-fallback queries across agents/permissions/credentials routes, admin.deleteUser + banUser bumping token_version in DB but never invalidating the Redis cache (3.11.5's "instant logout" was actually 5-second-delayed for bans), and missing rate limits on the audit query/export endpoints + logout + webhook CRUD. Plus 12 MEDIUM and 21 LOW.
fixedAUTH-CAS-RE-1 (HIGH): token_version cache snapshot-read failure now falls back to DB-trust instead of defaulting to "0". 3.11.5's CAS retry path treated a Redis GET error as if the epoch was "0", which guaranteed a CAS-fail (and after 3 retries returned -1 → mass 401 TOKEN_REVOKED) for any user whose epoch had ever been bumped. Any Redis hiccup would force-logout every active user. Now a snapshot-read failure is treated as "Redis unavailable for this request": trust the DB read, skip the cache write, return tv
fixedWH-001 (HIGH): undici Agent connect.lookup callback now handles BOTH single-address mode (callback(err, addr, family)) AND opts.all=true array mode (callback(err, [{address, family}])). 3.11.5 only wired the single-address form; undici uses opts.all=true on Node 18+ for dual-stack connect, so every pinned-IP webhook delivery threw ERR_INVALID_IP_ADDRESS. The SSRF guard was effectively a 100% delivery failure
fixedF2 owner-fallback (HIGH): all 7 sites in routes/agents.ts, routes/permissions.ts, routes/credentials.ts that fall back to the org owner when called with an API key now filter `AND deleted_at IS NULL`. Without this, a soft-deleted owner's id silently became the user_id for new agents/permissions/delegations created via API key — orphaning audit-trail attribution to a deleted user
fixedTV-CACHE-NOT-BUSTED (HIGH): admin.banUser, admin.deleteUser, and team.removeMember now bump token_version + wipe refresh_tokens + tombstone the email + call invalidateTokenVersionCache. 3.11.5's soft-delete in admin.deleteUser bumped the DB token_version but never busted the 5-second Redis cache, so the "instant logout" guarantee held in DB while requireAuth admitted the user for up to 5 more seconds. banUser previously didn't bump token_version at all. removeMember was completely missing all three steps
fixedRL-01 (HIGH): GET /v1/audit/events, /stats, /hourly now rate-limited at 120/min per API key (or per user for JWT). POST /v1/audit/export at 10/min (each request can pull up to 1M rows). POST /v1/auth/logout at 30/min (an attacker with a stolen access token could otherwise spam bumpTokenVersion). POST /v1/webhooks at 20/min (mass-creation of alert rules pointing at internal URLs amplifies DNS lookups even when delivery is blocked)
fixedF1 trial signed_bundles (HIGH): starter plan now includes signed_bundles so trial customers can exercise the headline offline-verifiable-audit-export demo during onboarding. Previously every dashboard quickstart that called /v1/audit/export returned 403 FEATURE_NOT_AVAILABLE, breaking the first-five-minutes experience
fixedAUTH-CAS-RE-3: tv-epoch TTL race against natural expiry + concurrent INCR is now bounded — the snapshot-read-failure fix (above) means a brief race doesn't cascade into mass-401s when combined with the 3-attempt cap
fixedWH-003: webhook URLs are now restricted to https:// only. http:// + pinned-IP fetch would give an attacker an unauthenticated TCP connection with no TLS cert binding — receivers in production should always terminate TLS
fixedEmail tombstone on soft-delete: admin.deleteUser, admin.banUser, team.removeMember now rename the email to `deleted-<id>+<original>@vorim.invalid` so the original address can be re-registered. Without this the UNIQUE constraint on users.email permanently blocked re-registration. Register flow already filters deleted_at IS NULL so the pre-check correctly sees a freed address
fixedRedis Cluster compatibility: token_version cache keys use the `{userId}` hash tag (tv:{u1} + tv-epoch:{u1}) so EVAL across both keys lives on the same Redis Cluster slot. Without this, cluster-mode Vorim deployments would fail with CROSSSLOT
fixedcredential_audit_events.agent_id now has FK ON DELETE RESTRICT (migration applied) to mirror the 3.11.4 audit_events constraint — credential audit history can't be silently orphaned by a hard agent delete
fixedPassword complexity: register requires at least one digit or non-letter character in addition to the 8-character minimum. Catches the most common trivial passwords ("password", "qwertyuiop") without blocking legitimate passphrases ("correct horse battery staple")
fixedQuickstart code samples now include a Restore-the-signing-key step before emit() with explicit warning that without it events ship unsigned and will fail @vorim/verify. Both TypeScript and Python snippets updated
fixedLogout Promise.race: the slow-network branch now has its own .catch() so the late settlement of the API call doesn't produce an unhandledrejection in the page lifetime
changedVersions: server-only patch + web. No SDK / MCP / verify bumps this round
changedTests: 160/91/50/148 all pass. JCS parity byte-identical. End-to-end smokes confirm chained bundle round-trip, refresh-token rotation, 10-way concurrent refresh race, and logout-bumps-token_version (which now exercises the AUTH-CAS-RE-1 fix path correctly — a Redis-flake-induced snapshot failure no longer 401s legitimate users)
v3.11.5June 5, 2026
Security Hardening Round 7 — Three HIGH Findings (Two Are Regressions in 3.11.4)
Another adversarial workflow against the 3.11.4 stack confirmed 27 findings. The three HIGH bugs include two regressions in patches I shipped earlier today: the AUTH-2 Lua CAS still leaked stale tv values via the singleflight return path (the CAS only protected the Redis write, not the in-flight Promise resolution), and the cache literally never populated for any user who had never been bumped because the script compared "0" snapshot against Redis nil. Also closes a DNS TOCTOU in the webhook SSRF guard (fetch re-resolved separately from dns.lookup) and aligns admin.deleteUser with the new audit_events ON DELETE RESTRICT semantics by switching to soft-delete. Published @vorim/mcp-server 1.1.7.
fixedAUTH-CAS-1: token_version cache singleflight no longer returns stale values when an invalidate interleaves. The Lua CAS write protected the Redis cache from poisoning but the singleflight Promise resolution still served the stale DB read to every blocked caller. Now the function inspects the EVAL result; on CAS-fail it re-reads the cache (which the invalidator may have just populated via another reader) and retries the DB query up to 3 attempts before failing closed with -1. Password resets, role demotions, and force-logouts now propagate correctly even under concurrent load
fixedAUTH-CAS-2: Lua CAS script now treats a missing tv-epoch:userId key (Redis nil → Lua false) as equivalent to the snapshot "0" and SETs the epoch key with the same TTL. Previously the CAS always failed for any user who had never had bumpTokenVersion called on them (the vast majority of active sessions), so the cache literally never populated. Every authenticated request was hitting Postgres for a SELECT token_version — silent perf regression since 3.11.4
fixedAUTH-CAS-3: tv-epoch:userId now refreshes its TTL on every CAS write, so a dormant user whose epoch key naturally expired no longer enters a 5-second stale-cache window on their next request. The epoch TTL (1 hour) is longer than the tv:userId TTL (5 seconds) so the epoch outlives the cache value
fixedWEBHOOK-001: webhook delivery now pins the resolved IP into the undici fetch dispatcher's lookup function. The prior pattern did dns.lookup for validation but the fetch call resolved its own connection separately, leaving a TOCTOU window where the OS resolver cache could flip to a different IP between check and connect. With the pinned dispatcher, the actual TCP connect uses the exact IP we already validated against the private/loopback/metadata block list
fixedF1 admin.deleteUser: switched from hard DELETE FROM users to soft-delete (deleted_at = NOW() + token_version bump + refresh_tokens consume). The hard DELETE would have failed under the new audit_events.agent_id ON DELETE RESTRICT (3.11.4) for any user who owned an agent with audit history, AND the old "cascades agents, permissions, etc." comment was always wrong — that cascade never existed. Soft-delete preserves owner_user_id references for historical audit + permission grants and forces the deleted user out via token_version bump
fixedAUTH-CAS-2/3 bootstrap: cache now populates on the first request for a fresh user. Previously every authenticated request fell through to Postgres because the CAS always rejected the write. Cache hit rate goes from ~0% (only users who had been bumped) to ~100% (every active user)
fixedWEBHOOK-002: WEBHOOK_SIGNING_SECRET unset now logs a loud warning on first delivery so operators don't accidentally ship to prod without HMAC. Followup deliveries stay quiet to avoid log spam
fixedWEBHOOK-003: 3xx redirects (the operator's legitimate "moved domain" case) now produce a dedicated log line explaining the operator must update the URL — replaces the generic "Delivery failed" message. SSRF protection of refusing-to-follow-redirects is unchanged
fixedWEBHOOK-005: webhook signing comment now documents the receiver verification recipe end-to-end including the 5-minute timestamp tolerance window (replay defense) and the warning that receivers must verify against the RAW request body, not parsed JSON
fixedFrontend F1+F2: loadPersistedAuth now wipes stale sessionStorage user/org when the access token is missing (avoids a brief "logged in" flash on first paint after expiry). Logout also races against a 3-second timeout so a slow/hung network can't trap the user in an unresponsive logout state
fixedLOGOUT-SUB-NOT-UUID-500: POST /auth/logout now validates the JWT sub matches UUID format before passing to pg. A signature-valid but malformed token (e.g. from an external integration that mis-mints sub) no longer crashes the route with 500
fixedOAUTH-02: frontend OAuth callback now REQUIRES a stored expectedState in sessionStorage. The prior `expectedState && ...` skipped validation when storage was empty (eg the user opens the callback URL directly), which a captured state token could replay. Server-side state validation remains the authoritative defense
changedMCP version drift documentation: removed the false comment claiming a CI assertion enforces MCP_VERSION_FALLBACK matches package.json. The fallback now triggers a `console.warn` when read fails (silent drift no longer hides). MCP server bumped to 1.1.7
changedTests: 160/91/50/148 all pass. JCS parity byte-identical. End-to-end smokes confirm the chained bundle round-trip, refresh-token rotation, 10-way concurrent refresh race (1 OK + 9 REFRESH_TOKEN_REUSED), and the new logout-bumps-token_version flow which now correctly exercises the CAS-fail-then-retry path (200 → 401 TOKEN_REVOKED on next request)
v3.11.4June 5, 2026
Security Hardening Round 6 — Eleven HIGH Findings from Post-3.11.3 Audit
A comprehensive verification workflow against the 3.11.3 stack surfaced 43 more findings, 11 of them high-severity. Two were critical regressions in the 3.11.3 patches themselves: the safeClientIp helper used a CJS `require()` that ReferenceError-ed in the ESM build (every refresh_tokens.ip_address was silently NULL), and the token_version cache "fix" used SET NX which does not actually close the read-stale-then-late-write race. Both are now closed end-to-end: ip_address smoke-tested as populated, and the cache write is now a Lua-script CAS gated on a per-user epoch that bumpTokenVersion increments. Also closes OAuth state CSRF + PKCE, webhook SSRF + HMAC signing, per-recipient throttle on forgot-password, frontend refresh singleflight + sessionStorage-only access tokens (refresh tokens are now memory-only), and the credential lazy-reencrypt UPDATEs that previously targeted the wrong tables. Published @vorim/sdk 3.4.3, vorim 3.7.3, @vorim/mcp-server 1.1.6.
fixedAUTH-1: safeClientIp now uses a static `import { isIP } from "node:net"` instead of a dynamic require() that ReferenceError-ed in the production ESM build. The try/catch wrapping the require silently swallowed the error and returned false unconditionally, so every refresh_tokens.ip_address was NULL since 3.11.3. Smoke-tested against production: new refresh rows now show ::ffff:172.18.0.8 (the nginx container) for ip_address. With TRUST_PROXY=true behind a real load balancer the X-Forwarded-For first hop is captured instead
fixedAUTH-2: token_version cache write is now a Lua-script atomic CAS, gated on a per-user epoch sentinel that invalidateTokenVersionCache INCRs alongside the DEL. The prior SET NX only prevented two concurrent writers from clobbering each other — it did NOT prevent a slow reader from writing back a stale value AFTER an invalidate ran. The 3.7.3 "instant revocation propagation" claim now actually holds: a reader whose epoch snapshot is stale fails the EVAL and leaves no stale value behind
fixedF1+F2 credentials: lazy-reencrypt UPDATEs in 3.11.3 targeted the wrong tables (credential_delegations columns that don't exist + provider_nonce instead of secret_nonce). All rows would have silently failed to re-encrypt under a rotated key. The UPDATEs now correctly target oauth_connections.refresh_token_enc/token_nonce and oauth_providers.client_secret_enc/secret_nonce via JOIN subquery
fixedF3 credentials: legacyKey() now slices the resulting Buffer to its first 32 BYTES instead of relying on String.prototype.slice (which operates on UTF-16 code units). A JWT_SECRET containing multi-byte UTF-8 characters would previously produce a Buffer > 32 bytes, AES-256-GCM would throw on createCipheriv, and every credential read would crash. Defensive padding with zero BYTES on the short branch
fixedF2 frontend: tokens moved out of localStorage. accessToken now lives in sessionStorage (closed tab = fresh login, 15-minute TTL bounds blast radius) and refreshToken lives in memory only (never persisted; a closed tab is a guaranteed logout, dramatically reducing XSS-stolen-refresh-token blast radius). Legacy localStorage tokens are cleaned up on first clearTokens() call so existing users get migrated
fixedF4 frontend: refresh now uses singleflight. Multiple concurrent 401s share one in-flight refresh Promise instead of each firing their own POST /auth/refresh. Without this, the server's atomic-consume (3.11.2) would reject all but one as REFRESH_TOKEN_REUSED, wipe the token family, and force a logout on every parallel-request page load. REFRESH_TOKEN_REUSED + TOKEN_REVOKED + INVALID_REFRESH_TOKEN now correctly trigger clearTokens + redirect to /login instead of silent retry loop
fixedOAUTH-CSRF-001: state token is now bound to provider + PKCE code_verifier in Redis with 10-minute TTL, validated single-use on callback. Without this, an attacker could trick a victim's browser into completing OAuth with the attacker's code and log them into the attacker's account (classic login-CSRF). Both Google and GitHub flows now also use PKCE (S256) so an intercepted authorization code cannot be redeemed without the server-held verifier
fixedssrf-no-url-validation + ssrf-redirect-follow: webhook delivery now validates URL host (via DNS resolution at delivery time) against private/loopback/metadata IP ranges including 169.254.0.0/16 (AWS metadata), 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10, link-local IPv6, ULA, multicast, IPv4-mapped IPv6. fetch() now uses redirect: "error" so a 302 from a legitimate webhook to 169.254.169.254 cannot bypass the IP block. createAlertRule pre-validates URLs so bad webhooks fail loud at registration
fixedno-hmac-signature: webhook deliveries now carry X-Vorim-Timestamp and X-Vorim-Signature (HMAC-SHA256 over `${timestamp}.${body}` with WEBHOOK_SIGNING_SECRET) so receivers can authenticate the request came from Vorim. Operators set WEBHOOK_SIGNING_SECRET in env; if unset, signatures are omitted (recommended to set in prod)
fixedEMAIL-DOS-02: forgot-password now enforces a per-recipient cooldown of 1 hour in addition to the IP-keyed rate limit. An attacker rotating IPs can no longer spam a victim's inbox at 3 messages per IP per hour — the per-recipient cap is 1 mail per hour regardless of source IP. Still returns { sent: true } to avoid email enumeration
changedPOST /auth/logout no longer requires a valid (non-revoked) access token — a second logout call from a tab that already revoked itself is now idempotent. Signature + sub verification is sufficient. Frontend logout button now calls the endpoint so refresh tokens are server-consumed (not just dropped from localStorage)
changedaudit_events.agent_id gets an FK ON DELETE RESTRICT (migration applied to prod). A future code path that hard-DELETEs an agent will fail at the FK check instead of silently orphaning audit history. Soft-delete via status='revoked' remains the canonical path
changedOAuth + PKCE: Google + GitHub authorization URLs now include code_challenge (S256) + code_challenge_method. The callback exchanges the code with the server-held verifier so an intercepted code (eg via Referer leak or proxy logging) can't be redeemed by an attacker who lacks the verifier
changedVersions: @vorim/sdk 3.4.2 → 3.4.3 (no code change, README parity); vorim 3.7.2 → 3.7.3 (new AsyncVorimCallbackHandler for true non-blocking emit on LangChain ainvoke/astream; create_vorim_claude_agent now keyword-only; auto-derive warnings now logged not swallowed); @vorim/mcp-server 1.1.5 → 1.1.6 (version read from package.json so MCP_VERSION cannot drift; better non-JSON response handling)
changedTests: 160/91/50/148 all pass. JCS parity byte-identical. End-to-end smokes confirm chained bundle round-trip, refresh rotation, 10-way concurrent refresh race (1 OK + 9 REFRESH_TOKEN_REUSED), and the new IP-capture behavior on refresh_tokens.ip_address (was uniformly NULL since 3.11.3)
v3.11.3June 5, 2026
Security Hardening Round 5 — Twenty-Eight Medium + Low Findings
Closes the remaining 13 medium-severity and 15 low-severity findings the post-3.11.1 audit workflow surfaced. Logout now bumps token_version (access tokens die immediately, not after 15 min); refresh path validates its own tv claim against current token_version (closes the bumpTokenVersion-during-refresh race); credential decrypt has lazy re-encryption under the current key + audit-trail logging on failure + cache for normalization hashes; fingerprint backfill batches by cursor with a safety cap warning; Python integration factories for OpenAI and Claude now thread replay-evidence kwargs; LangChain callback handler now supports async LangChain runs. Published @vorim/sdk 3.4.2, vorim 3.7.2, @vorim/mcp-server 1.1.5.
fixedAUTH-R-03: refreshToken now validates the refresh JWT's own tv claim against the user's current token_version. A refresh token minted before bumpTokenVersion now correctly fails with TOKEN_REVOKED instead of silently rotating into a fresh access token under the new tv (which previously let a stale refresh bypass revocation until natural expiry)
fixedAUTH-R-04 + AUTH-R-07: logout() now calls bumpTokenVersion() instead of only consuming refresh tokens. The current access token dies on the very next request (TOKEN_REVOKED), not after up to 15 minutes. Smoke test against production confirms: GET /me 200 → POST /logout 200 → GET /me with same access token returns 401 TOKEN_REVOKED within milliseconds. Also closes the logout race where a concurrent refresh could re-create an active token row that survived logout
fixedAUTH-R-06: refresh route now validates the X-Forwarded-For + req.socket.remoteAddress IP via node:net.isIP before passing to the INET column. Previously a malformed X-Forwarded-For (e.g. attacker-spoofed nonsense, or comma-separated junk) would throw inside the INSERT and crash the request handler. user_agent is also now truncated to 512 chars to bound the row size
fixedTV-005: singleflight on the token_version DB read. Concurrent identical requests for the same userId now coalesce onto one in-flight Promise instead of stampeding Postgres when Redis is down. Map is cleared in the finally block so the promise is short-lived and not a leak vector
fixedF6 decrypt error: credential decryption failures throw a typed CREDENTIAL_DECRYPT_FAILED AppError instead of the raw Node crypto Error. useCredential also logs the failure to credential_audit_events so operators see rotation-related decryption issues without raw error strings leaking into responses
fixedF6 lazy re-encrypt: useCredential tracks which key (current / prev / legacy) successfully decrypted, and re-encrypts under the current key in the background when it was decrypted under a non-current key. The legacy key window now drains itself on read traffic — no separate backfill job needed
fixedfp-4: recomputeKeyFingerprintsOnce now paginates by id cursor in 1,000-row batches up to a 1,000,000-row safety cap. Operators with > 1M agents see a clear warning and can bump the cap. The _ran flag also only flips to true after FULL successful completion (was previously flipped at the top, so a mid-loop throw silently abandoned the remaining rows for the process lifetime)
fixedfp-1 + fp-2: generateFingerprint() now trims leading whitespace before matching the PEM wrapper, validates that the decoded DER is at least 32 bytes (rejecting garbage that decoded short), and handles chained PEM by fingerprinting only the FIRST key (matches ssh-keygen behavior on multi-key files) instead of concatenating bodies
fixedF2 (Python): create_vorim_openai_agent and create_vorim_claude_agent now accept model_version, tool_catalogue_hash, and system_prompt_hash kwargs and thread them through to the underlying VorimToolRegistry. The factories previously dropped replay evidence entirely, matching the bug the prior workflow had already flagged in langchain.py
fixedF3 (Python LangChain): VorimCallbackHandler now exposes async equivalents (on_tool_start_async, on_tool_end_async, on_tool_error_async) so audit events are emitted on LangChain ainvoke / astream pipelines. Previously async runs landed silently. Docstring also clarifies that permission enforcement happens in wrap_tool / wrap_tools — the callback handler is observational only
fixedsdk-v1-default-2 (Python): canonical_form="v0" deprecation now uses warnings.warn(DeprecationWarning) instead of a logger.warning call. The previous routing through the "vorim" logger was silently discarded in apps that hadn't configured a handler — warnings.warn surfaces under Python's default -W filter and CI / CloudWatch log scrapes
fixedMCP server (1.1.5): vorim_ping uses the MCP_VERSION constant for User-Agent (was hardcoded 1.0.0); vorim_export_audit description documents the 90d cap and the truncated flag; 401 / 403 responses now produce friendly error messages naming VORIM_API_KEY and INSUFFICIENT_SCOPE; security comment block at API_KEY load documents the process-wide nature of the stdio auth surface so operators understand the least-privilege scoping recommendation
fixedLOW F6: normaliseKey memoizes its SHA-256 result per input string (process-wide map), removing the per-call hash cost on high-throughput credential reads. Negligible memory (one Buffer per distinct key string, capped at three by the configuration shape)
changedTests: 160/160 services/api, 91/91 @vorim/sdk, 50/50 @vorim/verify, 148/148 vorim. JCS parity byte-identical. End-to-end smoke battery on production confirms: 5-event v1 chained bundle round-trip ok=true, refresh-token rotation flow with reuse detection, 10-way concurrent refresh race yields 1 OK + 9 REFRESH_TOKEN_REUSED, and the new logout-bumps-token_version flow (access token dies immediately after logout)
changedVersions: @vorim/sdk 3.4.1 → 3.4.2 (langchain-async + README polish), vorim 3.7.1 → 3.7.2 (factories + warnings.warn), @vorim/mcp-server 1.1.4 → 1.1.5 (error messages + version sync). @vorim/verify unchanged
v3.11.2June 5, 2026
Security Hardening Round 4 — Eight HIGH Findings from Post-3.11.1 Audit
An adversarial verification workflow run against the 3.11.1 stack surfaced 36 more confirmed findings, including 8 high-severity bugs in the security primitives shipped overnight. Two were critical: OAuth login was completely broken because services/oauth.ts had its own pre-3.11.1 generateTokens that minted refresh tokens the new server immediately rejected; refresh-token reuse detection was defeated by a TOCTOU window between SELECT and UPDATE that let two concurrent refreshes both succeed. All 8 patched within the hour and confirmed end-to-end on production: 10 concurrent refreshes with the same jti now yield exactly 1 success and 9 REFRESH_TOKEN_REUSED responses. Published @vorim/sdk 3.4.1, vorim 3.7.1.
fixedAUTH-R-01: OAuth flow now uses the canonical generateTokens exported from services/auth.ts. The previous private copy in services/oauth.ts minted refresh tokens with no jti / family / tv claims and access tokens using userId instead of sub — every Google/GitHub login produced credentials the new server immediately rejected. Smoke against production confirms /v1/auth/oauth/google and /v1/auth/oauth/github still mint state tokens correctly under the new flow
fixedAUTH-R-02: refresh-token consume is now atomic. The prior SELECT + JS-check + UPDATE pattern left a TOCTOU window where two concurrent refreshes with the same jti could both succeed, defeating the entire reuse-detection design shipped in 3.11.1. The new UPDATE refresh_tokens SET consumed_at = NOW() WHERE jti = $1 AND consumed_at IS NULL RETURNING jti pattern guarantees exactly one winner. Smoke proof: 10 parallel refreshes with the same jti now yield 1 OK + 9 REFRESH_TOKEN_REUSED
fixedTV-001: token_version cache uses SET ... NX so a slow-writer-after-DEL race no longer poisons the eviction with a stale value. Without this, a bumpTokenVersion DEL between a reader's cache-miss and its setex could be silently overwritten, extending staleness past the 5s TTL
fixedTV-002: requireAuth now treats soft-deleted users as token_version = -1 sentinel. A legitimate JWT carries tv >= 0, so the sentinel never matches and the user is rejected on the next request after soft-delete. The previous code missed the deleted_at filter and let a soft-deleted user keep authenticating until natural JWT expiry
fixedF6-LEGACY-DECRYPT: the candidate-keys fallback for the credentials AES-GCM decryption now uses the legacy JWT-derived key as literal ASCII bytes (matching the pre-3.11.1 code) instead of running it through SHA-256. Without this, every pre-3.11.1 encrypted credential row was permanently undecryptable after the 3.11.1 deploy
fixedF1 (Python): create_vorim_agent now auto-derives tool_catalogue_hash from the tools list when the caller doesn't supply one, matching what the docstring promised. The factory previously passed None straight through, silently shipping v0-style replay events with no tool-catalog evidence
fixedsdk-v1-default-1: both SDK READMEs updated to document the v1 canonical formula (RFC 8785 JCS over the full event minus signature and canonical_form) as the default. The previous text described only the deprecated v0 pipe-joined six-field form, which would mislead anyone building a third-party verifier from the docs
fixedF2 (AUTH-R): oauth.ts now awaits the now-async generateTokens. Without await the route would have returned an unresolved Promise as "tokens" — a follow-on consequence of the AUTH-R-01 fix
changedVersions bumped to @vorim/sdk 3.4.1 (README only) and vorim 3.7.1 (auto-derive). @vorim/verify and @vorim/mcp-server unchanged this round. No new migration required
changedTests: 160/160 services/api (mocks updated for the deleted_at column), 91/91 @vorim/sdk, 50/50 @vorim/verify, 148/148 vorim. JCS parity script byte-identical
v3.11.1June 5, 2026
Security Hardening Continuation — All Deep-Audit HIGH Patched
Closes the seven remaining high-severity findings the deep-audit workflow surfaced in 3.11.0. Real-time access-token revocation via token_version; refresh-token rotation with reuse detection that nukes the whole token family on replay; v1 canonical form is now the SDK default; key fingerprints are now DER-based for stable cross-tool agreement; OAuth credential encryption now uses a dedicated CREDENTIAL_ENC_KEY env var (with backward-compat decrypt fallback); MCP server hardened with URL encoding, destructive-tool annotations, and version sync; Python create_vorim_agent factory now threads replay-evidence context through to every tool emit. Published @vorim/sdk 3.4.0, vorim 3.7.0, @vorim/mcp-server 1.1.4.
fixedAUTH-002: refresh-token rotation with reuse detection. Every login mints a new token family (UUID), every refresh redeem consumes the previous jti and issues a fresh jti in the same family. Redeeming an already-consumed token wipes the entire family — attacker and victim both forced to re-authenticate. Smoke test against production confirms first refresh succeeds, replay returns REFRESH_TOKEN_REUSED, and any other token from the same family is also wiped. Closes the OAuth 2.0 BCP §4.14 / RFC 6819 §5.2.2.3 anti-pattern
fixedAUTH-003: token_version baked into every issued JWT. requireAuth verifies the claim against the user's current token_version (5s Redis cache; falls through to Postgres). resetPassword now calls bumpTokenVersion() so a credential compromise response invalidates every outstanding access and refresh token. New POST /v1/auth/logout consumes outstanding refresh tokens for the caller; access token continues for its natural 15-minute expiry
fixedcrypto-1: SDK canonical_form now defaults to v1. v0 leaves metadata, replayable-evidence, and delegation-context fields unsigned — passing canonical_form: "v0" explicitly now emits a deprecation warning. Existing v0 verifiers continue to work; new SDK installs get full tamper coverage out of the box. Applies to both @vorim/sdk (TS) and vorim (Python sync + async)
fixedcrypto-2: key_fingerprint now hashes the SPKI DER bytes after stripping the PEM wrapper, matching what ssh-keygen and openssl produce for the same key. PEM whitespace and header variation no longer change the fingerprint. A one-shot recompute job runs 45 seconds into API boot and idempotently rewrites any agent rows whose stored fingerprint differs from the canonical DER-based value
fixedF6: separate CREDENTIAL_ENC_KEY env var for the AES-256-GCM key that encrypts stored OAuth refresh tokens and provider client secrets. The previous code derived the AES key from JWT_SECRET, conflating session-token signing with at-rest credential encryption — a single env-var leak collapsed both surfaces. The decrypt path tries CREDENTIAL_ENC_KEY first, then CREDENTIAL_ENC_PREV_KEY (set during rotation), then falls back to the legacy JWT-derived key so already-encrypted rows still decrypt
fixedMCP server hardened: every user-supplied agent_id, scope, and chain_id is now URL-encoded before path interpolation (encId helper); destructive tools (vorim_revoke_agent, vorim_revoke_permission) declare destructiveHint annotations so MCP clients can gate them; vorim_check_permission, vorim_list_permissions declare readOnlyHint; the User-Agent + advertised version are now MCP_VERSION-driven and match package.json. vorim_emit_event now documents that MCP-emitted events ship unsigned (the MCP server does not hold agent private keys) and points callers at the SDK for tamper coverage. Published @vorim/mcp-server 1.1.4
fixedPython create_vorim_agent factory now accepts model_version, tool_catalogue_hash, and system_prompt_hash kwargs and threads them through both wrap_tools and VorimCallbackHandler. Previously the helper that most LangChain users adopt silently dropped VAIP -02 replay context even when the underlying wrap_tool layer supported it
changedNew prod migration applied (2026-06-05-refresh-token-rotation): refresh_tokens table (jti PK, family_id, user_id, consumed_at, expires_at, user_agent, ip_address). Existing JWTs minted before this deploy carry no jti claim — the next /auth/refresh call against an old token returns INVALID_REFRESH_TOKEN with "format is outdated; please sign in again", which is the correct rollover behavior
changedTests: 160/160 services/api, 91/91 @vorim/sdk (4 tests updated to reflect the new v1 default), 50/50 @vorim/verify, 148/148 vorim (Python). JCS parity script still byte-identical. End-to-end smoke confirms the 5-event v1 chained bundle round-trip + the refresh-token rotation flow + REFRESH_TOKEN_REUSED detection all work against production
v3.11.0June 5, 2026
Security Hardening Pass — Deep Audit Round
A deep adversarial audit workflow across surfaces the prior signing-focused hotfixes did not touch (auth, multi-tenancy, rate limit, MCP, public trust surface, Stripe, websocket, audit export, crypto) surfaced 52 confirmed findings — 21 high-severity. The ten most critical have been patched in a single security-hardening release. Server-only changes; no SDK bump.
fixedJWT signing secrets: the API now refuses to boot in any non-dev environment when JWT_SECRET or JWT_REFRESH_SECRET are missing or equal to their dev fallback string. The fallback strings double-served as the AES key for stored OAuth refresh tokens, so the previous silent-default behavior was a single-misconfiguration auth-and-credential-disclosure risk
fixedAPI key scope enforcement: api_keys.scopes was previously stored on every key but never read at request time, so a "read-only" key could still ingest events, register agents, or revoke. New requireApiKeyScope() middleware enforces a per-route scope (audit:read, audit:write, etc.) and rejects with 403 INSUFFICIENT_SCOPE if missing. Applied to every /v1/audit route
fixedWebSocket cross-tenant leak: the /ws endpoint accepted any client with no authentication and broadcast every org's audit events to every connected client globally. Connections now require a JWT in the ?token= query string, are tagged with the connecting user's orgId, and the broadcast helper filters every message by orgId so an event from org A is never sent to a connected client for org B. Added 30s ping/pong heartbeat so silent socket leaks close on their own
fixedStripe webhook signature verification: express.json() was applied globally before the /v1/billing/webhook route, so req.body was the parsed JSON object by the time the route tried to verify against raw bytes. Webhook signature checks silently failed (or passed only after Stripe retries via specific request shapes). The webhook is now mounted with express.raw() BEFORE the global JSON body parser. handleStripeWebhook() is the single canonical handler
fixedStripe webhook idempotency: every Stripe event id is INSERT ON CONFLICT DO NOTHING into a new stripe_events_processed table at the top of the handler. Replayed events (Stripe retries on 5xx and may deliver the same event multiple times in normal operation) short-circuit with a 200 ack and never re-execute UPDATE statements
fixedPlan limits now respect subscription_status: a past_due / canceled / incomplete_expired subscription falls back to free-tier limits instead of retaining full enterprise entitlements indefinitely. Applies to enforceAgentLimit, enforceEventLimit, and requireFeature
fixedAudit ingest rate limit: POST /v1/audit/events is now rate-limited per-API-key at 600 requests/minute (10/sec sustained). A leaked or misbehaving API key can no longer DoS the platform hot path
fixedAudit export validation: AuditExportSchema rejects from > to, caps the window at 90 days, and requires ISO timestamps. exportAuditBundle adds a 1M-event LIMIT with a truncated=true flag in the response when hit. buildChainTokens filters to status='active' chains so revoked / expired delegation tokens no longer ship in current-state exports
fixedPublic trust surface minimization: /v1/trust/verify/:agentId no longer leaks org_slug, key_fingerprint, active_scopes, or last_audit_event to anonymous callers. revokeAgent and status-update flows now invalidate the 60s trust cache so a revoked agent doesn't serve as verified: true for up to a minute post-revocation
fixedLogin rate limit re-keyed on IP only (was IP+email, which an attacker could bypass by rotating the email field). 10 attempts per 15min per IP is generous enough for shared NAT while still capping credential stuffing
changedNew migration applied to production: stripe_events_processed table + token_version column on users (latter is groundwork for the AUTH-003 real-time access-token revocation flow shipping in 3.11.1)
changedTest suite: 160/160 services/api tests pass. End-to-end smoke against production confirms the 5-event chained bundle round-trip with latency_ms=0 still returns ok=true after the security hardening
v3.10.3June 5, 2026
Audit Hotfix Round Three — Remaining Workflow Findings
Closes the 12 medium-severity findings the post-hotfix verification workflow surfaced in 3.10.2 — non-determinism in the bundle manifest hash, partial-bundle chain head edge case, delegation-chain valid_from check, server-side cross-checks on signed delegation creation, sync↔async SDK keyring sharing pitfalls, and chain-state memory hygiene. Published @vorim/verify 0.3.2, @vorim/sdk 3.3.2, vorim 3.6.2. No new migrations; server-only API redeploy.
fixedBundle manifest hash is now byte-deterministic across deploys: bundle.agents[] is selected with ORDER BY agent_id ASC and the agentIds union is sorted before being passed to ANY($2). Earlier the manifest could differ between two exports of the same window if PostgreSQL chose a different plan ordering
fixedPartial / sub-range exports no longer report chain_broken for their first event. New chain verdict `chain_continuation` surfaces informationally when prev_event_hash references an event outside the bundle window — the verifier can't validate it, but operators exporting a continuation shouldn't see ok=false just because the predecessor lives in a different file
fixedDelegation chain verifier now honors valid_from with the same 60s skew tolerance as valid_until. A not-yet-valid chain returns the new `delegation_not_yet_valid` verdict and fails the bundle, preventing a malicious delegator from minting a chain that "verifies" before it was intended to be active. Malformed valid_from / valid_until strings now produce delegation_chain_broken rather than silently passing
fixedSigned delegation link creation refuses to mint a sub-link off an unsigned parent chain — the prior code treated parent.claims=NULL identically to "no parent", allowing a depth-≥2 link to impersonate a root link offline. Now throws SIGNED_LINK_REQUIRES_SIGNED_PARENT pointing operators to re-create the parent with a signed_link first
fixedBatched identity-chain lookup on the ingest hot path now pairs each delegate agent with ITS chain_id (via UNNEST parallel arrays) instead of seeding a Cartesian product. Agents participating in two different chains in the same ingest batch are no longer at risk of picking the wrong root for on_behalf_of
fixedPython SDK share_keyring_with() now raises a ValueError when called with an already-aliased peer, preventing the silent A→B then B→C orphan pattern. Both Vorim and AsyncVorim affected. Docstring updated with the "pick one canonical holder" guidance
fixedTypeScript SDK chain state (chainLocks, agentForgetEpoch, lastEventHash) is now capped at 10,000 per-agent entries with lazy eviction of the oldest non-active entries. Long-running processes that cycle through many short-lived agent_ids no longer leak memory unbounded
fixedforgetAgentKey now also closes the signing race, not just the chain race: an in-flight emit that started before forget will skip signing if the key has been revoked or the forget-epoch advanced between lock acquisition and the sign call. The event still ships with canonical_form retained so the missing signature is detectable downstream
fixedVerifier advisories now fire on every v0 event carrying v1-only fields, not only on verdict=verified events. Unsigned v0 events with replayable-evidence or delegation-context fields get the same advisory note so the reader is never misled about what is or isn't covered by a signature
fixedServer attestations envelope is now symmetric: when caller_supplied is true, every delegation field is owned by the signed event body and the attestation echoes null on all four. Previously delegation_chain_id and delegation_depth were echoed unconditionally, creating inconsistent ownership semantics
fixedcreateAgentDelegation parent-chain lookup now includes an explicit org_id filter as defense-in-depth alongside the UUID-PK filter, and listChainEntries finds the chain root by current_depth=1 rather than rows[0] so a future ORDER BY change can't silently break onBehalfOf attribution
fixedSDK signing-failure console.warn truncates agent_id to its 12-character prefix so log sinks don't accumulate full per-org-identifiable strings on noisy signing failures. Vorim 3.6.0 wheel and sdist removed from packages/python-sdk/dist to prevent accidental re-upload
changedTests added for chain_continuation and not-yet-valid delegation; existing tests updated where the verdict semantics shifted. Full test suites pass: @vorim/verify 50/50, @vorim/sdk 91/91, services/api 160/160, Python SDK 148/148. JCS parity still byte-identical
v3.10.2June 4, 2026
Audit Hotfix Round Two — Five Production Bugs Found by Adversarial Workflow
An adversarial verification workflow run against the 3.10.1 stack surfaced 36 more confirmed findings, including six high-severity bugs that would have visibly broken the offline-verification demo or attribution-forgery resistance. All six high-severity bugs patched within the hour and confirmed end-to-end against production with a five-event v1 + chained smoke test that includes the latency_ms=0 edge case the first hotfix missed.
fixedFalsy-but-valid event values (latency_ms: 0, empty-string resource / error_code / input_hash) were being coerced to NULL on ingest by JavaScript `||`, then stripped from the v1 bundle export — but the SDK had already signed JCS bytes that included the falsy value, so every such event reported bad_signature on round-trip. Switched to `??` so only true undefined collapses to null. Realistic inputs (cache-hit latency, mocked test runs, no-error path) no longer silently break the headline offline-verification claim
fixedEvery identity delegation create call was failing on production: the ON CONFLICT inference predicate in createAgentDelegation still read `WHERE chain_type = 'identity'` but the migration shipped earlier in the day tightened the partial unique index to `WHERE chain_type = 'identity' AND status = 'active'`. PostgreSQL requires logical implication, so the INSERT threw immediately before any row was written. Existing tests mocked the result so the regression survived CI. Updated the ON CONFLICT clause to match the index predicate verbatim
fixedCaller-supplied delegation context (on_behalf_of, delegator_agent_id, delegation_chain_id, delegation_depth) was accepted unconditionally on ingest, signed under v1, and verified offline as `verified` — without any server-side cross-check that the claimed chain id, depth, or principal corresponds to an active chain the caller actually belongs to. A malicious tenant could attribute its own agent's actions to a third party's chain id from a leaked bundle and produce a valid signature naming someone else's principal. Server now rejects with DELEGATION_CONTEXT_MISMATCH when any supplied field disagrees with the active chain
fixedSame-millisecond batched emits could export in random order because event_id used `crypto.randomBytes(10)` with no monotonic counter, so ORDER BY (timestamp ASC, event_id ASC) collapsed to random order within a batch. Legitimate hash-chained emits could spuriously report chain_broken. Added a per-process millisecond-bucketed counter to the event_id's tiebreaker slot (24-bit counter + 40 bits of randomness for cross-process uniqueness)
fixedPre-hotfix audit rows with caller-signed delegation fields would have failed verification because the new caller_supplied_delegation column backfilled NOT NULL DEFAULT FALSE, treating them as server-stamped on export and stripping the signed fields. Shipped a backfill migration that flips the flag to TRUE for rows where canonical_form='v1', signature is non-null, and any delegation column is set — the rows almost certainly emitted by a caller pre-stamping context. Zero rows updated on Vorim's production database (clean state); the migration is in place for any customer who upgrades from a v1+delegation deployment older than 3.10.0
changedEnd-to-end smoke test on production: five v1-signed chained events including three with latency_ms=0 round-trip through bundle export → verifyBundle as ok=true, verified=5/5, chain_intact=5, chain_broken=0. The headline "every server-produced bundle verifies offline" claim now holds under realistic edge-case inputs
changedServer-only patch: no SDK or verifier change required. @vorim/sdk@3.3.1, @vorim/verify@0.3.1, vorim==3.6.1 remain the current versions. One additional migration (caller_supplied_delegation backfill) applied to production
v3.10.1June 4, 2026
Offline Bundle Verification Hotfix
Patches 33 audit findings surfaced by an end-to-end review of the 3.10.0 stack. Headline fix: every server-produced audit bundle was reporting ok=false because the manifest hash, v1 signatures, and v1 hash-chain all disagreed with the verifier on what bytes to cover. Offline verification now works on real exports — confirmed by an end-to-end round-trip on production. Published @vorim/verify 0.3.1, @vorim/sdk 3.3.1, vorim 3.6.1. Two database migrations applied.
fixedManifest hash shape: @vorim/verify and the in-browser verifier now hash {events, agents, delegation_tokens} to match the server. Without this, every bundle returned manifest_ok=false and report.ok=false even when every signature verified
fixedv1 signatures + hash chain verifiable on bundle round-trip: server export now strips DB-only columns (id, org_id, UUID agent_id, timestamp), drops null-valued optional fields and server-generated event_id from v1 event bodies, and moves server-auto-stamped delegation context into a sibling server_attestations[] envelope. The v1 RFC 8785 JCS bytes the verifier reconstructs now match what the SDK signed
fixedEmpty-string prev_event_hash no longer bypasses chain validation in @vorim/verify. The prior code combined "!= null" (true on empty string) with "if (prevField)" (false on empty string), letting an attacker rewrite every prev_event_hash to "" and still surface chain_intact + ok=true
fixedBundle export ORDER BY adds event_id ASC as a secondary key so batched ingest emits sharing one NOW()-derived timestamp don't spuriously break chains. uniq_identity_chain partial index now filters on status='active' so revoking a chain no longer blocks re-delegating the same pair (was 409 forever)
fixedDelegation chain verifier: signature checks now run before structural/scope/expiry checks (a real forgery is no longer masked by a downstream chain_broken or expired marker). Multiset scope-subset compare so duplicate-padded scopes can't widen past parent. Server-side parent_link_hash equality check on signed link creation. 60s skew tolerance on chain expiry. Bundle agents[] widened to include intermediate delegators so multi-hop chains (depth ≥ 3) verify end-to-end
fixed@vorim/sdk + vorim signing-failure path logs a warning and retains canonical_form="v1" so a transient signing failure surfaces as v1 signature_missing instead of masquerading as a legacy v0 unsigned event. Python sync emits get per-agent threading.Lock; async emits get asyncio.Lock — concurrent emits to the same agent no longer race. forgetAgentKey race closed via per-agent forget-epoch in both SDKs so an in-flight emit completing after forget cannot repopulate the chain tail
fixedIngest hot path: per-agent identity-chain lookup batched into 2 queries regardless of batch fan-out (was N to 2N serialised round trips on every POST /v1/audit/events). listChainEntries.onBehalfOf now returns the chain root for every depth (was the immediate delegator at depth ≥ 2, contradicting audit-event stamping). vorim-verify --version corrected from stale 0.1.0 to 0.3.1
fixedBrowser signEvent helper dispatches on canonical_form so a v1-configured caller actually produces a v1 signature (was silently signing v0 regardless). emit / emitBatch in both SDKs warn when the server returns ingested < submitted (typically an unknown agent_id), surfacing silent partial-batch drops. share_keyring_with() helper lets a sync Vorim + async AsyncVorim in the same process share one keyring + chain state
fixedv0 advisory verdicts: verifier now emits a per-event advisories[] note listing v1-only fields present on a v0-signed event (model_version, tool_catalogue_hash, system_prompt_hash, prev_event_hash, on_behalf_of, delegator_agent_id, delegation_chain_id, delegation_depth). Verdict stays "verified" — the signature does verify — but the reader is told which fields aren't covered, so the "per-event delegation context cryptographically pinned" claim isn't silently misleading on v0 events
changedTwo prod migrations: caller_supplied_delegation BOOLEAN column on audit_events distinguishes caller-signed delegation context from server auto-stamped (controls bundle export envelope split). uniq_identity_chain rebuilt as partial unique index on status='active'. New CHECK constraint chk_chain_parent_only_for_credential enforces that identity chains never carry parent_delegation_id (which targets credential_delegations only)
changedTest coverage: @vorim/verify 50/50, @vorim/sdk 91/91, services/api 160/160, Python SDK 148/148. JCS parity script extended to cover control characters (C0 range), integer-valued floats in nested arrays, and empty containers; Python and TypeScript output remains byte-identical. End-to-end smoke test against production confirms a v1 + chained bundle round-trip now returns ok=true (manifest_ok=true, verified=1, chain_intact=2)
v3.10.0June 4, 2026
Multi-Hop Attenuated Delegation with Offline Chain Verification
Closes the third (and final) of the three "absent from all eight vendors" gaps identified in the June 2026 white paper. Agents can delegate the right to act on their behalf to other agents in the same org with a strict scope subset, up to 32 hops, with cascading revocation. Audit events emitted by a delegated agent carry four delegation-context fields (on_behalf_of, delegator_agent_id, delegation_chain_id, delegation_depth) so a verifier walking the bundle can reconstruct the chain. The whole chain is offline-verifiable: each delegation link is a JCS-canonical claims object Ed25519-signed by the delegator; bundle exports include delegation_tokens[] which @vorim/verify 0.3.0 walks to detect tampered signatures, scope-exceeded children, broken parent links, expiry, and missing delegator public keys. Vorim is not in the trust path for delegation verification. Published @vorim/sdk 3.3.0, vorim 3.6.0, @vorim/verify 0.3.0.
addedAgent-to-agent identity delegation endpoints under /v1/agents/:id/delegations: POST to delegate, GET to list active chains the agent participates in, GET /delegation-chain/:chainId to walk a chain, DELETE to revoke (cascades to all descendants beneath the requesting agent's depth)
addedaudit_events delegation-context columns: on_behalf_of, delegator_agent_id, delegation_chain_id, delegation_depth (with CHECK 0..32). Server auto-stamps on ingest when the emitting agent has an active identity chain; callers using v1 signatures can also set fields explicitly so the signature covers them. Two partial indices designed for replay and incident-response chain walking
addedVAIP delegation chain v0 format: each delegation step is a JCS-canonical claims object {v, type, chain_id, depth, delegator, delegate, scopes, max_chain_depth, valid_from, valid_until, parent_link_hash} Ed25519-signed by the delegator. delegation_chains gains claims/signature columns; bundle export emits delegation_tokens[] with one entry per chain referenced in the bundle
added@vorim/verify 0.3.0 verifyDelegationChain(): walks every link checking signature, parent_link_hash, depth, scope subset, expiry, and delegator-known. Returns one of 7 verdicts in priority order (delegation_unverifiable, delegation_unknown_agent, delegation_invalid_signature, delegation_chain_broken, delegation_scope_exceeded, delegation_expired, delegation_valid). verifyBundle aggregates per-chain verdicts into report.delegations[] and counts; an invalid chain fails the bundle (ok=false); unverifiable and unknown_agent surface for the reader to decide
added@vorim/sdk 3.3.0 vorim.delegateToAgent() / vorim.signDelegationLink() — the latter signs claims with the delegator's in-memory key using the SDK's local JCS, byte-equivalent to the server and verifier (locked by scripts/check-replay-parity.sh which now covers v0, v1, replay helpers AND delegation_link bytes). vorim 3.6.0 ships identical Python API with cross-language signature compatibility proven by tests
changedCross-language replay-parity script extended to assert byte-identical canonical delegation_link bytes across Python and TypeScript; passes for a representative claims object including all VAIP -02 § 5 fields
changedWhite paper "What Your Agent Audit Cannot Prove" (vorim.ai/whitepaper) Section 7 row 5 (Multi-hop attenuated delegation) upgraded from PARTIAL to HAS_FEATURE; row 6 (Per-event delegation context) text rewritten to reflect the now-shipped first-class columns. Executive summary updated: Vorim AI now ships HAS_FEATURE on all three "absent from all eight vendors" gaps. Two remaining PARTIALs in the Vorim column (public per-unit pricing, cross-vendor attestation) flagged honestly
changedDefault behavior preserved: existing @vorim/sdk 3.2.x and vorim 3.5.x callers see no change. delegateToAgent's signed_link parameter is optional — chains without a signed link still work server-side (auto-stamped audit context, scope/depth enforcement, cascading revocation) but the bundle reports them as delegation_unverifiable rather than offline-walkable
v3.9.0June 4, 2026
Replayable Decision Evidence, v1 Canonical Form & Hash-Chained Ingest
Closes the second of the three "absent from all eight vendors" gaps identified in the June 2026 white paper. Audit events now carry four dedicated replayable-evidence fields (model_version, tool_catalogue_hash, system_prompt_hash, prev_event_hash). The v1 canonical signature form (RFC 8785 JSON Canonicalization Scheme) brings them under the Ed25519 signature so tampering with the recorded model, tool catalog, or system prompt invalidates verification. Hash-chained ingest is opt-in and produces per-agent chains the open-source verifier walks to detect deletion of a single audit row. Per-event canonical_form field lets bundles spanning an SDK upgrade window mix v0 and v1 events without issue. Published @vorim/sdk 3.2.0, vorim 3.5.0, @vorim/verify 0.2.0.
added@vorim/sdk 3.2.0 + vorim 3.5.0 (PyPI): four new optional fields on AuditEventInput — model_version, tool_catalogue_hash, system_prompt_hash, prev_event_hash. Five framework integrations (LangChain, OpenAI, Anthropic, CrewAI, LlamaIndex on TS; LangChain, OpenAI Agents, Anthropic, CrewAI on Python) auto-derive tool_catalogue_hash from the registered tool list and accept replay context via constructor or per-call kwargs. The hashes are computed once per tool-call (lazy-cached, invalidated on tool registration changes) so the cost stays sub-millisecond
addedVAIP v1 canonical form: RFC 8785 JSON Canonicalization Scheme over the full event minus the signature and canonical_form fields. Brings replayable-evidence fields and free-form metadata under the Ed25519 signature, which v0 did not. Per-event canonical_form: "v1" on the wire lets the server and verifier dispatch correctly; v0 events remain backward-compatible by default. New canonicalPayloadV1 / canonical_payload_v1 helpers exported from both SDKs and @vorim/shared-types
addedHash-chained ingest: enable with chainEvents: true on the SDK and each event for an agent carries prev_event_hash = SHA-256 of the previous event's canonical bytes. Per-agent serialisation inside the SDK ensures concurrent emits to the same agent build a deterministic chain. Chain restart on process restart or forgetAgentKey produces a chain_restart verdict in the verifier (informational); a real chain break (tampered prev_event_hash or sub-range export) produces chain_broken and fails the bundle
added@vorim/verify 0.2.0: per-event canonical_form dispatch (v0 vs v1), chain validation that walks events in (agent_id, timestamp) order and tallies chain_intact / chain_restart / chain_broken on VerifyReport. Suppresses chain fields when the bundle has no chained events so non-chained bundles report identically to 0.1.0. New tests cover the property that v0 and v1 events coexist in the same bundle (per-event dispatch), so bundles spanning an SDK upgrade window verify cleanly
addedaudit_events schema: model_version (VARCHAR 128), tool_catalogue_hash / system_prompt_hash / prev_event_hash (VARCHAR 72 each, sha256: prefix), canonical_form (VARCHAR 8 with CHECK ("v0" or "v1" or NULL)). Migration applied to production. Two partial indices on (org, model_version) and (org, tool_catalogue_hash) for replay queries. prev_event_hash deliberately unindexed (chain validation is timestamp-ordered, no hash lookup)
changedAudit export bundle now reports canonical_form as "v0", "v1", or "mixed" based on what its events contain (informational; verifier dispatch is per-event). Each event's prev_event_hash and canonical_form fields are included in the bundle so a verifier can reproduce the chain walk and pick the right signature recipe per event
changedCross-language canonical-form parity script (scripts/check-replay-parity.sh) extended to assert byte-identical v1 output across Python and TypeScript SDKs for a representative event with replayable-evidence fields and metadata. Locks in v0, v1, tool catalog, system prompt, and JCS canonicalisation as a single CI guard
changedWhite paper "What Your Agent Audit Cannot Prove" (vorim.ai/whitepaper, June 2026) Section 7 row 12 (Replayable decision evidence) upgraded from PARTIAL to HAS_FEATURE based on the shipped 3.2.0 / 3.5.0 / 0.2.0 packages. Executive summary updated to reflect two of three "absent from all eight vendors" gaps now closed by Vorim (the third, multi-hop attenuated delegation, ships next)
changedDefault SDK behavior preserved: existing @vorim/sdk 3.1.x and vorim 3.4.x users see no change without opting into the new fields, canonicalForm: "v1", or chainEvents: true. Stripe ACP integration deliberately unchanged (no LLM in the flow, so the new fields are not meaningful for it)
v3.8.0June 4, 2026
Per-Event Signing by Default, Offline Verifier & Live Demo
Audit events are now signed at source by the Vorim SDKs, by default, without a code change. A new pure-Node verifier @vorim/verify reads an exported bundle and produces a clean PASS/FAIL report offline, with no Vorim API call. A live in-browser demo at /verify-demo walks a visitor through register → emit → export → verify in five steps so they can prove the round-trip without trusting Vorim. The audit export endpoint now embeds each agent's public key in the bundle so verification is self-contained. Public Starter and Growth pricing reverted to "Custom" pending an A/B test of conversion; existing customers keep their existing pricing. Published @vorim/sdk 3.1.0, vorim 3.4.0, and @vorim/verify 0.1.0.
added@vorim/sdk 3.1.0 auto-signs every audit event at source with the agent's Ed25519 private key. register() and registerEphemeral() cache the returned key in an in-memory keyring; emit() and emitBatch() attach the signature transparently. New useAgentKey() and forgetAgentKey() restore or revoke keys across process restarts. Opt out per call with { sign: false } or globally with autoSign: false. All six framework integrations (LangChain, OpenAI, Anthropic, CrewAI, LlamaIndex, Stripe ACP) inherit signing with zero code change
addedvorim 3.4.0 (PyPI) brings full parity to the Python SDK on both Vorim and AsyncVorim. Same auto_sign config, use_agent_key / forget_agent_key methods, and per-call sign=False opt-out. Adds cryptography>=42 as a runtime dependency (Python stdlib has no Ed25519 primitive)
added@vorim/verify 0.1.0 — new offline verifier CLI + library. Pure Node.js, zero runtime dependencies, no network call, no telemetry. vorim-verify bundle.json returns exit code 0 on a clean bundle and 1 if any signature fails or the manifest mismatches. --json for machine-readable output, --explain for per-event verdicts. The README is honest about what offline verification proves (signature binding, bundle integrity) and what it does not (identity beyond the embedded key, freshness, missing events)
addedLive in-browser demo at /verify-demo: generate an Ed25519 keypair via Web Crypto, emit three signed audit events, build a bundle with a SHA-256 manifest, verify it offline in the page, then tamper with one byte and watch verification fail. Footer prompts the visitor to reproduce the same check locally with npx @vorim/verify so the page is auditable, not a trust ask
changedPOST /v1/audit/export bundle now self-contained: includes bundle_version (vaip-v0), canonical_form spec, an agents[] array with each referenced agent's public key, and a SHA-256 manifest over events + agents. A verifier never needs to call the API to check signatures, which is what makes offline verification meaningful
changedCanonical signing bytes (event_type|action|resource|input_hash|output_hash|result) are now defined once in @vorim/shared-types and imported by the API. The TypeScript SDK keeps a local copy to preserve zero runtime dependencies; a parity test in the SDK suite asserts byte-equality with the shared-types definition across a fixture matrix so drift fails CI before it ships
changedPublic pricing on /pricing reverted: Starter and Growth shown as "Custom" pending an A/B test of conversion. Mirrored across in-app settings (existing Starter/Growth customers still see their actual price; only Free/no-plan users see "Custom"), the public FAQ, Terms of Service, the AI chatbot system prompt, WebMCP vorim_get_plans, the trial-expiring transactional email, and the docs "How Pricing Works" section. Enforcement limits in billing.ts unchanged
fixedStale subscription plans in services/web/public/terms.html (showing $49 Starter / $499 Growth from an older pricing iteration with wrong event/agent limits) corrected to match the current billing.ts limits and the new "Custom" policy. Closes a legal-document accuracy miss
v3.7.3June 2, 2026
Instant Revocation Propagation & Opt-In Audit Signature Verification
Two infrastructure improvements to the permission and audit paths. Revoking an agent now invalidates its cached permission decisions immediately, closing a window where a revoked agent could still be allowed by a cached check until the 5-minute Redis TTL expired. Server-side Ed25519 verification of audit-event signatures is now available behind an opt-in environment flag, so signed events become server-verified rather than only stored alongside the signature. Also closes three PostHog funnel gaps (oauth_completed/failed, quickstart_viewed, first_ws_connection) so the activation funnel is fully observable.
fixedAgent revocation now busts the Redis permission cache immediately. Previously a revoked agent could still be allowed by a cached permission decision for up to the cache TTL (5 minutes); revocation is now effective on the next request
addedServer-side Ed25519 signature verification on POST /v1/audit/events, gated on VORIM_VERIFY_AUDIT_SIGNATURES. Off by default so existing clients are not broken. When on, events with present-but-invalid signatures are rejected; unsigned events still ingest. Uses a v0 pipe-joined canonical form (event_type|action|resource|input_hash|output_hash|result) ahead of an RFC 8785 JCS payload in a future VAIP revision
addedActivation-funnel events in PostHog: oauth_completed, oauth_failed, quickstart_viewed, first_ws_connection. Closes the previously dark stretch between signup and first SDK call so we can see exactly where new users drop off
v3.7.2May 29, 2026
WebMCP Tools, Tool-Count Corrections & SDK/CLI Fixes
Vorim.ai now registers read-only WebMCP tools so agent-capable browsers can query public product, SDK, permission-scope, and plan information directly from the site. Corrected a stale "13 tools" count to the accurate 17 across the MCP manifest, marketing pages, and the published @vorim/mcp-server README. Fixed a TypeScript type error in the Stripe ACP integration, removed an inaccurate "Python" label from the LlamaIndex integration, and made SDK/CLI version strings dynamic so they stop going stale. Published @vorim/mcp-server 1.1.3, @vorim/sdk 3.0.1, and vorim 3.3.1.
addedWebMCP tools registered at page load via navigator.modelContext — vorim_get_product_overview, vorim_list_sdks, vorim_list_permission_scopes, vorim_get_plans. All read-only and public; no-op in browsers without a WebMCP runtime. New /.well-known/webmcp.json discovery manifest
fixed/.well-known/mcp.json manifest corrected from a stale 13-tool list to the accurate 17 tools, with correct vorim_ prefixes matching the MCP server (was advertising fewer tools than the server actually exposes, with names that did not match)
fixedStale "13 tools" copy corrected to 17 across the Integrations, Features, and Blog pages, and the published @vorim/mcp-server README (the server has shipped 17 tools since v3.0.0)
fixedStripe ACP integration (@vorim/sdk) TypeScript type error — the SDK type was imported as a value and used as a type, which broke type-checking for consumers. Published in @vorim/sdk 3.0.1
fixedLlamaIndex integration mislabeled as "TypeScript + Python" on /integrations — corrected to TypeScript only (no Python LlamaIndex module exists)
fixedPython SDK User-Agent header now reports the installed package version dynamically instead of a hardcoded, stale string. Published in vorim 3.3.1
fixedTypeScript SDK and CLI version strings made dynamic / corrected so they no longer drift from the published package version
fixedCLI public commands (verify, status) no longer require an API key — trust verification and health checks are public endpoints and now work without authentication
changedPublished package updates: @vorim/mcp-server 1.1.3, @vorim/sdk 3.0.1, vorim (PyPI) 3.3.1
v3.7.1May 19, 2026
Site-Wide Premium Pass, Docs Redesign & Book a Demo
Premium obsidian icon treatment extended to /features, /security, /compliance-info, /contact, and /enterprise. Docs page restructured with grouped sidebar, prominent top search bar, and the new /documentation preview structure for the phased migration. Site-wide CTAs renamed from "Book a Call" to "Book a Demo." New ambient hero element (slowly rotating concentric rings) hidden on mobile to fix headline overlap. Agent lifecycle section rebuilt as five stacked cards to fix the scroll-stuck bug. Slower, more confident transition timing site-wide.
changedPremium pass on /features — calmer hero (no gradient text, no decorative diagonals), 64×64 obsidian icons on the three core cards, 48×48 obsidian icons on the 12-card More Features grid, monospace detail chips
changedPremium pass on /security — quieter hero, 56×56 obsidian icons on the 6 practice cards, slower hover timing
changedPremium pass on /compliance-info — quieter hero, 52×52 obsidian icons on capability cards with hover lift
changedPremium pass on /contact — quieter hero, 52×52 obsidian icons on contact info cards
changedPremium pass on /enterprise — quieter hero, 52×52 obsidian icons on value props grid
changedDocs page (/docs) restructure — compact white hero replacing full-screen black, three install lozenges instead of three stacked code blocks, grouped sidebar (Get Started, Framework Integrations, Protocols, SDK Reference, Advanced, Reference) replacing flat 22-item list
addedTop-of-page search bar on /docs with ⌘K key hint, opens the existing search modal. Replaces the sidebar search input which is now removed
addedNew /documentation preview structure with shared DocsLayout (sticky sidebar, scrollspy, prev/next nav, mobile drawer). 6 routes live as placeholders pending content migration: /documentation, /documentation/quickstart, /documentation/integrations, /documentation/sdk, /documentation/protocols, /documentation/advanced
addedAmbientMark component on homepage hero — three concentric rings (two rotating in opposite directions, one static Vorim V at center). Hidden below 880px viewport to prevent headline overlap on mobile
addedAuditChain component replaces the single-line ticker on the homepage hero. Animated horizontal flow of hash-linked event blocks (agent_id, action, result, SHA fragment, latency). New block enters every 3s, oldest fades
addedFilm-grain texture utility class (.brand-noise) in global.css. Applied to homepage hero, framework logos bar, and code preview section for material surface depth
changedAgent lifecycle section rebuilt as five stacked cards (Provision, Scope, Act, Audit, Revoke) instead of scroll-bound pinned panel. Fixes the "stuck while scrolling" bug on common screen sizes
changedAll site-wide CTAs renamed "Book a Call" → "Book a Demo" — navbar (desktop + mobile), homepage, /about, /features, /contact, /pricing (all 3 plans), /partners, /demo, /integrations, /enterprise, /agent-identity-scorecard, /eu-ai-act-scorecard, /case-study/fintech-soc2, login page badge. Analytics event names unchanged for continuity
changedSite-wide transition timing slowed — --brand-motion-fast 120ms→200ms, --brand-motion 180ms→400ms, --brand-motion-slow 280ms→700ms. Button hover transitions extend to 400–600ms with Linear easing curve
changedHero copy on homepage — removed "Identity layer for AI agents" eyebrow row, "Three lines of code" → "3 lines of code", primary CTA now opens with the headline directly
changedEnterprise stats grid now shows 4 cards instead of 3 — added "Aug 2026 — EU AI Act high-risk obligations apply" with source citation Regulation 2024/1689
changedAbout values icons given premium obsidian treatment (84×84) with multi-stop graphite gradient, top edge reflection, diagonal gloss sweep, engraved icons with drop-shadow
changedCareers /careers icons on How We Work given the same obsidian treatment (56×56). Roles dropdown collapsed from 12 individual roles to 6 grouped categories with <optgroup> headers. Form heading "Send Us Your Resume" → "Tell Us About Yourself." What We Offer perks section hidden until actively hiring
addedNew /press page with downloadable press kit (vorim-press-kit.zip — 4 SVG logo variants + brand guide). Linked from navbar Company dropdown and footer Company column. Includes Machine Brief (April 2026) and WORLD INSIGHT (April 2026) coverage
fixedWorld Insight press card date corrected — was showing May 2026, actually published April 8, 2026 (last updated April 14, 2026)
fixedPress cards reordered newest-first — Machine Brief (April 15) above World Insight (April 8)
v3.7.0May 19, 2026
Press Page, Premium Icons, Long-Form SEO Pages & Hero Animations
New /press page with downloadable press kit and links to recent coverage (Machine Brief, World Insight). Premium obsidian icon treatment rolled out across homepage features, /about values, and /careers. Three long-form SEO pages shipped (EU AI Act checklist, CrewAI audit logs walkthrough, fintech SOC 2 case study). Homepage gets a coordinated set of motion primitives (staggered hero reveal, magnetic CTA, parallax grid, count-up stats, section dividers, live audit log ticker).
addedNew /press page — recent coverage cards (Machine Brief, World Insight), downloadable press kit zip (logos in SVG + brand guide), and press@vorim.ai contact section. Linked from navbar Company dropdown and footer Company column
added/vorim-press-kit.zip — four logo variants (mark + wordmark, black + white) plus a brand guide covering colors, naming, usage rules, and 80-word company boilerplate
addedNew /eu-ai-act-compliance-checklist long-form page (~3,500 words) — Articles 9, 10, 11, 12, 13, 14, 15, 26 + Article 99 penalties, engineering checklist per article, every citation verified against the consolidated regulation text. Funnels to existing /eu-ai-act-scorecard
addedNew /crewai-audit-logs technical post — working code using the real @vorim/python-sdk CrewAI integration (register_crew, vorim_tool decorator). Cross-post target for r/CrewAI, r/LangChain, dev.to
addedNew /case-study/fintech-soc2 page — anonymised representative case study mapping Vorim controls to 8 AICPA Trust Services Criteria (CC6.1, CC6.3, CC7.2, CC7.3, CC4.1, CC2.1, A1.2, C1.1) for SOC 2 Type II audits
addedHero motion primitives on homepage — StaggerReveal (sequenced fade-in), AuditLogTicker (live faux audit log beneath stats), MagneticButton (cursor-pull on primary CTA), ParallaxGrid (10% scroll speed on hero grid), CountUp (stats animate on scroll-into-view), SectionDivider (line grows left-to-right between sections)
addedSite-wide spring easing baseline in global.css — every button, anchor, and role="button" now uses Linear's cubic-bezier(0.32, 0.72, 0, 1) for hover transitions
changedPremium obsidian icon treatment rolled out — homepage features bento (6 cards + stat pills), /about values (4 cards), /careers How We Work (4 cards). Multi-stop graphite-to-black gradient, hairline top reflection, diagonal gloss sweep, layered shadows, engraved icons at 1.5px stroke
changed/careers copy — "We're Not Actively Hiring Right Now" replaced with "Always Open to Exceptional People." Resume form heading changed from "Send Us Your Resume" to "Tell Us About Yourself." "What We Offer" perks section hidden (commented out) until we're actively hiring
changed/careers area of interest dropdown — collapsed from 12 individual roles to 6 grouped categories under <optgroup> headers (AI, Engineering, R&D, Design, Sales & Marketing, Support, + Other). Label changed from "Role of Interest" to "Area of Interest"
changedHomepage hero copy — "Three lines of code" replaced with "3 lines of code" to match the rest of the homepage messaging
added/press, /eu-ai-act-compliance-checklist, /crewai-audit-logs, and /case-study/fintech-soc2 added to sitemap.xml for search indexing
v3.6.3May 17, 2026
In-App Pricing Hidden, Marketing Hero Refresh
In-app /settings now hides Starter and Growth prices behind "Custom" to match the public /pricing page. Stripe Checkout still shows real prices and self-serve payment still works. Homepage hero restyled for premium Linear/Vercel aesthetic.
changedIn-app /settings now shows Starter and Growth as "Custom" (was $49 and $499). Stripe Checkout still shows real prices and self-serve upgrade still works — checkout uses plan slug, not displayed price
changedHomepage hero restyled — left-aligned, stripped visual noise (grid bg, gradient text, accent lines), generous whitespace, three monospaced stats: Open Protocol · 9 frameworks · <5ms permission check
addedBrand token system in global.css (--brand-*) for marketing pages — monochrome palette, premium typography scale, Linear-style cubic-bezier transitions
v3.6.2May 16, 2026
Support Chatbot (Claude Haiku 4.5) + Admin Tab
Floating support assistant on every page of vorim.ai. Answers product, pricing, and integration questions from a hand-written system prompt; captures email leads opportunistically and emails Kwame the transcript. New admin tab surfaces every conversation for follow-up.
addedChatWidget component mounted globally — floating launcher bottom-right, opens to a 380px chat panel on desktop and full-screen on mobile
addedPOST /v1/chatbot/message — anonymous, Claude Haiku 4.5 backend, sub-second responses with 12-message rolling history
addedPOST /v1/chatbot/email — opportunistic email capture, triggers transcript notification to team@vorim.ai
addedchatbot_conversations and chatbot_messages tables — per-session conversation history persisted for review
addedEmail transcript notification template — Kwame gets the full conversation when a visitor drops their email
addedAdmin /admin → Chatbot tab — paginated list of all conversations (email captured filter, click any row for full transcript drawer with mailto: reply)
addedGET /v1/admin/chatbot-conversations and /v1/admin/chatbot-conversations/:id — admin-only endpoints powering the new tab
v3.6.1May 16, 2026
@vorim/agent-audit Released & Starter Moved to Sales-Led
New free CLI for AI agent code hygiene published on npm. Starter tier moved to sales-led pricing on the public site (in-app /settings unchanged for billing).
added@vorim/agent-audit v0.1.0 published to npm — free CLI for AI agent code hygiene. Public repo at github.com/Vorim-AI-Labs/vorim-agent-audit
changedStarter tier on public /pricing now shows "Custom" instead of "$49/month" — moves Starter to sales-led pricing. In-app /settings keeps $49 for billing logic
changedStarter CTA changed from "Start Free" to "Book a Call" to match the Custom-pricing pattern
changedStarter tier "Priority email support" renamed to "Priority support" on /pricing and in-app /settings — channel-agnostic, no functional change
v3.6.0May 13, 2026
Pricing Reconciliation, Free Tools Hub & Conversion Instrumentation
Public and in-app pricing now match (Free, Starter, Growth, Enterprise). Growth tier repriced for serious buyers. Scorecards surfaced on the homepage. New PostHog conversion events to actually see what users do.
changedPublic /pricing now mirrors the in-app /settings plan picker: Free, Starter ($49), Growth (from $499), Enterprise. Starter restored, "Team" naming dropped
changedGrowth tier repriced from $199 to from $499/month. Stripe price object updated, no existing customers to grandfather
changedGrowth tier support upgraded from "Priority email support" to "24-hour response SLA"
changedStarter tier support upgraded from "Email support" to "Priority email support"
addedPricing page "Not sure which plan?" escape valve — email team@vorim.ai or book a 15-min call via cal.com
addedNew pricing_help_clicked PostHog event — tracks email vs cal destination so we can see which low-commitment path converts
addedHomepage "Free Tools" section — surfaces EU AI Act Scorecard and Agent Identity Scorecard above the bento grid
addedscripts/vorim-maintenance.sh — weekly cron for Docker prune, log rotation, disk-space alerts
addedscripts/collect-metrics.sh and scripts/sdk-downloads-report.sh — weekly npm + PyPI download tracking
improvedHomepage scorecard card icons unified to black background + white icon for visual consistency
improvedAdmin revenue MRR map updated for the new Growth price point
v3.5.0May 9, 2026
Compliance Scorecards: EU AI Act + Agent Identity
Two free 10-question scorecards graded live by Claude Sonnet. Personalized PDF report, email gate, and a 3-step nurture sequence (D0, D2, D5) ending at cal.com.
addedEU AI Act Scorecard (/eu-ai-act-scorecard) — 10 questions mapped to high-risk obligations, for CISO, GRC, legal, platform leads at regulated companies
addedAgent Identity Scorecard (/agent-identity-scorecard) — 10 technical questions on identity, scope, audit, revocation for staff engineers and security architects
addedcompliance.ts service — multi-category grader (eu_ai_act, agent_identity) backed by Claude Sonnet with deterministic fallback
addedCompliance routes: POST /v1/compliance/grade, POST /v1/compliance/lead, POST /v1/compliance/leads/:id/pdf-downloaded
addedD0 email — immediate scorecard result + Book a Call CTA. Sent on lead capture
addedD2 follow-up email — "what teams in your band do next", with cal.com link
addedD5 nudge email — last touch, idempotent via d5_sent_at column on compliance_leads
addedEmail scheduler jobs processScorecardD2Emails and processScorecardD5Emails wired into runEmailScheduler()
addedCompliance leads admin tab — category-filtered view with PDF download status, source attribution
addedMigrations: 2026-05-09-compliance-scorecard.sql, compliance-category.sql, compliance-followup-emails.sql
addedHomepage and Navbar Resources dropdown and Footer Product column all link to both scorecards
improvedllms.txt and sitemap.xml updated with both scorecard URLs
v3.4.0May 2, 2026Major Release
OAuth Login, MCP Server & Distribution Surfaces
Google and GitHub OAuth login, public MCP server in @vorim/mcp-server with 13 tools, OpenClaw skill submission, and static legal pages required for Google OAuth verification.
addedGoogle OAuth 2.0 login flow — sign in with Google on /login and /register, profile auto-populated
addedGitHub OAuth 2.0 login flow — sign in with GitHub, same surface as Google
addedoauth.ts service — token exchange, user matching, account linking for both providers
added/oauth-callback route — handles the redirect, sets session, sends new users to /quickstart
addedMigration 2026-05-02-oauth.sql — oauth_providers and oauth_accounts tables with provider+subject unique constraint
added@vorim/mcp-server — Model Context Protocol server packaged for Claude Desktop, Cursor, and other MCP clients
addedMCP server ships 13 tools: register agent, list agents, grant permission, verify identity, emit audit event, query events, and more
addedpackages/mcp-server with smithery.yaml and server.json for marketplace listings
addedOpenClaw skill (packages/openclaw-skill/) submitted to ClawHub marketplace
addedStatic /privacy and /terms HTML pages for Google OAuth app verification (privacy.html, terms.html in public)
addedllms.txt and agents.md at the site root — AI-discoverable summary of products, repos, and SDKs
added.well-known/ directory for protocol discovery (mcp.json, ai-plugin.json)
addedTrust badge widget served from /widget/ for embedding on third-party sites
improvedCORS and CSP updated to whitelist Google and GitHub OAuth domains
v3.3.0April 15, 2026Major Release
Platform Integrations, Admin Dashboard Expansion & New Pages
Google A2A Protocol integration, Pydantic AI integration, CLI tool, interactive playground, enterprise page, 10 new admin tabs, and subscriber email capture.
added@vorim/a2a — identity and trust layer for Google A2A Protocol. Extends Agent Cards with Ed25519 identity, live trust scoring, and permission verification
addedPydantic AI integration — VorimDeps dependency injection, vorim_tool decorator for automatic permission checks and audit logging
added@vorim/cli — CLI tool for terminal-first developers. npx @vorim/cli init scaffolds projects in 60 seconds
addedInteractive Playground (/playground) — try agent identity in the browser without signing up
addedEnterprise page (/enterprise) — qualification form for enterprise teams with compliance, self-hosted, SSO value props
addedIntegrations page (/integrations) — lists all 8 framework integrations, 2 protocols, 2 SDKs, and public APIs
addedAdmin: Activity tab — platform-wide event feed with filters across all organizations
addedAdmin: Analytics tab — outcome breakdown, hourly chart, top actions, most active agents, permission denials
addedAdmin: System Health tab — DB/Redis latency, connection count, error rate with auto-refresh
addedAdmin: API Keys, Email Logs, Revenue, Feature Flags, Announcements tabs
addedAdmin: Global search across users, agents, organizations, and events
addedAdmin: Agent detail modal — click any agent to see permissions, events, delegations
addedSubscriber email capture — whitepaper PDF gate and blog newsletter now store emails with source tracking
addedBlog posts: A2A Protocol integration announcement, Pydantic AI integration tutorial
improvedHomepage: interactive playground replaces old demo section, CLI added to install options
improvedHomepage: Pydantic AI and A2A Protocol added to framework logos bar
fixedMobile PDF download — whitepaper now prints all sections, not just executive summary
fixedAdmin SQL queries — fixed column name mismatches (outcome→result, created_at→timestamp, action_type→action)
v3.1.0April 14, 2026
Whitepaper Expansion, State Laws & UI Improvements
Whitepaper updated with 7 diagrams, credential delegation and ephemeral identity sections, 6 US state laws in compliance mapping, and pricing updated to Book a Call.
addedWhitepaper: 7 inline SVG diagrams (architecture, agent lifecycle, hash chain, trust score, delegation flow, multi-hop, ephemeral lifecycle)
addedWhitepaper: Sections 5-7 (Credential Delegation, Ephemeral Identity, Open Standards) with code examples
addedWhitepaper: email gate for PDF download — captures subscriber email before generating PDF
addedWhitepaper: reading progress bar and back-to-top button
addedCompliance mapping: Connecticut SB 1103, NYC LL144, California SB 942, Utah columns added
addedSection 2.5: expanded regulatory pressure with 6 US state laws detailed
addedClaude/Anthropic added to framework integrations in whitepaper and docs
addedVorim logo added to all email templates (9 headers, 5 footers)
changedPricing: paid plans replaced with "Book a Call" for value-based pricing
changedLogin: organization registration redirects to cal.com booking page
changedAll "Book a Demo" buttons renamed to "Book a Call" across the site
fixedWhitepaper Fig. 1, 2, 5, 7 diagram text overlap issues
v3.0.0April 11, 2026Major Release
Credential Delegation & Ephemeral Identity
Major release with OAuth credential delegation for agents, W3C did:key ephemeral identity, cascading revocation, and VAIP protocol v3.0.0.
addedCredential delegation — agents can safely access OAuth providers (Google, GitHub, Slack, etc.) through scoped, time-limited delegations without ever seeing refresh tokens
addedOAuth provider management — register, configure, and manage OAuth provider connections per organization
addedOAuth connection storage — AES-256-GCM encrypted refresh token vault with per-connection key isolation
addedMulti-hop delegation chains — Agent A can delegate to Agent B with scope attenuation and depth limits
addedCascading revocation — revoking a connection or delegation propagates to all downstream chains
addedCredential audit trail — immutable log of every delegation grant, revoke, token use, and error
addedEphemeral agent identity — W3C did:key format for short-lived agents with configurable TTL (60s to 24h)
addedEphemeral agent cleanup job — auto-expires agents past their TTL every 5 minutes
addedToken request endpoint — agents request short-lived access tokens, platform proxies the OAuth refresh
addedSDK methods: registerEphemeral(), registerProvider(), delegateCredential(), requestToken(), and more
addedVAIP protocol v3.0.0 — credential delegation and ephemeral identity extensions added to specification
addedIETF Internet-Draft updated with credential delegation section and Cred protocol reference
v2.5.0April 11, 2026
Interactive Demo, Partners Page & Mobile Improvements
Interactive live demo on homepage, partners page with inquiry form, mobile navbar scroll fix, and demo recording page.
addedInteractive live demo on homepage — 5-step animated terminal showing agent registration, permissions, audit, and trust verification
addedDedicated demo page (/demo) — full-screen, distraction-free layout optimized for screen recording
addedPartners page (/partners) — partner inquiry form with Technology, Solutions, and Channel partner types
addedPartner form notifications — submissions sent to team@vorim.ai via Fastmail SMTP
addedNewsletter subscribe on blog — email capture with notification to admin
improvedMobile navbar — full-height scrollable menu with 120px bottom padding, visible even with cookie banner
improvedInteractive demo auto-scrolls to CTA button on mobile after completion
improvedDark mode toggle — now shows sun/moon icon with "Light Mode" / "Dark Mode" label
fixedMobile menu Book a Call and Sign In buttons hidden behind cookie banner
v2.4.0April 7, 2026
Premium UI Redesign & Mega Navigation
Complete frontend redesign across all marketing pages with premium design system, mega dropdown navigation, and consistent visual language.
changedHomepage — complete rewrite with gradient text headlines, noise texture overlays, geometric accents, bento feature grid, and atmospheric CTA sections
changedAll marketing pages (Features, Pricing, About, Security, Contact) redesigned with matching premium design system
changedNavigation — flat links replaced with mega dropdown menus (Solutions, Resources, Company) with descriptions per page
addedFramework integration logos bar with real SVG logos (LangChain, OpenAI, CrewAI, Claude, LlamaIndex)
addedCode preview section with macOS-style terminal chrome (colored traffic lights, filename tab)
addedScroll-triggered fade-in animations on all sections with staggered delays
improvedFeature cards — bento grid layout with 2 large + 4 small cards, stat badges, hover lift with icon scale and rotate
improvedPricing cards — uniform white styling, removed "Recommended" tag, hover lift effects
improvedAdmin dashboard — dark mode text visibility fixed using theme-aware useColors() hook
v2.3.0April 1, 2026
Email Lifecycle, Blog Routing & SOC 2 Cleanup
Automated email lifecycle system, blog deep-linking via React Router, newsletter subscribe, and removal of SOC 2 overclaims.
addedEmail lifecycle scheduler — quickstart guide (after verification), 3-day check-in, 7-day check-in, 14-day tips, 30-day feedback, trial expiring emails
addedBlog deep-linking — individual posts accessible via /blog/:slug with React Router
addedSMTP email system activated on production (Fastmail) — contact, careers, welcome, verification, password reset emails
addedClaude (Anthropic) added to supported framework integrations across FAQ, docs, and SDK
added6 new FAQ questions — free trial, integration time, self-hosting, encryption, multi-agent, cancellation
addedFAQ section link in footer
addedBook a Call button in navbar linking to cal.com
changedRemoved SOC 2 Type II overclaims from pricing, terms, FAQ, and settings — replaced with "compliance-ready audit exports"
changedPricing headline changed to "Pricing That Scales With You"
changedUse cases page hidden — route commented out, footer link removed
changedHomepage badge changed from "Open Protocol" to "Open Source · Production-Grade Platform"
fixedEmail template text visibility — all emails now use branded template with explicit color styling
fixedOG image changed from SVG to PNG for Twitter/X card compatibility
fixedCookie consent text changed from "analyze" to "analyze"
v2.2.0March 30, 2026
Dark Mode, Email Verification & Rate Limiting
Added dark mode toggle, email verification on registration, brute-force protection on auth endpoints, and webhook notifications.
addedDark mode with automatic system preference detection and manual toggle
addedEmail verification on registration — verification email with 24-hour token
addedRate limiting: 5 login attempts/15min, 3 registrations/hour, 3 resets/hour per IP
addedWebhook notifications — create alert rules, filter by event type, toggle on/off
addedTeam invites — invite by email, accept invite page, manage members in Settings
addedCookie consent banner with Google Analytics consent mode v2
improvedMobile dashboard — horizontal scrolling tables, touch-friendly interactions
v2.1.0March 29, 2026
Password Reset, 404 Page & Usage Tracking
Complete password reset flow, proper 404 page, plan usage tracking with progress bars, and loading skeletons.
addedPassword reset flow — forgot password email, reset page with token validation
addedCustom 404 page with navigation buttons
addedUsage tracking — agents and events progress bars in Settings with plan limits
addedLoading skeleton components for dashboard stats
addedQuick Start page (/quickstart) — dedicated page with SDK examples
improvedNew users redirect to Quick Start after registration
improvedDashboard quickstart moved to compact banner to reduce clutter
v2.0.0March 28, 2026Major Release
Stripe Billing, Admin Dashboard & Agent Activity
Major release with Stripe payment integration, platform admin dashboard, agent activity feeds, and comprehensive testing.
addedStripe billing integration — checkout sessions, customer portal, 14-day trial
addedPlan enforcement — agent limits per plan (3/10/50/unlimited), trial expiry auto-downgrade
addedPlatform admin dashboard — cross-tenant user, org, agent management
addedAdmin actions: ban/unban users, delete accounts, promote/demote admins
addedAgent detail modal with Activity, Permissions, and Integrate tabs
addedPost-registration quickstart with SDK code snippets
addedSession persistence — auth tokens in localStorage survive page refresh
addedSEO meta tags, Open Graph, Twitter cards, sitemap.xml, robots.txt
addedGoogle Analytics with consent mode
addedContact and careers forms send email notifications
fixedAgent revoke FK constraint error when using API key auth
fixedGET /organisations/me double-pathed route (404)
fixedAudit "All" filter showing no events (URLSearchParams undefined bug)
fixedAgent list SQL — revoked agents from other orgs leaked across tenants
improved146 unit tests across 7 test files, all passing
v1.5.0March 27, 2026
Production Deployment & US AI Compliance
First production deployment to AWS EC2. Added US AI law compliance references across protocol and platform.
addedProduction deployment infrastructure — EC2, Docker Compose, nginx, SSL, Certbot
addedUS Executive Order on AI (EO 14110) compliance references
addedUS State AI Laws (Colorado, Illinois, Texas, California) in SPEC and compliance page
addedGitHub Actions CI/CD pipeline (deploy on push to main)
changedRemoved self-hosting documentation and waitlist (app is live)
improvedHomepage CTA changed from waitlist to direct registration
v1.4.0March 12, 2026
Free Plan & Blog Launch
Introduced a generous free tier, launched the Vorim AI blog, and added all legal and compliance pages.
addedFree plan: 3 agents, 10K audit events/month, all 7 permission scopes, 30-day retention, full SDK access
addedBlog with 5 in-depth articles on agent identity, trust protocols, permissions, audit trails, and standards
addedPrivacy Policy, Terms of Service, Security, Compliance, and Careers pages
changedStarter plan upgraded to 10 agents (from 5), Growth plan upgraded to 50 agents (from 25)
improvedPricing page now displays 4-tier grid: Free, Starter, Growth, Enterprise
improvedHomepage and FAQ updated to highlight free plan benefits
v1.3.0March 8, 2026
SDK npm Publishing & Documentation Search
Made @vorim/sdk publishable to npm as a zero-dependency, self-contained package. Added search to documentation.
added@vorim/sdk now builds with tsup — dual ESM/CJS output with inlined type declarations
addedSDK types inlined from @vorim/shared-types for zero-dependency npm installation
addedDocumentation search: filter sections by keyword with instant results
addedSDK README with Quick Start guide, API reference, and error handling examples
fixedSDK audit event validation — corrected event_type and result field mappings
improvedDocumentation code examples now match actual SDK API surface
v1.2.0March 5, 2026
API Key Auth & Permission Fixes
Fixed API key authentication across all routes and resolved foreign key constraint issues for agent operations.
fixedAPI key auth now resolves org owner for foreign key constraints on permissions.granted_by
fixedapi_keys.key_prefix column widened from VARCHAR(12) to VARCHAR(24) for agid_sk_live_ prefix format
fixedExpress 5 req.params type handling — String() wrapping on all route parameter accesses
improvedAll 7 SDK operations verified end-to-end: register, get, list, grant, check, emit, verify
improvedTrust API /trust/verify/:agentId and /trust/badge/:agentId.svg confirmed working
v1.1.0February 28, 2026
Trust Scoring & Public Verification
Launched the public Trust API with real-time scoring, SVG badges, and unauthenticated verification endpoints.
addedTrust scoring algorithm: 5-factor weighted computation (status, age, success rate, denials, scope breadth)
addedGET /trust/verify/:agentId — public, unauthenticated agent identity and trust verification
addedGET /trust/badge/:agentId.svg — embeddable SVG trust badges with real-time score
addedTrust scores update in real-time based on agent behavior and permission check history
improvedDashboard trust score display with visual indicators and factor breakdown
v1.0.0February 20, 2026Major Release
Vorim AI Public Beta
Initial public beta release of the Vorim AI platform — agent identity, permissions, audit trails, and the web dashboard.
addedAgent registration with Ed25519 cryptographic keypairs and SHA-256 fingerprints
added7 hierarchical permission scopes: read, write, execute, transact, communicate, delegate, elevate
addedTime-bounded permissions with rate limits and conditional constraints
addedImmutable audit event logging with TimescaleDB and SHA-256 hash chaining
addedSigned audit bundle export with cryptographic manifests
addedWeb dashboard: agents, permissions, audit log, compliance reports, settings
added@vorim/sdk TypeScript client for agent operations
addedAPI key management for server-to-server authentication
addedMulti-tenant architecture with organization-scoped data isolation
addedDocker Compose deployment with PostgreSQL, Redis, and Kafka
v0.9.0February 10, 2026
Pre-Release: Dashboard & Marketing Site
Built the complete web dashboard and marketing site ahead of public beta.
addedDashboard pages: Overview, Agent Registry, Permissions, Audit Log, Compliance, Settings
addedMarketing site: Homepage, Features, Pricing, About, Contact, Documentation
addedResponsive design with mobile sidebar navigation
addedJWT authentication with access/refresh token rotation
addedReal-time WebSocket events for dashboard updates
v0.5.0January 15, 2026
Core API & Database Schema
Implemented the core API routes, database schema, and service layer architecture.
addedExpress 5 API with Zod validation and pino structured logging
addedPostgreSQL schema: 11 tables with indexes, TimescaleDB hypertable, RLS declarations
addedRedis caching layer for permission checks (sub-5ms latency)
addedKafka event streaming in KRaft mode (no Zookeeper)
addedService layer pattern: identity, auth, permissions, audit, trust, apiKeys