Files
curriculum-project-hub/hub/deploy

Hub service installation

The initial production target is a supported Linux host with systemd, PostgreSQL, Node.js 20 or newer, pg_isready, runuser, setpriv, bubblewrap and cph. The Hub runs as the dedicated non-login cph-hub user; do not create or run the unit as root.

Build or deploy the Hub under /srv/curriculum-project-hub, then run:

sudo BASE=/srv/curriculum-project-hub \
  HUB_DIR=/srv/curriculum-project-hub/hub \
  bash /srv/curriculum-project-hub/hub/deploy/install_service.sh

On a clean host the first invocation creates /srv/curriculum-project-hub/.secrets/platform.env with every supported production key and exits with status 78. Fill every required blank, set CPH_BIN to an executable compatible cph, keep the file mode at 0600, and run the same command again. No systemd unit is installed until configuration, paths, Node, bubblewrap, cph --version, PostgreSQL connectivity and directory ownership all pass preflight.

An existing service account is accepted only when its primary group, home and non-login shell match the requested configuration and it has no supplementary groups. After provisioning, the installer executes path-access, built Hub, Prisma, an authenticated database query, cph and bubblewrap namespace probes as that account with no_new_privs set. Inaccessible parents, UID-specific database/network policy, or a host that forbids unprivileged user namespaces therefore fail before unit installation.

The default persistent layout is deliberately outside every source/release tree:

/var/lib/cph-hub/home
/var/lib/cph-hub/state
/var/lib/cph-hub/workspaces
/var/cache/cph-hub

Custom paths are supported with SERVICE_HOME, STATE_DIR, CACHE_DIR and WORKSPACE_ROOT, but all must be absolute. The installer rejects any persistent path that is equal to, above, or below BASE; this keeps rsync, release switching, rollback and pruning unable to traverse customer workspaces. Set the same custom workspace in HUB_PROJECT_WORKSPACE_ROOT inside platform.env.

After a successful installation:

sudo systemctl start cph-hub.service
sudo systemctl status cph-hub.service

HOST and PORT in platform.env are passed to the real HTTP listener. The default 127.0.0.1:8788 expects a local TLS reverse proxy; the public OAuth URL must be an HTTPS HUB_PUBLIC_BASE_URL.