Service card · reviewed 2026-09-17
LetThemBuild puts several models in one room to build and check software. This page says what the room does on its own, where a person stays in control, which controls a team can set and where each one is enforced, what each model provider keeps, and how that maps to the frameworks a questionnaire names. It is written from the code and is not a certification.
What it decides on its own, and what it does not
- The room reads, writes and runs inside a workspace on the person's machine or in a discussion on our servers. Commands run under a kernel sandbox with an outbound allowlist.
- Anything that leaves the machine, changes settings, spends money, pushes, publishes or touches production asks a person first, unless a rule they wrote or a mode they chose says otherwise; a team can raise the floor and cannot lower it below what its members chose.
- No model is trained on anything here. The masks strip credentials and, when a team asks, personal data before a prompt leaves.
- Every run leaves a receipt with the commands that ran and their exit codes, and every governance action is an audit event in a published catalogue.
The controls, and where each is enforced
Mac means the member's app applies it and a member with admin rights on the machine could work around it. Server means our servers refuse on their own. Mac + Server means both.
Run · what may run
| Models and providers | Mac + Server |
| Approved models only | Mac + Server |
| Guest models | Mac + Server |
| Ask before a push that follows an install | Mac |
| Ask when a seat repeats the same call N times | Mac |
| N, for a repeated call | Mac |
| Tell members when a seat repeats the same call | Mac |
| Stop after this many tool calls | Mac |
| Hand over after so many failed fixes | Mac |
| Tasks one delegate call may start | Mac |
| Cloud commands without approval | Server |
| Cloud command network | Server |
| Cloud Computer | Server |
| Refuse tools without the kernel sandbox | Mac |
| Sandbox protections members cannot switch off | Mac |
| Rules with patterns | Mac |
| Classified tool results | Mac |
| No run until this policy has been fetched | Mac + Server |
| MCP servers | Mac |
| Private hosts | Mac |
| Skills registry | Server |
| A note shown with every refusal | Mac + Server |
| Tool families | Mac |
| Hooks | Mac |
| Permission mode floor | Mac |
| Members start with | Mac |
| Depth floor and reviewer independence | Mac + Server |
| MCP tools and skills by name | Mac |
| Warden seat | Mac + Server |
| Keep the same model on credit when no seat is left | Mac + Server |
| Hand a cut-off lead to the next seat | Mac + Server |
| Browser asks before acting on a site | Mac |
| Objectives on the room's rates | Server |
| Minimum app version | Mac |
| Production markers | Mac |
| Opening a way in from the internet | Mac |
| Robots.txt honoured on every fetch | Mac + Server |
| Messaging channels (Telegram, Slack DMs, Discord, iMessage) | Server |
| Approvals answered on the Mac only | Mac |
| Previewing a local app on a phone | Mac |
| Paths that may run lighter | Mac |
Keep · what leaves and what stays
| Personal data redacted from what the room fetches | Mac + Server |
| Least private a Mac discussion may be | Mac |
| Managed seats reach only providers that keep nothing | Server |
| No sources that train on prompts | Mac + Server |
| Managed model calls stay inside the EU | Server |
| Personal data masked before a prompt leaves | Server |
| Models whose provider keeps data | Mac |
| Public links | Server |
| Shared discussions use the discussion's AIs | Server |
| Retention, server and Mac | Mac + Server |
| Public repositories | Mac |
| Unattended pushes | Mac |
| Repositories by remote | Mac |
| Secrets in commands | Mac |
| Product analytics | Mac + Server |
| Memories the room extracts | Mac |
Answer · who answers, who pays
| Accountable owner | Server |
| Members acknowledge the policy | Mac + Server |
| Who joins | Server |
| Every discussion belongs to a team project | Mac + Server |
| Who pays in team discussions | Mac + Server |
| Auditors | Mac + Server |
| Projects a member sees | Server |
| Team accounts only on member Macs | Mac |
| CLI seats signed in with team accounts | Mac |
| IP access list | Server |
| Team API key may pull the spend export | Server |
| Schedules may run overnight at half price | Server |
| Say when a day's spend spikes | Server |
| Say when code health slips | Server |
| Alerts may act | Server |
| Members may mint tokens | Server |
| Ship check | Mac |
| Pause everything | Mac + Server |
| Ceiling per discussion | Mac + Server |
| Tokens a minute, per seat | Mac |
| Calls a minute, per MCP server | Mac |
Prove · what we can show
| Members can read What's metered | Server |
| Audit deliveries | Server |
| Auditor pack and compliance API | Server |
| Alert deliveries | Server |
| A fresh sign-in before an admin action | Server |
| Multi-factor sign-in | Server |
| Take everything | Server |
| Transcript feed to your bucket | Server |
| Device roster | Server |
| Behaviour baseline | Server |
| Guardrails on what people ask | Mac + Server |
| Policy version members carry | Server |
Beside the lines a team sets, 24 kinds of audit event record floors that are on for everyone: pinned MCP tools, masked secrets, folder trust, memories reviewed, the judge's sentences, production touches, the warden, guardrails, secrets given to runs, and retention sweeps. The full catalogue is at /docs/audit-events.
What the Mac app listens on
The desktop app runs on a developer's machine, so a review asks what it exposes. Remote control dials out and opens no port; what does listen is on this machine only, or opt-in and gated, as below. Read from the code on 2026-11-07, with the file that enforces each row; a test fails the build when a listener appears in the desktop code without a row here.
| Surface | Interface | When it listens | What gates it | Enforced in |
|---|---|---|---|---|
| Remote control and Run on my Mac | Nothing: dials out | Never. While "Run requests from my other devices" is on and the app is signed in, the Mac opens connections to our server: a wake stream and the sync every 45 seconds. A request from a phone or the web arrives on those. | The account's own sign-in token. A request names the person and the device it came from, and the Mac's own approval rules still apply to what it runs. | desktop/sync.js |
| Private live view and phone pairing | Nothing: dials out | Never. A paired phone reads a private discussion through sealed boxes the Mac posts to our relay and polls back, only while a phone is paired or pairing. | A key pair made at pairing, confirmed by six digits on both screens; the relay passes boxes it cannot open. At most ten phones, each unpairable. | desktop/lib/private-live.js |
| Outbound allowlist proxy | This machine only (127.0.0.1) | Always, from launch, on ports chosen by the system: one for the room and one per seat. | No token: a local process may use it, but only to hosts on the list. A private address or the cloud metadata service is refused unless the person or the team named that one host exactly. TLS passes through untouched; nothing is intercepted. | desktop/lib/egress.js |
| Sign-in return address for an MCP server | This machine only (127.0.0.1) | Only while the person is signing in to a plugin by URL from Settings → Plugins: one address on 127.0.0.1, on a port chosen by the system, closed after the first answer or after five minutes. | Answers one path, /callback, and only with the random state this sign-in sent; anything else is refused and the address closes. The code it receives is exchanged with the server's own sign-in service, never shown or stored as is. | desktop/lib/mcp-signin.js |
| A discussion's own files in the browser pane | This machine only (127.0.0.1) | Only after a seat or the person opens a file from a discussion's workspace in the browser pane, until the app quits. One read-only server for the app, on a port chosen by the system. | Each mounted folder sits under its own random token, and only the discussion's workspace and reports folder are mounted. Files are checked by real path, so a symlink out is refused; dot-files and dot-folders are never served; GET and HEAD only. | desktop/lib/workspace-files.js |
| Logs from a running app | This machine only (127.0.0.1) | Only after the person switches it on under Logs & Diagnostics. Off at every launch; a team policy cannot switch it on. | The peer must be this machine, the method POST, and a header must carry a random token minted when the app started. Bodies are capped at 1 MB and kept in memory only. | desktop/lib/log-ingest.js |
| Phone preview | The local network, one address | Only while a preview the person started is running, after "Preview on my phone" is on and the team has not switched it off. It binds one private address of this machine, never every interface, and forwards to one local port. | A one-time code in the link, traded on the first visit for an HttpOnly, SameSite=Strict cookie; without either the answer is 403. It stops after the minutes chosen, after ten idle, on Stop, or at quit. | desktop/lib/phone-preview.js |
| Cloud Computer dev-server preview | The Cloud Computer only, behind the preview router | Only when the app runs as a Cloud Computer (LTB_CLOUD_HOST=1) on a machine of ours, never on a person's own computer. Port 8080 of that machine, reached only through the preview router's replay by machine id; a request never starts the machine. | A web-signed Ed25519 token naming this box, this machine and the port, good for two minutes and once, traded for an HttpOnly, Secure cookie for that one host; without either the answer is 401 or 403. Never forwards to its own port or below 1024. | desktop/lib/preview-proxy.js |
| Browser bridge for CLI seats | A unix socket, owner only | From the first run of a CLI seat until the app quits, as a socket in the user's own temporary directory with mode 0600. | Every message carries a token: one per seat and run, dropped when the run ends. The app-wide token may only read. The app's own approval rules apply to every browser action either way. | desktop/main.js |
| Sign-in callback | This machine only (127.0.0.1) | Only during sign-in, for at most five minutes, on a port chosen by the system. | Only the callback path answers; a random state must match and the PKCE verifier is needed to redeem the code. One callback is handled, then it closes. | desktop/sync.js |
| Update installer (macOS) | This machine only (127.0.0.1) | Only after an update has downloaded, until it installs; a listener inside the updater library, not our code. | Basic auth with a random password and a random path, both minted for that one install. Updates themselves are fetched over https from our own feed. | desktop/lib/updates.js |
| Dev servers the room starts | Nothing: dials out | The app opens nothing for them; a project's own command binds what it binds. Under "Close the network" the kernel sandbox lets it bind this machine and unix sockets only. | The project's own. The one way from the local network to such a server is the phone preview above. | desktop/lib/sandbox.js |
What each model provider keeps
As each provider publishes it, read on the date shown. A team's keep-nothing policy routes managed seats only to endpoints that keep nothing, and a model with an exception is off for that team until an admin allows it.
| Provider | Keeps | Trains | Where | Attests | Exceptions | Checked |
|---|---|---|---|---|---|---|
| Anthropic (Claude) [email protected] | API inputs and outputs are kept for up to 30 days for trust and safety, then deleted; zero data retention is available on request for eligible accounts. | Not used to train models by default for the API. | United States; processing in other regions on request for enterprise agreements. | SOC 2 Type II, ISO 27001, ISO/IEC 42001, HIPAA-eligible configurations | claude-fable-5-1, claude-fable-5, claude-mythos-5-1: Inputs and outputs may be kept for safety review beyond the default window; under a keep-nothing policy these seats are off until an admin allows them (row 180). | 2026-09-17 |
| OpenAI (ChatGPT models) [email protected] | API inputs and outputs are kept for up to 30 days for abuse monitoring; zero data retention is available for eligible endpoints. | Not used to train models by default for the API. | United States; data residency in other regions for eligible enterprise projects. | SOC 2 Type II, CSA STAR | Models on the Free Key training-consent source: A free account whose provider learns from prompts is a training source, and a team may forbid it (row 195). | 2026-09-17 |
| Google (Gemini API) https://www.google.com/appserve/security-bugs/m2/new | Paid API traffic is kept briefly for abuse detection, then deleted; the free tier may be kept longer. | Paid API traffic is not used to improve models; free-tier traffic may be. | Google Cloud regions; the API's default is not pinned to one. | ISO 27001, SOC 2, SOC 3 | The free tier: Prompts may be used to improve products; a keep-nothing team routes managed seats away from it (rows 136, 195). | 2026-09-17 |
| xAI (Grok) [email protected] | API inputs and outputs are kept for up to 30 days for abuse monitoring. | Not used to train models by default for the API. | United States. | SOC 2 Type II | None published | 2026-09-17 |
| OpenRouter (the catalogue) [email protected] | OpenRouter keeps no prompt content by default; each upstream provider's own policy applies, and the router marks endpoints that keep nothing (zero data retention). | Not used by OpenRouter; upstream providers marked as training on prompts are refused for managed seats under a keep-nothing policy. | Depends on the upstream provider the call is routed to. | SOC 2 Type II | Every catalogue model: A managed seat under keep-nothing asks the router for zero-retention endpoints only and refuses providers that collect data (row 136). | 2026-09-17 |
How the controls map to the frameworks
| Framework | Item | Answered by |
|---|---|---|
| NIST AI RMF | Govern: accountability and policy | Accountable owner, the policy acknowledged per version, the audit catalogue, the profiles |
| NIST AI RMF | Map: what the system does and for whom | The inventory (who runs which model), the provider register, project signals |
| NIST AI RMF | Measure: evidence of behaviour | Receipts with commands and exit codes, the hash-chained audit export, the model scoreboard |
| NIST AI RMF | Manage: controls and response | Tool rules, the sandbox, egress, production markers, the warden, the pause |
| ISO/IEC 42001 Annex A | A.5 impact assessment | The risk register (docs/AI-RISK-REGISTER.md), guardrails on asks |
| ISO/IEC 42001 Annex A | A.6 lifecycle of AI systems | Retirement notices, the model that answered on every receipt, the brief changelog |
| ISO/IEC 42001 Annex A | A.8 information for interested parties | This page, the privacy page, the AI-generated mark on pages and reports |
| ISO/IEC 42001 Annex A | A.10 third-party relationships | The provider register, keep-nothing routing, models whose provider keeps data |
| EU AI Act | Article 50 transparency | AI-generated marks on published pages, reports and shares; models named in exports |
| EU AI Act | Article 4 AI literacy | The help article for customers' own duties; the policy each member reads and acknowledges |
| EU Cyber Resilience Act | Vulnerability handling and reporting | security.txt, the SBOM per release, docs/CRA-REPORTING.md |
The internal risk register behind this table is in the repository as docs/AI-RISK-REGISTER.md; a customer's own assessment is answered from this page and the auditor pack a team owner can download.
Personal data
- Our Data Processing Agreement is part of the Terms and applies without a signature: roles, the Standard Contractual Clauses for transfers from the EU, UK and Switzerland, breach notice within 72 hours, deletion, and audits.
- The 10 services that process customer personal data for us are listed in its Annex III, with what each receives and where.
- What we store and how to delete it is in the Privacy Policy.
Cryptography and post-quantum readiness
Where the product signs, encrypts and negotiates keys, and what a quantum computer would break — read from the code (lib/crypto-posture.ts), held to it by a test, reviewed 2026-10-07. The first threat is traffic recorded today and opened later, which breaks a key exchange and nothing else retroactively, so key exchange comes first. The edges were read with scripts/pqc-probe.mjs. Not a certification.
| Where | What | Kind | If recorded today | Post-quantum |
|---|---|---|---|---|
| letthemchat.com, letthembuild.com, status.letthembuild.com | TLS 1.3 at the edge (Cloudflare)X25519MLKEM768 negotiated with a client that offers it; X25519 otherwise. | key exchange | recorded traffic could be opened later | hybrid (X25519MLKEM768) |
| Cloudflare → Fly.io origin (letthemchat.fly.dev) | TLS 1.3 on the origin leg, terminated by Fly's proxyX25519 only: Fly's proxy does not offer ML-KEM yet. The planned change is below. | key exchange | recorded traffic could be opened later | classical |
| Mac app → the service | TLS 1.3 from Chromium's stack (Electron 43)Chromium offers X25519MLKEM768 by default since 131; Electron 43 carries a newer one. | key exchange | recorded traffic could be opened later | hybrid (X25519MLKEM768) |
| iPhone app → the service | TLS 1.3 from URLSessioniOS 26 offers X25519MLKEM768; a phone on iOS 17–18 (our deployment target is 17) offers X25519 only. | key exchange | recorded traffic could be opened later | hybrid (X25519MLKEM768) |
| Private link between a person's own devices | X25519 key agreement, HKDF-SHA256, AES-256-GCMA relay recording the boxes today could open them later. The planned change is below. | key exchange | recorded traffic could be opened later | classical |
| Secrets at rest in the database | AES-256-GCM under a key derived with scrypt from the server's secret; the derivation is versioned (letthemchat-key-v1)AES-256 is not weakened materially by a quantum computer. Rotation is a new version tag and a re-seal, not a redeploy of every row at once. | symmetric | not materially weakened | — |
| Secrets at rest on the Mac | Keychain-backed sealing (Electron safeStorage) | symmetric | not materially weakened | — |
| Access and sync tokens | HS256 (HMAC-SHA256) JSON Web Tokens under a scrypt-derived secretSymmetric MAC: no public key to break. Short-lived. | signature | forgeable later, not retroactively | — |
| Webhooks we send (audit, alerts, transcripts, releases, Slack) | HMAC-SHA256 signature in a headerSymmetric MAC per destination secret. | signature | forgeable later, not retroactively | — |
| Webhooks we receive (Paddle, GitHub, Slack) | HMAC-SHA256 verification of the sender's signature | signature | forgeable later, not retroactively | — |
| GitHub App and Microsoft Teams bot | RS256 (RSA-2048) private keys the platforms issueBreakable once the computer exists, not retroactively; the platforms, not we, choose the algorithm. | signature | forgeable later, not retroactively | classical |
| Sign in with Apple | ES256 (P-256) client-secret signingApple's choice; rotated every six months by expiry. | signature | forgeable later, not retroactively | classical |
| Team entitlement files | Ed25519 signature over the entitlement | signature | forgeable later, not retroactively | classical |
| Backups to object storage | SigV4 (HMAC-SHA256) request signing; the bucket's own encryption at rest | signature | not materially weakened | — |
| Mac and Windows installers | Developer ID (Apple) and Authenticode code signing by the platform's certificateThe platforms' algorithms; no post-quantum code signing exists on either yet. | signature | forgeable later, not retroactively | classical |
The migration, in the order the exposure dictates
- Hybrid key exchange on every public endpoint — done: Cloudflare's edge, confirmed by the probe.
- Hybrid key exchange on the origin leg — planned: When Fly's proxy offers ML-KEM, or by terminating TLS in the application (Node already offers it) behind a raw TCP handler.
- Post-quantum private link — planned: Replace X25519 with a hybrid X25519 + ML-KEM-768 agreement in desktop/lib/private-link.js; both devices must update, so it ships with a version in the box.
- Long-lived data at rest — done: AES-256-GCM under a versioned derivation; no change needed.
- Signatures and code signing — planned: Follow the platforms: Ed25519 entitlements to ML-DSA when Node ships it; installers when Apple and Microsoft do.
Suppliers
| Supplier | Role | Readiness | How we know |
|---|---|---|---|
| Cloudflare | edge TLS for every public host | Hybrid ML-KEM key exchange on by default for TLS 1.3 | scripts/pqc-probe.mjs: X25519MLKEM768 negotiated |
| Fly.io | application hosting; TLS to the origin | Classical (X25519); no announced date for ML-KEM on the proxy | scripts/pqc-probe.mjs on letthemchat.fly.dev |
| Node.js (OpenSSL 3.6) | the server's own TLS client to model providers, webhooks and object storage | Offers X25519MLKEM768 when the far end does | node -p process.versions.openssl |
| Chromium (Electron) | the Mac app's TLS | Hybrid by default | Electron 43 in desktop/package.json |
| Apple (iOS) | the phone's TLS, code signing, Sign in with Apple | Hybrid key exchange on iOS 26; signatures classical | deployment target 17.0 in the Xcode project |
| Anthropic, OpenAI, Google, xAI, OpenRouter | model providers our server calls | Depends on each provider's edge; our client offers hybrid | not probed here — their endpoints change without notice |
| Paddle, GitHub, Slack, Microsoft | webhooks and platform keys | HMAC and RSA/ECDSA by their choice | lib/paddle.ts, lib/github-app.ts, lib/slack.ts, lib/teams-bot.ts |
The ten questions a questionnaire asks, answered
- Do you keep an inventory of where you use cryptography?
- Yes — the table on this page, read from the code by a test, reviewed on the date shown.
- Is each use classified by what a quantum computer would break?
- Yes: key exchange (recorded traffic opened later), signature (forgeable later, not retroactively), symmetric and KDF (not materially weakened).
- Do your public endpoints offer a hybrid post-quantum key exchange today?
- Yes, every public host negotiates X25519MLKEM768 with a client that offers it; the Mac app and iOS 26 do.
- Where is the key exchange still classical?
- The Cloudflare-to-Fly origin leg, the device-to-device private link, and phones on iOS 17–18. Each is named above with its plan.
- Can you change an algorithm without a redeploy of every record?
- Secrets at rest are sealed under a versioned derivation; a new version re-seals on read and write. Signatures are per integration and swappable one at a time.
- How are keys rotated?
- Server secrets by versioned derivation; webhook secrets per destination; Apple's client secret by expiry every six months; platform keys by their consoles.
- What about long-lived encrypted data?
- At rest it is AES-256-GCM, which a quantum computer does not materially weaken. Nothing long-lived travels under a classical key exchange except the origin leg and the private link, both listed.
- Are your suppliers ready?
- The table on this page says what each one offers and how we know.
- Is code signing post-quantum?
- No; neither Apple nor Microsoft offers it yet. Installers are signed by the platform's certificate, which is forgeable only once the computer exists.
- Which standards do you follow?
- FIPS 203 (ML-KEM) through the hybrid X25519MLKEM768 group; AES-256-GCM, HMAC-SHA256, HKDF and scrypt as named per row. No FIPS validation is claimed.
Software supply chain and reporting
- A CycloneDX SBOM for the web app is at /api/sbom; the desktop app's is made at release time and kept beside the installers.
- Vulnerabilities go to [email protected]; the path from a report to the EU single reporting platform within 24 hours is written in
docs/CRA-REPORTING.md. - Whether the service is up, and what broke before, is at status.letthembuild.com, hosted apart from the service so it answers when the service does not, with the same incident list at /status.