Skip to content

Move system startup orchestration out of Astro middleware #64

Description

@pablopunk

Context

All critical system initialization currently runs from the auth middleware on the first SSR request, at commit 3531916:

  • src/middleware.ts:57-69runInitialization() sequentially runs: ensureDatabaseReady(), ensureEffectQueueWorkerStarted(), ensureDoceSharedNetwork(), ensureGlobalPnpmVolume(), ensureGlobalOpencodeStarted(), startImagePrewarm(), startDefaultsBootstrap(), ensureTailscaleStarted().
  • It's triggered via ensureInitialized() inside getSetupNeeded(), which the auth middleware awaits on every request (with a 5s retry cooldown on failure).
  • src/middleware.ts:99-109 — a session-cleanup setInterval is registered at module-eval time.
  • Module-scope side effects at import time: initConfig(), shutdown-handler installation (src/middleware.ts:27-37).

Problems: the first request (and every request during a failed-startup window) pays for Docker/OpenCode/Tailscale orchestration; a failure in any subsystem turns into request-path errors; boot order is untestable; and the middleware file mixes two unrelated concerns (boot + auth).

What to do

  1. Extract runInitialization() and the cleanup interval into a dedicated server bootstrap module (e.g. src/server/bootstrap.ts), keeping the same ordering and the existing retry-cooldown semantics.
  2. Trigger it from the Node entrypoint instead of per-request. With @astrojs/node standalone, the conventional hook is Astro's server entry / astro:server:start in dev — make sure BOTH pnpm dev and the production node ./dist/server/entry.mjs path initialize. (If a per-request await ensureInitialized() guard must remain as a fallback for edge cases, it should be a cheap no-op after first success — it already is — but it should no longer be the primary trigger.)
  3. Keep src/middleware.ts responsible only for security headers + auth/setup redirects.
  4. Preserve current behavior: setup detection (getSetupNeeded) must still work on a fresh DB, and startup failures must still be retried with the cooldown rather than crashing the process.

Escape hatch

If Astro 6 / @astrojs/node offers no clean pre-request server hook for both dev and prod, document that constraint in the PR and instead reduce the middleware's role to calling a single ensureBootstrapped() from the new module — the extraction is still worth it for testability and separation.

Acceptance criteria

  • Fresh-clone first run: setup flow at /setup still works; after setup, projects/queue/OpenCode/Tailscale come up as before.
  • src/middleware.ts contains no subsystem imports (docker/opencode/tailscale/queue/defaults).
  • App restart with existing data: first request does not block on Docker/OpenCode startup (verify via logs/timing).

Dependency

Land the verification baseline (separate issue) first — this refactor touches the boot path and needs pnpm check as a safety net.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions