How your code, credentials and sessions stay yours
You have clicked "forgot password" on every site you have ever used, and it has always worked. Somebody on the other end could put you back in. That convenience is telling you something about where the keys live, and it is the one thing we deliberately gave up.
A Knockout session touches your real codebase, your real provider keys and a piece of every file the agent reads. Your machine turns your vault password into a key and encrypts that material before it goes anywhere. What lands on our side is ciphertext plus a random salt. We can store it, back it up and hand it back to you, and we cannot open it, because the password that derives the key never arrives here in the first place.
So the honest version of the promise is narrow and checkable. There is no plaintext copy on our side to leak, no support process that can decrypt your data for you and no recovery if you lose the password. The rest of this page is the mechanism, the parameters and the parts that are deliberately not encrypted.
Recent hardening
| Data | In the vault | Notes |
|---|---|---|
| Session prompts & responses | Encrypted | Every Knockout turn, system prompts, generated code |
| LLM provider API keys | Encrypted | OpenAI, Anthropic, Google, Groq, etc. |
| Signup IP | Encrypted | GDPR personal data |
| Last-login IP | Encrypted | GDPR personal data |
| Tool call outputs in session history | Encrypted | Captured inside the message stream |
| File paths & names quoted in prompts | Encrypted | Sit inside encrypted message bodies |
| Account email & username | Plaintext | Required plaintext for login + uniqueness |
| Account password hash | Plaintext | scrypt-hashed, never reversible |
| Wallet balance & subscription status | Plaintext | Required for billing flows |
Checking a password normally means comparing it to something stored. Here nothing is stored. When you set a vault password your machine derives a key from it, encrypts a small known phrase with that key and sends us the scrambled phrase and a random salt. Next unlock, we encrypt the same phrase again and check the results match. That proves you know the password while we hold neither it nor the key. It is also why anyone walking off with the whole database walks off with noise.
On unlock, the derived key is wrapped with the server master key and cached with a 1 hour rolling idle TTL on the web. Only real cryptographic use rolls that window forward. A status poll does not, so a forgotten tab checking whether the vault is unlocked cannot act as a heartbeat that keeps it unlocked. Web sessions also hit a 12 hour absolute cap. The ko CLI vault runs a 4 hour idle window on the same use-only rule, with no absolute cap.
Change the vault password and every provider key is decrypted under the old key and re-encrypted under the new one in a single pass, then every open vault session is invalidated so nothing keeps running on the key you just retired. Forget it and there is no recovery. We hold no copy to reset you back to, so the only way forward is to reset the vault and enter your provider keys again. That is the cost of the server never having the password in the first place.
A PostgreSQL CHECK constraint on ko_sessions requires is_encrypted=true OR legacy_plaintext=true. The application refuses plaintext writes on its own, and the constraint is there for the day a code path forgets. Two layers, because one of them is written by people.
unlock
verifier check
use
decrypt → +1 h
1 h idle
no crypto → expire
Status reads (isVaultUnlocked) are passive and do NOT extend the TTL. Only real cryptographic work does.
Each ko token carries fine-grained scopes such as session:create, sync:push and config:write, so a token minted for CI cannot rewrite your config. Server-side they are stored as SHA-256 hashes. The full token is shown once at creation and can never be read back, which also means a database dump yields nothing you can authenticate with. Revoke any token instantly from settings.
The CLI keeps the vault password in your operating system keychain (macOS Keychain, Linux libsecret, Windows Credential Manager) rather than in a plaintext config file. Passwords left in a config file by an older version get migrated into the keychain on first run.
ko auth login opens a browser flow. You authenticate on the platform and the CLI receives a scoped token, so no password is ever typed into a terminal where a shell history or a screen recorder could catch it. CSRF state validation protects the handshake.
Every self-update is checked against a SHA-256 manifest fetched over TLS before it is applied. A mismatch is rejected, which stops a corrupted or tampered binary landing on the update path. Ed25519 signature verification of the release manifest is built and ships disarmed today; it becomes mandatory once the release-signing key is armed (coming soon).
The CLI's file Read/Write/Edit tools are restricted to your project's working directory. Symlink escapes are detected and blocked. Access to ~/.ssh, ~/.aws, ~/.gnupg and other sensitive paths is denied by a hardcoded denylist.
The CLI detects and blocks compound shell commands (pipes, semicolons, backticks, subshells) in tool calls. Dangerous commands require explicit user approval. Server-side admin commands use execFile (no shell) with strict input validation.
Code pushed via ko sync push is encrypted on your machine before upload, using AES-256-GCM with a key derived from your vault password (scrypt). The platform stores only ciphertext.
The Web UI defaults to loopback (127.0.0.1) and gates every WebSocket connection on a one-time cryptographic nonce plus an Origin check against the server's own host. So a malicious website or a stray local process cannot connect even if you deliberately bind it to a LAN address with --listen.
Every Knockout API endpoint is rate-limited by class: 5/min on auth routes, 20/min on resource creation, 30/min on mutations and 60/min on reads. Sliding window per IP, held in memory, which is enough to make credential stuffing pointless.
Every mutating browser request is validated against Origin and Referer headers. CLI requests use Bearer tokens and are stateless, so they bypass CSRF cleanly without losing protection on the browser path.
Every input is checked for length, type, enum membership and regex shape. Hostnames, SSH usernames and ports go through allowlist patterns. No unbounded input reaches the database.
HSTS (1 year), X-Frame-Options DENY, strict CSP, X-Content-Type nosniff, strict Referrer-Policy enforced on every response. frame-ancestors none prevents clickjacking.
Daily PostgreSQL dump → AES-256-CBC + pbkdf2 wrapper (passphrase only on the source box) → OVH S3. Two keys required to read: the backup passphrase plus every affected user's vault password. 30-day retention.
The restore script downloads, decrypts and lands the dump into a separate fightclub_restore database. It never touches the live one without an explicit operator promotion. Row counts are verified against live during deploy, because an untested backup is a story rather than a backup.
The vault password cannot be recovered. Forget it and your encrypted sessions, provider keys and personal data are permanently unreachable. Resetting the vault is possible, and it deletes everything encrypted so you can start again with new keys. The old data does not come back. Nobody here has a copy of your key to fall back on, and the crypto forbids reconstructing it, which is exactly the property that makes the rest of this page true. Put the password in a password manager today.
Save your vault password somewhere durable
Password manager, paper, anywhere that isn't this machine. Forget it and every encrypted thing on the platform is gone, and there is no support ticket that changes that.
Unlock the vault when you sit down to work
Click Unlock vault on the dashboard. The session rolls forward every time you actually use it, and a forgotten tab polling for status will not keep it alive.
Lock the vault on shared computers before walking away
On a work laptop, university PC, or anywhere someone else might use the machine, explicitly lock + sign out. The login session itself auto-expires after 1 hour idle.
Clean up any ⚠ unencrypted sessions on your sessions list
Old rows from before the encryption rollout auto-upgrade the next time you open them with the vault unlocked, or you can Delete them to redact entirely.
Every claim above is a property of how the thing is built rather than a policy we promise to follow. Set a vault password, unlock it, and check the behaviour against this page.