| 1 | # CONTRA |
| 2 | |
| 3 | π€ *This project β code and documentation β was written by an LLM (Claude), |
| 4 | on the ideas, instruction, and editing of humans, per |
| 5 | [factor X](https://11factor.org/#x).* |
| 6 | |
| 7 | **A password manager where the server knows nothing about you.** |
| 8 | |
| 9 | Not "we promise not to look." Not "we encrypt at rest." The server that |
| 10 | holds your vault cannot open it, cannot tell you apart from anyone else, |
| 11 | and does not know your email address. If it is stolen tomorrow, the thief |
| 12 | gets a pile of ciphertext and the times of day you connected. |
| 13 | |
| 14 | CONTRA is a [CARLOS](https://carlosframework.com) app, built to the |
| 15 | strictest class the family publishes β **A1, host-blind**: keys live on |
| 16 | your devices, the instance never holds one that opens your content, and |
| 17 | nothing derived from your content is stored in the clear. Item names, |
| 18 | URLs, usernames, folder structure and vault names are sealed exactly like |
| 19 | the passwords are, because a name is a secret too. |
| 20 | |
| 21 | ## Honest trade-offs β read before trusting it |
| 22 | |
| 23 | This is the part most password managers put in a support article. It goes |
| 24 | here instead. |
| 25 | |
| 26 | - **Lose your kit and your passphrase and your data is gone. That is the |
| 27 | product, not a gap in it.** There is no reset link, no support override, |
| 28 | no "verify your identity and we'll restore it," because a server that |
| 29 | could give your vault back is a server that could give it to someone |
| 30 | else. Every password manager that can email you a way back in has, by |
| 31 | definition, a way in. We don't, and we are not going to apologise for |
| 32 | the consequence. |
| 33 | - **The clearest way to think about it: the kit is your vault. The |
| 34 | passphrase is just the convenient way in.** On a device you already |
| 35 | use, the passphrase is all you need. But a brand-new device asks for |
| 36 | your Account Key β so if you lost the kit *and* every device you had, |
| 37 | remembering the passphrase will not be enough. Same rule, and it |
| 38 | surprises people, which is why we are saying it here rather than in a |
| 39 | support article. |
| 40 | - **So do two things, and we will keep asking until you have.** Keep the |
| 41 | kit somewhere real, and set up a second device. Either one alone |
| 42 | rescues the other. If you lose the kit while a device is still signed |
| 43 | in, reprint it that day β it works right up until your last device is |
| 44 | gone. |
| 45 | - **Why we ask you to keep paper at all.** We could have let a passphrase |
| 46 | alone open your vault from anywhere. Then the encrypted copy of your |
| 47 | data would only ever be as strong as the phrase you chose, and most |
| 48 | chosen phrases fall to a patient machine. The Account Key means that |
| 49 | whatever you pick, the stored copy cannot be broken into. We would |
| 50 | rather ask you to keep a piece of paper than quietly cap how safe your |
| 51 | vault can be. |
| 52 | - **Removing someone from a shared vault does not un-give what they |
| 53 | already have.** Their access dies immediately and the vault's key |
| 54 | rotates forward, so they see nothing new. What they already downloaded |
| 55 | is already theirs. Nobody's design changes that; ours says so. |
| 56 | - **The server still learns transport shape** β that someone connected, |
| 57 | roughly when, and roughly how big the reply was. That is the residue A1 |
| 58 | permits, and we are not going to pretend it is zero. |
| 59 | - **Passkeys stored in 1Password cannot be exported.** Not by us, not by |
| 60 | any tool β 1Password does not let them out. Everything else in your |
| 61 | vault comes across; those need to be re-created by hand, and we will |
| 62 | show you which ones. |
| 63 | - **A web browser trusts the server to send it honest code.** This one is |
| 64 | subtle and most of our competitors do not say it. When you use the web |
| 65 | console, the server hands your browser the JavaScript that handles your |
| 66 | passphrase β so a server that has been *actively taken over* could send |
| 67 | code that steals it as you type. Our at-rest promise ("steal the |
| 68 | database and get nothing") holds absolutely. The stronger promise β |
| 69 | "compromise the running server and still get nothing" β is only fully |
| 70 | true for the signed native apps, where the code is not fetched from us |
| 71 | each time. We serve the console from a separate, static, signed origin |
| 72 | to narrow this as far as a browser allows, and we would rather tell you |
| 73 | than let you assume. |
| 74 | - **The server can see who shares a vault with whom.** Not what is in it, |
| 75 | not its name, not a single value β but the wrapped keys that let your |
| 76 | colleague open a shared vault are visible as a shape, so the server |
| 77 | learns that two accounts share something. Personal vaults reveal |
| 78 | nothing. We could hide this and have chosen not to spend the complexity |
| 79 | yet; it is listed here rather than left for someone to discover. |
| 80 | - **It is early.** What is built and what is not is in `PLAN.md`, with |
| 81 | triggers for each deferral rather than vague intentions. |
| 82 | |
| 83 | ## Leaving 1Password |
| 84 | |
| 85 | CONTRA imports 1Password's `.1pux` export β items, types, TOTP seeds, |
| 86 | attachments and vault structure. **The file never reaches our server**: it |
| 87 | is opened, parsed and sealed in your browser, and only ciphertext is |
| 88 | uploaded. Bitwarden and Apple Passwords importers are wanted and not built |
| 89 | yet. |
| 90 | |
| 91 | ## How you get in |
| 92 | |
| 93 | - A **passphrase** signs you in to the web console. |
| 94 | - An **Emergency Kit** β printed once at signup, generated in your browser |
| 95 | and never sent to us. It is **two documents**, on purpose: |
| 96 | - an **Account Key card** β a new device asks for this once. **Store it |
| 97 | somewhere safe**: a drawer, a wallet, the folder your documents live |
| 98 | in. Somewhere you can find it and nobody else goes. On its own it |
| 99 | opens nothing, because without your passphrase it is useless. |
| 100 | - a **Recovery sheet** β the way back if you forget your passphrase. |
| 101 | **Store this one securely**, and it is the stricter of the two: keep |
| 102 | it where you'd keep a passport or a will, and never photograph it. |
| 103 | This sheet alone can open your vault, and nobody can reissue it. |
| 104 | - On a device you have already set up, the passphrase alone is enough. |
| 105 | Setting up a *new* one takes the Account Key β scan it from the kit, or |
| 106 | let a device you already use hand it over. |
| 107 | - A **passkey** on a device you trust is a fast unlock on top β a |
| 108 | convenience, never the only door. The passphrase is offered beside it on |
| 109 | every screen, because passkey ceremonies fail in ways that are not the |
| 110 | user's fault. |
| 111 | |
| 112 | Your email address is stored only as a blind index β a keyed hash. There |
| 113 | is no address column in the database. It proves you are you, and it can |
| 114 | never decrypt anything. |
| 115 | |
| 116 | ## Agents |
| 117 | |
| 118 | CONTRA can be driven by an AI agent, deliberately and narrowly. An agent |
| 119 | can **list** what is in a vault (names, sites, usernames), **use** a |
| 120 | secret without ever receiving it β the local daemon does the authenticated |
| 121 | thing on the agent's behalf β and **add** new secrets, which needs no |
| 122 | unlock at all. An agent cannot read a value into its own context. There is |
| 123 | no setting that turns that on. |
| 124 | |
| 125 | ## Status |
| 126 | |
| 127 | Pre-alpha. Nothing to sign up for yet. |
| 128 | |
| 129 | ## Licence |
| 130 | |
| 131 | **Mozilla Public License 2.0** β see `LICENSE`. |
| 132 | |
| 133 | MPL's file-level copyleft is the right fit for a security product: anyone |
| 134 | who ships a modified CONTRA has to publish the modified files, so the |
| 135 | thing you are running can always be checked against the thing we |
| 136 | published. That is the same argument as the rest of this README β you |
| 137 | should not have to take our word for it. Factor VI, and it is not |
| 138 | deferred. |
| 139 | |