Skip to content

ok/pagecase-startos

Repository files navigation

Pagecase on StartOS

Upstream docs: none — Pagecase has no separate upstream project. The Go controller (cmd/pagecase, internal/) and the StartOS package (startos/) are both maintained in this repository. Sections below describe the package as shipped.

Pagecase hosts static websites built from Git repositories. Push a commit to your repo and Pagecase clones it, runs the build inside a bubblewrap sandbox, and serves the output through a built-in HTTP router. One instance hosts many sites with independent custom domains.


Table of Contents


Image and Container Runtime

Field Value
Image source Custom Dockerfile (multi-stage: Go build + Alpine 3.22 runtime)
Base image alpine:3.22 (Node.js 22.x, bubblewrap, git, git-lfs, tini)
Architectures x86_64, aarch64
Entrypoint /sbin/tini -- /usr/local/bin/pagecase serve --data /data --addr :8080 --addr-webhook :8081
Runtime user root inside the StartOS-isolated subcontainer (per-build isolation is enforced by bubblewrap, not by the container's USER)

Volume and Data Layout

Field Value
Volume name main
Mount point /data
StartOS-managed /data/store.json — site list and global SSH keypair
Controller state /data/state.db — SQLite (WAL) build history
Per-site layout /data/sites/<site-id>/{repo,builds/<sha>,current,logs}

current is an atomic symlink that points at builds/<sha>; deployments swap it via rename(2). Rollback re-aims the symlink at an earlier <sha> directory.


Installation and First-Run Flow

On install (kind == 'install'), init/seedStore.ts generates an Ed25519 SSH keypair and writes store.json with:

{
  "globalSshPublicKey": "ssh-ed25519 AAAA... pagecase",
  "globalSshPrivateKey": "<PEM>",
  "sites": []
}

No build wizard. No initial site. The operator's next step is to attach public domains to the StartOS interfaces and run Add Site for each project they want to host. The full step-by-step deploy guide lives in instructions.md (shown in the StartOS UI under the "Instructions" tab).


Configuration Management

StartOS-Managed (via Actions / env vars) Operator-Managed (outside StartOS)
Site list, per-site repo URL, branch, build command, output directory, custom domains, timeout Git repository contents and package.json / package-lock.json
Global SSH keypair (auto-generated on install) Adding the public key as a deploy key on each private Git repo
Webhook secrets (auto-generated per site) Pasting the secret + URL into each Git host's webhook configuration
StartOS-attached public domains (StartTunnel, Tor, custom domains via Gateways) DNS records for any custom clearnet domain

Environment variables consumed by the controller:

Variable Default Purpose
PAGECASE_LOG_LEVEL info Reserved for future use; currently unread by controller
PAGECASE_DISABLE_SANDBOX unset If 1, skips bubblewrap (development only)

Network Access and Interfaces

Two independent StartOS hosts so each can carry its own public domain.

Interface Host ID Container port Type Path Notes
sites sites 8080 API / Serves built sites; routes by Host header. Declared as api (not ui) so no "Open UI" button appears — there is no single launchable UI; each site is reached via its own custom domain.
webhooks webhooks 8081 API /_hooks/ Git provider webhook receiver; masked by default

Host resolution checks X-Forwarded-Host, then X-Original-Host, then Host (port stripped, case-insensitive). A /_/<site-id>/... path-prefix fallback is available when no Host matches, but absolute asset URLs in typical Vite/React output won't resolve under that prefix — use a real custom domain for production traffic.

Access methods supported by StartOS (all work without further package config): LAN IP, mDNS .local, Tor .onion, StartTunnel, custom clearnet domains via Gateways. Operators typically attach a site's public hostname (e.g. www.example.com) to the sites interface and a separate hostname (e.g. pc-wh.example.com) to the webhooks interface, then list the site's hostname under the site's Custom Domains field.


Actions (StartOS UI)

ID Name Visibility Availability Inputs Outputs
add-site Add Site enabled any site ID, repo URL, branch, build command, output dir, domains, timeout confirmation
edit-site Edit Site enabled any site select + all editable fields (pre-filled from store) confirmation
remove-site Remove Site enabled any site select (warning shown) confirmation
trigger-build Trigger Build enabled only-running site select, optional Git ref build ID
rollback Rollback enabled only-running site select, commit select (deduped, newest first, with build timestamp) commit SHA confirmation
show-webhook-url Show Webhook URL enabled any site select primary URL, signing secret (masked), at most one alternate URL
show-ssh-key Show SSH Public Key enabled any none the global Ed25519 public key
list-builds List Builds enabled only-running site select last 20 builds with status, trigger, timestamps
test-webhook Test Webhook Delivery enabled only-running site select controller HTTP status + response body (also queues a real build)

Backups and Restore

Field Value
Backed up The entire main volume (/data) via sdk.setupBackups(['main'])
Includes store.json (site list + SSH keypair), state.db, all sites/
Excluded None
Restore behaviour Volume contents restored byte-for-byte; controller boots, re-applies SQLite migrations idempotently, and resumes serving the previously deployed current symlinks

Health Checks

Field Value
Daemon controller
Check sdk.healthCheck.checkPortListening(effects, 8080, …)
Success msg "Controller is listening"
Failure msg "Controller is not responding"

The webhook port (8081) is not separately health-checked; the same process serves both, so the port 8080 listener implicitly covers webhook availability.


Limitations and Differences

  1. Node-based renderers only. Hugo, Zola, Jekyll, mdBook, and other non-Node static-site generators are not supported in v1.
  2. Single build worker. The FIFO queue runs one build at a time. Concurrent pushes to multiple sites queue serially.
  3. One global SSH key. All sites share globalSshPrivateKey. Per-site deploy keys are not yet supported.
  4. No build cancellation. A queued or running build runs to completion or the per-site timeout (default 600 s).
  5. No live log streaming. Build logs are written to /data/sites/<id>/logs/<build-id>.log and can be inspected via start-cli package attach pagecase cat …. There is no HTTP endpoint for log tailing.
  6. SSH host-key checking is disabled for Git cloning. gitssh.NewPublicKeys uses InsecureIgnoreHostKey() because per-deploy-key authorization on the Git host is the trust boundary.
  7. No automatic cleanup of old build artifacts. /data/sites/<id>/builds/<sha>/ directories accumulate indefinitely until the site is removed or the operator deletes them manually.
  8. Webhook handler ignores non-matching branches with a 200. A push to a non-configured branch returns "ignored: wrong branch" — the Git host considers the delivery successful, but no build is queued.

What Is Unchanged from Upstream

Not applicable — there is no separate upstream project.


Contributing

See CONTRIBUTING.md for build instructions, the project layout, the type-check command, and the commit-message convention.


Quick Reference for AI Consumers

package_id: pagecase
upstream_version: none
image: pagecase (built from ./Dockerfile, multi-stage Go + alpine:3.22)
architectures: [x86_64, aarch64]
volumes:
  main: /data
ports:
  sites: 8080
  webhooks: 8081
dependencies: none
startos_managed_env_vars:
  - PAGECASE_LOG_LEVEL
  - PAGECASE_DISABLE_SANDBOX
actions:
  - add-site
  - edit-site
  - remove-site
  - trigger-build
  - rollback
  - show-webhook-url
  - show-ssh-key
  - list-builds
  - test-webhook

About

Simple website deployments and hosting for StartOS

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors