AfterHook Released to Send Webhooks From Software That Never Had Any
Aftermarket 0.5.0 adds AfterHook, run as aftermarket hook. It’s for the app that changes data and never tells anyone.
You know the kind. An order system writes its status to a file. A vendor tool drops CSV exports into a folder at odd hours. A small internal app keeps everything in SQLite and has no API at all. Something else needs to know when those things change, and the usual answer is a cron job someone wrote years ago that diffs a file and sends an email. Sometimes.
AfterHook watches from the side and sends a signed webhook for each change. The app stays exactly as it is.
A config
state: hook-state.json
webhooks:
- id: ops
url: https://hooks.example.com/orders
secret_env: HOOK_SECRET
sources:
- id: order-status
file: {path: status.json}
ignore: [updated_at]
- id: exports
folder: {path: exports, pattern: "*.csv"}
- id: orders-table
command:
run: [sqlite3, -json, app.db, "select id, status, total from orders order by id"]
interval: 15s
There are four kinds of source: a file, a folder, an HTTP endpoint, and a command’s output. The last one is how it watches SQLite. It runs the sqlite3 program and compares what comes back, so there’s no database driver inside AfterHook and nothing to link against.
What an event says
A JSON, YAML or .env file is compared field by field, by meaning. A change of spacing or key order sends nothing. So does a timestamp you told it to ignore. When an order gets paid, the webhook gets this:
{"op": "changed", "path": "status", "old": "open", "new": "paid"}
A new row in the SQLite table arrives as one added row. A new CSV in the folder arrives with its name, size and SHA-256.
What it promises
- Every change gives exactly one event. The first check only sets a baseline, so starting AfterHook on a busy folder doesn’t flood anyone.
- Nothing gets lost. Events wait in a state file and are retried with growing waits until the receiver takes them, in order, across restarts. A change found again after a crash keeps its event id, so the receiver can drop the repeat.
- A half-written file is never reported. A file is read twice before a change counts. A file that disappears or suddenly shrinks has to look the same on the next check too.
- Secrets stay out. Values under keys like
passwordortokengo out asREDACTED, and so do passwords inside URLs. The signing secret comes from an environment variable and never touches the config, the state file or the log.
Each event is signed with HMAC-SHA256 over a timestamp and the body. The example folder has a receiver of about fifty lines that checks the signature, rejects old timestamps and drops repeats.
hook check validates a config, hook run --once fits a cron job, --dry-run prints events without sending them, and hook test runs before and after fixtures in CI. The AfterHook reference has the full format, and the download page has the binaries.
0.5.1 followed the same day with one fix, for events sent after a restart. How AfterHook was tested tells that story.