Skip to content
GitHub

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 sent

A 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 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.