# Systemd release hooks

Release hooks let the packaged target version extend both CLI and web
deployments without changing the admin API or the currently running installer.

- `pre-activate.d`: runs after the target package is staged and before
  `/opt/<service>/current` or any systemd unit changes. Use it for idempotent
  migrations and host prerequisites.
- `post-activate.d`: runs after the release symlink and units are installed but
  before any service restarts. Use it for validation or release metadata that
  requires the activated filesystem. A failure restores the previous symlink,
  Bun runtime link, units, and worker policy.

Hook names must match `NNN-name.sh`, are executed in lexical order, must be
regular executable files, and stop the deployment on the first failure. Hooks
must be idempotent because a failed deployment may be retried.

The runner exports this context:

- `RELEASE_HOOK_PHASE`
- `RELEASE_HOOK_SERVICE_NAME`
- `RELEASE_HOOK_INSTALL_ROOT`
- `RELEASE_HOOK_RELEASE_DIR`
- `RELEASE_HOOK_RELEASE_ID`
- `RELEASE_HOOK_PREVIOUS_DIR` (possibly empty)
- `RELEASE_HOOK_BUN_BIN`
- `RELEASE_HOOK_ENV_FILE`
- `RELEASE_HOOK_WORKER_ENABLED`

Do not source the environment file from shell. Programs that need application
configuration should use `"$RELEASE_HOOK_BUN_BIN" --env-file="$RELEASE_HOOK_ENV_FILE" ...`.

## Host memory profile

`100-configure-host-memory.sh` detects physical RAM and online CPUs before
activation. It writes `50-memory.conf` drop-ins for the API, worker, and daily
job under `/etc/systemd/system`, so packaged unit limits remain safe fallbacks
instead of imposing the same ceiling on every host. Hosts with 8 or more CPUs
receive additional API and worker headroom when they have more than 4 GiB RAM.

Operators can pin a value in MiB with
`HOST_MEMORY_<API|WORKER|DAILYJOB>_<HIGH|MAX|SWAP_MAX>_MIB`. The soft `HIGH`
value must remain below `MAX`; invalid overrides fail deployment before the
release is activated.

## 1Panel OpenResty upgrade guard

`105-configure-openresty-upgrade-guard.sh` is a no-op on hosts without the
1Panel OpenResty app. On matching edge hosts it installs a persistent systemd
timer that keeps the newest Noble image from the local 1Panel catalog pulled
and validated against the live configuration mounts. A path unit reacts to
1Panel catalog refreshes, while the timer also checks the configured registry
mirror's tag list so stale catalogs do not delay preloading. The guard tags the
healthy running image as `1panel/openresty:local-last-known-good`, preventing
1Panel's upgrade cleanup from deleting the only rollback image.

The timer never replaces or restarts the running container. Its readiness
record is written to `/var/lib/openresty-upgrade-guard/ready`; failures are
visible through `systemctl status openresty-upgrade-guard.service` and
`journalctl -u openresty-upgrade-guard.service`.
