| new file mode 100644 |
| index 0000000..ab70793 |
| --- /dev/null |
| +++ b/AGENTS.md |
| @@ -0,0 +1,232 @@ |
| +# AGENTS.md |
| + |
| +🤖 Guidance for everyone — human or agent — changing this repository. This |
| +is the canonical instructions file; tool-specific files (`CLAUDE.md`) are |
| +pointers to it, so keep it current *here*. |
| + |
| +What CONTRA *is* lives in `README.md`; the protocol in `DESIGN.md`; the |
| +roadmap and its reasoning in `PLAN.md`. This file is how we build and the |
| +decisions we have committed to. |
| + |
| +CONTRA is a member of the CARLOS app family (`carlosframework/platform`); |
| +see that family's `carlos:building-carlos-apps` skill for the shape it |
| +follows. Where this file and the skill disagree, this file wins. |
| + |
| +# CONTRA |
| + |
| +An end-to-end-encrypted password manager. The server is host-blind: it |
| +holds ciphertext, public keys, and opaque ids, and nothing else. |
| + |
| +## The one rule |
| + |
| +**Absolutely nothing is revealed.** |
| + |
| +CONTRA is **CARLOS A1** — host-blind, in the strict sense the family |
| +publishes at carlosframework.com: |
| + |
| +> Keys live on the user's own devices; the instance never holds one that |
| +> opens user content. The only residue is transport shape: who connects |
| +> when, object sizes, timing. **No content-derived identifiers at rest.** |
| + |
| +Not A2. A2 permits an enumerated application-level residue published in |
| +the open, and it is where Eleven sits — precisely because Eleven's thread |
| +membership is visible to the server. CONTRA does not get that allowance. |
| +If a change would put a content-derived identifier in SQLite, it is wrong. |
| + |
| +The instance's whole SQLite file is continuously replicated to object |
| +storage, so **a plaintext column is a plaintext leak**. Metadata — item |
| +names, URLs, usernames, folder structure, vault names — seals client-side |
| +exactly like values do. |
| + |
| +Enforced, not promised: the end-to-end suite pushes canary strings through |
| +every write path and greps the raw SQLite bytes *and* the bucket objects |
| +for them. Keep that test merciless. A new storage surface lands with its |
| +canary or it does not land. |
| + |
| +## Settled decisions (don't relitigate casually) |
| + |
| +Each is dated and attributed. Rejected alternatives are recorded beside |
| +them. These change with new facts, dated and attributed — not in a |
| +drive-by. |
| + |
| +### 1. Trust class: A1 strict, on the Slopbox shape (Paul, 2026-09-01) |
| + |
| +Instance-per-account. A shared vault lives on its owner's instance; other |
| +members are identities on that instance holding the vault's group key |
| +wrapped to their own public key. |
| + |
| +**Rejected: Eleven's sharing model as-is.** It was the starting proposal |
| +and it is the thing that makes Eleven an A2 app. Membership of a thread on |
| +a shared multi-tenant instance is a content-derived identifier. Membership |
| +of a vault on *your own* instance is not — it is the access-control list |
| +the instance must consult to decide who it serves sealed bytes to, which |
| +is transport shape. That distinction is load-bearing; if a change erodes |
| +it, the class claim goes with it. |
| + |
| +The consequence we accept: revocation is Slopbox's honest three-part |
| +answer — credentials die immediately, the group key rotates forward for |
| +new and changed items, and **nothing already downloaded is un-downloaded**. |
| +No copy pretends otherwise. |
| + |
| +### 2. Recovery: a passphrase and print-once codes (Paul, 2026-09-01) |
| + |
| +A passphrase signs in to the web console. Recovery codes are shown |
| +**exactly once**, at signup. Lose both and the data is gone — no restore, |
| +no operator escape hatch, ever. |
| + |
| +**This reverses two dated family decisions**, deliberately: |
| + |
| +- Eleven, *Paul 2026-07-25*: "recovery is a passkey, never a passphrase or |
| + a printed code." The paper kit was killed and the passphrase-wrap |
| + primitive deleted the same day. |
| +- Slopbox, *2026-07-26*: "the mandatory paper recovery kit is gone; |
| + passkeys hold the keys." |
| + |
| +**Why it is right here and nowhere else in the family:** a password |
| +manager cannot store its own recovery credential in a password manager. |
| +The family's recovery passkey lives in iCloud Keychain or 1Password — |
| +which is the exact product CONTRA replaces. The circularity is worst for |
| +the person migrating off 1Password, who is our first user. Paper is back |
| +because paper is the only medium that is not the thing we are replacing. |
| + |
| +Secondary reason, not the deciding one: the 1Password Emergency Kit means |
| +this pattern is already understood by the audience. |
| + |
| +### 3. Email exists only as a blind index (Paul, 2026-09-01) |
| + |
| +Kass's shape: an HMAC under a server-held pepper. It proves identity for |
| +sign-in and **can never decrypt anything**. There is no stored address |
| +column and no provider user-id. Email-gated key escrow is the forbidden |
| +shortcut and stays forbidden — a compromised server plus a passed email |
| +gate must still yield nothing. |
| + |
| +Direction of travel (Paul, 2026-09-01): email + magic link for signup, |
| +then passkey for quick sign-in thereafter, is intended to become the |
| +**CARLOS default**. CONTRA is where it gets built first. Build it so it |
| +can be lifted. |
| + |
| +### 4. Passkey + PRF is a fast unlock, never the only door (Paul, 2026-09-01) |
| + |
| +A third wrap of the same key material, not a shortcut around it. The |
| +failure that forced Eleven's rethink — the macOS ceremony sheet reaching |
| +only iCloud Keychain, and 1Password's macOS provider returning a PRF that |
| +does not match its own extension's for the same credential — means a |
| +present passkey can still fail. Every screen that offers it must offer the |
| +passphrase beside it, always, with no dead ends. |
| + |
| +### 5. Agents may use a secret; they may not see one (Paul, 2026-09-01) |
| + |
| +An agent gets a scoped, per-agent credential that can **list** metadata, |
| +**use** a value without receiving it, and **write** new values (free — |
| +sealing to a public key needs no unlock). Reading a value into a model's |
| +context is **not a path that exists in v1**. |
| + |
| +This is sharper here than elsewhere because this repo publishes a prompt |
| +ledger: a secret that reaches a model's context is close to a secret that |
| +gets published. |
| + |
| +### 6. Build order (Paul, 2026-09-01) |
| + |
| +1. rastrillo instance + **client-shape** web console — sealing happens |
| + where the keys are, so this surface is client-shape by force. |
| +2. Native Mac app — the *second* implementation of a settled contract. |
| +3. AutoFill Credential Provider extension (macOS). |
| +4. iOS. |
| + |
| +The protocol is proven once, in one place, before a second client exists. |
| + |
| +### 7. Import is 1PUX, and it is client-side (Paul, 2026-09-01) |
| + |
| +1Password's 1PUX export for v1: it carries item types, TOTP seeds, |
| +attachments and vault structure, where CSV loses nearly all of it. The |
| +file is parsed and sealed **entirely in the browser**. An unsealed export |
| +touching the server would be an A1 violation on day one. |
| + |
| +Stated honestly in the copy: **passkeys stored in 1Password cannot be |
| +exported by anyone**, us included. That is 1Password's limit, not ours, |
| +and it needs a manual migration path rather than a pretence. |
| + |
| +Bitwarden and Apple Passwords importers are wanted, later — tracked as |
| +discussions on this repo, not built now. |
| + |
| +### 8. Ledgers on, at `prompts` level (Paul, 2026-09-01) |
| + |
| +Prompt ledger and carbon ledger both enabled (`amadan ledger init -level |
| +prompts`). Not `dialogue` or `full`, and the reason is this product: a |
| +fuller level publishes more of what passes through a session working on a |
| +password manager. |
| + |
| +**The law that goes with it: no real secret ever enters a prompt, a test |
| +fixture, a commit, or a canary.** Fixtures are generated. If a real value |
| +is ever typed, `amadan ledger redact` / `amadan ledger withhold` are |
| +routine tools here, not emergency ones — use them the same day. |
| + |
| +### 9. Money: free rung, then €5/year (Paul, 2026-09-01) |
| + |
| +Slopbox-inspired: a genuine free rung, paid starting at **€5/year**. |
| +Cheap on purpose — a password manager people cannot afford to keep is a |
| +password manager that loses their data. Integer cents; a float never |
| +touches an amount. |
| + |
| +## Build and test |
| + |
| +- Go, one static binary, `CGO_ENABLED=0`, thin `main.go` dispatch. |
| +- **rastrillo** is the framework (`github.com/carlosframework/rastrillo`) — |
| + its packages exist to be used, not hand-rolled. Read the repo's own |
| + `SKILL.md` first; it postdates most models' training data. Honest note |
| + carried from the family: rastrillo does not yet support the E2EE app |
| + story at framework level, so the sealed flow is app-side, built on the |
| + Keymail/Kass/Slopbox patterns. Don't claim the framework does it. |
| +- **Stdlib first.** Adding a dependency is a decision, not a default. |
| +- `modernc.org/sqlite`, WAL (`busy_timeout` set *before* `journal_mode`), |
| + `SetMaxOpenConns(1)`, additive-only migrations. New code over an old DB |
| + must always be safe. Never delete data to update. |
| +- `go test ./...`, `go vet ./...`, `gofmt -l .` — clean, always. Nothing |
| + lands untested; a bug fix carries its regression test. Never pipe a test |
| + run you are gating on: a pipe eats the exit code. |
| +- Crypto is byte-identical in Go and JS, pinned by shared golden vectors. |
| + Change one side and you must change the other, plus the context strings. |
| +- JS: small self-contained ES modules, no build step, `go:embed`, **300 |
| + lines per module enforced by test**, caps ratchet down only. Never |
| + `innerHTML` untrusted content — build DOM nodes. |
| +- The merciless greps (above) run in the end-to-end suite. |
| + |
| +## Workflow |
| + |
| +- **Worktree per session, always.** Never build in the main checkout; |
| + reading it is fine, writing is not. |
| +- **On amadan the branch is the pull request.** Take a worktree, take a |
| + branch, push it, and describe it *before* the first edit: |
| + `amadan branch describe <branch> -body -`. The branch page is the only |
| + view of your work anyone gets — keep the description true, put the plan |
| + in Tasks, and put questions and decisions in Discussions. |
| +- Agents post as their agent account (`claude~paul@keymail.dev`, via |
| + `AMADAN_HOME`), never as the human. The 🤖 badge is not optional. |
| +- Squash-merge to main only when all three hold: the work is done, a human |
| + has approved it, and CI is green. Never commit to main. |
| +- Commit subjects are area-prefixed prose that says why. Every commit |
| + carries a `Co-Authored-By:` trailer; the human is the author of record. |
| +- Review happens on a deployed canary, never on localhost. Report the |
| + clickable canary URL in every reply while iterating. |
| +- **Never echo a secret, a claim link, or a recovery code into a session.** |
| + |
| +## AI authorship |
| + |
| +Machine-authored text is marked 🤖 where it surfaces. A person marker |
| +(👨/👤/🧑) certifies human text an LLM must not rewrite, rephrase, condense |
| +or delete. AI features in the product are opt-in, always disclosed, and |
| +every path works with the AI switched off. |
| + |
| +## Deliberately not built yet |
| + |
| +Each with the trigger that would start it. |
| + |
| +| Not built | Trigger | |
| +|---|---| |
| +| Bitwarden and Apple Passwords importers | 1PUX import is shipped and a real user asks | |
| +| Passkey migration out of 1Password | A documented manual path exists first; automation only if the export limit ever lifts | |
| +| iOS app and its AutoFill extension | The Mac app and its extension are shipped and settled | |
| +| Cross-instance vault sharing (federation) | Never in v1 — the biggest scope trap; revisit only after single-instance sharing is proven | |
| +| Agent *read* of a secret value into context | Not planned. Would need a written argument that use-without-seeing genuinely cannot serve a real case | |
| +| Breach / reused-password monitoring | Requires sending something derived from a value off-device; needs an A1-compatible design before it is even sketched | |
| +| Secure sharing with non-members (one-off links) | After vault sharing ships | |