Horopter

horo

The horo command line, which reads Horopter's changes and records new ones.

horo reads changes from egress with a key holding read, and records changes through ingress with a key holding append. Each subcommand's URL and key flags default to the variables in Configuration.

Exit Codes

codemeaning
0success
1an error from Horopter, or a --strict refusal
2a usage error
3horo show or horo refold found no such ref

horo run exits with its command's own code instead; see horo run.

Reading Flags

horo changes and horo show take:

flagdefaultmeaning
--egress-url$HOROPTER_EGRESS_URLegress's base URL
--key$HOROPTER_EGRESS_KEYa key holding read
--formattable on a terminal, jsonl otherwisetable, json or jsonl
--json--format json
--utcshow times in UTC

horo changes

horo changes                         # the last 24 hours
horo changes --since 168h --subject acme/prod --status failed
horo changes --from 2026-09-14T00:00:00Z --to 2026-09-15T00:00:00Z --json

Lists the changes overlapping a window. It follows egress's next links, so it returns every matching change however many there are.

flagdefaultmeaning
--since24hthe window, back from now
--from, --tothe window, RFC 3339, in place of --since
--subjecta subject pattern, as egress's subject filter
--type, --status, --locusthe matching filter, exactly
--sourcea source that reported the change
--attrkey=value on a filterable attribute; repeatable
--limit0at most this many changes; 0 for all

horo show

horo show ci/dep-1

Shows one change, by its stored <locus>/<ref>, and every event in it. It exits 3 when there is no such change.

Recording Flags

horo run and horo event take:

flagdefaultmeaning
--ingress-url$HOROPTER_INGRESS_URLingress's base URL
--key$HOROPTER_INGRESS_KEYa key holding append
--typethe event type; horo run takes the change's type and adds the suffix
--subjectwhat the change happened to, e.g. acme/prod
--titlea human-readable title
--actorwho or what performed it
--refthe change's ref, as the producer sends it; without one, horo run makes one, and an event is a point
--attra key=value attribute; repeatable

horo run

horo run --type deployment --subject acme/prod --title "Deploy api" -- ./deploy.sh

Records a command as a change: posts <type>.started, runs the command with its output untouched, then posts <type>.completed, <type>.failed with exit_code, or <type>.cancelled with signal. Why recording never blocks the work, and how signals reach the command, is Recording a Change.

flagdefaultmeaning
--strictrefuse to run the command when its start cannot be recorded, and fail when its end cannot
--report$HORO_RUN_REPORTwrite a JSON report of what was recorded to this file

It exits with the command's own code: 128 + n when signal n ended it, 127 when it could not start, and 1 under --strict when the start or the end could not be recorded.

It waits up to 10 seconds for the start to be recorded, and retries the end for up to 30 seconds after the command. On stderr it prints the stored ref, which horo show takes, and the plain ref, which --ref takes; if the end cannot be confirmed, it prints the horo event command that closes the change.

The Report

{"start_posted":true,"end_posted":true,"ref":"ci/dep-1","producer_ref":"dep-1",
 "outcome":"completed","exit_code":0,"reason":null}
fieldmeaning
start_postedwhether the start was recorded
end_postedwhether the end was recorded; null when the command was not run and no change was closed
refthe stored ref; null when nothing was stored
producer_refthe plain ref
outcomecompleted, failed or cancelled; cancelled too when the command was not run but the change was closed
exit_codehoro run's own exit code
reasonwhy the change was not recorded in full; null when it was

The report is written on every path past argument parsing. If it cannot be written, horo run warns and keeps its exit code.

horo event

horo event --type incident.resolved --subject acme/prod --title "Queue drained"

Records one event.

flagdefaultmeaning
--lifecyclestarted, completed, failed, rolled_back, cancelled or none; see Type Suffixes
--occurred-atnowwhen it happened, RFC 3339
--idempotency-keyone per invocationthe key a retried post repeats

horo refold

horo refold ci/dep-1

Rebuilds one stored change from its events, through POST /v1/admin/refold, and prints its answer. It exits 3 when the ref has no events.

flagdefaultmeaning
--ingress-url$HOROPTER_INGRESS_URLingress's base URL
--key$HOROPTER_ADMIN_KEYa key holding admin; never $HOROPTER_INGRESS_KEY
--tenantthe tenant to act in, for a global key

Version and Build Information

Every Horopter binary, horo, horoctl and the daemons alike, answers --version with the release it was built from (horo v0.13.0) and --build-info with the release, the commit, the commit's time, whether the tree was modified, the Go version, the OS and the architecture. Either must be the first argument.

flagmeaning
--format textthe default
--format json, --jsonJSON; the version without its v, "version": "0.13.0"
--format barethe version alone, 0.13.0, for a script to compare; --version only

A binary built by go install …@v0.13.0 reports the module version.

On this page