CLI
The cosmoner command line tool — validate deployment files, deploy image apps, upload to web hosting, and manage secrets from a terminal or CI.
@cosmoner/cli is built on the JavaScript SDK and gives the most common
pipeline steps a one-line command. Run it with npx, with nothing to install:
npx @cosmoner/cli validateOr install it globally to get a cosmoner command:
npm install -g @cosmoner/cliIt is useful in any repository, not only JavaScript ones. A deployment file describes a deployment, not a Node project, so the CLI works the same in a Go or Rust repository.
Credentials
The file commands — validate, fmt, init, schema and agents — work
offline. They need no account, no API key and no network.
deploy, upload, secrets and variables call the API, and read a key from
one of two places:
- On your own machine, run
cosmoner login. It opens your browser, where you pick a project and approve. The CLI saves a key for that project that expires after 90 days. See Keys Created by the CLI. - In CI, set
COSMONER_API_KEYto a key you created, andCOSMONER_PROJECT_IDto its project. The environment variable always wins over a saved login.
There is deliberately no flag for passing a key: a flag ends up in shell
history and CI logs. cosmoner whoami shows which key and project the CLI is
using, and cosmoner logout revokes the saved key.
Exit codes
Every command uses the same three codes, so a CI step can tell a broken deploy from a typo in its own command line:
| Code | Meaning |
|---|---|
0 | Success. |
1 | The operation failed — a file did not validate, a deploy failed, the API refused a request. |
2 | The command itself was wrong — an unknown option, an unreadable path. |
Validate a deployment file
cosmoner validate.cosmoner/deployment.yaml
2:11 error Service names must be lowercase letters, numbers, or hyphens, and start with a letter or number services.0.name
4:18 error Static sites cannot define a run_command services.0.run_command
5:11 warning Unknown field "prot" — it will be ignored services.0.prot
2 errors, 1 warningWith no argument, it finds the file the way the platform does. It runs the same check as the SDK validators and adds the line and column of each finding.
| Option | |
|---|---|
--strict | Fail on warnings too, such as an unknown key. |
--format text|json|github | github annotates findings on the pull request diff. |
--quiet | Report through the exit code only. |
# GitHub Actions
- name: Validate deployment file
run: npx @cosmoner/cli validate --strict --format githubOther file commands
| Command | What it does |
|---|---|
cosmoner init | Writes a starter .cosmoner/deployment.yaml, with comments on the fields you are most likely to set. --type static for a static site. |
cosmoner fmt | Rewrites the file in canonical field order and adds a $schema header for editor completion. Comments are kept. --check exits 1 instead of writing. |
cosmoner schema | Prints the JSON Schema for the file. --url prints its published URL. |
cosmoner agents | Adds a short "Deploying to Cosmoner" section to AGENTS.md, so coding agents validate, deploy and handle secrets the way the CLI expects. Safe to re-run. |
Deploy an image app
cosmoner deploy web --tag v2Deploying web (tag v2)
PENDING
DEPLOYING
✓ web is live on registry.cosmoner.com/acme/web:v2 after 41sDeploys an image app — one that runs an image from a Cosmoner registry — and
waits for the rollout. web is the app's name or id. The key needs
apps:read and apps:write.
| Option | |
|---|---|
--tag <tag> | Deploy this tag. A commit SHA pushed as a tag goes here. |
--digest <digest> | Deploy this exact image. |
--no-wait | Return as soon as the deploy is accepted. |
--timeout <seconds> | How long to wait. Defaults to 600. A timeout stops the wait, not the deploy. |
--project <id> | Defaults to COSMONER_PROJECT_ID, then the project you logged in to. |
--format text|json | json prints the app and the final deployment. |
With neither --tag nor --digest, the image the app already names is pulled
again.
# GitHub Actions
- name: Deploy
run: npx @cosmoner/cli deploy web --tag ${{ github.sha }}
env:
COSMONER_API_KEY: ${{ secrets.COSMONER_API_KEY }}
COSMONER_PROJECT_ID: ${{ vars.COSMONER_PROJECT_ID }}Upload to a web hosting site
cosmoner upload my-site dist --deleteUploading dist to my-site:/my-site.cosmoner.com/public_html (42 files, 1.3 MB)
✓ Uploaded 42 files (1.3 MB), removed 3 in 6sUploads a directory to a web hosting site over
SFTP. The SFTP login is fetched with the API key, so CI needs no password of
its own. The key needs hosting:read.
Existing files are overwritten. Files on the site that are not in the directory
are left alone unless you pass --delete. An empty directory is refused,
since it is usually a build that produced nothing.
| Option | |
|---|---|
--remote <path> | Folder to upload into. Defaults to the one the site's own hostname serves. |
--delete | Remove what is on the site but not in the directory. |
--dry-run | List what would change without changing anything. |
--host-key <sha256> | Refuse a server with a different host key. Defaults to COSMONER_SFTP_HOST_KEY. |
Pin the host key so the password is never sent to a server pretending to be ours. The gateway's key is:
SHA256:PfqYSl1pbMjfMKAbcmjzGZ0t1kpuCZ2mtymdyLu9HwA# GitHub Actions
- name: Upload site
run: npx @cosmoner/cli upload my-site dist --delete
env:
COSMONER_API_KEY: ${{ secrets.COSMONER_API_KEY }}
COSMONER_PROJECT_ID: ${{ vars.COSMONER_PROJECT_ID }}
COSMONER_SFTP_HOST_KEY: SHA256:PfqYSl1pbMjfMKAbcmjzGZ0t1kpuCZ2mtymdyLu9HwASecrets and variables
echo "$DB_PASSWORD" | cosmoner secrets set DB_PASSWORD --environment production
cosmoner variables set LOG_LEVEL --value debug
cosmoner secrets listset creates a value, or replaces one that already exists with that name in
that environment. rm removes it, and list shows what is there.
| Option | |
|---|---|
--environment <env> | default, development, staging or production. Defaults to default. |
--value <value> | The value, taken literally. |
--from-file <path> | Read the value from a file. |
--description <text> | Set alongside the value. |
--format text|json | json output never includes a secret's value. |
Pipe secret values in. A value passed with --value can be recovered from
shell history and may be echoed by a CI runner. cosmoner secrets never prints
a value back, for the same reason.
The key needs secrets:write or variables:write, and its owner must be an
owner or admin of the project — see
Secrets and Variables.
# GitHub Actions
- name: Set the database password
run: echo "$DB_PASSWORD" | npx @cosmoner/cli secrets set DB_PASSWORD --environment production
env:
COSMONER_API_KEY: ${{ secrets.COSMONER_API_KEY }}
COSMONER_PROJECT_ID: ${{ vars.COSMONER_PROJECT_ID }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}