T24: self-hosting guide + bun run setup #23

Merged
dzenda merged 3 commits from t24-self-hosting into t23-account 2026-11-14 17:49:43 +01:00
Owner

T24: Self-hosting guide + bun run setup

Stacked on #22 (t23-account) → … → #1. Merge those first.

bun run setup (never contacts Cloudflare)

  • Default (local dev): creates .dev.vars from the template with generated 32-byte TOKEN_ENCRYPTION_KEY and BETTER_AUTH_SECRET. On an existing file, every line is kept verbatim: setup only fills generated secrets that are present but empty, and appends template keys that are missing. The parser understands CRLF, export, spaces around = and quotes, so an existing key is never overwritten. The file is set to chmod 600, even if it already existed with 644. Then it applies local D1 migrations and warns if FIT_AUTH_CLIENT_* are empty.
  • --d1-id <uuid> --kv-id <hex32>: validates the IDs and writes them into wrangler.jsonc. The DB / OAUTH_KV binding objects are found structurally (a brace scanner that skips strings and comments, so property order doesn't matter), and it fails if a binding or key is missing. Then it regenerates worker-configuration.d.ts.
  • --production: prints the deploy checklist (create D1/KV, secrets, remote migrations, deploy) for the self-hoster to run.
  • .dev.vars.example documents ALLOWED_USERS.

docs/self-hosting.md

Prerequisites and quick start, then:

  • FIT OAuth app: redirect <PUBLIC_URL>/auth/callback, the exact scopes (and never course-restricted), and the lesson from T7 that Usermap, Sirius, Grades and Marast must be enabled as services/resources, not just scopes.
  • Optional GitLab app, and that the PAT fallback always works.
  • Deploy commands, a full configuration table, and operations: key rotation semantics, updates, D1 backups, account deletion, the MCP smoke test.
  • Security notes.

The README now links all docs, documents setup, mcp:smoke and fixtures:record, and the quick start uses bun run setup.

Verified

  • bun run lint && bun run typecheck && bun run test: 271 tests, 12 of them for setup.
    • .dev.vars handling: new file, never overwriting, unknown keys kept, idempotent, CRLF/export/spaces/quotes kept verbatim, quoted-empty counts as empty.
    • Other: random keys; IDs written for a reordered layout with an unrelated database_id binding and a comment containing a brace; loud failures; malformed IDs; the real wrangler.jsonc; the production checklist never says --local.
  • Ran bun run setup on the real dev .dev.vars: byte-identical afterwards, mode 600, local D1 migrated. --production prints the checklist, and an invalid --d1-id is rejected before any write.

Astra review (gpt-6-astra)

Fixed:

  1. (P1) Valid dotenv syntax (CRLF, export, spaces) wasn't recognised, so setup could overwrite an existing encryption key and strand stored tokens → existing files are kept verbatim with a full dotenv parser. Tests added.
  2. (P1) An existing 644 .dev.vars stayed world-readable after secrets were written → explicit chmod 600.
  3. (P2) ID replacement depended on text layout and could silently miss or hit the wrong binding → structural lookup by binding name, failing loudly.

🤖 Generated with Claude Code

## T24: Self-hosting guide + `bun run setup` **Stacked on #22 (`t23-account`) → … → #1.** Merge those first. ### `bun run setup` (never contacts Cloudflare) - **Default (local dev):** creates `.dev.vars` from the template with generated 32-byte `TOKEN_ENCRYPTION_KEY` and `BETTER_AUTH_SECRET`. On an **existing file, every line is kept verbatim**: setup only fills generated secrets that are present but empty, and appends template keys that are missing. The parser understands CRLF, `export`, spaces around `=` and quotes, so **an existing key is never overwritten**. The file is set to `chmod 600`, even if it already existed with 644. Then it applies local D1 migrations and warns if `FIT_AUTH_CLIENT_*` are empty. - **`--d1-id <uuid> --kv-id <hex32>`:** validates the IDs and writes them into `wrangler.jsonc`. The `DB` / `OAUTH_KV` binding objects are found **structurally** (a brace scanner that skips strings and comments, so property order doesn't matter), and it **fails** if a binding or key is missing. Then it regenerates `worker-configuration.d.ts`. - **`--production`:** *prints* the deploy checklist (create D1/KV, secrets, remote migrations, deploy) for the self-hoster to run. - `.dev.vars.example` documents `ALLOWED_USERS`. ### `docs/self-hosting.md` Prerequisites and quick start, then: - **FIT OAuth app:** redirect `<PUBLIC_URL>/auth/callback`, the exact scopes (and never `course-restricted`), and the lesson from T7 that **Usermap, Sirius, Grades and Marast must be enabled as services/resources**, not just scopes. - **Optional GitLab app**, and that the PAT fallback always works. - **Deploy commands**, a full configuration table, and operations: key rotation semantics, updates, D1 backups, account deletion, the MCP smoke test. - **Security notes.** The README now links all docs, documents `setup`, `mcp:smoke` and `fixtures:record`, and the quick start uses `bun run setup`. ### Verified - `bun run lint && bun run typecheck && bun run test`: 271 tests, 12 of them for setup. - **`.dev.vars` handling:** new file, never overwriting, unknown keys kept, idempotent, CRLF/`export`/spaces/quotes kept verbatim, quoted-empty counts as empty. - **Other:** random keys; IDs written for a reordered layout with an unrelated `database_id` binding and a comment containing a brace; loud failures; malformed IDs; the real `wrangler.jsonc`; the production checklist never says `--local`. - Ran `bun run setup` on the real dev `.dev.vars`: **byte-identical** afterwards, mode 600, local D1 migrated. `--production` prints the checklist, and an invalid `--d1-id` is rejected before any write. ### Astra review (gpt-6-astra) **Fixed:** 1. *(P1)* Valid dotenv syntax (CRLF, `export`, spaces) wasn't recognised, so setup could **overwrite an existing encryption key** and strand stored tokens → existing files are kept verbatim with a full dotenv parser. Tests added. 2. *(P1)* An existing 644 `.dev.vars` stayed world-readable after secrets were written → explicit `chmod 600`. 3. *(P2)* ID replacement depended on text layout and could silently miss or hit the wrong binding → structural lookup by binding name, failing loudly. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dzenda merged commit 379af1f45d into t23-account 2026-11-14 17:49:43 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
dzenda/os!23
No description provided.