Automatic updates for v4.1 — bayanat update + admin UI + opt-in patch auto-apply#320
Open
level09 wants to merge 37 commits into
Open
Automatic updates for v4.1 — bayanat update + admin UI + opt-in patch auto-apply#320level09 wants to merge 37 commits into
level09 wants to merge 37 commits into
Conversation
Adds nav-bar chip that polls /admin/api/updates/available every 6h and shows a confirm dialog to trigger an update. Adds polling progress dialog that opens on update-started event and tracks /admin/api/updates/status at 2s intervals until the run completes.
…through serialize/validation
…cal/bin, exempt /health from setup redirect
…host+no-password (matches backup_utils.py)
…for update start _install_self previously relied on $0 to locate the installer script. Under the standard 'curl ... | sudo bash -s install' pipe pattern, $0 is 'bash' and readlink resolves to the shell binary, which would then be installed as /usr/local/bin/bayanat. Every subsequent CLI invocation and wrapper-driven update would exec bash with 'update' as argv. Silent. Catastrophic on first use. Fix: _install_self now takes a tag arg and copies from $RELEASES_DIR/$tag/bayanat (the just-cloned release, known-good source). Also called in do_switch_verify's SUCCESS branch so the system CLI stays in lockstep with the active release after every successful update. Separately: @auth_required(within=15, grace=0) on POST /api/updates/start matches the freshness discipline already used by /system-administration/ and other sensitive admin endpoints. A stale cookie is no longer enough to trigger a privileged update.
…ore command _pg_env previously emitted 'export KEY=VALUE' lines from shared/.env and callers ran eval on the output. A malicious .env value (writable by the bayanat user) could inject shell commands that run as root under 'bayanat update'. Lateral-to-root escalation path. Replace _pg_env with _pg_load: parses POSTGRES_* keys natively in bash and uses 'export "$key=$val"' which never interprets the value. Verified a malicious value like 'foo; echo PWNED > /tmp/...' is stored as a literal password instead of being executed. Separately: the snapshots UI showed 'sudo -u bayanat bayanat restore', which hits require_root and fails. Matches the same bug class as P4 (runbook) that was fixed in docs but missed in the Vue component. Updated to 'sudo bayanat restore <name>'.
Codifies the manual 45-min ad-hoc test into a single-command script. Provisions a disposable CPX22 Ubuntu 24.04 VM, installs via curl|bash from the test fork's v4.0.0 tag, runs S1-S4 (happy / bad-migration / bad-health / recovery), asserts final state per scenario, tears down. Env knobs: KEEP_VM=1 leave VM running for iteration VM_IP=x.y.z.w reuse existing VM, skip provision/install SCENARIOS='S1 S2' run subset TEST_FORK=you/yourfork point at a different test fork Requires hcloud CLI + ssh-agent loaded + gh CLI authenticated. Full run ~8-10 minutes, costs well under one cent per cycle.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
… through an exception' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a one-click update flow for partner operators. Admin clicks "Update now" in the nav-bar banner → the system clones the new release, takes a pg_dump snapshot, stops services, runs migrations, swaps the symlink, restarts, verifies health, and either lands on the new version (~30-90s of 502) or automatically reverts the symlink and restarts on the previous release.
Optional
AUTO_APPLY_PATCH_UPDATEStoggle (default off) lets partners opt in to silent background application of patch-only bumps (4.1.0 → 4.1.1, never4.1.x → 4.2). Minor and major bumps always require a human click.This is the simplified design approved in the "Bayanat Automatic Updates, Simplified Design (2026-04-16)" Notion page — 3 layers, 4 phases, single code path, reuses every v4 installer primitive. See the linked Notion page for the full spec; this PR description is intentionally short.
What ships
CLI (
bayanatscript):bayanat update [<tag>]/--check/--recoverbayanat snapshots,bayanat restore <name>bayanat statusextended with update phase/usr/local/sbin/bayanat-start-updateroot wrapper + fixed sudoers grants/opt/bayanat/state/update.json, atomic writes, crash-safe recovery dispatchFlask:
GET /health(exempt from setup-wizard redirect + rate limiter)GET/POST /admin/api/updates/{available,start,status}with@auth_required(within=15, grace=0)on the privileged start endpoint/admin/snapshots/read-only pagecheck_for_updatesevery 6h with opt-in patch auto-applyVue (Vuetify 3):
UpdateBannerin the admin nav-barUpdateProgressDialogpolling/admin/api/updates/statusSnapshotsListpage (read-only; restore is CLI-only by design)Config:
AUTO_APPLY_PATCH_UPDATESwired throughDEFAULT_CONFIG,CONFIG_LABELS,settings.Config,ConfigManager.serialize(), andFullConfigValidationModel— admin UI can show and save it.Docs + tests:
docs/deployment/auto-update-runbook.md— operator referencee2e-auto-update.sh— one-command Hetzner E2E against the public test forklevel09/bayanat-update-testSecurity-relevant design notes
POST /api/updates/start(within=15). Stolen cookie is not enough to trigger a privileged update._pg_loadparsesshared/.envnatively — no eval on values written by thebayanatOS user. Prevents lateral-to-root escalation via malicious.env._install_selfcopies from\$RELEASES_DIR/\$tag/bayanat, never\$0. Safe under the standardcurl | sudo bash -s installpattern.Testing done
uv run pytest tests/test_health.py tests/test_update_check.py tests/test_update_endpoints.py— 19/19 passing../e2e-auto-update.sh— S1 happy path + S2 bad migration (NEEDS_INTERVENTION) + S3 bad /health (ROLLED_BACK) + S4 recovery, all green. Install path uses `curl | sudo bash -s install` against the test fork'sv4.0.0tag.The 15-entry gotchas list from the initial build + first E2E run is captured in the local skill at `.claude/skills/bayanat-auto-update/references/gotchas.md` for future contributors — covers everything from
v-prefix handling to eval-on-.env to setup-wizard redirect to socket vs TCP pg auth.Known limitations (deliberate, not blocking)
AUTO_APPLY_WINDOW_UTC=02:00-04:00is a planned follow-up. If a partner opts in, it fires whenever the beat ticks./healthis intentionally minimal (DB + Redis). A release where Flask starts but routes 500 would pass. Richer/api/smokeis a planned follow-up.MIGRATE_DONEcrash recovery needs an operator to re-runbayanat update. Abayanat update --resumecommand is a planned follow-up.shared/config.jsondirectly; a proper installer change is pending.Not in scope
cdaa80fb493a.Review focus
Suggested reading order:
bayanatscript — the CLI pipeline (~500 added lines, mostly small functions)enferno/tasks/maintenance.py::check_for_updates— auto-apply logicenferno/admin/views/system.py— the three update endpoints +/admin/snapshots/enferno/public/views.py+enferno/setup/views.py— /health + its setup-redirect exemptiondocs/deployment/auto-update-runbook.md— operator-facing flowsTest plan