Skip to content

Commit ba28e83

Browse files
authored
docs: record the authentication decisions and spec v0.5 (#61)
Two principal tables, cookie sessions backed by a sessions row, crypto.scrypt for password hashing, and a setup token plus a count check on the first-run wizard. Owner authentication remains undecided. Also updates the spec section to v0.5 and reorders the task list. Signed-off-by: Matthew Wren <info@origindev.com>
1 parent 96627ff commit ba28e83

1 file changed

Lines changed: 27 additions & 8 deletions

File tree

docs/decisions.md

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,20 @@ Ownership was previously answerable only by joining `claims where status = 'appr
105105

106106
Docker-first. Four containers: `osds-app`, `osds-worker`, `postgres`, `minio` — the last two replaceable by managed services through environment variables alone. First-run browser wizard, never config files. Named Docker volumes on Windows, not bind mounts.
107107

108+
### Authentication
109+
110+
Decided; not implemented. Blocks `packages/api`.
111+
112+
| Decision | Value | Reasoning |
113+
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
114+
| **Principals** | Two tables. `operators` holds the deployment operator and tenant staff, with a `staff_memberships (operator_id, tenant_id, role)` join. `users` holds listing owners and claimants | One login may administer many tenants; a user belongs to one. Both have an email address, which is not a reason to merge them. Spec §4.3 |
115+
| **Transport** | Cookie. HttpOnly, Secure, SameSite=Lax, `__Host-` prefix | `packages/web` is server components with no client JS, so a bearer token has nothing to attach it and localStorage is XSS-readable. Machine auth is already HMAC per §8.4 |
116+
| **Session state** | A sessions table, not a stateless signed token | Revocation is a delete, and `tenant.suspended` kills every session for a tenant in one statement. A stateless token needs a denylist, which is a table with extra steps. An admin panel does single-digit requests per second |
117+
| **Password hashing** | Node's `crypto.scrypt` | Zero dependencies, no native build, no Windows toolchain, no stop-and-ask. Weaker than argon2id against GPU attackers, but a legitimate KDF at OWASP parameters. Store parameters in the hash string so raising them is a rehash-on-login. `maxmem` must be raised explicitly or it throws at high `N` |
118+
| **First-run wizard** | Setup token printed to container logs, plus the route disappearing once `operators` is non-empty | §13 targets someone who rents a server. The gap between `docker compose up` and opening a browser is a real window on a public IP, and the count check alone does not cover it |
119+
120+
**Owner authentication is not decided.** A claimant proves control of an email or phone during verification but never sets a credential, and §6.5 requires owners to reach a dashboard. The proposal on the table is no owner passwords at all — an expiring one-time code to the address on the `users` row. Issue #56. The deployment operator keeps a password regardless, because the wizard runs before mail is necessarily configured.
121+
108122
---
109123

110124
## 3. Current state
@@ -132,20 +146,25 @@ CI provides only an owner database URL. `osds_app` is created `NOLOGIN` with no
132146

133147
### Spec
134148

135-
`docs/spec/events-and-adapters.md`, currently **v0.4**. Authoritative — where code and spec disagree, the spec wins. Spec edits are a maintainer action, not an agent action.
149+
`docs/spec/events-and-adapters.md`, currently **v0.5**. Authoritative — where code and spec disagree, the spec wins. Spec edits are a maintainer action, not an agent action.
150+
151+
v0.5 closed the four gaps the command layer opened: §7.1 (`listing.upsert` payload and match key), §9.5 (verification code lifetime per method), §4.3 (the user entity and how a claimant row is created), §11.2 (the command log's write discipline and its nullable `tenant_id`).
136152

137-
Seven open issues are spec gaps this session's code had to decide provisionally: #36, #38, #42, #44, #48, #49, #50. Each names the assumption the code makes and where it should be written down.
153+
Three of those left the code behind the spec, deliberately and tracked: #42 (`categories` accepted by §7.1, rejected by the handler), #54 (TTL relayed rather than computed), #58 (`user.created` specified but neither emitted nor typed).
138154

139155
---
140156

141157
## 4. Next tasks, in order
142158

143-
1. **Admin auth decision** — blocks `packages/api`. Cookie vs bearer token; deployment-level admin vs per-tenant staff against the same `users` table; password hashing, which is a new runtime dependency and therefore a stop-and-ask. Maintainer decision.
144-
2. **`packages/api`** — the request-handling library. Tenant resolution from the host header, command dispatch into `@osds/core/persist`, RFC 7807 responses, `409` on idempotency replay.
145-
3. **Outbox consumer in `packages/worker`.** `LISTEN/NOTIFY` with polling fallback, exponential backoff (1s → 1h, 12 attempts), dead-letter queue, 30s handler timeout. Nothing external fires until this exists.
146-
4. **Bundled `smtp` and `webhook` adapters.** Without `smtp`, no claim verification code can be sent, so nobody can claim a listing.
147-
5. **Claim flow end to end** — the first vertical slice touching every layer.
148-
6. **Slot allocator** wrapping the approved `FOR UPDATE SKIP LOCKED` SQL.
159+
1. **#20** — CLAUDE.md scope wording, supervised local session versus the autonomous Odin agent.
160+
2. **#54 and #58 together** — bring the claim submit path in line with v0.5. One code path, one PR.
161+
3. **#42**`persistListingUpsert` writes `listing_categories`.
162+
4. **Admin auth** — the rulings above, implemented. Blocks everything below it.
163+
5. **`packages/api`** — the request-handling library. Tenant resolution, command dispatch into `@osds/core/persist`, RFC 7807 responses, `409` on idempotency replay.
164+
6. **Outbox consumer in `packages/worker`.** `LISTEN/NOTIFY` with polling fallback, exponential backoff (1s → 1h, 12 attempts), dead-letter queue, 30s handler timeout. Nothing external fires until this exists.
165+
7. **Bundled `smtp` and `webhook` adapters.** Without `smtp`, no claim verification code can be sent, so nobody can claim a listing.
166+
8. **Claim flow end to end** — the first vertical slice touching every layer.
167+
9. **Slot allocator** wrapping the approved `FOR UPDATE SKIP LOCKED` SQL.
149168

150169
Deferred until there is traffic: the Odin repo-watching agent.
151170

0 commit comments

Comments
 (0)