hekla, the runtime
All ten subcommands, what each one needs, and what each one refuses.
hekla [OPTIONS] <COMMAND>
Ten subcommands. --no-color is global and is accepted before or after the subcommand;
colour is off anyway when output is not a terminal or NO_COLOR is set to a non-empty
value.
Every DIR argument defaults to ., and every --data-dir defaults to <dir>/data.
What each one needs
| project | data dir | takes the lock | master key | exit 1 when | |
|---|---|---|---|---|---|
hekla check |
yes | no | no | no | any error finding |
hekla test |
yes | no | no | no | a failing test or an error |
hekla secrets |
yes | no | no | no | a required credential is unset |
hekla openapi |
yes | no | no | no | errors, or nothing to describe |
hekla plan |
yes | yes | no | with --replay |
errors, or an unmigrated directory |
hekla serve |
yes | yes | yes | if any @subject |
it could not start |
hekla verify |
yes | yes | yes | if any @subject |
the report is not clean |
hekla rewind |
yes | yes | yes | no | no such effect, or the lock is held |
hekla erase |
no | yes | no | no | no operational database |
hekla rotate |
no | yes | no | yes | the key is unset |
The lock column decides what you can run against a live server: plan, erase and rotate
are fine; verify and rewind need it stopped.
check
hekla check .
checked 2 module(s): 1 command(s), 1 projector(s), 0 effect(s), 1 event(s)
ok: no errors, 0 warning(s)
Runs the compiler and adds the runtime’s own rules: that a declaration sits in the directory its kind requires, and that a read model can be keyed and indexed the way the read API needs.
It runs nothing, opens no data directory and reads no environment, which is what keeps it usable as a CI gate.
Findings print as severity: file:line:col: message, with an optional = hint under them.
What it adds over the compiler
Errors: a command outside commands/, a projector outside projectors/, an effect
outside effects/, an event field beginning with the reserved _hekla_, a sealed entity
column that is not optional, an index covering a subject-encrypted column, and a filterable
field colliding with a reserved read query parameter (limit, cursor, after,
timeout_ms).
Warnings: a fold slice with no high-cardinality filter, an over-constrained slice, a
declared secret nothing reads, and a [secrets] entry naming no declaration.
test
hekla test .
ok: "a new address joins the list"
ok: "the same address twice is refused"
2 passed, 0 failed
Runs every test declaration in the program, wherever it is declared, synthesising the
event envelope the runtime would produce. A project with no tests prints 0 passed, 0 failed and exits 0.
serve
hekla serve . --addr 0.0.0.0:8080
| Flag | Default |
|---|---|
--addr |
127.0.0.1:8080 |
--data-dir |
<dir>/data |
--verify |
off |
See Running a project for startup order, shutdown and every configuration key.
plan
hekla plan --replay --json .
| Flag | |
|---|---|
--json |
the whole plan on stdout, for a deploy gate |
--replay |
re-run recorded effect invocations against the candidate code |
--replay-limit N |
how many of each effect’s most recent invocations, default 1000, minimum 1 |
--data-dir |
--replay-limit requires --replay, because a cap on a replay that is not happening is a
request the command would otherwise accept and drop.
Exits 0 whether or not anything would change: a change is the answer, not a fault. Full treatment in Change safety.
verify
hekla verify .
Checks that a projector rebuilt from position 0 matches the live one, and that every recorded effect invocation still replays without performing anything.
Takes the lock, so it refuses against a directory a server has open. Verify a copy, which checks the backup at the same time. Exit 0 only when the report is clean.
secrets
hekla secrets .
DISCORD_WEBHOOK set 2633c771 env DISCORD_WEBHOOK_URL
SENTRY_DSN unset (optional) env HEKLA_SECRET_SENTRY_DSN
STRIPE_KEY MISSING file /run/secrets/stripe
failed: 1 of 3 credential(s) unset; serving would refuse to start
Never prints a value. Reads the environment and [secrets] and nothing else: no data
directory, no log, no lock. Exits non-zero when a required credential is unset, so it stands
alone as a pre-deploy gate.
erase
hekla erase customer_id 7 .
erased subject `customer_id` = `7`
Deletes that subject’s encryption key, making every value scoped to it unreadable across the log and every read model at once. Irreversible. Needs no master key and takes no lock.
A subject with no key answers no key for subject ... (already erased or never created).
Both exit 0.
rotate
hekla rotate .
rewrapped 412 subject key(s) under the primary master
Rewraps every subject key under HEKLA_MASTER_KEY, unwrapping with
HEKLA_MASTER_KEY_PREVIOUS as needed. Ciphertext is unchanged, so reads keep working. The
ordered procedure is in Keys and secrets.
rewind
hekla rewind NotifyCustomer 412 .
Moves an effect back to a position so it reprocesses everything after it and performs those side effects again. Irreversible.
| Flag | |
|---|---|
--live |
also lower an on live boundary. Permanent, and off by default |
--yes |
answer the prompt without silencing the summary |
--data-dir |
Position 0 reprocesses the whole log. Refuses while a server holds the directory, and
without a TTY refuses unless --yes is given.
Deliberately not an HTTP endpoint. See Effects in production.
openapi
hekla openapi . > openapi.json
The generated OpenAPI 3.1 document on stdout, findings on stderr, so the redirect is safe.
The same document /openapi.json serves.
Refuses a path that is not a directory, a project with errors, and a directory that declares nothing at all.
There is no hekla fmt or hekla lsp
Neither exists. They arrive when heklang has a language server of its own. Formatting is
hek fmt’s job, and it rewrites every .hk file a project holds today.