Zumik
Security & compliance

Tenant isolation

How Zumik namespaces every object by tenant, uses opaque IDs and HMAC fingerprints, and verifies isolation with destructive cross-tenant tests against BOLA and IDOR.

No reusable state is shared across tenants by default. Isolation is enforced at the data layer, not bolted on in application code, and it is verified destructively rather than assumed.

Namespacing every object

Every reusable object is namespaced by tenant ID at the database layer: artifacts, bundles, sessions, branches, snapshots, materializations, KV realizations, traces, replay runs, purge jobs, alias policies, and usage events. Application-layer filtering is never the only line of defense, because a single missing WHERE project_id = should not be able to leak another tenant's data.

Every authenticated request resolves to one project. The handlers operate inside that boundary, and reads are filtered by the caller's project before any object is returned. A request for an object that exists but belongs to another tenant returns a not-found, the same response as an object that does not exist, so existence is not leaked.

Opaque public IDs

Customer-facing handles are random opaque identifiers (art_…, bnd_…, ses_…, br_…, snp_…, rsp_…), never raw content hashes.

Raw content hashes would be dangerous as public handles: customers might treat them as durable capabilities, accidental cross-tenant correlation would get easier, re-uploaded content could resurrect old cache relationships, and a leaked hash could reveal that two systems hold equal content. Opaque IDs carry none of that signal. See handles and fingerprints for the full identity model.

HMAC fingerprints stay internal

Deduplication and trace analysis need to recognize equal content, but that recognition must never escape the tenant boundary. Zumik computes a tenant-scoped keyed fingerprint:

logical_fingerprint = HMAC-SHA256(
  tenant_fingerprint_key,
  canonicalization_version || artifact_type || normalized_content
)

Because the key is per-tenant, two tenants holding identical content produce different fingerprints, so the fingerprint cannot be used as a cross-tenant equality oracle. The fingerprint is used only inside the isolation boundary. Rotating a tenant's fingerprint key intentionally breaks equality linkage, which is part of the resurrection-prevention story for purge.

Sealed secrets reinforce this: the same plaintext sealed twice produces different ciphertext (random nonce per seal), so ciphertext can never be used as an equality oracle either.

Tested, not assumed

Cross-tenant isolation is verified with destructive tests that run in CI and attempt to access one tenant's data using another tenant's credentials. The scope explicitly covers the access-control failure modes:

  • BOLA (Broken Object Level Authorization). Verify that one tenant cannot read or mutate another tenant's artifacts, sessions, branches, API keys, billing records, or purge receipts by supplying their object IDs.
  • IDOR (Insecure Direct Object Reference). Manipulating an ID in a path or body must never reach an object outside the caller's project.
  • Shared-cache bleed. Shared cache paths are tested for cross-tenant KV bleed, since a physical cache hit must respect the KV compatibility key, which includes the isolation namespace.

Authorization on every route

Every API endpoint enforces authorization; no endpoint is reachable by a caller lacking the required project or tenant permission. Platform-level mutations such as managing model aliases require an admin-scoped key, so an inference-only key cannot reshape routing for the whole project. See authentication for scopes.

The full vulnerability scope

Multi-tenant data leakage and broken access control are tracked alongside the rest of the checklist.

On this page