---
title: "Naming conventions"
summary: "How Onebox names things — casing, number, mood, and which words are reserved. Written down so a new field or flag has an answer before the review."
description: "The rules every command, flag, field and error code in Onebox follows."
status: shipped
read_when:
  - "Adding a field, flag, command or error code and choosing what to call it"
  - "Wondering why two similar things are named differently"
---
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.

## The project file

**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.

| Plural | Singular |
| --- | --- |
| `workloads` `services` `registries` `environments` `hooks` `notifications` `backup_targets` `external_services` | `runtime` `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`).

```yaml
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.

## Runtime resources

**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.

## Commands

**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 act | Nouns — you ask it for a fact |
| --- | --- |
| `deploy` `rollback` `abort` `resume` `approve` `bootstrap` `destroy` `eject` `exec` `init` `plan` `preview` `validate` | `status` `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.

| Producer | Consumer flag | Default filename |
| --- | --- | --- |
| `ob plan` | `--plan` | `ob-plan.json` |
| `ob approve` | `--approval` | `ob-approval.json` |
| `ob job plan` | `ob job run --plan` | `ob-job-plan.json` |
| `ob plan --backup-report-out` | `--backup-report` | none — 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.)

## Flags

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

| Form | Means | Example |
| --- | --- | --- |
| bare noun | **also** do this — additive, and the only additive flags are destructive | `--volumes` `--proxy` |
| `-only` suffix | print this **instead** of the default output | `--digest-only` `--origins-only` |
| adjective or `no-` prefix | modify 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`.

## Error codes

`<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
```

## Reserved words

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

| Word | Means | Never means |
| --- | --- | --- |
| **server** | the machine you deploy to, in the project file and in every artifact | a backup repository |
| **target** | a backup destination — `backup_targets`, `protection.target` | the machine you deploy to |
| **workload** | a container Onebox runs from your declaration | a 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.

## Inherited vocabulary is exempt

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.