Files
teahub/docs/rebuild.md
T

3.4 KiB

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.