Skip to content

amrltqt/runhook

Repository files navigation

runhook

Runhook is a tiny webhook runtime that executes user‑defined TypeScript/JavaScript functions with Deno workers. It ships with a minimal REST API, an MCP SSE endpoint, and a SQLite backing store.

Features

  • Execute run(ctx) on webhooks
  • Store functions in SQLite (with ULID ids)
  • MCP SSE endpoint for tool-based management
  • Per‑request worker isolation + timeout
  • Optional admin token for management endpoints

Requirements

  • Deno 2.x

Quick start

deno task dev

By default it listens on http://0.0.0.0:8787. The dev task enables --unstable-worker-options so per‑worker permissions work.

REST API

All /functions* endpoints require admin auth when RUNHOOK_ADMIN_TOKEN is set.

Create a function:

curl -X POST http://localhost:8787/functions \
  -H "content-type: application/json" \
  -H "authorization: Bearer $RUNHOOK_ADMIN_TOKEN" \
  --data-binary '{"name":"echo","description":"Echo body","code":"export function run(ctx){ return { ok:true, body: ctx.body }; }"}'

List functions:

curl -H "authorization: Bearer $RUNHOOK_ADMIN_TOKEN" http://localhost:8787/functions

Get one function (includes source):

curl -H "authorization: Bearer $RUNHOOK_ADMIN_TOKEN" http://localhost:8787/functions/echo

Update a function (partial update: code and/or description):

curl -X PUT http://localhost:8787/functions/echo \
  -H "content-type: application/json" \
  -H "authorization: Bearer $RUNHOOK_ADMIN_TOKEN" \
  --data-binary '{"description":"New description"}'

Delete a function:

curl -X DELETE http://localhost:8787/functions/echo \
  -H "authorization: Bearer $RUNHOOK_ADMIN_TOKEN"

Webhook execution (public by default):

curl -X POST http://localhost:8787/webhook/echo \
  -H "content-type: application/json" \
  -d '{"hello":"world"}'

Execution context

The function must export run(ctx). Example:

export function run(ctx) {
  return {
    status: 200,
    headers: { "content-type": "application/json" },
    body: { ok: true, name: ctx.name, body: ctx.body },
  };
}

Context shape:

type ExecContext = {
  name: string;
  url: string;
  path: string;
  method: string;
  headers: Record<string, string>;
  rawHeaders: Array<[string, string]>;
  body: string;
  json: unknown | null;
  query: Record<string, string>;
  now: string;
};

Return values:

  • Response object
  • { status, headers, body }
  • string (plain text)
  • any JSON‑serializable object

MCP (SSE)

SSE endpoint: GET /mcp/sse
Messages endpoint: POST /mcp/messages?session=...

Tools:

  • server.info
  • server.about
  • functions.list
  • functions.create
  • functions.get
  • functions.update
  • functions.delete

Management endpoints are protected by the admin token.

Configuration

Environment variables:

  • RUNHOOK_HOST (default: 0.0.0.0)
  • RUNHOOK_PORT (default: 8787)
  • RUNHOOK_TIMEOUT_MS (default: 1500)
  • RUNHOOK_NET_ALLOWLIST (comma‑separated allowlist; default: all)
  • RUNHOOK_ADMIN_TOKEN (or RUNNHOOK_ADMIN_TOKEN)
  • RUNHOOK_DB_PATH (default: runhook.db)

Packaging

Build a single binary:

deno task build

Run the compiled binary:

./runhook

Tests

deno task test

Docker

Build locally:

docker build -t runhook:dev .

Run:

docker run -p 8787:8787 -e RUNHOOK_ADMIN_TOKEN=secret runhook:dev

Release (semver)

  1. Update deno.json version (e.g. 0.1.1)
  2. Tag and push: git tag v0.1.1 && git push origin v0.1.1

The GitHub Action publishes ghcr.io/<owner>/<repo> with tags:

  • 0.1.1
  • 0.1
  • latest

License

Licensed under either of:

  • Apache License, Version 2.0 (LICENSE-APACHE)
  • MIT license (LICENSE-MIT)

About

No description, website, or topics provided.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages