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.
The project file
Section titled “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 backupTargets externalServices | runtime 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 # grantsrequire_migration_backup: true # demandsmanaged: false # stateOne 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
Section titled “Runtime resources”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:
| Form | For | Examples |
|---|---|---|
onebox-<name> | named things on the host and in containers | onebox-postgres, onebox-ingress, onebox-job-nightly, onebox-run, /tmp/onebox-drain |
onebox_<a>_<b> | names joined from several identifiers, which must stay unambiguous | onebox_postgres_data, onebox_services, onebox_web_r0 |
onebox.<key> | labels | onebox.app, onebox.release |
ONEBOX_<KEY> | environment variables | ONEBOX_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.
| What | Name |
|---|---|
| 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 units | onebox-job-<job>.service and .timer |
| Backup units | onebox-backup-<service>-<operation> |
| Proxy routers and services | onebox_<workload>_r<n>, onebox_<workload> |
| Ownership labels | onebox.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.
| Namespace | Separator | Holds |
|---|---|---|
<app>- | hyphen | your workload containers |
onebox- | hyphen | containers and the ingress network Onebox runs on the host |
onebox_ | underscore | volumes, service projects and networks |
Commands
Section titled “Commands”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 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; no flag turns it off.)
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
Section titled “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_unreachableprotection_disable_pendingdrill_schedule_too_sparseroute_collisionReserved words
Section titled “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: backupTargets, backup.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
Section titled “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.