Security model
This page is the engineer-facing breakdown of how the security mechanisms work — defense-in-depth layers, signing algorithms, the SQL parser deny-list, recommended DB grants. If you're evaluating Ask2Do for procurement and want to know where your data goes and who sees it, read Data protection.
Threat model: (1) a curious or malicious end-user in the panel, (2) a compromised browser, (3) a compromised cloud orchestrator. Each layer below blocks one or more of those.
Layers, summarized
| Layer | What it blocks |
|---|---|
| 1. JWT auth | The browser can't lie about who's logged in or what role they have. |
| 2. Role gate | Non-admin/owner users can't drive the assistant. |
| 3. SQL parser | Catastrophic statements (DROP, TRUNCATE, bare DELETE, multi-stmt) are rejected before reaching your DB. |
| 4. DB role | Even allowed statements run with whatever permissions you grant the sidecar. |
| 5. Two-phase approval | Every write is shown to an admin as SQL preview; nothing runs until they click Approve. |
| 6. Audit log | Tamper-evident record on your DB of every approved or rejected action. |
JWT auth (layer 1)
Browsers never hold the long-term tenant key. Instead, your backend exchanges it for a 15-minute JWT signed by cloud's Ed25519 key. The widget includes the JWT in its WebSocket auth frame; cloud verifies the signature on every connect.
Browser ─── (1) GET /api/ask2do/token (your endpoint, your auth)
↓
Your backend ─── (2) POST cloud /auth/token (with tenant key)
←── (3) JWT signed by cloud's Ed25519 key
↓
Widget ─── (4) WebSocket /chat with { type: "auth", jwt }
←── auth_ok if signature valid + role permittedJWT TTL: 15 minutes. Re-auth happens automatically on the next page load. A leaked JWT only buys an attacker 15 minutes; a leaked tenant key is much worse, which is why only your backend ever holds it.
Role gate (layer 2)
The JWT's payload carries role: "admin" | "owner". Cloud verifies this on every WebSocket message; junior staff can't send a single chat turn even if they were given the script tag.
SQL parser (layer 3)
The sidecar parses every SQL statement the AI proposes and rejects anything in the deny-list before it reaches your DB:
DROP,TRUNCATE,ALTER,CREATE,GRANT,REVOKE,SET,CALL,DODELETEentirely (writes-only allow-list excludes it)UPDATEwithout aWHEREclause (would touch every row)- Multi-statement queries (
;-separated)
See sidecar/internal/sqlguard for the implementation and full test cases.
DB role grants (layer 4)
The sidecar connects with whatever DB role you give it — usually a least-privileged role. Even when the parser allows an UPDATE, your role's grants ultimately decide what runs:
-- Recommended baseline grants for the sidecar's DB role:
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ask2do_role;
GRANT INSERT ON specific_audit_friendly_tables TO ask2do_role;
GRANT UPDATE ON specific_user_settings_tables TO ask2do_role;
GRANT INSERT ON ask2do_audit TO ask2do_role;
-- NO GRANT for DROP, TRUNCATE, schema modifications, GRANT itself.Two-phase approval (layer 5)
For any INSERT or UPDATE, the AI emits a SQL preview shown to the admin in the widget. The sidecar holds the proposed SQL in a one-shot in-memory cache; it executes only when the admin clicks Approve. Cancel deletes the cache entry; the audit log gets a rejected row.
Audit log (layer 6)
See audit log docs for schema and sample queries.
Release integrity
Sidecar updates are Ed25519-signed. The cloud's release manifest (a compact JWS) lists every released binary's URL, size, and SHA-256. Each sidecar holds the corresponding public key compiled into the binary at build time, so:
- A compromised CDN can't push a malicious binary — the sidecar verifies the manifest's signature before downloading anything.
- A compromised cloud orchestrator can't silently swap binaries either — cloud holds the private signing key but the public key is in every customer's sidecar; rotating it requires a rebuild and reinstall.
- The sidecar verifies the downloaded binary's SHA-256 matches the manifest entry before atomic-replacing itself.
See sidecar/internal/update for the verifier and sidecar/scripts/release/sign-manifest.sh for the cloud-side signing pipeline.
Key rotation
Rotate your tenant key from the portal: /dashboard → pick your tenant → + Issue new key. The new raw key is shown once; copy it to a password manager. Then:
- Update your sidecar host's env (
ASK2DO_TENANT_KEY) and restart. - Update your backend's env var.
- Verify the new key's
last_used_atupdates in the portal. - Click Revoke on the old key.
The cloud caches verified keys for 60 seconds, so rotations propagate in ≤ 1 minute. For incidents requiring instant cutover, contact hello@ask2do.com — we can flush the cache server-side.
Reporting a vulnerability
Email support@ask2do.com with details. We'll acknowledge within 24 hours. We don't run a formal bug bounty yet, but we appreciate disclosure and credit finders publicly (with permission) when fixes ship.