ninepm
v0.30.0
Published
CLI for deploying built apps to 9pm.ai.
Readme
9pm.ai CLI
CLI for deploying built applications to 9pm.ai.
This package contains only client-side deploy code. It does not include the 9pm.ai control plane, infrastructure provisioning code, migrations, staging config, operator tokens, or smoke-test scripts.
Install
npm i -g ninepm # persistent `9pm` binary
npx ninepm <args> # run without installingConfigure
Authenticate in your own browser. The CLI stores the token in native protected storage: macOS Keychain, Windows DPAPI, or Linux Secret Service when available. If native storage is unavailable, plaintext file storage is opt-in with --allow-file-credential-store or NINEPM_ALLOW_FILE_CREDENTIAL_STORE=1.
9pm login
9pm whoami
9pm doctorFor CI, set NINEPM_API_KEY yourself in the CI secret store. --token and NINEPM_API_KEY still override the stored login token.
Inventory
List the apps you have on 9pm.ai before deploying or modifying:
9pm apps
9pm apps --jsonThe slug column is the handle you pass to access, delete, and files --project.
Versioning
Check installed vs published CLI version:
9pm --version # installed
9pm latest # installed vs latest, prints update one-liner when behind
9pm doctor # surfaces the same drift line alongside credential/account healthUpgrade:
npm i -g ninepm@latestDeploy
Deploy a static directory:
9pm deploy . --name my-app --slug my-appDeploy a built framework output directory:
npm run build
9pm deploy ./dist --check
9pm deploy ./dist --name my-app --slug my-appDeploy with a managed SQL database:
9pm deploy ./dist --name my-app --slug my-app --with-dbAttach an optional short description to the deploy (shown next to the version in the dashboard; up to 200 characters):
9pm deploy ./dist --name my-app --slug my-app --description "Fix login redirect"The CLI prints the public app URL after upload and finalization.
Store and share working files:
9pm files put ./report.pdf
9pm files share <file-id> --expires 24h
9pm files put ./notes.md --project my-app --path notes/release.md
9pm files share <file-id> --project my-app --login-requiredPersonal files and project files both count toward the account file storage limit. Share links expire and are capped at 7 days. Login-required links are owner-only.
Deploy a first-class Worker runtime app:
9pm deploy ./examples/worker-app --name worker-app --slug worker-app --runtime worker --entry src/index.js --with-dbThe --runtime worker entry must be a bundled fetch-compatible JavaScript module with a default export. With --with-db, the app receives env.DB and should use SQLite-compatible prepared statements.
Deploy a container app from a local Docker build:
9pm deploy . --check --runtime container --port 8080
9pm deploy . --name container-app --slug container-app --runtime container --port 8080 --health-path /health --region autoContainer deploys require Docker locally. The CLI builds a linux/amd64 image, exports it, checks image size against the per-app limit and account retained-image quota, uploads through a short-lived session, and finalizes a queued deployment. The container must listen on 0.0.0.0:$PORT or the selected --port; set --health-path when the app has a cheap readiness endpoint.
Use --with-db when the container needs managed SQL. The app receives NINEPM_SQL_URL and NINEPM_DB_BINDINGS; send POST requests to NINEPM_SQL_URL with { "binding": "DB", "sql": "SELECT 1", "params": [] }. For local development, use the app's normal local database adapter behind a repository layer and keep deployed SQL compatible with SQLite.
Regions are provider-neutral: auto, us-east, us-west, europe, asia, south-america, middle-east, oceania, africa.
When a container app fails to come up or you want to investigate it after the fact, retrieve its recent logs:
9pm logs my-app # recent container logs + the failure reason, if any
9pm logs my-app --lines 50 # last N lines
9pm logs my-app --json # machine-readable9pm logs reports the latest container deployment's status, health, the failure reason, and the captured startup output when a deploy failed to come up. It is read-only and does not affect deploys.
Delete an app, its managed database, managed container app, and retained image:
9pm delete my-app --confirm my-appAccess Modes
Apps are public by default.
One-time PIN with email/domain allowlist:
9pm access allow email my-app [email protected]
9pm access allow domain my-app example.com
9pm access otp my-app
9pm access revoke email my-app [email protected]
9pm access revoke domain my-app example.comIdentity provider access:
9pm access idp my-app --provider googleMachine access:
9pm access service create my-app --name backendReturn to public:
9pm access public my-appCreate an app-scoped runtime token for server-side allowlist management:
9pm access token create my-app --name production-app-serverUse 9pm_app_... runtime tokens only from server-side code. They can list, add, and remove email/domain grants for one app. They cannot deploy, create databases, create API keys, or manage another app.
Service token secrets returned by access service create are also server-side secrets.
Commands
9pm --version
9pm deploy [directory] --name <name> [--slug <slug>] [--with-db]
9pm deploy [directory] --check [--runtime worker --entry <path>]
9pm deploy [directory] --runtime container --port 8080 [--health-path /health] [--region auto] [--with-db]
9pm signup
9pm login
9pm logout
9pm doctor [--json]
9pm whoami
9pm apps [--json]
9pm latest [--json]
9pm files list [--project <site>]
9pm files put <local-path> [--path <remote-path>] [--project <site>]
9pm files get <file-id> [--output <path>] [--project <site>]
9pm files share <file-id> [--expires 24h] [--login-required] [--project <site>]
9pm files delete <file-id> [--project <site>]
9pm deploy [directory] --runtime worker --entry <path> [--with-db]
9pm delete <site> --confirm <slug>
9pm access show <site>
9pm access public <site>
9pm access otp <site>
9pm access idp <site> [--provider google|github|entra]
9pm access allow email <site> <email>
9pm access allow domain <site> <domain>
9pm access revoke email <site> <email>
9pm access revoke domain <site> <domain>
9pm access service create <site> [--name <name>] [--duration 8760h]
9pm access service revoke <site> <service-token-id>
9pm access token create <site> [--name <name>]
9pm access token revoke <site> <token-id>Options:
--api-url <url>overridesNINEPM_API_URL--token <key>overridesNINEPM_API_KEY--allow-file-credential-storepermits plaintext fallback storage forloginandsignupwhen native protected storage is unavailable--name <name>sets the app display name--slug <slug>sets the URL-safe app slug--with-dbrequests managed storage for the app--database-name <name>customizes the requested database name--binding <name>customizes the database binding name--environment <name>sets deployment environment metadata--runtime <static|worker|container>selects static asset hosting, app-owned Worker code, or local Docker container deployment--entry <path>selects the Worker runtime server entry file--port <port>selects the HTTP port exposed by a container app--health-path <path>optionally lets 9pm.ai check container health before marking it healthy--region <region>optionally selects provider-neutral container placement--project <site>scopes file commands to an app--path <remote-path>stores a file at a specific path--output <path>writes a downloaded file to disk--expires <duration>sets public share-link expiry, capped at 7 days--login-requiredcreates an owner-only share link--confirm <slug>confirms app deletion by requiring the slug--provider <google|github|entra>selects a configured identity provider--duration <duration>sets a service token duration
Access command options:
--api-url <url>overridesNINEPM_API_URL--token <key>overridesNINEPM_API_KEY--name <name>names an app-scoped runtime token--expires-at <iso-date>optionally expires an app-scoped runtime token
