A tiny, self-hosted webhook inbox for agents and humans. Receive webhooks, keep the original payload in Poche, inspect them as JSON, and replay one to a new destination. Poche uses the Grange document engine under the hood; machhook remains a small native binary.
Agent guide · CLI specs · machin
Webhook debugging usually becomes a temporary tunnel, a log grep, and a hand written curl command. machhook makes that loop durable and scriptable:
producer ──POST /v1/hooks──▶ machhook ──HTTP──▶ Poche ──Grange──▶ list / show / replay
It is designed for agent operators:
| command | purpose |
|---|---|
machhook guide |
embedded mental model and operating loop |
machhook help-json |
machine-readable command catalog |
machhook init |
create/expose the Poche hooks collection |
machhook list / show |
inspect captured events without a dashboard |
machhook replay ID URL |
send one captured event to a test or production URL |
machhook feedback MESSAGE |
report feedback to Poche and optionally the relay |
machhook update |
verify-then-swap content-hash update |
machhook telemetry |
disclosed, opt-out, non-identifying usage signal |
The development build needs a current machin
checkout (for framework/machweb.src), its compiler binary, and a C compiler:
./build.shStart Poche first. Its init command creates the Grange-backed store and prints
an admin token:
export POCHE_DB=./poche.data
../poche/poche init
../poche/poche serve 7700Configure machhook with that token and bootstrap its collection. The URL and
token can also be passed as --poche-url and --poche-token.
export MACHHOOK_POCHE_URL=http://127.0.0.1:7700
export MACHHOOK_POCHE_TOKEN=<poche-admin-token>
bin/machhook init
bin/machhook serve --port 8787Send a webhook from another shell:
curl -sS -X POST http://127.0.0.1:8787/v1/hooks \
-H 'Content-Type: application/json' \
-H 'X-Event-Type: invoice.paid' \
--data '{"event":"invoice.paid","amount":42}'Then inspect or replay it:
bin/machhook list
bin/machhook show <id>
bin/machhook replay <id> http://127.0.0.1:9999/hooksMACHHOOK_POCHE_URL— Poche base URL; defaults tohttp://127.0.0.1:7700.MACHHOOK_POCHE_TOKEN— Bearer token sent to Poche. An admin token is needed formachhook init; a token withhooksread/create permissions is enough for normal operation.MACHHOOK_FEEDBACK_RELAY— optional feedback relay URL.
Stores the body, selected headers, source address, and receive timestamp in the
Poche hooks collection. The request body is capped at 1 MiB. Success:
{"ok":true,"id":"...","received":true}If Poche is unavailable or rejects the request, machhook returns 502 Bad Gateway
with the standard JSON error envelope. GET /healthz checks the Poche dependency
and reports poche / grange as the storage stack.
The first milestone preserves text/JSON webhook bodies. Binary payload support, HMAC signature verification, filtering rules, and a web UI are intentionally follow-up features rather than hidden promises.
machhook follows the open conventions at cli-specs.intrane.fr:
- Output: stdout is JSON data; stderr is context; semantic exit codes are 80–119; no internal retries.
- Guide: the mental model is embedded in the binary and available offline.
- Feedback: submissions are size-capped and dual-written to Poche and the optional central relay.
- Update:
updatedownloads to a temporary file, checks the full SHA-256, smoke-testsversion, then atomically swaps with a.bakrollback. - Telemetry: a single disclosed, allow-listed, non-identifying payload is
opt-out via
DO_NOT_TRACK=1; CI is off by default. Inspect withmachhook telemetry --dry-run.
./verify.sh creates an isolated temporary Poche/Grange store, starts both
services, bootstraps the hooks collection, ingests a webhook, and checks list/show
and replay behavior. The verifier needs POSIX shell, curl, sed, cmp, and
python3 for its local replay sink. Override the binaries or ports with
POCHE_BIN, MACHHOOK_POCHE_TEST_PORT, MACHHOOK_TEST_PORT, and
MACHHOOK_REPLAY_TEST_PORT.
MIT.