steid

@jamesgill /

steid/plans/runbook.md
5.1 KBCode·Blame·Raw
1# Runbook
2
3> Attempt #2's only setup instructions lived in a plan file describing an architecture
4> that had already been deleted, so they were actively wrong. Keep this file honest:
5> if a command here doesn't work, fix it or delete it.
6
7## Status
8
9The app boots and serves. Everything marked **(#2)** is carried from the previous
10attempt and has **not** been re-verified against Topcoat — treat it as a sketch.
11
12## Requirements
13
14- **rustc ≥ 1.95** — Topcoat 0.5 requires it, and on an older toolchain `cargo add
15 topcoat` silently resolves to an empty `topcoat v0.0.0` placeholder rather than
16 failing. Verified on 1.97.1.
17- `git` on `PATH` (from Milestone 3 — the protocol server shells out to it)
18
19## Dev setup
20
21```bash
22cargo run # serves on http://127.0.0.1:3000
23cargo test
24```
25
26```bash
27cargo install topcoat-cli # dev server: watch, rebuild, asset bundling
28topcoat dev # working
29```
30
31`topcoat dev` builds, bundles assets, watches sources, and live-reloads pages that
32include `topcoat::dev::script()`. Press `r` to force a rebuild.
33
34## Configuration
35
36`STEID_`-prefixed env vars via `dotenvy` + `envy`, read into `AppConfig`. Every value
37has a default, so a bare `cargo run` works with no environment at all.
38
39Live now:
40
41```
42STEID_DATABASE_URL=sqlite:steid.db?mode=rwc # default
43STEID_DATA_DIR=./data # default; bare repos, used from M4
44STEID_INSECURE_COOKIES=false # default; see below
45```
46
47There is deliberately **no owner password in configuration** — the owner is created
48through the claim flow instead. See
49[0002]decisions/0002-first-run-claim-not-config-bootstrap.md.
50
51The bind address is **not** a `STEID_` variable — Topcoat owns it:
52
53```bash
54HOST=0.0.0.0 PORT=8080 cargo run
55```
56
57That supersedes attempt #2's `STEID_LISTEN_ADDR`.
58
59### `STEID_INSECURE_COOKIES` — development only
60
61Topcoat's session cookie is `__Host-` prefixed and `Secure`. `Secure` means the browser
62only keeps it over a trustworthy origin, and browsers disagree about whether
63plain-HTTP `localhost` qualifies. Where it doesn't, **the failure is completely
64silent**: the server issues a session and records the row, the browser discards the
65cookie, and every page renders signed out with no error anywhere. This cost an
66afternoon; the symptom looks exactly like broken auth logic.
67
68Setting `STEID_INSECURE_COOKIES=true` swaps in `InsecureCookieTokenStore` — the same
69cookie without `Secure` and without the prefix, named `steid-dev-session` so it can
70never be confused with a hardened one. `HttpOnly` and `SameSite=Lax` are kept. Boot
71prints a warning while it's on.
72
73**Never set this on a deployed instance.** Without `Secure` the session cookie travels
74unencrypted and anyone on the network path can lift it and become that user. Behind
75TLS, leave it unset.
76
77A gitignored `.env` in the repo root sets it for local work. Keep `.env` and
78`.env.prod` out of git — both are gitignored.
79
80## First run
81
82```bash
83cargo run # or: topcoat dev
84```
85
86An unclaimed instance prints a setup token and redirects every route to `/setup`.
87Paste the token, choose a handle, email, and password, and the owner is created and
88signed in.
89
90The token is **held in memory only**, so every restart mints a new one — including
91each rebuild under `topcoat dev`. Use the most recent one printed. Once claimed, no
92token is minted at all and `/setup` redirects away.
93
94Sign in at `/login` with the **email**, not the handle.
95
96## Repo layout on disk (#2)
97
98Bare repos at `{STEID_DATA_DIR}/{org}/{repo}.git`. Created empty — no initial commit.
99
100## Git transport (Milestone 5)
101
102Smart HTTP, delegated to `git http-backend`, authenticated with personal access tokens
103over HTTP Basic — see [0001]decisions/0001-git-over-http-not-ssh.md. No SSH, no host
104keys, no `authorized_keys`.
105
106```bash
107git clone http://host/owner/repo.git
108```
109
110Fill in the token workflow and the `body_limit` setting once this is built.
111
112## Manual verification checklist (#2)
113
114Attempt #2 verified these by hand each milestone but never wrote down the steps. They
115are the smoke test for Milestones 4–5. The auth rows assumed SSH keys; the shape of
116the check still holds with tokens substituted:
117
118- [ ] Create a repo via the web UI → bare repo appears at
119 `{data_dir}/{org}/{repo}.git`
120- [ ] `git clone` an empty repo → succeeds
121- [ ] `git clone` a repo with history → succeeds
122- [ ] `git clone` a non-existent repo → clean error, not a hang or panic
123- [ ] First push to an empty repo → succeeds
124- [ ] Push to a repo with history → succeeds
125- [ ] Push a repo large enough to exercise the `body_limit` cap → succeeds
126- [ ] Clone with no credentials → rejected, and the prompt is comprehensible
127- [ ] Clone with a valid token → succeeds
128- [ ] Revoke the token → subsequent clone rejected at auth
129- [ ] Clone a private repo as a non-member → rejected
130- [ ] Push as a non-owner member → rejected
131
132Worth automating as an integration test rather than re-running by hand a fourth time.
133
134## `.gitignore`
135
136Applied: `/target`, `/data`, `*.db*`, `.env`, `.env.prod`.
137
138**Do not add `/plans`.** Attempt #2 did, and that is why these docs had to be
139hand-carried between repos.