Otto

Security at Otto

What actually protects your data — stated plainly, including what we don't have yet. Last reviewed 2026-10-06.

The honest summary

Otto is a small, early product run by one person in the United States. It does not yet hold a SOC 2 report, an ISO 27001 certificate, or any other third-party attestation — and we won't imply otherwise anywhere. What it does have is a set of real, tested technical controls, listed below exactly as they exist, and a formal SOC 2 readiness program working toward an independent audit.

Encryption

All traffic is encrypted in transit with TLS; the app sends HSTS with preload, so browsers refuse to speak plain HTTP to it. OAuth tokens and connected-account API keys are encrypted at rest with AES-256-GCM, and the decryption path fails closed: a value that isn't a valid ciphertext is refused, never used. Otto does not ask for the password to any other website and never uses one — it cannot sign in to a site on your behalf, so there is nothing it could do with one. A password saved by an earlier version of Otto stays encrypted and unused until you ask Otto to forget that saved login. Your own Otto password is never stored — only a bcrypt hash. Password-reset and email-verification tokens are stored only as SHA-256 hashes, expire quickly, and are single-use. The encryption key supports staged rotation without downtime.

Sessions and sign-in

Sessions live in an httpOnly, SameSite cookie that expires after 7 days without use; a session you keep using is renewed, so it does not run out on its own while you are active. You can see every signed-in device in Settings → Account and sign any one of them out. Changing or resetting your password invalidates every outstanding session immediately, and "sign out everywhere" does the same on demand — including paired browser-extension devices. Sign-in is protected by layered rate limits, a per-account lockout — five attempts in fifteen minutes, cleared the moment you sign in successfully or finish a password reset — and timing-equalized responses that don't reveal whether an email has an account. Every attempt spends one of the five, correct ones included, and that is deliberate: letting only wrong passwords count would have meant the lockout answered a question the rest of this paragraph is built to refuse, namely whether an address has an Otto account at all. A sign-in from a device Otto hasn't seen before triggers a notification email with the IP and device; a sign-in from a browser and network you have used before does not, so the alert that matters is not buried under alerts about yourself. If an account is suspended for misuse, every one of its sessions ends at once and the sign-in page says why.

Two-factor authentication is available on every account, using any standard authenticator app. With it on, a correct password is only half a sign-in: the server issues no session at all until the code is verified — the second step is enforced server-side, not a screen you can skip past. With it on, turning it off, making new recovery codes or deleting the account also needs a code from your authenticator or a recovery code, not just the password.

Getting an account at all is invite-only right now. An invite is a single-use link: the database stores only a SHA-256 hash of the token, the link expires in 7 days, both the page and its API are rate-limited per IP, and the invited person sets their own password — nobody hands one over, so no password ever travels through email or chat.

Your actions stay yours

Anything Otto starts on its own — a routine, a message that arrived from someone else, or a turn in which it read words someone else wrote — cannot send, post or delete until you give an explicit yes. That gate is enforced server-side at a single chokepoint and fails closed. When you ask Otto yourself, in the app, to send or post something, the request is your go-ahead, with three brakes that stay on: an email is saved to your Gmail drafts unless you switch that setting off, a calendar invite to other people always waits for your tap, and a message to more than ten people always asks first. Requests are additionally checked for cross-site forgery at every authenticated gate, and every account's data is tenant-scoped: queries are structurally pinned to the signed-in user, and automated tests fail the build if a route forgets.

Audit trail

A dedicated, append-only security log records sign-ins and failed sign-ins, password changes, session revocations, data exports, account deletions, and administrative actions — with IP and browser info, retained for 400 days. Nothing in the codebase can edit a security-log row; an automated structural test enforces that only the retention sweep may remove them, and deleting your account removes the rows tied to it (the Privacy Policy, §7, names the one that stays).

Backups and availability

The database is backed up automatically every night, and the newest seven copies are kept. The restore procedure is scripted and verifiable — the drill script rebuilds a scratch database from a backup and checks row counts and primary keys, not just "the file exists." Two limits on that are listed below. A health endpoint continuously reports database, scheduler, and backup freshness, and deployments fail closed: a migration error aborts the deploy and the previous version keeps serving. If our AI provider has an outage, Otto degrades honestly — a clearly-disclosed backup model with no ability to take real-world actions — rather than pretending nothing happened.

Engineering practice

The test suite is in the hundreds of files and includes structural security guards: tests that read the source and fail if an API route lacks an auth gate, if an API route builds a database query from a raw string instead of bound parameters, if a secret-shaped string could reach logs, or if the account-deletion path misses a table that holds user data. Dependency vulnerabilities are gated: the gate fails on any high or critical advisory that is neither fixed nor covered by a written, owner-signed risk acceptance naming that specific advisory and an expiry date — a waiver never stretches to cover an advisory it doesn't name, so silent snoozing isn't possible. The gate is a check we run, not a clean bill of health, and it is failing today; what is outstanding is listed below rather than left for you to assume. Production secrets live only in the deployment platform's environment — never in a committed file, a script, or a chat — and log output is scrubbed against secret-shaped strings as a backstop; the one standing exception is named below too.

Who processes your data

The complete list of sub-processors — every company that touches your data and what each one does — is published in the Privacy Policy (§5.7), along with what happens to your content when it's sent to an AI model and the routing restriction that keeps OpenRouter requests away from model providers that collect user data.

What we don't have yet

  • No SOC 2 report or other third-party attestation yet — a readiness program is under way; the audit itself comes when the operating company is formalized.
  • No bug bounty program — good-faith reports are still genuinely welcome.
  • No backup copy outside our hosting provider yet. Otto’s own nightly copies sit on the same storage as the live service, and the hosting provider’s daily backups of that storage are held by the same provider, so they cover a bad change, a deleted row or a lost disk, but not losing the provider itself.
  • No restore drill on record yet. The drill script exists and is described above; we have not yet run it against a real backup and kept the result, so we do not claim one.
  • Our dependency gate is red right now. High and critical advisories have been published against libraries Otto is built on, and they are neither upgraded past nor signed off yet, so the gate described above is failing rather than passing. These are advisories in other people's code, not findings in Otto's; the fix is an upgrade and a re-run. We are not going to tell you they are harmless, because we have not finished checking each one — we would rather say the gate is red than let the paragraph above imply it is green.
  • Until recently one credential did live in the repository: the sign-in for a shared internal test account our own automated browser checks use. It holds no customer data and has no administrative power, but it is a real account on the real app. It has been taken out of every file, the scripts that need it now read it from a private setting that is never committed, and a check fails the build if it ever comes back. We are naming it here rather than quietly deleting the confession, because removing a value from today’s files does not remove it from the project’s history — until that account’s password is changed, anyone who has ever held a copy of the code could still recover it.
  • No signed data processing agreement with Recall.ai yet — the company that holds a meeting recording between Otto's notetaker joining a call and the transcript being saved (Privacy §5.7). That is the same open status as our other vendors, but a recording of people's voices is the most sensitive thing any vendor briefly holds for us, so it deserves its own line here. Until a DPA is signed, what protects that recording is Recall's published commitments, the deletion Otto performs the moment your transcript is saved, and a timed backstop that deletes it regardless.

Reporting a vulnerability

If you believe you've found a security issue, email team.ottohq@gmail.com with what you found and how. Reports go directly to the people who build Otto. Please give us a reasonable window to fix the issue before public disclosure; we'll tell you plainly what we found and what we did.