Operations

Updates & releases

Vortexa is intentionally release-only: neither the panel nor a node will ever install an arbitrary commit. The /update route in the panel UI is a dead redirect — all real update actions live on the CLI (autoscript update) for the panel, and on the Nodes/Infrastructure pages (or autoscript update-nodes) for the fleet.

src/routes/_authed.update.tsx unconditionally runs beforeLoad: () => redirect({to: "/"}) — it is a placeholder route kept for backward-compat with older bookmarks/links, not a functioning screen. There is no in-browser "click to update the panel" button; the update surface for nodes lives inline on /nodes and /infrastructure (an "Agent versions" widget flags which nodes are behind), and the update surface for the panel itself is the on-VPS CLI.

Why no panel self-update button in the UI

Updating the panel rebuilds and restarts the very process serving that UI page, and can fail mid-way (see Rollback below) — it is deliberately kept to a root-only terminal action rather than a button a browser tab can trigger.

Panel

current_release() resolves the installed version as git describe --tags --exact-match, falling back to git describe --tags --abbrev=0, inside the install directory. latest_release_tag() hits the GitHub Releases API for Ghostrelicc/Vortexa and reads tag_name from the JSON response. Both are shown by autoscript status and autoscript release.

Nodes

The panel's Infrastructure page tracks each node's agent version from heartbeat data and flags nodes whose version is behind the panel's own bundled agent build. The panel's own single-box "local node" self-updates automatically in the background (autoUpdateLocalNode()) whenever it reports an older version than the panel; remote nodes never auto-update — they always require an explicit "Update" click or CLI call.

Before touching anything, update_now() calls check_authenticity(), which compares the install's git remote.origin.url against the hardcoded OFFICIAL_REPO_URL (https://github.com/Ghostrelicc/Vortexa). A mismatch means this is a modified/rebranded copy — it keeps working, but the update refuses to run:

bash
[err] Refusing to update a modified/rebranded copy. Reinstall from https://github.com/Ghostrelicc/Vortexa.

If the origin matches, update_now() pins it explicitly (git remote set-url origin ...) before fetching, so a stale/hijacked remote URL can never silently redirect an update.

  1. 1
    Diff current_release() against latest_release_tag(). If equal, the CLI reports "Already on the latest release" and stops.
  2. 2
    git fetch --all --tags --prune --force, then git checkout -f tags/<tag> and git reset --hard tags/<tag> — the working tree becomes an exact copy of the release tag, never a merge.
  3. 3
    Reinstall /usr/local/bin/autoscript from the freshly-checked-out backend/cli.sh, so the CLI itself picks up any fixes in the same release.
  4. 4
    purge_old_code() wipes every build artifact and untracked file from the install tree (git clean -xdff, plus a belt-and-braces rm -rf of dist/.output/.nitro/.vite/node_modules) — the Python venv is kept to avoid a slow rebuild. Panel data in /etc/autoscript and /var/lib/autoscript is explicitly outside this tree and is never touched.
  5. 5
    Run backend/scripts/migrate.sh if present (schema/data migrations).
  6. 6
    Refresh the SSH login MOTD helper script, backfill any missing AGENT_URL/WEB_INTERNAL_PORT env vars for older installs, and reinstall the systemd unit files so hardening/launcher fixes land on existing VPS installs too.
  7. 7
    Enter maintenance mode, ensure Node.js 22+ is present (ensure_node22), and rebuild the web UI from a completely clean tree (build_web_ui) — bun if a lockfile is present, else npm with automatic retry and a specific repair path for the rolldown native-binding npm bug.
  8. 8
    Only if the build succeeds: repair_services(), then restart_stack(), then explicitly re-enable ssh/sshd and xray/autoscript-ssh-ws.

From the panel UI

  • Per-node "Update" action on the /nodes row (RPC agent.update).
  • Bulk "Update" action across a multi-select on the same page.
  • Nodes flagged out-of-date on /infrastructure link back into the same per-node action.

From the CLI (fleet fan-out)

autoscript update-nodes [ids] mints a short-lived JWT with the panel's JWT_SECRET, loads the node roster from the panel's own SQLite/JSON store, filters to non-master, non-disabled nodes (optionally scoped to a comma-separated id list), and fires agent.update at each one concurrently through the panel's local RPC proxy.

What agent.update does on the node

Runs /usr/local/bin/autoscript-node-update if present; else curl | bash against AGENT_UPDATE_URL if set; else self-heals by re-pulling $MASTER/api/public/node-agent.tar.gz. It always runs detached, via systemd-run --unit=autoscript-node-update (falling back to a detached Popen), specifically so the sandboxed autoscript-node service restarting itself mid-update cannot kill the updater before it finishes copying files.

agent.update_safe — the cautious path

agent.update_safe chains backup.snapshotsystem.tune node.rebuild → a post-update health check, and only then calls into agent.update. If any step fails it aborts with a concrete error before detaching, instead of leaving the node mid-upgrade with no rollback data captured.

Always update the master panel before updating nodes. The panel's own bundled agent build is the reference version shown in "needs update" indicators on Infrastructure, and node-facing endpoints (node-handshake, node-heartbeat, node-rpc) are the contract nodes depend on — updating nodes against an older master risks a protocol mismatch on newly added RPC methods. Within the fleet there is no required order between nodes; update them in small batches (using update-nodes <ids> or the UI's per-node action) rather than all at once, so a bad release only affects a subset of traffic while you validate it.

ScenarioWhat actually happensRecovery
Panel web build failsupdate_now() dies BEFORE restarting autoscript-web. The old server bundle stays in place and keeps serving the previous working release.Read /var/log/autoscript-web-build.log (last 30 lines are echoed automatically), fix the underlying issue (usually a dependency/toolchain problem), then re-run autoscript update.
Panel git checkout succeeds but migrate.sh failsThe script only warns (warn "migrate failed") and continues — it does not abort the update.Inspect the migration script's own log output; if data-shape assumptions are now wrong, restore /etc/autoscript from a recent backup (see Backups & restore) before repeating the update.
Node agent.update fails mid-copyBecause it runs in its own systemd-run unit, a crash of the (also-updating) autoscript-node service does not kill the updater. Node stays reachable via the previous agent binary until the updater either succeeds or the unit itself fails.Check /var/log/autoscript-node/agent-update.log (or the equivalent path reported in journalctl -u autoscript-node-update) on the node; re-trigger agent.update, or fall back to re-running the node install one-liner, which is fully idempotent.
Node update leaves service plane brokennode.health (part of agent.update_safe) exists specifically to catch this before the update is considered complete.Run RPC node.rebuild to re-apply tuning and restart planes without touching users/certs/domains, or fall back to service.restart per affected service.

There is no automatic 'downgrade to previous tag'

Vortexa does not ship a scripted rollback-to-previous-release action. Because updates only ever move to a published release tag, the practical rollback path is: fix forward, or manually git checkout -f tags/<previous-tag> inside the install root followed by the same purge/build/restart steps update_now() runs — take a backup first.