Deploy the web surfaces
What ships as public web, what stays private, and how to check each surface before a deploy. For maintainers.
Oktapod has two public web surfaces and one private operator surface.
| Surface | Source | Audience | Ships as |
|---|---|---|---|
| Landing | apps/landing | Public | Static Vite site, including /update/, /security/, and the branded 404. /docs/ redirects to docs.oktapod.ai. |
| Docs | docs-site | Public | Static pages at docs.oktapod.ai, built by apps/landing/docs-build. |
| Control UI | apps/ui | Private | Built into dist/control-ui and served by each Oktapod. |
There is no public API site. apps/gateway is the authenticated private
gateway/control-plane boundary. It serves runtime endpoints and the access-key-gated
Control UI bundle; it is not a marketing or docs site. Keep app.oktapod.ai,
api.oktapod.ai, and any public route to apps/gateway or apps/ui parked.
Build and preview
Use Node 24.
pnpm build:sites
This builds the landing, the docs site, and the Control UI, and validates the docs. Preview them locally:
pnpm build:landing
pnpm build:ui
Serve apps/landing/dist or apps/ui/dist with any static server to check
layout. A static preview only proves layout and assets; check real access and
live activity through an actual Oktapod address, such as
http://127.0.0.1:8788/ or your Tailscale address.
Checks before a public deploy
pnpm sites:check
pnpm public-sites:browser-check --screenshot-dir=.oktapod/reports/public-sites
sites:check runs pnpm build:sites and pnpm docs:check. The browser check
renders the public sites at phone, tablet, and desktop widths. Save screenshots for review instead
of opening new visible browser windows.
A page passes at 360, 390, 820, and 1440 pixels wide when it has no horizontal overflow, mobile navigation opens and closes, primary buttons stay visible and tappable, and the first screen hints at the next section.
The full web gate
Before calling the web work deploy-ready, run the single gate with the private Control UI address:
pnpm web:deploy-check --control-ui-url=https://<your-tailnet-host>/ --screenshot-dir=.oktapod/reports/web-deploy/full-gate
It checks the public build, metadata, icons, and social preview, runs the
Control UI tests and browser e2e, confirms the live Control UI serves the same
hashed bundle as the local build, and captures phone, tablet, and desktop
screenshots. The auth evidence includes
control-ui-auth/control-ui-locked-recovery-phone-390.png and
control-ui-auth/control-ui-reconnect-banner-phone-390.png, so raw
401 unauthorized states cannot be missed during review. It writes
index.html and summary.json in the screenshot folder for one-page review.
Use --skip-live-control-ui only for public or local-only evidence.
To check just the live Control UI:
pnpm control-ui:live-check --url=https://<your-tailnet-host>/
pnpm control-ui:live-browser-check --url=https://<your-tailnet-host>/ --screenshot-dir=.oktapod/reports/web-deploy/control-ui
Deploy
Public binary downloads are open to everyone. Publish only the explicit runtime
and setup binary allowlist under /downloads/<tag>/, with SHA-256 checksums.
The binary delivery tree excludes source archives, transfer bundles, private
release feeds, runtime credentials, and operator data.
Repository publication is a separate step. The planned open-source release uses Apache-2.0 and must first pass the dependency-notice and source/history review. The repository is currently private; do not infer publication from open binary downloads. Source publication does not authorize disclosure of operator data or private operational artifacts.
Public deploy:
- landing: sync
apps/landing/distto the public host, then make directories0755and files0644so nginx can read them; - docs:
pnpm build:landingalso rendersdocs-siteintoapps/landing/dist-docs(navigation fromdocs-site/docs.json); sync that folder to the docs host with the same permissions. The docs host must return its404.htmlfor unknown paths and must not fall back toindex.html.
The public nginx site serves /install.sh and /install.ps1 as plain text
from the verified release commit. It serves the branded 404.html for unknown
paths and /site.webmanifest as
application/manifest+json, and must not fall back to index.html.
Private Oktapod deploy:
- private gateway/control-plane boundary
- Control UI bundle served by Oktapod
- local service and connectors
After a private deploy, rebuild the Control UI with the same Node 24 the service uses, restart Oktapod, and rerun the two live Control UI checks above.
Do not claim public API-site readiness. Claim private gateway/control-plane readiness only after gateway health, access-key auth, and Control UI checks pass through the actual Oktapod service.