Orbit hosts your app’s frontend. You build a static SPA locally, run nuble deploy, and seconds later it’s live at http://{app}.{org}.local for every device on the network. You interact with Orbit exclusively through the CLI — there is no JavaScript SDK.
Get up and running
- Create the app in the Console. This reserves
{app}.{org}.localand issues your API key (nbl_…). - Connect the CLI:
npm install -g @nublestation/cli nuble init # prompts for gateway URL, API key, app slug - Build and deploy:
npm run build nuble deploy - Open it. Visit
http://{app}.{org}.localfrom any device on the LAN. No restart, no pipeline — the new files are served immediately.
Everyday usage
Deploy
nuble deploy # zips and uploads ./dist
nuble deploy --dist build # custom build output directory
Build and deploy in one step
nuble deploy --build
Runs your project’s build script first, loading .env automatically so build-time variables (like VITE_…) are baked into the bundle. Remember: changing a VITE_ variable requires a rebuild and redeploy — it’s compiled in, not read at runtime.
Roll back
Orbit keeps the previous deployment on disk and can swap back to it. There’s no CLI wrapper yet (coming soon), so call the endpoint directly:
curl -X POST http://api.clinic.local/v1/orbit/rollback \
-H "Authorization: Bearer nbl_<key_id>.<secret>"
Or simply redeploy the older build artifact: nuble deploy --dist ./path/to/previous-dist.
Check connectivity
nuble status # verifies the gateway is reachable for each profile
Reference
All endpoints are reached through the Gateway at api.{org}.local with your API key. The CLI handles this for you.
| Method | Path | One-liner |
|---|---|---|
POST | /v1/orbit/deploy | Upload a zipped bundle as multipart/form-data (field name: bundle) |
POST | /v1/orbit/rollback | Swap back to the previous deployment (filesystem rename, milliseconds) |
GET | /healthz | Liveness probe |
GET | /readyz | Readiness probe |
Error codes
| Status | error | Meaning |
|---|---|---|
400 | invalid_content_type / missing_bundle_field | Request isn’t multipart, or the bundle field is missing |
401 | unauthorized | Bad or missing API key (returned by the Gateway) |
409 | no_previous_version | Rollback requested but no previous deployment exists |
413 | bundle_too_large | Bundle exceeds the 50 MB limit |
422 | missing_index_html | The zip has no index.html at its root — check your build output |
500 | deploy_failed / rollback_failed | Server-side failure — check nuble server logs orbit |
CLI flags (nuble deploy)
| Flag | Description |
|---|---|
--dist <path> | Built frontend directory (default: dist) |
--build | Run the project build script before deploying |
--profile <name> | Config profile to use (default: default) |
See CLI Commands for the full reference.
Limits & gotchas
- One previous version only. Each deploy overwrites the old
previous/— there is no full deployment history, and only one rollback step. - Static SPAs only. Pre-built files are served as-is; server-side rendering is not supported.
- 50 MB max bundle. Larger uploads are rejected with
413. index.htmlmust be at the zip root. Deploying a zip that wraps everything in a subfolder fails with422.- No deploy hooks. Post-deploy webhooks or notifications are not yet implemented.
- No streaming deploys. The entire bundle uploads before extraction begins.
✦ Tip
For large bundles, strip source maps from your build (--no-sourcemap in Vite). Smaller zips upload faster, especially on slow LAN links.
Go deeper
How the upload is authenticated and atomically swapped on disk (HMAC-signed forwarding, current//previous/ layout under /var/nuble/, Caddy’s wildcard serving) is covered in the concepts and security sections: