Overview
shipd is the command-line interface for config as code. It syncs TypeScript, Go, or JSON flag configs with the server, generates flag types, and runs snapshot recovery operations. Every command supports --json. Use --yes or --ci to skip confirmation prompts for unattended runs.
@shipdit/cli. Binary: shipd.
Global options
Global options go before or after the command name and apply to every command.
Confirmation is also skipped when
CI=1, CI=true, or SHIPD_CI=1. Without any of these, a command that needs confirmation and has no TTY (for example, a CI job) is cancelled with exit code 1.
Configuration precedence
Each setting resolves from the first source that has it:
The project root is the nearest directory, walking up from the current directory, that contains
shipd.config.ts, shipd.config.go, shipd.config.json, or .shipd/config.json. Relative configFile and typesOut paths resolve against the current directory, so run shipd from the project root.
If no source sets the API URL, shipd login uses https://api.useshipd.com and saves it to credentials.json. Every other command falls back to http://localhost:8000.
Credentials
Both files are written with mode
0600. When XDG_CONFIG_HOME is set, the directory is $XDG_CONFIG_HOME/shipd instead of ~/.config/shipd.
SHIPD_API_KEY overrides the stored key. It accepts a management API key (shipd_mgmt_…) or a service account token (shipd_sa_…). SDK keys (shipd_sdk_…) do not work with the CLI. See SDK keys, management API keys, and service account tokens.
Environment variables
JSON output
With--json, results print to stdout as pretty-printed JSON. Errors print to stderr as one line:
code is the control-plane error code, or a CLI-side code. See Errors.
shipd login
Authenticates the CLI through browser device authorization and stores a management API key.
Behavior.
- Prints a verification URL and a one-time code, then polls until you approve or the code expires.
- In the browser you choose an organization, a project, and an environment. The key is bound to exactly those three.
- Writes the key and API URL to
credentials.json. - Saves the organization, project, and environment slugs as defaults in the global
config.json. Later commands need no--projector--env.
shipd login again to switch the default project or environment.
Output
--json, the code and URL print to stderr as JSON and the result prints to stdout.
Exit codes. 0 on success. 1 if authorization is denied, expires, or times out.
Related: shipd logout · shipd whoami · Keys
shipd logout
Deletescredentials.json.
org, project, and env in the global config.json are kept. The key is not revoked on the server; revoke it in the dashboard under Management keys.
Exit codes. 0.
shipd whoami
Prints the identity behind the current credential.Output
API key: <id> for a shipd login key, Service account: <id> (token <id>) for a service account token, or User: <name> <email> for a user session. Project and environment lines appear only when the credential is bound to them.
Exit codes. 0, or 1 if unauthenticated.
shipd push
Plans the changes needed to make the server matchshipd.config.ts, asks for confirmation, applies them, and regenerates flag types.
Also uses the global
--yes / --ci, --project, --env, --flags-config, and --json.
Sequence
- Load and validate the selected TypeScript, Go, or JSON flag config (see validation rules).
- Ask the server for a plan against the target project and environment. Print it.
- If the plan is empty, print
Nothing to push.and exit0. Types are not regenerated. - Confirm (
Apply this plan? [y/N]) unless--yes,--ci, orCIis set. - Apply the plan with the flag versions seen in step 2.
- Regenerate types to
typesOutfrom the local file.
Output
What each change does
Field scope
Changing
default replaces the target environment’s fallthrough with that fixed value, including a weighted fallthrough set in the dashboard. Rules and the Enabled in <env> switch are not changed. Because the plan compares against the live fallthrough, pushing a file that still has an old default reverts a fallthrough edited in the dashboard. Run shipd pull or read the default: line in the plan first.
Targeting rules, rollouts, and the enabled switch are never in shipd.config.ts. Set them in the dashboard. See Targeting.
managedBy
managedBy (code or dashboard) records which surface last changed a flag. It is attribution, not a lock: shipd push updates dashboard-edited flags, and the dashboard edits code-managed flags. --adopt does nothing.
Permissions
Push checks permissions against the whole plan. The credential needs:
A management API key from
shipd login acts with your role in its project. Service account tokens need these actions as scopes; see Sync flags in CI for scope names. Service account tokens bound to a single environment cannot create, update, or archive flags because those changes are project-wide; bind CI tokens to the project.
Conflicts
Apply is a compare-and-swap. The CLI sends the definition version and environment-state version of every flag it plans to change. If either changed on the server after the plan, the server rejects the whole push with409 conflict (exit 3) and applies nothing:
--force skips the version check, applies the local definitions over the remote ones, and records the push in the audit log with the reason Forced CAS override. It does not bypass permissions or approvals. See Why did shipd push return a conflict?.
Approvals
When an approval policy covers the target environment:- A
defaultchange, or the enable-and-fallthrough step of a new flag, is queued as an approval request instead of applied. Push exits5and printsN environment change(s) queued for approval.The queued state is not live. - Changes that no policy matches in the same push still apply.
- A second push for a flag that already has a pending request in that environment fails with
409 conflict(exit3). - A definition change (create, archive, or a project-global field) when a policy covers any environment in the project cannot be submitted for review. The whole push fails with
409 conflict(exit3) and nothing applies.
--yes, --ci, and --force never approve anything.
Limits
- At most 500 flags per push. Larger files fail validation.
- Flag keys must be unique.
- Apply is transactional. A conflict, permission error, or invalid flag applies nothing and queues no approval requests.
- Creating flags counts against your plan’s flag allowance. See Usage and limits.
JSON output
outcome is applied or pending_approval. When the plan is empty, only plan and remoteOnly are printed.
Example
0 applied or nothing to push · 1 error, or confirmation declined · 3 conflict · 5 some changes are pending approval.
If generated output cannot be written after apply, the remote changes remain applied. Fix the output path or permissions and run shipd types gen to regenerate the local output.
Related: shipd diff · defineFlags · Push, pull, and diff
shipd diff
Prints the planshipd push --prune would apply, without changing anything.
Behavior.
diff needs only flag.read, so any credential that can read the environment works, including a shipd login key. Remote flags missing from the file appear as - archive flag lines, and the CLI adds Note: N remote flag(s) are missing locally. shipd push leaves them alone unless you pass --prune. They count as drift for --exit-code.
Output
No drift.
With --json, prints { creates, updates, archives, unlinked, conflicts, remoteOnly }.
Example
0 no drift, or --exit-code not set · 1 error · 2 drift (with --exit-code) · 4 unlinked (with --exit-code). Current servers do not report unlinked flags because managedBy is not a lock.
Related: shipd push · Sync flags in CI
shipd pull
Overwrites the selected flag config with the flags on the server, preserving TypeScript, Go, or JSON format, then regenerates TypeScript types or Go accessors. Go input defaults to Go output unlesstypesLanguage overrides it.
- Writes every non-archived flag in the project. Each flag’s
defaultis the target environment’s fallthrough. A disabled fallthrough becomes the type’s zero value, and a weighted fallthrough becomes its first variation. Go and JSON preserve the disabled state; TypeScript definitions currently cannot represent it. - The file is regenerated from scratch. Comments, formatting, helper code, and custom imports are lost.
- Needs only
flag.read.
Output
0 or 1. If writing generated output fails, the config may already have been replaced. Fix the output path or permissions and run shipd types gen to regenerate it.
Related: shipd push · Why did shipd push return a conflict?
shipd types gen
Writes TypeScript types or Go accessors from the local TypeScript, Go, or JSON flag config. Does not contact the server. Loadingshipd.config.go requires Go 1.23+ on PATH; Go input selects Go output unless typesLanguage or --language says otherwise.
Output
0, or 1 if the file, language, package name, or generated identifiers are invalid.
Related: Generate flag types · Generated types
Go generation needs no credentials:
--language switches away from the configured language, its default output path is used unless --out is supplied. Set typesLanguage in project settings to select Go output for push and pull too.
shipd flags list
Lists flags in the project, one per line: key, type, lifecycle,managedBy.
Output
--project or a project-bound credential). Exit codes. 0 or 1.
shipd flags create
Creates one flag without editingshipd.config.ts.
Behavior. The flag is created in every environment with a disabled fallthrough. Requires
flag.create. Add the flag to shipd.config.ts (or run shipd pull) so the next push does not report it as remote-only.
Example
0 or 1.
shipd flags archive
Archives a flag after confirmation.
Requires
flag.archive. Archive is not a hard delete. Exit codes. 0, or 1 (including when confirmation is declined).
shipd projects list
Lists projects in the organization: slug, name, ID.0 or 1.
shipd environments list
Lists environments in the project: slug, name,production or the environment type, ID.
0 or 1.
shipd snapshots publish
Publishes the environment’s current configuration again when an earlier publication failed.snapshot.publish. See Publish a snapshot.
shipd snapshots outbox
Lists publication jobs for the environment. Use this to check whether a saved change is waiting to publish or has failed.dead_lettered means automatic retries have stopped; contact support if failures persist.
Output
snapshot.publish.
shipd snapshots requeue
Retries publication jobs that stopped after repeated failures. Use this after the cause of a publication failure has been resolved, or when support directs you to retry.Output
snapshot.publish.
shipd snapshots rebuild
Requests a rebuild of the current published configuration without changing its version. Use this when support asks you to rebuild an environment; it does not roll back or change flag values.
Requires
snapshot.publish. Environment-bound credentials cannot rebuild a whole project.
shipd snapshots publications
Lists publication history for the environment. Use the version and time to check whether a recent change published, and share publication details with support when troubleshooting update delivery.snapshot.read (admins and owners), which project- or environment-bound credentials cannot use. snapshot.publish is held by every role that can update flags (editor, admin, owner).
Exit codes
This is the single list ofshipd exit codes. Causes and fixes are in Errors.
Related:
defineFlags and config files · Errors · Sync flags in CI