steid

@jamesgill /

docs: record milestone 0 progress and routing findings

Tick off the completed skeleton steps and capture what was learned about
Topcoat routing: discover() collects pages at link time so layering stays
our choice, module_router! is deferred to M1 when there are real routes to
design against, and HOST/PORT replace STEID_LISTEN_ADDR.

Runbook now documents the rustc >= 1.95 requirement and the silent
topcoat v0.0.0 fallback that hides it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
JamesPatrickGill authored 1 month agoparent0d12f7dBrowse filesd7b99d9b3f8108bc7472cc11e8441261e7dfa10d

3 files changed+72 −26

plans/ROADMAP.md+1 −1View file
@@ -49,7 +49,7 @@ that proved. Treat that code as reference, not as a baseline.
4949
5050 | # | Milestone | Status |
5151 |---|---|---|
52| 0 | **Skeleton** — Topcoat app boots, config, one page, SQLite wired | active |
52+| 0 | **Skeleton** — Topcoat app boots, config, one page, SQLite wired | nearly done |
5353 | 1 | **Identity** — users, orgs, memberships, sessions, registration policy | not started |
5454 | 2 | **Repo model** — repo records + bare repos on disk | not started |
5555 | 3 | **Git over SSH** — clone and push via embedded russh | not started |
plans/current.md+31 −10View file
@@ -12,21 +12,42 @@ committing the architecture to it.
1212
1313 ### Steps
1414
15- [ ] Add `topcoat` 0.5 + `tokio` to `Cargo.toml`; install `topcoat-cli`
16- [ ] Get the getting-started hello-world page rendering
17- [ ] Work out how `#[page]` / `module_router!` discovery wants the source tree laid
18 out — this constrains everything after it
19- [ ] Config from env via `envy` (`STEID_*`), mirroring `.env.dev` from the previous
20 attempt (see [runbook.md](runbook.md))
21- [ ] SQLite pool registered as app context; confirm a page can read it via
22 `app_context::<T>(cx)`
15+- [x] Add `topcoat` 0.5 + `tokio` to `Cargo.toml`
16+- [x] Get the getting-started hello-world page rendering
17+- [x] Work out how routing wants the source tree laid out — see
18+ [routing findings](#routing-findings) below
19+- [x] Config from env via `envy` (`STEID_*`) — see [runbook.md](runbook.md)
20+- [x] SQLite pool registered as app context; page reads it via `app_context::<T>(cx)`
21+- [ ] Install `topcoat-cli` and confirm `topcoat dev` watch/reload works
2322 - [ ] Decide the layering question in [architecture.md](architecture.md#open-question-topcoats-data-access-vs-clean-architecture)
2423 — this is the one genuinely open design question and it blocks Milestone 1
2524
2625 ### Done when
2726
28`cargo run` (or `topcoat` CLI) serves a page that renders a value read from SQLite,
29with config supplied by env.
27+`cargo run` serves a page that renders a value read from SQLite, with config supplied
28+by env. **Met** — renders the SQLite version, and `STEID_DATABASE_URL` redirects it to
29+a different file.
30+
31+### Routing findings
32+
33+- **Topcoat 0.5 requires rustc ≥ 1.95.** On an older toolchain `cargo add topcoat`
34+ silently resolves to an empty `topcoat v0.0.0` placeholder instead of failing. Local
35+ stable is now 1.97.1. Worth a `rust-toolchain.toml` if this ever builds on another
36+ machine.
37+- `Router::builder().discover()` collects `#[page]`-annotated items **at link time**,
38+ so pages can live in any module. Layering is our choice, not the framework's — the
39+ home page sits in `infrastructure/web.rs` per `architecture.md`.
40+- `module_router!` is Topcoat's recommended alternative, deriving each URL from the
41+ module tree rather than a path string. **Deferred to Milestone 1.** Steid's URL space
42+ is parameterised at the root (`/{owner}`, `/{owner}/{repo}`, `/{owner}/keys`), which
43+ means `path_param!` declarations inside route modules; worth designing once there are
44+ real routes rather than guessing now.
45+- Path and query params are read from `Cx` via `path_param!` / `#[query_params]`, not
46+ injected as handler arguments. Parses are memoized per request.
47+- Layouts wrap by path prefix and nest outermost-first, and a layout can catch a page's
48+ `NotFoundError` to render a branded 404 — that replaces attempt #2's `not_found.html`.
49+- `HOST` / `PORT` configure the bind address, so `STEID_LISTEN_ADDR` from attempt #2 is
50+ gone. `runbook.md` reflects this.
3051
3152 ## Backlog
3253
plans/runbook.md+40 −15View file
@@ -6,38 +6,63 @@
66
77 ## Status
88
9Milestone 0 is in progress and the app doesn't boot yet. Everything below marked
10**(#2)** is carried from the previous attempt and needs re-verifying against Topcoat
11before it can be trusted.
9+The app boots and serves. Everything marked **(#2)** is carried from the previous
10+attempt 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)
1218
1319 ## Dev setup
1420
1521 ```bash
16cargo install topcoat-cli # dev server, asset bundling, watch mode
17cargo run # or the topcoat CLI once routing is wired
22+cargo run # serves on http://127.0.0.1:3000
23+cargo test
24+```
25+
26+```bash
27+cargo install topcoat-cli # dev server: watch, rebuild, asset bundling
28+topcoat dev # not yet tried on this project
1829 ```
1930
20Topcoat's CLI builds the app, bundles assets, and watches source directories for
21rebuilds. How that interacts with the embedded SSH server is unresolved — see
22[current.md](current.md#open-questions).
31+`topcoat dev` builds, bundles assets, watches sources, and live-reloads pages that
32+include `topcoat::dev::script()`. How that interacts with an embedded SSH server is
33+unresolved — see [current.md](current.md#open-questions).
34+
35+## Configuration
36+
37+`STEID_`-prefixed env vars via `dotenvy` + `envy`, read into `AppConfig`. Every value
38+has a default, so a bare `cargo run` works with no environment at all.
39+
40+Live now:
41+
42+```
43+STEID_DATABASE_URL=sqlite:steid.db?mode=rwc # default
44+STEID_DATA_DIR=./data # default; bare repos, used from M2
45+```
46+
47+The bind address is **not** a `STEID_` variable — Topcoat owns it:
48+
49+```bash
50+HOST=0.0.0.0 PORT=8080 cargo run
51+```
2352
24## Configuration (#2)
53+That supersedes attempt #2's `STEID_LISTEN_ADDR`.
2554
26Env vars, `STEID_` prefixed, loaded with `dotenvy` + `envy`. From attempt #2's
27`.env.dev`:
55+Arriving with Milestone 1 (#2) — owner bootstrap and registration policy:
2856
2957 ```
3058 STEID_REGISTRATION=personal # personal | invite | open
3159 STEID_OWNER_EMAIL=admin@localhost.dev
3260 STEID_OWNER_PASSWORD=changeme
3361 STEID_OWNER_USERNAME=admin
34STEID_DATABASE_URL=sqlite:steid.db?mode=rwc
35STEID_LISTEN_ADDR=127.0.0.1:3000
36STEID_DATA_DIR=./data # bare repos live here
3762 ```
3863
3964 In `personal` mode the owner account is bootstrapped from `STEID_OWNER_*` on first
40boot. Keep `.env.prod` out of git.
65+boot. Keep `.env` and `.env.prod` out of git — both are gitignored.
4166
4267 ## Repo layout on disk (#2)
4368