Security model
The honest version of "secure, no leaks." The Platform's posture is a trustworthy data custodian — not end‑to‑end encryption.
Why not end‑to‑end (the Telegram comparison)
Telegram's strong guarantee (secret chats) is E2E — the server can't read your data. That can't apply here: the engine must read and act on your OData data (run queries, execute workflow actions). You can't end‑to‑end‑encrypt data the server has to process. So the realistic target is strong tenant isolation, encrypted secrets, least privilege, and auditability — and we never imply zero‑knowledge.
The structural advantage: AI emits data, not code
"AI‑built apps leak" almost always because the AI writes executable code that then runs with real privileges. The Platform doesn't:
prompt → AI proposes app.json (DATA) → Zod validation → interpreted by code WE wrote (the only executor)The model never produces code that runs. The worst a bad generation can do is propose configuration, which is schema‑validated and only interpreted by our renderer. This eliminates the entire class of AI‑injected backdoors/eval.
app.json as an attack vector
Declarative ≠ automatically safe; the renderer treats app.json as untrusted:
| Vector | Rule |
|---|---|
| HTML injection / XSS | Never render raw HTML from app.json. Markdown renders as text. |
Custom section | Only registered components render; props are JSON (no functions). Unknown → visible fallback. |
query.filter strings | Run against the tenant's own service with its own creds (within their authority); URLs are encoded, results capped. |
Oversized / deep app.json | Size + max‑depth caps at validation. |
Custodian controls
| Surface | Control |
|---|---|
| Tenant isolation | tenant_id on every row + Postgres RLS (FORCE) — a missed WHERE can't leak across tenants. |
| Credential vault | AES‑256‑GCM at rest; secrets never in app.json/prompts/logs; no API returns a secret value; injected only at egress. |
| Agent writes | Allow‑list + mandatory human approval for write/destructive + audit (see AI & agent). |
| Prompt injection | Data‑is‑data isolation; the model proposes, the platform disposes. |
| AuthN/Z | JWT + API keys; RBAC; least‑privilege agent principal (not operator rights). |
| Transport / at‑rest | TLS everywhere; secrets encrypted at rest; app.json itself is non‑secret config. |
| Audit | Append‑only record of agent proposals, approvals, writes, and app.json versions. |
Credential vault
Secrets (OData bearer tokens, etc.) are stored encrypted at rest (AES‑256‑GCM), tenant‑scoped, referenced from app.json by auth.tokenRef. The key comes from a secrets manager (VAULT_KEY), never the database. list returns metadata only; get exists solely for server‑side egress (injecting the token when the engine calls the customer's OData service) — there is no client‑facing path to a secret value.
What we do NOT promise
- ❌ End‑to‑end / zero‑knowledge encryption of OData data.
- ❌ Preventing a tenant's own over‑broad query against their own service (it's within their authority; we rate‑limit, not forbid).
- ✅ Tenant isolation, encrypted custody, no‑codegen execution, approval‑gated writes, full audit.
Pre‑launch gate
Before any external tenant: cross‑tenant isolation tests green (incl. RLS), vault never logs/returns secrets, renderer hardening (size/depth caps, no raw HTML), agent write‑path approval+audit enforced, pen‑test of the prompt‑injection→write path and the auth boundary, TLS + secrets at rest, dependency audit.
Bottom line: the AI layer is the least of the risk — it emits validated data, not code. The real work is operational custody: tenant isolation, the encrypted vault, and the security review.