[Unit] Description=Curriculum Project Hub Silo (__INSTANCE_ID__) After=network-online.target postgresql.service Wants=network-online.target # ADR-0018: refuse to start when the configured OS sandbox binary disappears # after installation. Deployment preflight validates the executable itself; # this start-time assertion keeps later package/host changes fail-closed. AssertPathExists=__BWRAP_BIN__ AssertPathExists=__SOCAT_BIN__ AssertPathExists=__KEYRING_FILE__ [Service] Type=simple User=__SERVICE_USER__ Group=__SERVICE_GROUP__ WorkingDirectory=__HUB_DIR__ EnvironmentFile=__ENV_FILE__ Environment=HOME=__SERVICE_HOME__ Environment=XDG_STATE_HOME=__STATE_DIR__ Environment=XDG_CACHE_HOME=__CACHE_DIR__ Environment=HUB_SKILL_STORE_ROOT=__SKILL_STORE_ROOT__ Environment=PATH=__RUNTIME_PATH__ # ADR-0024: the root-owned source remains unreadable by the service account; # systemd materializes a read-only per-unit credential at runtime. LoadCredential=cph-secret-keyring:__KEYRING_FILE__ UMask=0027 # Run prisma migrations before each start, then launch the Hub. ExecStartPre=__NODE_BIN__ __HUB_DIR__/node_modules/prisma/build/index.js migrate deploy --schema __HUB_DIR__/prisma/schema.prisma ExecStart=__NODE_BIN__ __HUB_DIR__/dist/server.js Restart=on-failure RestartSec=5 MemoryMax=__MEMORY_MAX__ CPUQuota=__CPU_QUOTA__ TasksMax=__TASKS_MAX__ OOMPolicy=stop # Graceful shutdown: lark ws close + in-flight runs. KillSignal=SIGTERM TimeoutStopSec=30 # Hardening: no new privileges. ProtectSystem/ProtectHome are NOT set here — # they would break cph's render-cache extraction (~/.cache/cph/) and the # bwrap user-namespace setup. The OS-level sandbox (ADR-0018) is the hard # boundary; systemd hardening is kept minimal to avoid interfering with it. NoNewPrivileges=true [Install] WantedBy=multi-user.target