CLI surface¶
The contract for how iiirisd is invoked: what flags exist, what
they do, what they print, and what they exit with. This is the
surface 1.0 freezes — after 1.0, removing or renaming a flag is a
major bump (releases.md "Versioning").
Operator how-to lives in configuration.md
"Command-line flags" and deployment.md.
Invocation¶
One binary, no subcommands, stdlib flag parsing. Flags may be
written -flag or --flag; -flag=value and -flag value are
both accepted, per stdlib.
Flags¶
| Flag | Effect |
|---|---|
| (none) | Serve with built-in defaults + IIIRIS_* env overrides. The zero-config path. |
-config <path> |
Serve, overlaying the YAML file at path between defaults and env vars. A missing file is an error. |
-version |
Print build identity, exit. |
-check-config |
Load + validate the configuration, print the verdict, exit. |
-print-config |
Print the effective configuration as YAML, exit. |
-h / -help |
stdlib usage listing, exit 0. |
-version, -check-config and -print-config are one-shot:
they report and exit without binding a listener, building a
subsystem, initialising libvips, or entering the Windows service
dispatcher. -check-config and -print-config honour -config;
-version ignores it. When more than one is given, the first of
-version, -check-config, -print-config wins — defined, not
an error.
Exit codes¶
| Code | Meaning |
|---|---|
| 0 | Success. Graceful shutdown for a serving run; the requested output for a one-shot flag. |
| 1 | Runtime failure — listener failed, or a subsystem that must work (source, store) failed to build. |
| 2 | Configuration failure — unreadable file, unparseable YAML, or a value Validate rejects. Also unparseable flags (stdlib's own exit code). |
Operators script against these; they are part of the contract.
Output¶
-version— one line to stdout:iiirisd <version> (<goos>/<goarch>, <go version>, libvips <version>). The version string is the-ldflagsbuild stamp,devin an unstamped build. libvips is reported because it is a runtime dependency resolved differently in every shipping form (distroless, debian-slim, Windows DLL bundle, local build), and is otherwise not answerable from outside the process.-check-config— on success, one line to stdout naming what was checked; on failure,config: <error>to stderr and nothing to stdout.-print-config— YAML to stdout, one document, no leading---. Errors go to stderr with an empty stdout.
Contracts¶
- Flags describe the binary, never the configuration. There is
no
-addr, no-fs-root, no flag mirroring a YAML field. Configuration has three layers with defined precedence (defaults → YAML →IIIRIS_*) and an admin editor; a fourth layer would fork that contract. A new flag is justified only when it changes what the process does, not what the server is. - Zero-config is untouched.
iiirisdwith no flags, no file, and no env vars serves the bundledtestdata/on:8080. No flag is required for any code path. -check-configruns exactly what startup runs, and no more. Both call the same load-and-validate path incmd/iiirisd(config.Load→Config.Validate), so anything the preflight rejects would have stopped a start with the same message and exit code. The converse does not hold: it does not build subsystems, so cache-backend, auth-wiring and hook errors — today raised insidecache.Build*/auth.Build/hook.Buildrather than inValidate— still surface only at startup (also exit 2). WideningValidateto cover the pure-config half of those checks would tighten the preflight without giving it side effects; it has not been done.-print-confignever prints a secret. Output passes throughconfig.Config.Redacted(), which maskssentry.dsn,metrics.password,content_state.store.write_token,hook.webhook.auth_header, everyredis.password, and the inlineauth.profiles.*.usershashes, replacing each non-empty value with***. An empty secret stays empty, so a dump never implies a credential exists when none does. A new secret-bearing config field must be added toRedacted(); the enumeration ininternal/config/redact_test.gois the guard.-print-configdoes not validate. A config that parses but wouldn't start still prints — that is precisely when reading the effective values matters.-check-configis the flag that judges.- A
-print-configdump is diagnostic, not a config file. Feeding it back in would install***as a literal secret. It is for reading, pasting into an issue, and diffing between hosts. - Windows service mode is unaffected. The one-shot flags are handled before service-context detection, and the Service Control Manager passes none of them.
Out of scope¶
- Subcommands (
iiirisd serve,iiirisd config check). A subcommand grammar is a bigger contract to freeze at 1.0 than this binary needs, and it would break every existing invocation and service definition. Rejected in favour of flags. - A third-party CLI framework. stdlib
flagcovers a surface this small; a dependency here buys help-text polish at the cost of another vendored contract. - Flags for config fields. See the first contract above.
- A
-check-configthat builds subsystems. Connecting to Redis, creating filesystem cache directories and compiling hook scripts would make the preflight authoritative — and give it side effects on the host it is meant to be inspecting. The cheaper half of that win (moving pure-config checks intoValidate) stays available. -print-configoutput as an input format. Round-tripping a redacted dump is not supported and not intended.- Machine-readable
-version(--version=json). Nothing consumes it yet; the one-line form is greppable. Additive later if a need appears.