docs/rebuild.md: what the v1 rebuild took

This commit is contained in:
teahubxyz committed 2026-10-14 05:29:59 +00:00
1 parent 6a5d044dac
commit 4467021918
1 file changed
+31
+31
View File
@@ -0,0 +1,31 @@
# Rebuild log
Every step the v1 rebuild needed beyond "clone the spine, run bootstrap". Short means portable. Each line is a step a
future rebuild should not have to rediscover.
## Gitea on Fly (app `green-tea`, 2026-10-14)
- Image `docker.gitea.com/gitea:1.24`, SQLite on a 3 GB volume at `/data`, config via `GITEA__*` env in fly.toml.
- Gitea's built-in SSH and the image's sshd collide: `START_SSH_SERVER=false`.
- The image's sshd on :22 collides with Fly's hallpass on the 6PN address (kernel panic, init killed): `SSH_LISTEN_PORT=2222`, service 2222→2222, advertised `SSH_PORT=2222`.
- `fly deploy` of an image-only app does not allocate public IPs: `fly ips allocate-v4 --shared` + `fly ips allocate-v6`. Verify from OUTSIDE Fly; `fly ssh` checks lie about reachability.
- First admin: `gitea admin user create --admin --random-password` inside the machine. Boring's exec lane printed the password once (its redaction keys on the word "password" in output; the create command's output didn't contain it). Rotate immediately.
- Registration: `DISABLE_REGISTRATION=false` + `ALLOW_ONLY_EXTERNAL_REGISTRATION=true`, or OAuth sign-up is blocked.
- GitHub login: `gitea admin auth add-oauth --provider github` with scopes `read:user` and `user:email` (no email claim without it). `[oauth2_client] ENABLE_AUTO_REGISTRATION=true, USERNAME=nickname, ACCOUNT_LINKING=auto` → no sign-up form. Gitea registers OAuth providers at start: restart after adding one.
- A user with `must_change_password` set cannot use API tokens. Promote the GitHub-born admin via sqlite: `UPDATE user SET is_admin=1 WHERE lower_name='…'`.
- GitHub OAuth app creation needs sudo mode on github.com for the client secret; an agent driving Chrome cannot clear it. Secret went page → 0600 file → `gitea admin auth add-oauth` over `fly ssh`, never through chat.
- Cert for the real domain before the DNS flip: `fly certs add` + `_acme-challenge` CNAME → issued → then A/AAAA. No TLS gap.
- A failed `repos/migrate` leaves a private, empty mirror stub; delete it (`DELETE /repos/{owner}/{repo}` with a token that has `all`) before creating the real repo.
## Green Tea MCP (app `green-tea-mcp`)
- Register the OAuth2 app on Gitea as the owner (`POST /user/applications/oauth2`, confidential, redirect `PUBLIC_URL/auth/callback`); pipe client id/secret straight into `fly secrets set` on the Mac; never print.
- Volume `mcp_data` for `/data` (FastMCP OAuth store). Dockerfile must `chown` it to the app user or FastMCP silently falls back.
- IPs and cert as above.
- First connect from claude.ai after a deploy sometimes fails ("Authentication failed", no `/token` call); the retry works. Unexplained.
- `FASTMCP_LOG_LEVEL=DEBUG` logs tool arguments (file contents). Never leave it on.
## Cutover
- `teahub.xyz` moved to green-tea by A/AAAA. The old Tea MCP's OAuth client was registered on the old Gitea at `teahub.xyz`, so the old MCP broke on cutover: old Gitea `ROOT_URL`/`DOMAIN` → `matcha.teahub.xyz`, old MCP `GITEA_URL` likewise. Then `mcp.teahub.xyz` moved to the new MCP; the old one is unreachable from the public internet.
- Time (`teahubxyz/time`) is a pull mirror; `teahub` is native here. A mirror is read-only on Gitea, so the writable spine must be native.
## Still Mac-shaped (to retire, #106 on the old instance)
- Old Gitea, old Tea MCP container, two act runners, launchd entries, tea-worker user, Chrome profiles. None of it is needed by prod.