🚀 Deploy

The deployment target is one publicly usable Bun application at pwiz.loca.zone and one public Quartz wiki at wiki.pwiz.loca.zone. D-169 (2026-09-26) retains anonymous shared public access while optional Authelia SSO supplies signed-in identities and admin groups.

Service contract

  • service name: pwiz.service
  • service type: simple
  • user and group: app operator account
  • working directory: ${APP_ROOT}
  • environment:
    • NODE_ENV=production
    • HOST=${BIND_HOST}
    • PORT=52222
    • HOME=${OPERATOR_HOME}
    • PATH=${OMP_BIN_DIR}:${BUN_BIN_DIR}:/usr/local/bin:/usr/bin:/bin
  • exec:
    • ${BUN} run server/index.ts
  • restart:
    • always
    • restart delay: 5 seconds
    • stop timeout: 20 seconds
[Unit]
Description=Prompt Wizard application
After=network-online.target
Wants=network-online.target
 
[Service]
Type=simple
User=${APP_USER}
Group=${APP_GROUP}
WorkingDirectory=${APP_ROOT}
Environment=NODE_ENV=production
Environment=HOST=${BIND_HOST}
Environment=PORT=52222
Environment=HOME=${OPERATOR_HOME}
Environment=PATH=${OMP_BIN_DIR}:${BUN_BIN_DIR}:/usr/local/bin:/usr/bin:/bin
ExecStart=${BUN} run server/index.ts
Restart=always
RestartSec=5
TimeoutStopSec=20
 
[Install]
WantedBy=multi-user.target

App nginx contract

  • host: pwiz.loca.zone
  • app backend: ${BACKEND_ORIGIN}
  • optional-SSO identity boundary (D-169):
    • include app security headers and preserve the installed optional identity subrequest/fallback locations
    • use trusted signed-in user/groups when SSO succeeds; fall back to group-less public for anonymous visits, without redirecting app XHR into a required login
    • overwrite client-supplied identity headers before proxying
    • admin routes require admins or pwiz-admins; anonymous and signed-in non-admin requests receive 403
    • retain effective-identity scoping of ordinary library queries
  • upload limit:
    • client_max_body_size 8m
  • SSE route:
    • /api/events
    • proxy buffering off
    • read timeout 3600 seconds
  • AI route:
    • /api/ai/
    • read timeout 600 seconds
  • general route:
    • proxy HTTP 1.1
    • upgrade headers retained for compatibility
    • forwarded host and protocol headers retained

The installed vhost is the load-bearing routing source. The older nginx example that pinned every request to public is intentionally removed: copying it would undo D-169 and make admin tools unreachable. Likewise, the original all-gated vhost is a historical rollback anchor, not the current optional-login deployment recipe. Auth/nginx changes require their own approved mission; an application deployment does not change this boundary.

Wiki nginx contract

  • host: wiki.pwiz.loca.zone
  • root: ${WIKI_ROOT}/current
  • public without an identity gate
  • immutable cache for assets
  • route fallback:
    • try exact file
    • try generated .html
    • try directory
    • otherwise 404
server {
  listen 443 ssl;
  server_name wiki.pwiz.loca.zone;
 
  include snippets/security-headers.conf;
  root ${WIKI_ROOT}/current;
  index index.html;
 
  location ~* \.(css|js|png|webp|ico|json|woff2?|svg|map)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    try_files $uri =404;
  }
 
  location / {
    try_files $uri $uri.html $uri/ =404;
  }
}

Certificate and registration

  • certificate:
    • one SAN certificate for app and wiki hosts
    • nginx authenticator and installer
    • ECDSA key type
  • port registry:
    • app port: 52222
    • reserve adjacent future port: 52223
  • startup order:
    • install dependencies
    • build app
    • seed app data
    • install service file
    • enable and start service
    • publish wiki
    • reload nginx after valid config

See 🔒 Private runbook (login) before declaring deploy complete.