ID Side
SDK Security model

Security model

A consent authority is only as good as the worst thing it could do with what it holds. So we hold as little as possible, chain what we record, and publish the keys that let you check our work without asking.

What ID Side never sees

No prompts, no completions

The authorize call carries a SHA-256 hash of the request — enough to bind the proof to that exact request, useless for reconstructing it. Your users' content goes from your backend to your AI provider. It never routes through us.

No raw user identifiers

Your user_id is pseudonymized with a keyed HMAC before it reaches our database, our logs, or the ledger — with a per-app key derivation, so one leak cannot re-identify users across tenants. What we store looks like psu_4f2a… and nothing else.

Nothing leaks back to developers

When a user links her ID Side account to an app, the app learns one bit: a choice exists. Never her email, never what she chose. End-user emails are encrypted at rest, and there is no API that returns them.

No preference write API — for anyone

The only writers of consent are the user's own widget choice, her browser's GPC signal, and her ID Side account. The SDK cannot set preferences. The dashboard cannot set preferences. That absence is deliberate, and it is the product.

An organization's policy restricts, and only restricts

The one thing that writes without the user is an organization's policy — and it can only take away. Only its refusals are read, it cannot switch an option back on, and a person's own refusal survives it. A policy able to permit would be a customer overturning their users from their own dashboard: the exception that would empty the rule above.

An append-only ledger you can audit yourself

Every authorization is appended to a per-app hash chain: each entry commits to the one before it. Rewriting any historical entry breaks every link after it — silently editing the past is not an option, for you or for us.

You do not have to take our word for it. Recompute the chain whenever you like:

GET /v1/ledger/verify → { "intact": true, "verified_count": 1842, "head_hash": "9c41…" }

A broken chain reports exactly where it broke. Export your entries, recompute the hashes offline, compare — the format is documented and boring on purpose.

Fail-closed, always

If ID Side is unreachable, the SDK raises ConsentUnreachableError and the AI call does not leave your backend. There is no silent fallback and no "best effort" mode — an unverifiable call is a call that doesn't happen. The same rule applies to billing: exceeding a quota returns an explicit error, it never degrades enforcement.

One token, one request

Each ACT token is bound to a request hash and a nonce, expires after one hour, and is never reusable. Signing keys live in a KMS on our side; what you get is the public half. Verify any token yourself against /.well-known/jwks.json — standard JWKS, standard ES256, no permission needed from us.

Honesty about what enforcement means

Forcing store=false does not mean "the provider deleted the data", and we will never tell you it does. What ID Side guarantees is precise: non-retention parameters are forced on the request and proven in the ledger. How far each provider's own guarantee goes — a real per-request parameter, a contractual commitment, or nothing at all on some tiers — is stated per provider in your dashboard, with sources and the date we last verified them.

The same precision applies to the law. GPC is legally binding in about a dozen US states; in Europe it has no legal force today, and we don't pretend otherwise. Proposed EU texts on machine-readable consent signals are proposals under negotiation, not law in force — if that changes, the enforcement changes with it, and your ledger will show exactly when.

Integrate it — the quickstart →