Configuration
Declarative YAML with environment overrides. You can validate it, diff it, template it out of a ConfigMap and reason about it without running it, which is everything a config file should let you do and nothing more. Every field below is optional and shown at its default.
server: host: 0.0.0.0 port: 1880 adminRoot: / # where the editor and admin API live httpRoot: / # where a flow's HTTP In nodes live readTimeout: 60s # generous: a big deploy over a slow edge link is legitimate writeTimeout: 0s # zero on purpose, the comms websocket is long-lived shutdownTimeout: 20s maxRequestBytes: 33554432 # 32 MiB, bounds a flow deploy
data: dir: /data # the PVC. Flows, credentials, the deployment log and state.db live here flowFile: flows.json credentialsFile: credentials.json credentialSecret: "" # empty means plaintext, which is refused by default allowPlaintextCredentials: false backupGenerations: 3
history: retain: 100 # deployment records kept under data.dir/deployments; 0 keeps all git: url: "" # empty is off; see "A git mirror" branch: main path: flows.json emailDomain: flow.invalid
auth: enabled: true # off refuses to start unless HOTLOOP_FLOW_INSECURE=true sessionTTL: 168h # sessions live in data.dir/sessions.json, hashed, and survive restarts lockout: attempts: 5 # failures for one account from one address before it locks there perAddress: 20 # failures from one address, any account, before the address locks window: 15m duration: 15m users: - username: admin passwordHash: "$2a$10$..." # bcrypt only, never plaintext permissions: ["*"] tokens: # API tokens for machines; the hash only, from: hotloop-flow token - name: ci hash: "sha256:..." permissions: ["flows.read", "flows.write"]
runtime: inboxCapacity: 1024 overflow: block # block | drop-newest | drop-oldest | error blockTimeout: 30s closeTimeout: 15s
store: path: "" # empty is state.db in data.dir: persistent context and durable wires synchronous: normal # normal | full. full fsyncs every commit, and wears the card context: persist: true # false is memory only flushInterval: 30s # a changed value waits this long; a kill -9 loses it. 0s writes at once durable: maxBytes: 268435456 # disk quota for every durable wire together ackFlush: 100ms # how long a delivered message waits to be marked done; 0s marks each one first
discovery: enabled: false allowedCIDRs: [] # enabled with this empty is a startup error
exec: enabled: false allowedCommands: [] # enabled with this empty is a startup error
files: allowedPaths: [] # extra trees on top of data.dir, which is always allowed
secrets: allowedPaths: [] # where a tls-config may read a certificate or key, on top of data.dir
logging: level: info # error | warn | info | debug | trace format: text # text for a terminal, json for a cluster
metrics: enabled: true path: /metrics
audit: maxBytes: 16777216 # data.dir/audit.log rotates at 16 MiB keep: 4 # rotated files kept beside it
tests: gate: false # refuse a deploy that fails data.dir/flows.test.yaml timeout: 2m # wall clock for one run of the flow tests
trace: # where every message went, kept in memory, bounded enabled: true traces: 256 # journeys kept, newest maxHops: 512 # hops one journey keeps maxMessageBytes: 16384 # the biggest message a hop keeps a copy of maxBytes: 16777216 # everything together, oldest forgotten first perSecond: 1000 # new traces a second; 0 traces every message otlp: endpoint: "" # http://collector:4318 sends every hop as a span serviceName: hotloop-flow interval: 2s # the longest a finished hop waits to be sentA typo in that file is a startup failure rather than a setting that silently does
nothing, because unknown fields are rejected. An operator who misspells
credentialSecret should find out immediately, not six months later when they
notice the credential file is plaintext.
Environment overrides
Section titled “Environment overrides”Environment beats the file, which is the only workable split on Kubernetes: a Helm chart puts the boring settings in a ConfigMap and injects the secrets from a Secret.
| Variable | Sets |
|---|---|
HOTLOOP_FLOW_CONFIG |
Path to the YAML file. |
HOTLOOP_FLOW_HOST, HOTLOOP_FLOW_PORT |
Listener. |
HOTLOOP_FLOW_ADMIN_ROOT, HOTLOOP_FLOW_HTTP_ROOT |
Path prefixes. |
HOTLOOP_FLOW_DATA_DIR, HOTLOOP_FLOW_FLOW_FILE |
Where state lives. |
HOTLOOP_FLOW_CREDENTIAL_SECRET |
Credential encryption secret. |
HOTLOOP_FLOW_ADMIN_USER, HOTLOOP_FLOW_ADMIN_PASSWORD_HASH |
A single admin account with full permissions, which is what makes a first-run container usable without mounting a file. Both must be set. |
HOTLOOP_FLOW_DEPLOY_TOKEN_HASH |
A deploy token called deploy, for CI: flows.read and flows.write, nothing else. The hash, never the token. |
HOTLOOP_FLOW_GIT_URL, HOTLOOP_FLOW_GIT_USERNAME, HOTLOOP_FLOW_GIT_PASSWORD |
The git mirror. The password (an access token) is environment only. |
HOTLOOP_FLOW_INBOX_CAPACITY, HOTLOOP_FLOW_OVERFLOW |
Scheduler defaults. |
HOTLOOP_FLOW_STORE_PATH, HOTLOOP_FLOW_STORE_SYNCHRONOUS |
The state database: where it is, and whether every commit is fsynced. |
HOTLOOP_FLOW_CONTEXT_PERSIST, HOTLOOP_FLOW_CONTEXT_FLUSH_INTERVAL |
Persistent context. false turns it off; the interval is a duration such as 30s. |
HOTLOOP_FLOW_TRACE_OTLP_HEADERS |
Headers for the collector, Name=value pairs separated by commas, added to any in the file. For an API key that has no business in a ConfigMap. |
HOTLOOP_FLOW_DURABLE_MAX_BYTES, HOTLOOP_FLOW_DURABLE_ACK_FLUSH |
The disk quota for durable wires, and how long a delivered message waits to be marked done. |
HOTLOOP_FLOW_TESTS_GATE |
true turns on the deploy gate. |
HOTLOOP_FLOW_LOG_LEVEL, HOTLOOP_FLOW_LOG_FORMAT |
Logging. |
HOTLOOP_FLOW_DISCOVERY_ENABLED, HOTLOOP_FLOW_DISCOVERY_CIDRS |
Discovery nodes. Comma-separated CIDRs. |
HOTLOOP_FLOW_EXEC_ENABLED, HOTLOOP_FLOW_EXEC_ALLOWED_COMMANDS |
The exec node. Comma-separated commands. |
HOTLOOP_FLOW_FILE_ALLOWED_PATHS |
Extra file node roots. Comma-separated. |
HOTLOOP_FLOW_SECRET_ALLOWED_PATHS |
Extra roots a secret file may be read from. Comma-separated. |
HOTLOOP_FLOW_INSECURE |
true runs with authentication off, and nothing else does. Only 1, true, yes and on count, so false or 0 can’t turn it off by accident. For an isolated network you’ve decided to own. It doesn’t waive the credential secret. |
HOTLOOP_FLOW_ALLOW_PLAINTEXT_CREDENTIALS |
Permits unencrypted credentials at rest. |
The on/off variables accept only 1, true, yes and on, in any case.
Anything else is off, so HOTLOOP_FLOW_ALLOW_PLAINTEXT_CREDENTIALS=0 means no,
not “it’s set, so yes”. A safety switch that flips on because somebody typed
=false is exactly the kind of thing that ends up in an incident report.
Anything not in that table is file-only, and that is deliberate: session TTL, backup generations, request size limits, and the server timeouts are decisions somebody should be reviewing in a ConfigMap, not typing into a shell at 3 AM while a line is down. I have been that person. Do not let that person edit timeouts.