carlos / contra Public

Joining the Oficina suite: what the suite may own, and the four things CONTRA refuses Question open

🤖 Decided by Paul, 2026-09-07, and recorded as AGENTS.md decision 11 in 51585d2. Opening this for the parts still to settle, not to reopen the direction.

The decision

CONTRA becomes an Oficina app at <team>.contra.oficina.works — one instance per team, the suite's own convention. Not contra.oficina.works: a single host means one multi-tenant deployment, which is what decision 1 exists to refuse.

Why it fits better than expected

Three things could have blocked this, and the suite's specs answer all three without either project having consulted the other:

  • Its directory is a blind index over the address — HMAC(pepper, lowercased address) — and the home's people table has no address column. That is decision 3, arrived at independently.
  • No person lookup exists. No search, no contact list, and a testing gate asserting one person's directory never contains another's rows: 404, never 403. Our refusal to build an address-to-account endpoint (#14) survives contact with the suite intact.
  • Assertions carry an opaque person ref, never an address, so joining teaches this instance nothing about anybody.

The mismatch, and why it points the other way

The suite chose sealed-at-rest with the key in instance memory and explicitly rejected full end-to-end encryption. Its doctrine then concedes that a compromised credential origin can sign in as anyone, owners included, and calls that bound absent rather than restating it in weaker words.

For every other app in the suite, that means read everything. Here it means a pile of ciphertext, because signing in was never what opens a vault. CONTRA is the one app that degrades gracefully under the failure the suite admits it cannot bound. That is the argument for putting it there.

What the roster fixes

Sharing currently means handing somebody 32 hexadecimal characters, because we will not build an address lookup. The team roster, scoped to your own team, lets the share dialog name real teammates while the grant stays a keyset sealed to their public key. Better to use, and no new leak — the sharing graph was already published residue.

The four refusals

Written down now rather than when somebody asks, because each one undoes a promise on the front page:

  1. No operator reset. The suite lists this as an open question. Here it is closed: an operator who could restore a vault is the support override this product's whole argument rests on not existing.
  2. No provisioned accounts. Provisioning creates an instance, never an account. Keys are born in the browser from a passphrase and a printed Account Key.
  3. Nothing feeds the suite's memory. There is nothing to index — the server has never seen a title.
  4. No activity pull. What this instance could truthfully report is exactly the residue we already publish; sending it elsewhere widens it.

Open, and wanted

  • The claim that narrows. Sign-in stops proving knowledge of the passphrase and starts meaning the home vouches for you. The key layer does not move. But "sign-in is a signature, not a stored verifier" becomes true of the key layer only, and README.md and PRODUCT.md must say so before this ships. A front-page claim that quietly narrows is worse than one never made.
  • Both shapes have to keep working. The lone shape — this app signing people in itself — stays correct for anyone self-hosting, and must not rot once the suite path exists.
  • person ref → account public key. The roster gives a person ref; sealing a member wrap needs a public key. That mapping presumably lives in this instance, registered by each member. Unspecified so far.
  • Not available yet. The Oficina home is a deployable skeleton and no app implements suite sign-in. This is direction, not integration.