Skip to content
Security

What protects a tenant, stated plainly.

No certifications we do not hold, no adjectives standing in for controls. Here is what is enforced, where it is enforced, and what is missing.

Sixteen workspaces, and nothing between themA four-by-four lattice of workspace cells, each holding operators, keys, config versions and an audit log. The gutters between them are drawn as solid rules, and no path crosses one anywhere in the drawing. One cell is opened up at three times the size: a workspace-scoped repository, an API key and a webhook secret that each carry a key id, and an append-only audit log enforced by a database trigger.WORKSPACESISOLATION IS STRUCTURAL01020304050607080910111213141516ONE WORKSPACEworkspace_idevery query in the data layer takes oneapi_key(kid)rotates on an overlapping windowwebhook_secret(kid)rotates the same wayaudit_logappend-only — enforced by a DB triggerNO PATH CROSSES A GUTTER — THE ABSENCE IS THE POINTEvery query in the data layer takes a workspace. There is no call site that can omit it.
Sixteen workspaces. Nothing is drawn between two cells, because nothing can be written between them.
Multi-tenancy

Isolation is structural, not policed.

Every query on workspace-owned data carries the workspace id in its predicate. There is no unscoped access path to forget to use.

The failure mode this design removes is the ordinary one: a correct system where one new endpoint, written in a hurry, omits a WHERE clause. If the data layer will not answer a question that does not name a workspace, that endpoint does not compile into a leak — it does not compile.

Adversarial leak tests pin the property rather than trusting the convention, because a convention is only as good as the last person who read the file.

internal/tenancy · migrations/0002

The record

The audit log is append-only in the database, not in the application.

A trigger raises on any update or delete. An application that decides not to rewrite history is a policy; a database that refuses to is a control.

This is the difference an auditor asks about and a vendor usually cannot answer. If the only thing preventing a retroactive edit is that no code path performs one, then a support script, a migration, or an operator with a console can perform one and the log will not know.

internal/tenancy · migrations/0002 audit_log_immutable()

Credentials

Keys rotate without an outage. Sign-in is phishing-resistant.

Rotation that requires a maintenance window is rotation that does not happen.

  1. 01

    Key ids with overlapping validity

    Per-workspace API keys and webhook secrets carry a key id. During a rotation both are valid, so you cut over on your own schedule and revoke afterwards.

    internal/tenancy · internal/outbox/sign.go

  2. 02

    Passkeys for members and for operators

    WebAuthn with user verification and a discoverable credential required — not optional, because these tokens sit in front of a money flow. Discoverable login resolves the account from the credential, so nothing is typed and there is nothing to phish.

    internal/passkey

  3. 03

    Authenticator two-factor, stored correctly

    A TOTP secret cannot be hashed — verification recomputes codes from it — so member secrets are sealed with AES-256-GCM under a per-context subkey rather than stored in the clear. An accepted code's counter is written back, so a code cannot be replayed inside its window.

    internal/membertotp · internal/secretbox

  4. 04

    Hash where we can, seal where we cannot

    A password or a recovery code is hashed, because the platform only ever needs to answer “is this it?”. A secret the platform must read back is sealed instead, and stores predating that construction are being migrated onto it rather than left as they are.

    internal/secretbox

Egress

Nothing leaves through a URL you configured.

A webhook destination is attacker-influenced input by construction — it is a URL a tenant types, and the platform then fetches it from inside its own network.

The dispatcher runs an egress guard before the request rather than trusting the hostname, which is the only ordering that helps: a check performed after resolution, or on the string rather than the address, is the check that DNS rebinding is designed to walk through. Loopback and private destinations are permitted only in sandbox and end-to-end runs, and that relaxation is an explicit flag rather than an environment sniff.

internal/outbox/ssrf.go · internal/outbox/dispatcher.go

Custody

We are not a custodian, and the code refuses to become one.

No member funds. No private keys. No platform credential inside a tenant's payment rail.

Members pay you

Every rail is configured with the tenant's own credentials, on the tenant's own accounts.

internal/rails

Configuration refuses ours

Storing a credential that matches the platform's own is rejected. The check exists because “use ours for now” is how a software vendor becomes a payment institution by accident.

internal/rails

Claims instruct, never move

A claim reserves exact ledger lines and emits an instruction. The execution is yours, and so is the money it executes against.

internal/claims

Identity

Verification without documents.

The provider session runs against your account. What returns is a decision and a non-reversible handle.

The handle is enough to enforce one person, one position, and nothing more. It is not enough to reconstruct an identity, which is the point: the safest way to hold a passport scan is not to hold one, and a breach cannot disclose data that was never stored.

internal/verification

Not claimed

What we do not claim.

If you are filling in a vendor questionnaire, these are the rows we would fail. Better here than in month four.

We do not hold a SOC 2 report. No third-party penetration test has been performed. We publish no availability commitment, because nothing here measures one yet. XEX gives you controls and records; it does not give you a licence, and it is not legal advice. Whether your compensation plan is lawful where you market it is yours to establish, and our platform terms require you to.
Disclosure
There is no bug-bounty programme today, and no payment is offered for a report. Report a vulnerability through the security route on the contact page; we will confirm receipt and tell you what we are doing about it. Please do not test against a live tenant's workspace — take a free sandbox, which is the whole product, and test there.

See /contact#security

Next

Send the questionnaire, or take a sandbox and look for yourself.

Both are fine. One of them is faster.