Expand description
User-configurable shell commands that fire on scheduler events.
Hooks are off by default and gated behind a confirmation dialog
(see scheduler::commands::hooks::set_hooks). The threat model is
documented in docs/HOOKS.md — anyone with write access to
settings.json can run arbitrary code as the user, so the master
hooks_enabled toggle is the sole trust boundary.
Structs§
- Hook
- One configured hook: subscribe to
event, runcommandwhen it fires (ifenabled).commandis POSIX-style argv that we split withshell-words— no shell is involved, so pipes / redirects need an explicitsh -cwrapper. - Hook
Context - Per-call context populated by the scheduler. Fields show up as
$ENTRACTE_KIND,$ENTRACTE_DURATION_SECS,$ENTRACTE_OUTCOMEwhen the hook child runs; empty when not applicable to the event. - Hook
Test Outcome - Result of a one-off hook test-run, surfaced in the Settings UI so a user can see what a command does before relying on it.
Enums§
- Hook
Event - The scheduler events a hook can subscribe to. Serialised as the
lowercase snake-case name (also the value passed in
$ENTRACTE_EVENT).
Constants§
- HOOK_
TEST_ TIMEOUT - Timeout for a one-off hook test-run from the Settings UI. Shorter than
the fire-path
HOOK_TIMEOUTso a hung command doesn’t leave the user staring at a spinner. - HOOK_
TIMEOUT 🔒 - Hard cap on how long a hook child may run before it’s killed. Hooks are fire-and-forget, so a hung one — an accidental infinite loop, a read that blocks despite the null stdin — would otherwise live until the app exits. 30s is generous for the quick notify/log commands hooks are meant for while still bounding a runaway.
- MAX_
HOOKS_ PER_ EVENT - Hard cap on hooks fired per event. A misconfigured (or malicious)
settings.jsoncould otherwise register thousands of entries and fork-bomb the host on every break boundary. 32 is well above any realistic per-event subscription count. - MAX_
TEST_ 🔒OUTPUT_ BYTES - Cap on captured stdout/stderr returned to the renderer, so a chatty command can’t balloon the IPC payload.
Functions§
- build_
env - Build the
(key, value)env vars handed to the hook child:ENTRACTE_EVENT,ENTRACTE_KIND,ENTRACTE_DURATION_SECS,ENTRACTE_OUTCOME. Missing fields are empty strings so consumers can shell-test them uniformly. - kind_
str 🔒 - matching_
hooks - Return the subset of hooks that should fire for
event. Returns empty when the masterhooks_enabledtoggle is off, regardless of per-hookenabledflags. - parse_
command 🔒 - Split a hook
commandinto(program, args)usingshell-words(no shell is involved). Errors are the user-facing strings the test-run surfaces; the fire path logs them. Pure, so the parse rules are testable. - program_
log_ 🔒label - run_
command_ capture - Run
commandonce withenv, capturing stdout/stderr and exit code, killing it aftertimeout. Powers the Settings “Test” button. Like the fire path, no shell is involved — pipes/redirects need an explicitsh -cwrapper. The capture/spawn is the OS shim; the parsing (parse_command) and output handling (truncate_output) are pure and tested, and this whole function is exercised against real commands in the unit tests. - run_
hooks - Fire every matching hook for
event. Each child runs on its own std::thread with stdio set to/dev/null. We don’t capture output, but the thread does reap the child (and kill it afterHOOK_TIMEOUT) so a fire-and-forget hook can’t leave a zombie or a runaway behind. - run_
hooks_ with - Same as
run_hooksbut delegates spawning tospawn. The callback receives eachHook(already filtered byeventandenabled, and already truncated toMAX_HOOKS_PER_EVENT) plus the env vars that would be passed to its child. Used by tests to verify the cap without actually shelling out hundreds of processes. - sample_
test_ env - The env a test-run exposes, so a command using
$ENTRACTE_*shows realistic values. Uses a representative break-start context. - spawn_
hook 🔒 - spawn_
hook_ 🔒with_ timeout - truncate_
output 🔒 - Lossy-UTF8 a captured stream and cap it at
maxbytes (on a char boundary), flagging truncation. Pure.