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.

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 backupTargets externalServicesruntime deployment proxy

Arrays are plural too: routes, envFiles, volumes, publishedPorts, needs, checks, env_checks. Four are not, because the singular reads as the thing being stated: 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.

ob is the command; onebox is everything it puts anywhere else. Only what an operator types or keeps next to the project uses ob: the command, the project file ob.yml, and the artifacts passed between commands (ob-plan.json, ob-approval.json, ob-backup-report.json). Every name Onebox creates on a host, inside a container, or for its own use begins with onebox, and the separator says what kind of name it is:

FormForExamples
onebox-<name>named things on the host and in containersonebox-postgres, onebox-ingress, onebox-job-nightly, onebox-run, /tmp/onebox-drain
onebox_<a>_<b>names joined from several identifiers, which must stay unambiguousonebox_postgres_data, onebox_services, onebox_web_r0
onebox.<key>labelsonebox.app, onebox.release
ONEBOX_<KEY>environment variablesONEBOX_RESULT_FILE
/var/lib/onebox/…paths/var/lib/onebox/app
.onebox-<name>Onebox’s own files inside a directory it shares.onebox-schedule.lease, .onebox-tmp

The application’s name appears only in the author’s own workload containers and Compose project, and where the application has to be identified beyond the host: the owner record, the backup repository path and the database name. A test fails the build when code introduces any other ob-, ob_ or ob. name.

Every workload container is <app>-<component>-<replica>. Replica ordinals are one-based and never omitted, including for a singleton, because replicas can change and a rollout moves containers between numbered slots. A three-replica server workload in the monk application is therefore monk-server-1, monk-server-2, and monk-server-3; a singleton workload is monk-feed-1.

Every container Onebox runs from its own images is onebox-<component>. There is no ordinal, because none of them has replicas. Managed services are onebox-postgres and onebox-redis; a restore drill beside one is onebox-postgres-restore; the host proxy is onebox-proxy and its discovery controller onebox-discovery. The application is not in these names: a host runs one application, and the onebox.app label records which. So docker ps reads as two groups: your images under your application’s name, and what Onebox provides under onebox-.

The same namespace covers the host’s other shared resources: the ingress network the proxy and routed workloads join is onebox-ingress. Because the namespace is Onebox’s, an application may not be called onebox or begin with onebox-, and proxy, discovery, ingress and services are not valid service names.

Everything else Onebox puts on the host is in the same namespace, without the application.

WhatName
State directory/var/lib/onebox/app (releases, current, journal, services, backup)
Host state/var/lib/onebox/_host (owner record, host lock, host journal, proxy), whatever basePath says
Scheduled job unitsonebox-job-<job>.service and .timer
Backup unitsonebox-backup-<service>-<operation>
Proxy routers and servicesonebox_<workload>_r<n>, onebox_<workload>
Ownership labelsonebox.app, onebox.workload, onebox.service, onebox.release, …

The onebox.app label, not any name, is what records which application owns a resource. Names can drop the application because a host has exactly one: the owner record is at a fixed path, so a different basePath does not give a second application a second record. Custom Traefik objects in a proxy configuration directory may not begin with onebox_.

Names do not carry the environment. Staging and production of shop both derive shop-web-1 and onebox-postgres. What keeps them apart is the host: its owner record names one application and one environment, and preflight refuses a deploy of any other.

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.

Every other resource Onebox derives is onebox_<component>_…. Volumes, service Compose projects, the service network and restore-drill resources: onebox_postgres_data, onebox_web_uploads, onebox_postgres, onebox_services. They do not carry the application: a host has one, the onebox.app label records which, and ob destroy releases the host only after removing them. They join with underscores because a component or volume name cannot contain one, so each name maps back to exactly one component and volume: a hyphen, which names may contain, could not promise that. The one exception is the application’s own Compose network, shop_default, which is the name Compose gives it.

NamespaceSeparatorHolds
<app>-hyphenyour workload containers
onebox-hyphencontainers and the ingress network Onebox runs on the host
onebox_underscorevolumes, service projects and networks

A command is an imperative verb, or a noun that prints. 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; 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
drill_schedule_too_sparse
route_collision

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: backupTargets, backup.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.