CLI reference
The Glotto CLI is invoked as glotto <command>. Run glotto with no arguments to print the
command list. Every command exits 0 on success, 1 on a run failure, and 2 on a usage or load
error (an unknown flag, a missing glotto.yml).
This page documents the CLI you can install, and only that. Every command below is in the
published @glotto/cli; there are no others, and nothing here requires a plan, a flag, or an
invitation to unlock. Glotto also delivers most of these capabilities without your running
anything — keeping your committed SDKs verified, detecting drift, publishing to registries — on
your pull request and your dashboard; those routes are described by what they produce and where you
collect it, under What Glotto also runs for you below.
Most commands discover the config automatically: with no --config, Glotto looks for
.glotto/workspace.json, then glotto.yml, then glotto.yaml — first in the current directory,
then in each parent directory up to the filesystem root. So commands work from anywhere inside your
project, not only from the directory holding the config. See Getting started
for the end-to-end workflow and the glotto.yml reference for the configuration
these commands consume.
The workspace file
A .glotto/workspace.json declares which project a directory belongs to. You only need one when a
single repository holds more than one glotto.yml, or when you want an SDK written somewhere
other than sdks/<language>/. A repository with one config at its root needs no workspace file —
the upward walk already finds that config from any subdirectory, which is why glotto init does not
write one.
{
"config": "api/glotto.yml",
"targets": {
"typescript": "clients/typescript",
"python": "clients/python"
}
}
config(required) — path to theglotto.ymlthis workspace binds to.targets(optional) — where each language’s SDK is written. A language you leave out is written tosdks/<language>/as usual. Drift detection reads the same map, so it compares each language against the directory declared here — there is no second root to keep in step with the layout.
Both are relative to the workspace root — the directory containing .glotto/ — and must stay inside
it. Absolute paths and .. escapes are rejected. glotto workspace writes,
checks, and explains the file: glotto workspace show prints the binding for the directory you are
standing in, glotto workspace validate checks the file on its own, and glotto workspace init
writes one with the config path filled in for you.
Two keys you might expect are deliberately not accepted, because glotto.yml already owns those
values and a second copy would drift out of step: project (that is hosted.project) and
openapi_spec (that is openapi.source, resolved relative to the config). Setting either one
reports GLOTTO_WORKSPACE_KEY_OWNED_ELSEWHERE naming where
the value belongs.
Nearest declaration wins. In a monorepo, give each project its own workspace file (or just its
own glotto.yml): a command run inside services/billing/ binds to the nearest declaration above
it, not to the repository root. Where output lands follows the same rule — with a workspace file in
effect, a relative output path is resolved against the workspace root rather than wherever you
happened to run the command, so glotto generate writes to the same place from every directory.
An explicit --out is always resolved against the current directory.
How you get each command
The published @glotto/cli on npm is a thin client: code generation runs on Glotto’s
infrastructure, so the codegen engines never ship in the installed binary. Sixteen commands are in
it — login, logout, init, migrate, schema, workspace, generate, mcp,
breaking-changes, verify, verify-compile, verify-contract, verify-upload,
verify-attestation, publish, and code-owners.
That list is complete. glotto <anything-else> exits 2 with unknown command, and every section
below carries the same label because every section below is a command you can run:
- Available in the published CLI —
npm i -g @glotto/cligives you this command.
Thin client does not mean everything is remote. Which side a command runs on follows from what
it needs, not from a policy. verify-compile and verify-contract invoke your committed SDKs’ own
toolchains and test suites, and publish pushes with your own registry credentials — all three run
entirely on your machine, and nothing about them reaches Glotto. generate, verify and
breaking-changes need the code-generation and spec-normalisation engines, which stay
server-side, so those three need glotto login or a GLOTTO_API_TOKEN. Each section below says
which it is.
What you will not find here is a reference section for a command you cannot obtain. Documenting one
would mean printing a flag table and a shell sample under a note saying you cannot run it, which is
not a caveat so much as an instruction that fails — so the one capability with no command left
(glotto migrate fern, which needs the spec pipeline) is described by its outcome instead. See
What Glotto also runs for you.
glotto init
Available in the published CLI —
npm i -g @glotto/cli
Scaffold a new glotto.yml and spec/openapi.yaml in the current directory, then print the next
steps. Without --force it refuses to overwrite an existing glotto.yml or spec/openapi.yaml.
--force— overwrite existing files without prompting.
glotto init # scaffold glotto.yml + spec/openapi.yaml
glotto init --force # overwrite an existing scaffold
glotto migrate
Available in the published CLI —
npm i -g @glotto/cli
Convert a competitor’s config into a glotto.yml, and print a migration report saying what
happened to every key — what was carried, what was normalized, what was dropped and why, and which
values are placeholders you have to fill in. Nothing already on disk is overwritten without
--force, and your OpenAPI document is never modified in place.
--in <path>— input file (default:stainless.yml).--out <path>— output file (default:glotto.yml).--force— overwrite an existing--out.--report <path>— also write the migration report to a file.--openapi <path>— readx-stainless-naming/x-stainless-paramintonaming, and resolve everytransformstarget against your real document so the translation is exact rather than inferred. See Migrate from Stainless.--openapi-out <path>— also write that document back out with the Terraform attribute-shaping extensions translated to theirx-glotto-*equivalents.
glotto migrate stainless # stainless.yml -> glotto.yml
glotto migrate stainless --openapi openapi.yaml # exact transform + naming translation
glotto migrate stainless --report migration-report.txt # keep the report alongside the config
Fern. Glotto also converts a fern/generators.yml (and fern/docs.yml), but that conversion
reads your API surface through the spec pipeline, which stays server-side — so it is not
something the published CLI runs. glotto migrate fern exits 2 and says so. What it converts is
documented in Migrate from Fern; to have us run it, email
hello@glotto.dev.
glotto workspace
Available in the published CLI —
npm i -g @glotto/cli
Inspect and author the workspace file. Every subcommand answers a question
about which project the current directory belongs to, so none of them takes --config — that
flag is what bypasses discovery, and these commands exist to report what discovery does.
glotto workspace show
Print the resolved binding for the current directory: the workspace root, the config it binds to,
which file declared it and how far up the tree that was, the root relative output is anchored to,
and where each enabled target’s SDK is written. Exits 2 when no config is found anywhere above the
current directory, or when the config it found cannot be parsed (the binding is still printed).
--json— emit the same facts as a JSON object instead of prose.
glotto workspace show # "which project am I in, and where does my output go?"
glotto workspace show --json # the same, for a script
workspace root: /repo
config: /repo/api/glotto.yml
declared by: .glotto/workspace.json (in this directory)
output root: /repo
targets:
python /repo/clients/python (declared)
typescript /repo/sdks/typescript
The directories it reports are the directories glotto generate writes to — both commands resolve
them through the same code, so the report cannot drift from the writer. (declared) marks a
directory that came from the workspace file’s targets map rather than the sdks/<language>/
default.
glotto workspace validate
Validate the nearest .glotto/workspace.json at or above the current directory, without generating
anything and without requiring glotto.yml to be valid. Prints nothing and exits 0 when the file
is clean, so it drops into a pre-commit hook or CI step. Exits 1 with the
diagnostic code when the file is wrong, and 2 when there is
no workspace file to check (an ambient glotto.yml is not a declaration and is never reported as
one).
--json— emit the outcome as a JSON object. The exit code is unchanged.
glotto workspace validate # silent + exit 0 when the file is clean
glotto workspace validate --json # {"ok": false, "code": "GLOTTO_WORKSPACE_CONFIG_MISSING", …}
glotto workspace init
Write a .glotto/workspace.json in the current directory, which becomes the workspace root. The
config path is derived rather than typed — from --config when you pass one, otherwise from the
config found by walking up — and written relative to the root with / separators, so the committed
file is portable. A config that does not exist, or that sits outside the workspace root, is refused
rather than written for a later command to reject.
targets is never written: it declares an output layout only you can choose. Add it by hand when
you want one, and glotto workspace validate will check it.
--config <path>— bind to this config (relative to the current directory) instead of the discovered one.--force— overwrite an existing.glotto/workspace.json.
glotto workspace init --config api/glotto.yml # writes {"config": "api/glotto.yml"}
glotto workspace init --force # overwrite an existing declaration
Note that glotto init deliberately does not write a workspace file — a fresh single-config
project does not need one. Reach for glotto workspace init when a repository grows a second
project, or when you want SDKs written somewhere other than sdks/<language>/.
glotto schema
Available in the published CLI —
npm i -g @glotto/cli
Emit the JSON Schema for glotto.yml (the same schema core-config exports as
glottoConfigJsonSchema()). With no flags it prints to stdout; reference it from your config with
a # yaml-language-server: $schema=… header for editor completion and validation.
--out <file>— write the schema to a file (relative to the current directory) instead of stdout.
glotto schema # print the schema to stdout
glotto schema --out glotto.schema.json # write it, then reference it from glotto.yml:
# # yaml-language-server: $schema=./glotto.schema.json
glotto generate
Available in the published CLI —
npm i -g @glotto/cli
Validate the config, then generate SDKs from the spec across enabled targets. Writes all files
atomically: nothing is written if the run fails. A target an engine refuses is different — it is
dropped, the remaining targets are written normally, and the command exits non-zero naming the
target it could not build (see Reserved model names). Each SDK’s customer-owned lib/ extension
files are written only if absent, and managed files with local edits are preserved (or, with
--merge, three-way-merged) unless --force is passed — see Custom code.
--config <path>— use this config instead of auto-discovery.--out <dir>— output directory, relative to the current directory (defaultsdks, resolved against the workspace root when a workspace file is in effect). Overrides anytargetspath a workspace file declares.--target <target>— restrict to a target; repeatable. Omit to generate all configured targets.--version <semver>— stamp the generated SDKs with this version (X.Y.ZorX.Y.Z-pre).--dry-run— print the files that would be generated without writing.--force— overwrite managed files that have local edits.--merge— three-way-merge managed files that have local edits with the regenerated output (conflicts are written with git conflict markers), keeping a pristine baseline under.glotto/baseline/(gitignore it). Without the flag, edited files are preserved untouched.--format— run each language’s native formatter over the generated source (gofmt,rustfmt,dart format,swift-format,mix format,rubocop,php-cs-fixer,google-java-format,ktfmt). A formatter that isn’t installed degrades with a warning — that language is emitted unformatted; pointGLOTTO_<TOOL>(e.g.GLOTTO_RUSTFMT) at a binary to override resolution.
glotto generate # generate every configured target into ./sdks
glotto generate --target typescript # one target only (repeat --target for more)
glotto generate --out build/sdks --version 1.4.0
glotto generate --dry-run # preview the file list, write nothing
glotto mcp
Available in the published CLI —
npm i -g @glotto/cli
Work with the MCP server artifact. Three subcommands:
glotto mcp generate— emit only the MCP server fromglotto.yml, without the SDKs or the docs site. Runs server-side, likegenerate, so it needsglotto login.--out <dir>— write the server here instead of the configured output directory.--config <path>— use thisglotto.ymlinstead of auto-discovery.--dry-run— print the file list, write nothing.--force— overwrite managed files that have local edits.
glotto mcp serve— run an emitted MCP server over stdio, so an MCP client can launch it directly. Local only: it spawns the artifact already on disk and generates nothing.--dir <path>— the emitted server to run (defaultsdks/mcp).- Any remaining flags are forwarded to the server unchanged.
glotto mcp annotate— author tool descriptions intoglotto.yml’smcp.operationsblock. Runs server-side and needsglotto login.--tool <name>— annotate one tool; repeatable.--config <path>,--dry-run,--force— as above.
glotto mcp generate # emit just the MCP server (runs server-side)
glotto mcp serve # run the emitted server over stdio
glotto mcp serve --dir build/mcp # run one from a non-default directory
glotto mcp annotate --tool listPets # author one tool's description into glotto.yml
glotto breaking-changes
Available in the published CLI —
npm i -g @glotto/cli
Classify what changed between your current spec and a baseline: which changes break a caller, which
do not, and what each does to the generated SDK surface. With --gate it fails the run when a
breaking change lands without a major version bump, which is what the CI workflow Glotto emits for
your repository does on every pull request.
The comparison builds two IRs through Glotto’s spec pipeline and diffs them, so it runs on
Glotto’s infrastructure and needs glotto login or a GLOTTO_API_TOKEN. Both spec documents are
read from your machine and sent with any configured companion spec documents. The published CLI
accepts local file sources. URL, git, command and introspect sources are refused: fetch, check out
or export those documents first, then point the config at local files.
--against <path>— the baseline spec document to compare against. A local file.--provider <github|gitlab|bitbucket|azure>with--repo <ref>and--branch <branch>— fetch the baseline from a repository instead.--spec-path <path>overrides which file is fetched.--gate— exit1when breaking changes were introduced without a major version bump. Also enabled bysettings.detect_breaking_changesin yourglotto.yml.--format json— emit the classified report as JSON instead of text.--emit-workflow <github|gitlab|bitbucket|azure>— print the CI workflow that runs this gate.
--against-ref <ref> is available in the first-party build only: it names a git object, which needs
the spec loader that clones repositories. Use --against with a checked-out file, or --provider.
The emitted workflows require a GLOTTO_API_TOKEN: configure an Actions secret on GitHub, a masked
CI/CD variable on GitLab, a secured repository variable on Bitbucket, or a secret pipeline variable
on Azure. The GitHub and Azure templates map that secret into the gate’s environment. For a
self-hosted control plane, follow the template’s GLOTTO_API_URL instructions; the default is
https://api.glotto.dev.
glotto breaking-changes --against baseline.openapi.yaml
glotto breaking-changes --against baseline.openapi.yaml --gate
glotto verify
Available in the published CLI —
npm i -g @glotto/cli
Verify committed SDK output against a fresh regeneration and write the verification report: per target, whether the committed files drifted from what your spec produces, whether any checksum-stamped managed file was hand-edited, and — when you feed it a check-results document — the real compile and contract status of each target. This is the gate step of the CI workflow Glotto emits for your repository.
It regenerates, so it runs on Glotto’s infrastructure and needs glotto login or a
GLOTTO_API_TOKEN. The comparison itself is local: your committed files are read and diffed on your
machine, and only the spec is sent.
--against <dir>— the committed output tree to verify.--against-default sdksyields to a.glotto/workspace.jsonwhen one is in effect, and falls back tosdks/.--provider <github|gitlab|bitbucket|azure>with--repo <ref>and--branch <branch>— verify the output committed on a branch, with no checkout.--prefix <path>names the subpath it lives under.--checks <path>— fold in a check-results document produced byverify-compile/verify-contract, so the report carries real compile and contract statuses instead of “not run”.--out <dir>— writeverification-report.jsonandverification-report.mdhere.
glotto verify --against-default sdks --out glotto-verification
glotto verify --against-default sdks --checks glotto-verification/check-results.json --out glotto-verification
glotto verify-compile
Available in the published CLI —
npm i -g @glotto/cli
Compile every committed SDK in place, with that SDK’s own toolchain, and write the
check-results document glotto verify folds into its report. This is the first produce step of the
CI workflow Glotto emits for your repository.
It runs entirely on your machine and needs no Glotto account: it regenerates nothing and sends nothing. What it does need is your targets’ toolchains installed in the job — it invokes them.
Each target’s command comes from targets.<slug>.verify.compile in your glotto.yml, or a built-in
default for the languages where compiling a committed SDK is unambiguous (TypeScript, React Native,
Go, Python, Rust). A target with neither is skipped rather than guessed at, and the document records
the omission.
--against <dir>/--against-default sdks— the committed output tree to compile.--out <dir>— where to writecheck-results.json.
glotto verify-compile --against-default sdks --out glotto-verification
glotto verify-contract
Available in the published CLI —
npm i -g @glotto/cli
Run the integration test suite each committed SDK ships — the self-contained suite Glotto emits
beside the client, which boots an in-process mock and round-trips the generated code against it —
and merge the contract statuses into the same check-results document verify-compile wrote.
It runs entirely on your machine, like verify-compile, and needs each target’s dependencies
installed as well as its toolchain: the emitted suite imports the built SDK.
--against <dir>/--against-default sdks— the committed output tree to test.--out <dir>— the directory holding the document to merge into (and to write).
glotto verify-contract --against-default sdks --out glotto-verification
glotto verify-upload
Available in the published CLI —
npm i -g @glotto/cli
Publish a check-results document to the control-plane run for a commit, so the console’s surface-health panel, the run history and the verification attestation carry your real compile and contract statuses instead of “not run”. This is the dashboard step of the CI workflow Glotto emits for you; leave it out and the report still reaches your pull request as a job summary and an artifact.
It is pure HTTP: it reads the produced document and posts it. It runs no generation and needs no toolchain.
--checks <path>— the check-results document to publish. Required.--project <id>— the control-plane project. Resolves--project, thenGLOTTO_PROJECT_ID, thenhosted.projectin yourglotto.yml.--commit <sha>— the commit the run belongs to. Read from the CI environment when omitted.
GLOTTO_API_TOKEN authenticates the upload. Verified provenance is optional and turn-key: set
GLOTTO_CI_OIDC_TOKEN to a pre-minted CI-run OIDC token (GitLab, Bitbucket, Azure Pipelines or
self-hosted), or grant GitHub Actions id-token: write — the dashboard then marks the run
verified, meaning it can prove which workflow produced the statuses, not merely that a valid
token uploaded them. The token’s audience must equal your API URL, which must match the server’s
CI_OIDC_AUDIENCE (default https://api.glotto.dev).
glotto verify-upload --checks glotto-verification/check-results.json
glotto verify-attestation
Available in the published CLI —
npm i -g @glotto/cli
Verify a downloaded verification attestation — the signed, self-contained DSSE envelope that binds one verification report to the run that produced it. This is the command a buyer’s compliance reviewer runs: it turns a JSON file into a verdict without requiring anyone to construct DSSE pre-authentication bytes by hand.
Exits 0 when the envelope is verified, 1 when it is not, and 2 on a usage or configuration
error — every usage error is reported before any network call, so a mistake never costs a round
trip and never looks like a failed verification.
--key <path>— a public key file: one SPKI PEM, or a bundle of several concatenated. Repeatable. Keys are matched to signatures by their own derived fingerprint, never by filename or the order you pass them.--offline— never contact the API. With--keymaterial covering every key the envelope names, verification is completely self-contained — which is the point of filing an attestation for years.--api-url <url>— control-plane base URL (envGLOTTO_API_URL; defaulthttps://api.glotto.dev). Used only for the keys--keydid not resolve.--allow-revoked— report a signature made by a revoked key as a warning instead of a failure.--require-countersignature— fail unless at least one verified non-issuing signature is present (see below).--countersign <pkcs8.pem>— co-sign this envelope with your own Ed25519 key. Verification runs first: an envelope that failed is never counter-signed. Your private key is never printed, logged, or uploaded — only the public half and the signature.--upload— send the co-signature to the run the attestation itself names (needsGLOTTO_API_TOKEN). There is deliberately no--project/--runflag: the signature can only be filed against the run it describes.--json— machine-readable verdict.
# Verify against the key the API serves for this envelope
glotto verify-attestation attestation.json
# Fully offline, with a key you archived alongside the artifact
glotto verify-attestation attestation.json --key glotto-attestation.pem --offline
# Co-sign it with your own key, so the artifact no longer rests on Glotto's alone
glotto verify-attestation attestation.json --countersign my-key.pem --upload
What the verdict means. The first signature is Glotto’s; any that follow are second-party
counter-signatures. A counter-signature that fails against a key you hold is tampering and fails
the whole verification; one whose key you simply do not hold is reported and does not fail anything —
holding fewer keys should not make an artifact look worse. If a key resolves as revoked, the
command tells you both facts separately: the signature is genuine, and the key is not to be
trusted. Under --offline no key carries a status at all, so the command says revocation was not
checked rather than implying a clean bill of health.
glotto publish
Available in the published CLI —
npm i -g @glotto/cli
Publish your generated SDKs to their language package registries — npm, PyPI, crates.io, Hex, Maven Central, NuGet, pub.dev and RubyGems. This is the single step of the release CI workflow Glotto emits for your repository, which runs when a release pull request merges.
It runs entirely on your machine, with your own registry credentials. Nothing is sent to Glotto, and Glotto never holds a registry token — each publisher reads the credential its registry expects from the environment, exactly as you would publishing by hand.
--all— publish every configured target.--lang <slug>— publish one target.--dir <path>— the committed output tree to publish from.--version <version>— the release version. Read from the committed manifest when omitted.--dry-run— print the plan without publishing.--emit-workflow <github|gitlab|bitbucket|azure>— print the release CI workflow.
glotto publish --all --dry-run
glotto publish --all
glotto code-owners
Available in the published CLI —
npm i -g @glotto/cli
Apply your glotto.yml’s code_owners block to a repository’s review configuration.
This exists because review ownership is a file on only half the forges Glotto manages: GitHub and
GitLab read the CODEOWNERS file glotto generate emits, so this command reports the path and
writes nothing. Bitbucket and Azure Repos have no such file — their equivalents are default
reviewers and a required-reviewers branch policy, both API configuration — so on those two this is
the only way a code_owners block takes effect at all.
It runs on your machine and talks to your forge, not to Glotto.
--provider <github|gitlab|bitbucket|azure>— required.--repo <ref>—owner/name, ororg/project/repofor Azure Repos. Required.--host <host>— self-managed GitLab or Azure DevOps host.--config <path>— path toglotto.yml.
Credentials come from GLOTTO_VCS_TOKEN, or GITHUB_TOKEN / GITLAB_TOKEN / BITBUCKET_TOKEN /
AZURE_DEVOPS_TOKEN.
glotto code-owners apply --provider bitbucket --repo acme/payments-sdks
glotto login
Available in the published CLI —
npm i -g @glotto/cli
Authenticate to the Glotto control plane via the device-authorization (RFC 8628) flow and store the
resulting token under ~/.glotto/. The published thin-client CLI requires this — glotto generate
dispatches codegen to the control plane.
--api-url <url>— control-plane base URL (envGLOTTO_API_URL; defaulthttps://api.glotto.dev).--scope <scope>— OAuth scope to request; repeatable.--refresh— renew the stored session instead of starting a new device flow.
glotto login # device-flow login to api.glotto.dev
glotto login --api-url https://api.example.com
glotto login --refresh # renew the stored session in place
A session token is valid for 30 days. --refresh renews it without a browser round-trip: the old
token is revoked and a replacement is stored, so a leaked copy stops working as soon as you renew.
The renewal inherits the existing token’s scopes (so --refresh cannot be combined with --scope),
and it never falls back to an interactive flow. Renewals are capped at 90 days from the original
login — past that, --refresh says so and you sign in again with glotto login.
glotto logout
Available in the published CLI —
npm i -g @glotto/cli
Remove the stored control-plane credential for the resolved API URL, leaving other URLs untouched.
Idempotent — exits 0 whether or not a credential was present.
--api-url <url>— control-plane base URL (envGLOTTO_API_URL; defaulthttps://api.glotto.dev).
glotto logout
glotto logout --api-url https://api.example.com
What Glotto also runs for you
Most of the capabilities above are also delivered without your running anything. Glotto produces them on its own infrastructure or inside the CI workflow it emits for your repository, and puts the result where you already look — a pull request, a job summary, your dashboard. The command and the delivery are two routes to the same output, not two different features:
- Verification report — what
glotto verifywrites, also produced for your release pull request and your dashboard, with a signed attestation you can check offline usingglotto verify-attestation. - Drift detection — the specific finding that committed SDK output no longer matches the spec it was generated from, including which files and why.
- Breaking-change detection — every pull request that changes your
spec is classified against the last build, whether or not you run
glotto breaking-changesyourself. - The release flow — the release pull request that precedes the
glotto publishstep above. - Migration — converting an existing project into a
glotto.yml, non-destructively: from Stainless (glotto migrate stainless), from Fern, or one endpoint at a time.
One capability is still delivery-only: the Fern conversion needs the spec pipeline, which runs
server-side, so glotto migrate fern refuses by name in the published CLI rather than half-running.
The boundary is which of Glotto’s engines would have to ship to your machine, not which capabilities
we are willing to offer.