Skip to content
Ask2Do

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

LayerWhat it blocks
1. JWT authThe browser can't lie about who's logged in or what role they have.
2. Role gateNon-admin/owner users can't drive the assistant.
3. SQL parserCatastrophic statements (DELETE, DROP, TRUNCATE, ALTER, multi-stmt) are rejected before reaching your DB.
4. DB roleEven allowed statements run with whatever permissions you grant the sidecar.
5. Two-phase approvalEvery write is shown to an admin as SQL preview; nothing runs until they click Approve.
6. Audit logAppend-only 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 permitted

JWT 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, DO
  • DELETE entirely (writes-only allow-list excludes it)
  • UPDATE without a WHERE clause (would touch every row)
  • Multi-statement queries (;-separated)

DELETE, DROP, TRUNCATE, and ALTER are blocked at the SQL parser — they never reach your database.

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:

sql
-- 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:

  1. Update your sidecar host's env (ASK2DO_TENANT_KEY) and restart.
  2. Update your backend's env var.
  3. Verify the new key's last_used_at updates in the portal.
  4. 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 security@ask2do.com with details. We acknowledge reports within two business days. We don't run a formal bug bounty yet, but we appreciate disclosure and credit finders publicly (with permission) when fixes ship.