Security architecture

How IsoKron stays safe

A summary of the controls IsoKron uses to isolate tenants, protect credentials, and keep the audit trail tamper-evident. Disclosure channel: security@isokron.ai.

Live compliance posture

Operator-run probes against the audit substrate, the per-tenant isolation layer, and the retention pipeline. Updated when the operator runs the verifier — typically nightly.

  • Hash chain integrity

    not reported

    Operator-side verifier hasn't published a result yet. See RB-FOUNDATION-003.

  • Tenant isolation

    not reported

    Probe results not yet published. CI runs 109 tenant-isolation tests on every PR; see /docs.

  • Audit log retention

    not reported

    Retention SLO target: 90 days hot, 24 months cold. Operator-published verifier shows live numbers when wired.

Four-layer tenant isolation

Cross-tenant leakage is the highest-impact failure mode for a multi-tenant compiler. We assume any single layer can regress; four layers run in series so a leak has to defeat all four.

  1. Postgres Row-Level Security. Every tenant table carries a workspace_id column and a USING clause that compares it to the JWT-supplied org id via current_setting('app.current_workspace_id'). The pool sets this GUC per-connection from the authenticated session.
  2. JWT preHandler at the API gateway. The Clerk middleware verifies the bearer token, extracts org_id, and binds it to the request. Every workspace-scoped route asserts the URL :wsId matches the JWT — a mismatch is a 403 before the handler runs.
  3. MCP host enforcement. The claim endpoint that hands tickets to your agent fleet pins the MCP tool-surface hash at Stage 7. A claim against a workspace the JWT doesn't own, or against a tool surface that drifted, fails closed with HTTP 412.
  4. Worker-side explicit workspace filter. The serviceQuery() wrapper that workers use to bypass RLS is runtime-checked: every call MUST include a workspace_id filter in its SQL, and the wrapper emits a service_role_query audit row each time it's invoked. A tenant- isolation regression test exercises this in CI.

Append-only audit substrate

Every security-relevant action lands in the platform's append-only audit log — a per-tenant chain where each row contains the SHA-256 of the previous row in that tenant's chain. Tampering with an event invalidates the chain from that point forward; chain replay verifies in 60 seconds against an hourly Merkle anchor we publish to immutable storage.

Payloads are normalized with RFC 8785 JSON Canonicalization Scheme (JCS) before hashing — the same JSON serializes to the same bytes on every node, so two readers compute the same chain hash independently. Roles have no UPDATE or DELETE on the audit log; the only state transition is INSERT.

BYOK credentials

Your upstream LLM keys live in Supabase Vault encrypted at rest (per-workspace, AES-256-GCM with a tenant-scoped KEK). Decryption only happens inside the compile worker for the duration of one upstream call. The plaintext lives in a sodium-native SecureBuffer and never becomes a JavaScript String — when the call completes, the buffer's bytes are zeroed before the GC reclaims it.

Operator-side credentials (Layer 4 critic, vendor SDKs) sit behind a separate secret-management surface (Doppler) and never touch the customer credential path. This separation is enforced physically: customer-stage code can read only the Supabase Vault BYOK client; operator-stage code can read only the Doppler client. The two clients live in separate workspace packages and ESLint + Semgrep block cross-imports at PR time.

Logs + observability

Logs use structured JSON and never contain plaintext credentials. The BYOK pipeline only logs the last-4 of a key for correlation; the auth pipeline logs the JWT's jti claim, not the token. The customer-facing payload surface goes through a serializeForCustomer hook that asserts no operator-internal field name (oracle markers, internal verdicts) leaks downstream.

Privileged Postgres functions

Schema migrations use SECURITY DEFINER sparingly; each definer-context function is reviewed at PR time and the migrations index documents the boundary. The validate_event_log_insert guard runs INSIDE the chain advance, not as an after-the-fact trigger, so an attacker who bypasses the application layer still hits the substrate's consistency check.

Recovery Kit

We ship a binary Recovery Kit (audit-log chain verifier + Merkle-anchor reader + DB snapshot replayer) so an organisation can independently verify their chain even if IsoKron the company disappears. Recovery procedures live in the docs.

Translation Layer: where frontier calls execute

IsoKron supports five BYOK provider modes. Four of them (Anthropic, OpenAI, Google AI, xAI) route compile-stage inference straight to the upstream vendor using the API key you stored in Supabase Vault — prompts cross IsoKron servers in transit, vendor sees declaration content under its own privacy policy.

The fifth mode, claude-max-fleet, runs differently. Inference happens on your BYO-fleet host (a machine you operate, typically a Mac with @anthropic-ai/claude-agent-sdk installed and claude login executed). IsoKron sends compile prompts to your fleet host over an authenticated MCP channel; your fleet host resolves your Claude OAuth locally and runs the inference against your Claude subscription's billing. IsoKron servers never hold your Claude OAuth credential.

The auth handshake is yours: you install the SDK on the fleet host, you run claude login, you provision the bearer token IsoKron uses to reach the fleet's MCP endpoint. Rotation is a single claude logout + claude login on your machine; nothing on IsoKron's side needs to re-issue.

Disclosure

Found a vulnerability? Email security@isokron.ai with a clear repro and the affected component. We acknowledge within two business days; coordinated disclosure runs on a 90-day window from acknowledgement. We don't run a bug bounty yet but credit researchers in release notes when they ask to be named.