steid

@jamesgill /

steid/plans/runbook.md
5.9 KBCode·Blame·Raw
ab7fea9chore: plans setup1mo
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
d7b99d9docs: record milestone 0 progress and routing findings1mo
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.
ca76e1bdocs: fix milestone cross-references after the reorder24d
17- `git` on `PATH` (from Milestone 3 — `git init --bare` creates repos, and from
02eb2e4feat: GitStorage port and DiskGitStorage24d
18 Milestone 4 `git http-backend` serves the protocol). **`cargo test` needs it too**:
19 `DiskGitStorage`'s tests run real `git init`, so a machine without `git` fails the
20 suite, not just the app.
ab7fea9chore: plans setup1mo
21
22## Dev setup
23
24```bash
d7b99d9docs: record milestone 0 progress and routing findings1mo
25cargo run # serves on http://127.0.0.1:3000
26cargo test
27```
28
29```bash
30cargo install topcoat-cli # dev server: watch, rebuild, asset bundling
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
31topcoat dev # working
ab7fea9chore: plans setup1mo
32```
33
d7b99d9docs: record milestone 0 progress and routing findings1mo
34`topcoat dev` builds, bundles assets, watches sources, and live-reloads pages that
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
35include `topcoat::dev::script()`. Press `r` to force a rebuild.
d7b99d9docs: record milestone 0 progress and routing findings1mo
36
37## Configuration
38
39`STEID_`-prefixed env vars via `dotenvy` + `envy`, read into `AppConfig`. Every value
40has a default, so a bare `cargo run` works with no environment at all.
41
42Live now:
43
44```
45STEID_DATABASE_URL=sqlite:steid.db?mode=rwc # default
02eb2e4feat: GitStorage port and DiskGitStorage24d
46STEID_DATA_DIR=./data # default; bare repos, read from M3
88583f2docs: bring tracking docs up to date with milestone 11mo
47STEID_INSECURE_COOKIES=false # default; see below
d7b99d9docs: record milestone 0 progress and routing findings1mo
48```
49
88583f2docs: bring tracking docs up to date with milestone 11mo
50There is deliberately **no owner password in configuration** — the owner is created
51through the claim flow instead. See
52[0002]decisions/0002-first-run-claim-not-config-bootstrap.md.
53
d7b99d9docs: record milestone 0 progress and routing findings1mo
54The bind address is **not** a `STEID_` variable — Topcoat owns it:
55
56```bash
57HOST=0.0.0.0 PORT=8080 cargo run
58```
ab7fea9chore: plans setup1mo
59
d7b99d9docs: record milestone 0 progress and routing findings1mo
60That supersedes attempt #2's `STEID_LISTEN_ADDR`.
ab7fea9chore: plans setup1mo
61
88583f2docs: bring tracking docs up to date with milestone 11mo
62### `STEID_INSECURE_COOKIES` — development only
ab7fea9chore: plans setup1mo
63
88583f2docs: bring tracking docs up to date with milestone 11mo
64Topcoat's session cookie is `__Host-` prefixed and `Secure`. `Secure` means the browser
65only keeps it over a trustworthy origin, and browsers disagree about whether
66plain-HTTP `localhost` qualifies. Where it doesn't, **the failure is completely
67silent**: the server issues a session and records the row, the browser discards the
68cookie, and every page renders signed out with no error anywhere. This cost an
69afternoon; the symptom looks exactly like broken auth logic.
70
71Setting `STEID_INSECURE_COOKIES=true` swaps in `InsecureCookieTokenStore` — the same
72cookie without `Secure` and without the prefix, named `steid-dev-session` so it can
73never be confused with a hardened one. `HttpOnly` and `SameSite=Lax` are kept. Boot
74prints a warning while it's on.
75
76**Never set this on a deployed instance.** Without `Secure` the session cookie travels
77unencrypted and anyone on the network path can lift it and become that user. Behind
78TLS, leave it unset.
79
80A gitignored `.env` in the repo root sets it for local work. Keep `.env` and
81`.env.prod` out of git — both are gitignored.
82
83## First run
84
85```bash
86cargo run # or: topcoat dev
ab7fea9chore: plans setup1mo
87```
88
1a58e00docs: runbook named routes that no longer exist24d
89An unclaimed instance prints a setup token and redirects every route to `/auth/setup`.
88583f2docs: bring tracking docs up to date with milestone 11mo
90Paste the token, choose a handle, email, and password, and the owner is created and
91signed in.
92
93The token is **held in memory only**, so every restart mints a new one — including
94each rebuild under `topcoat dev`. Use the most recent one printed. Once claimed, no
1a58e00docs: runbook named routes that no longer exist24d
95token is minted at all and `/auth/setup` redirects away.
88583f2docs: bring tracking docs up to date with milestone 11mo
96
1a58e00docs: runbook named routes that no longer exist24d
97Sign in at `/auth/login` with the **email**, not the handle.
ab7fea9chore: plans setup1mo
98
1a58e00docs: runbook named routes that no longer exist24d
99## Repo layout on disk (Milestone 3)
ab7fea9chore: plans setup1mo
100
02eb2e4feat: GitStorage port and DiskGitStorage24d
101Bare repos at `{STEID_DATA_DIR}/{handle}/{name}.git`, created with no template (so no
102`.sample` hooks) and `HEAD` pinned to `refs/heads/main` regardless of the host's
103`init.defaultBranch`. See [0006]decisions/0006-git-binary-behind-narrow-ports.md.
104
105Created empty — no initial commit and no branch, like GitHub.
106
107Creating one refuses rather than reusing a directory that already exists, so an orphan
108left by a create that died mid-way blocks that name until it is removed by hand.
ab7fea9chore: plans setup1mo
109
ca76e1bdocs: fix milestone cross-references after the reorder24d
110## Git transport (Milestone 4)
ab7fea9chore: plans setup1mo
111
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
112Smart HTTP, delegated to `git http-backend`, authenticated with personal access tokens
113over HTTP Basic — see [0001]decisions/0001-git-over-http-not-ssh.md. No SSH, no host
114keys, no `authorized_keys`.
ab7fea9chore: plans setup1mo
115
116```bash
1a58e00docs: runbook named routes that no longer exist24d
117git clone http://host/{handle}/repos/{name}.git
ab7fea9chore: plans setup1mo
118```
119
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
120Fill in the token workflow and the `body_limit` setting once this is built.
121
ab7fea9chore: plans setup1mo
122## Manual verification checklist (#2)
123
124Attempt #2 verified these by hand each milestone but never wrote down the steps. They
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
125are the smoke test for Milestones 4–5. The auth rows assumed SSH keys; the shape of
126the check still holds with tokens substituted:
ab7fea9chore: plans setup1mo
127
64222c3docs: record the icon staging trap and tick the repo-creation check24d
128- [x] Create a repo via the web UI → bare repo appears at
129 `{data_dir}/{handle}/{name}.git` *(Milestone 3; also checked that the name
130 normalises, that a duplicate re-renders the form, and that a private repo 404s
131 for a signed-out visitor)*
ab7fea9chore: plans setup1mo
132- [ ] `git clone` an empty repo → succeeds
133- [ ] `git clone` a repo with history → succeeds
134- [ ] `git clone` a non-existent repo → clean error, not a hang or panic
135- [ ] First push to an empty repo → succeeds
136- [ ] Push to a repo with history → succeeds
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
137- [ ] Push a repo large enough to exercise the `body_limit` cap → succeeds
138- [ ] Clone with no credentials → rejected, and the prompt is comprehensible
139- [ ] Clone with a valid token → succeeds
140- [ ] Revoke the token → subsequent clone rejected at auth
ab7fea9chore: plans setup1mo
141- [ ] Clone a private repo as a non-member → rejected
142- [ ] Push as a non-owner member → rejected
143
144Worth automating as an integration test rather than re-running by hand a fourth time.
145
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
146## `.gitignore`
ab7fea9chore: plans setup1mo
147
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
148Applied: `/target`, `/data`, `*.db*`, `.env`, `.env.prod`.
ab7fea9chore: plans setup1mo
149
150**Do not add `/plans`.** Attempt #2 did, and that is why these docs had to be
151hand-carried between repos.