Thanks to visit codestin.com
Credit goes to github.com

Skip to content

installer-setup WS-C: close /api/install/* after first-run sentinel#1126

Merged
thinmintdev merged 1 commit into
mainfrom
feat/1105-close-install-endpoints
Jul 6, 2026
Merged

installer-setup WS-C: close /api/install/* after first-run sentinel#1126
thinmintdev merged 1 commit into
mainfrom
feat/1105-close-install-endpoints

Conversation

@thinmintdev

Copy link
Copy Markdown
Contributor

Summary

Closes #1105.

ADR-0012 removed auth from /api/install/*, leaving the surface fully open on the local network. Once the first-run sentinel exists (written by POST /apply / /apply-selections / /complete per #1101/#1124), anyone reaching the port could still re-trigger a full install, rewrite slots, or re-dispatch pulls indefinitely.

  • Added InstallClosed (409, install.closed) and a _require_install_open() guard in src/hal0/api/routes/installer.py.
  • Guarded the three provisioning endpoints — POST /apply, POST /apply-selections, POST /complete — so each refuses fast (before any body parsing / hardware probing / provisioning) once .first_run_done exists.
  • GET /state is untouched and keeps reporting first_run: false regardless.
  • Left the day-2 tools that happen to live on this router open on purpose: POST /probe, PUT /slots/{name}/model (used by hal0 model swap long after setup — see src/hal0/cli/slot_commands.py), GET /curated-models, GET /services, POST /services/{unit}/repair. These aren't re-provisioning and gating them would break normal post-setup operation.
  • Updated docstrings (module header + each guarded route) to document the new closed behavior.

Checklist

  • /api/install/* mutating install endpoints return a closed status once the first-run sentinel is set
  • First-run flow still works before completion (unaffected — guard is a no-op without the sentinel)
  • GET /state still works and reports first_run: false post-close
  • A second /complete (or a re-run of /apply//apply-selections) after setup is a safe 409 refusal, not a crash or silent no-op

Test plan

  • uv run ruff format src tests — no changes
  • uv run ruff check src tests — all checks passed
  • uv run ruff format --check src tests — all formatted
  • uv run pytest tests/api/test_installer_routes.py tests/api/test_install_apply.py tests/api/test_apply_selections.py -q — 31 passed
  • uv run pytest tests/ -q --ignore=tests/e2e — 4641 passed, 14 known pre-existing failures + 1 pre-existing error (hermes venv/state-render/wrapper, comfyui phase4, proxmox host detection, container spec dispatch, config-url-proxy, models-preview, settings-store, openwebui prewire) — confirmed identical on origin/main before this change, unrelated to installer.py

🤖 Generated with Claude Code

…un sentinel

ADR-0012 removed auth from /api/install/*, so on a LAN-bound box anyone
reaching the port could re-run POST /apply, /apply-selections, or
/complete indefinitely — rewriting slots, re-dispatching pulls, or
re-flipping first_run long after setup finished. Once the first-run
sentinel exists (written by #1101/#1124), these three provisioning
endpoints now refuse with 409 install.closed instead of re-provisioning.
GET /state stays readable, and day-2 tools on this router (/probe,
PUT /slots/{name}/model, /curated-models, /services*) stay open since
they're legitimate post-setup operations, not re-provisioning.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@thinmintdev thinmintdev merged commit c127ac1 into main Jul 6, 2026
3 checks passed
@thinmintdev thinmintdev deleted the feat/1105-close-install-endpoints branch July 6, 2026 05:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

installer-setup WS-C: close /api/install/* after first-run sentinel

1 participant