vps-app-deployment

/home/avalon/.hermes/skills/devops/vps-app-deployment/SKILL.md · raw

VPS App Deployment — *.apps.poofc.com

Class-level workflow for building, deploying, verifying, and operating applications on Alex's shared VPS.

Default completion policy

For this user, an approved application build normally continues automatically through PM2, nginx/TLS, launcher/control-plane registration, feature-level smoke, and a verified public URL. Do not stop at “build passed” or introduce a second deployment gate unless the user explicitly requested build-only work.

Keep separate authorization only for materially different effects such as spending money, submitting fulfillment, sending messages, activating ads, or destructive infrastructure changes.

A filesystem build is intermediate state. A PM2 row marked online is also insufficient. Mark an application live only after its HTTPS URL, process, port ownership, nginx route, health check, and representative user flow are verified. Then immediately publish URL/runtime metadata back to Canvas or the relevant control plane.

See references/automatic-build-to-live-deployment.md for the detailed build-to-live contract and the PM2 ESM guard, browser-forbidden-port, scoped-sudo site-install, and feature-smoke pitfalls.

Finish-line discipline and visible continuity

When the user explicitly asks to “finish,” “push it through,” or go faster after a long implementation, treat that as a scope-freeze signal—not a stop signal and not permission for another broad review cycle.

When Alex explicitly says to stay lean and focused, treat that as an execution constraint for the rest of the release: one root-cause trace, one narrow regression, one production build, and one real acceptance probe. Do not add another broad audit, independent review, repeated full suite, or speculative hardening unless a new security/data-integrity blocker requires it. Report built/deployed/blocked first and keep progress commentary short.

For a mixed release spanning ordinary PM2 apps and stateful tenant appliances, compress the finish line by runtime class. For each standalone app: take an online SQLite backup when migrations are involved, build once, restart only its PM2 process, then verify public service identity and one defining read-only endpoint. For the appliance: build an immutable exact-commit image, run one identity-bound disposable boot/runtime canary, clean it completely, then update and re-read the controller's future-image selector. Treat existing tenant containers as a separate rollout axis; never imply selector promotion upgraded them, and never mass-recreate unnamed tenants merely to make the overall release report say “live.”

For signed onboarding that crosses from an app database into a separate agent runtime, visible transcript import is not enough: verify that the first live agent turn receives the imported context. See references/signed-seed-runtime-context-continuity.md.

Conventions

Standard workflow

1. Inspect live state

Read before writing:

Confirm the intended slug, hostname, runtime, health path, authentication model, persistent data path, and external service dependencies.

2. Claim a safe port

Cross-check PM2, launcher metadata, ecosystem configuration, and live sockets. A numerically free port may still be browser-forbidden; validate candidate ports with Node/browser fetch, not curl alone. Port 4045 is a known WHATWG-forbidden example.

Treat this as a reservation check, not permanent ownership: shared VPS state can change during a long build. Re-run the live socket/owner check immediately before PM2 start. After startup, require the listener PID to belong to the intended app and validate a service-specific health identity—not merely status 200—before continuing to nginx. Only then record the selected port in PM2 and launcher metadata; never reuse another app's port.

3. Prepare production runtime

For ESM applications imported by tests, do not assume a process.argv[1] direct-execution guard will start under PM2. Use an explicit production start environment flag and verify the socket actually binds.

4. Start and verify locally

Start/restart only the intended PM2 app, then verify:

Do not accept a homepage-only smoke when the product's primary feature depends on another API.

5. Install nginx and TLS

Prepare the site config in /tmp. Before activation, check sudo -n -l and directory permissions. Passwordless access to nginx, certbot, and systemctl reload nginx does not imply permission to create a new site file or symlink.

Activation order:

  1. verify PM2/port ownership;
  2. install the site config and enabled symlink using the approved scoped mechanism;
  3. run nginx -t;
  4. reload nginx;
  5. verify HTTP routing;
  6. issue/attach TLS with certbot;
  7. run nginx -t again;
  8. reload nginx;
  9. verify public HTTPS and certificate hostname.

If site-file installation is blocked, leave the PM2 app healthy, retain the prepared /tmp config, state the one missing privileged action precisely, and never claim the URL is live.

6. Register and project truth

After public verification:

For provider-backed creative labs, prefill safe adapter/model/schema/cost defaults, validate live read-only provider schemas, and verify through signed contract issuance. Keep the paid generation call as an explicit spending boundary unless the user authorized it. See references/production-design-lab-readiness.md.

7. Final verification

Run:

Report the real URL and concrete evidence. Mention genuine remaining integration gaps; do not describe a partial deployment as complete.

Operational pitfalls

Unknown or missing tiers must fail closed on central-passkey tenants; do not synthesize product Admin from a legacy local owner/admin role, configured password, absent durable access row, or old cookie. Disable legacy local login when central identity is authoritative. Fresh restricted-tier bootstrap must skip seeders/planners until signed access arrives and must refuse to adopt arbitrary existing businesses or referenced Portfolio profiles/threads/jobs rather than deleting or hiding them.

Before release, inventory every human route, agent/key-minting route, module router, entity-ID action, null/default scope, and request-body project field. Authorization must derive from signed/durable tier plus persisted entity ownership, never UI hiding or caller-supplied IDs. Inject foreign business/entities and referenced Portfolio state into a disposable database and prove list filtering plus direct-ID denial. Wait for every required asynchronous security review before default-branch integration or release-candidate labeling; a narrow pass does not override a broader pending audit.

Verify each tier through the real server entrypoint plus read-only durable-state assertions, then rerun every gate after post-review hardening. Build the tenant image from an exact reviewed commit archive, retain build logs on failure, and resolve build arguments from the controller-owned secret/config source rather than assuming the current container's baked environment contains the effective value. See references/versioned-entitlement-tenant-rollouts.md and coding-quality-workflowreferences/security-sensitive-entitlement-review-gates.md.

Reference index

Use the supporting files for specialized procedures:

When a referenced file and this umbrella differ, use the narrower procedure for its scenario while preserving the completion and verification contract above.