Skip to content

Naming conventions

These describe what the surface already does, so a new field or flag has an answer before anyone has to review it. Where a rule has known exceptions they are named inline — a rule with a hidden exception is worse than no rule.

Casing. Every field is snake_case. Abbreviations are admitted only where the short form is the name people use: protocol and format tokens (tls, http, tcp, udp, url), env, cron, args, config, and the recovery term pitr. Invented shortenings are not — no cfg, no dir, no max.

Number follows shape. A field holding a map of things is plural; a field holding one object is singular. The exceptions are mass nouns, where the plural names a single settings object rather than a collection — resources, credentials, overrides, logs, metrics, alerts — and env, which is a map named for the thing it configures.

PluralSingular
workloads services registries environments hooks notifications backup_targets external_servicesruntime deployment proxy observability

Arrays are plural too: routes, env_files, volumes, published_ports, needs, verifications, env_checks. Four are not, because the singular reads as the thing being stated rather than a list: order, on, require, present.

Booleans announce their direction. A boolean that grants permission is allow_*; one that imposes a requirement is require_*. A boolean describing state is a bare adjective, or the name of the thing it switches on (health.tcp).

allow_agent_proposals: true # grants
require_migration_backup: true # demands
managed: false # state

One grammar for time. Every duration is a string with the same syntax — 30s, 5m, 1h30m, 14d. No field carries its unit in its name, and no duration is an integer.

Every application container is <app>-<component>-<replica>. Replica ordinals are one-based and never omitted, including for a singleton. A three-replica server workload in the monk application is therefore monk-server-1, monk-server-2, and monk-server-3; singleton components are monk-feed-1, monk-postgres-1, and monk-redis-1.

A hyphen authored inside app or a component is doubled in the runtime name, so the separator remains unambiguous: app help-desk and component web-api produce help--desk-web--api-1.

The temporary container joining a rollout is <app>-<component>-new. It takes the stable numbered slot only after the previous occupant has drained and been removed. The host-scoped managed proxy is onebox-proxy.

Container names are the human-facing runtime grammar. Persistent volumes, Compose projects, networks, and proxy-provider objects use their own derived names because they have different collision and data-lifetime constraints.

A command is an imperative verb, or a noun that prints. Nothing in between. The split is grammatical, not about mutation — preview and validate are imperative verbs that change nothing.

Verbs — you tell it to actNouns — you ask it for a fact
deploy rollback abort resume approve bootstrap destroy eject exec init plan preview validatestatus logs audit doctor schema version preflight canonical

Groups that hold subcommands are nouns: job, proxy, secrets, and service. A group is singular when it names a subsystem (ob service apply converges the services: block) and plural only when its subcommands genuinely act on the whole set at once (ob secrets push).

An artifact keeps one word across all three of its surfaces — the command that produces it, the flag that consumes it, and its filename.

ProducerConsumer flagDefault filename
ob plan--planob-plan.json
ob approve--approvalob-approval.json
ob job planob job run --planob-job-plan.json
ob plan --backup-report-out--backup-reportnone — the path is required

The first three have a default path, so --out is optional. --backup-report-out is the exception: it has no default, and no report template is written unless you name a path — and then only when the plan actually requires one. ob-backup-report.json is a convention the guidance and the error messages suggest, not a default the binary supplies. ob job plan takes the same --backup-report-out, on the same terms.

The bypass gets harder as the blast radius grows. ob deploy takes -y to skip its prompt. ob destroy has no flag that skips its confirmation: the application name must be typed back. (It reads that from stdin, so it can still be piped — the point is that no flag turns it off.)

Shape carries meaning for flags that change what a command includes or prints. Three forms, three behaviours:

FormMeansExample
bare nounalso do this — additive, and the only additive flags are destructive--volumes --proxy
-only suffixprint this instead of the default output--digest-only --origins-only
adjective or no- prefixmodify how the command runs--raw --overwrite --redeploy --no-rollback

Overrides name the exact boundary they cross. --break-lock breaks only a stale operation or host lock after its holder is inspected. --allow-destructive-mounts permits only the mount detachments named by the service plan and cannot bypass an unsupported major version. --break-migration-gate applies only to abort recovery, while --override-migration-backup records the explicit reason for proceeding without the report policy required. There is no generic --force switch.

--out is where output goes. Not --to, not --file. Machine-readable output is the global --output, never a per-command --json.

<subject>_<condition>, snake_case, so codes for one subsystem sort together.

The lifecycle contract follows this. The loader’s own codes largely predate it and several are condition-first — unknown_field, no_environment, unknown_service_driver — so the environment codes do not sort together today. New codes should be subject-first.

backup_target_unreachable
protection_disable_pending
restore_drill_schedule_too_sparse
routing_incomplete

One concept, one word. These three are the ones most easily confused, so they are pinned:

WordMeansNever means
serverthe machine you deploy to, in the project file and in every artifacta backup repository
targeta backup destination — backup_targets, protection.targetthe machine you deploy to
workloada container Onebox runs from your declarationa supporting service, a Compose service

ob exec and ob logs take a <workload|service> because those are two different declared things, not two words for one.

Where a word arrives from Docker or Compose it keeps Docker’s meaning, even when that collides with a word above. Users arrive already knowing these, and renaming them would cost more than the collision does.

build.target is a Dockerfile stage, not a backup destination. registries.<name>.server is a registry hostname, from docker login <server>. init, tty, stdin_open, working_dir, cpus, extra_hosts and platform all mean what they mean in Compose.