Self-hosted ยท open architecture
Reach applications inside a private network โ without opening a single inbound port.
Run the OpenHook server in your own cloud. Install a small agent on Windows or Linux machines inside the network. Agents connect outbound and pull work. Your cloud application submits a task over a REST API and gets the result back โ typically in well under a second.
Deny-by-default agent policy ยท SHA-256 / scrypt credentials ยท CIDR & DNS-rebind-safe allowlists ยท audited every request
Why it's built this way
The design assumes the cloud side may be compromised, and the local machine should still be safe.
The local machine holds the authority
The cloud can only ask. Each agent has a local policy file listing the exact task types, hosts, paths and commands it will accept โ anything else is refused before it runs, no matter what the server sends.
Latency that feels synchronous
Agents hold a long poll open, so a task usually starts within milliseconds. Submitting with wait_ms turns the whole round trip into a single blocking HTTP call.
Nothing to install on machines you least want to touch
The agent is one static binary that registers itself as a native service. No runtime, no dependencies, nothing to patch across the fleet.
Small enough to audit
The server is plain TypeScript run directly by Node 24 โ no build step, and no runtime dependencies at all unless you choose Postgres.
Quick start
Requires Node.js 24+ for the server; the agent runs on Node 18+.
npm start
The server prints an owner email and generated password on first boot. Open http://localhost:8080 and:
- Agents โ Add agent gives you a one-line install command for Windows or Linux.
- API keys โ Create key gives your cloud application a credential.
- New task lets you try it before writing any code.
Install an agent โ Windows (elevated PowerShell)
$env:OPENHOOK_SERVER='https://openhook.example.com'
$env:OPENHOOK_ENROLL_TOKEN='ohe_โฆ'
irm https://openhook.example.com/download/install.ps1 | iex
Install an agent โ Linux (root)
curl -fsSL https://openhook.example.com/download/install.sh | \
sudo OPENHOOK_SERVER='https://openhook.example.com' \
OPENHOOK_ENROLL_TOKEN='ohe_โฆ' bash
Both write a deny-by-default policy โ the agent reports system info and calls localhost, nothing more, until you widen it in agent.config.json.
Calling a local service from the cloud
curl -X POST https://openhook.example.com/api/v1/call \
-H "Authorization: Bearer $OPENHOOK_KEY" \
-H "Content-Type: application/json" \
-d '{
"tag": "warehouse",
"url": "http://192.168.1.50:8080/api/stock/SKU-441",
"method": "GET"
}'
{
"task_id": "8f3cโฆ", "agent_id": "a55dโฆ",
"duration_ms": 37,
"status": 200,
"json": { "sku": "SKU-441", "on_hand": 128 }
}
Task types
Every type is off unless the agent's policy enables it.
| Type | What it does | Payload |
|---|---|---|
http.request | Calls an HTTP service only the agent can reach | { url, method?, headers?, body?, json?, timeout_ms?, max_bytes? } |
shell.exec | Runs an allow-listed executable | { command, args?, cwd?, env?, stdin?, timeout_ms? } |
script.run | Runs an inline script | { interpreter, code, args?, cwd?, env? } |
fs.read / fs.write / fs.list | File access under allowed roots | { path, โฆ } |
net.check | TCP reachability probe | { host, port, timeout_ms? } |
sys.info | Host facts: OS, CPU, memory, uptime, interfaces | {} |
Target with "agent": "warehouse-pc-01" for one machine, "tag": "warehouse" for whichever tagged agent is free, or neither for any capable agent. Tasks wait in the queue, so a machine can be offline when the work is submitted.
Security model
The agent decides what it will run, from a config file the machine's administrator controls.
"policy": {
"enabled_types": ["http.request", "fs.read"],
"http": { "allow_hosts": ["10.0.0.0/8", "*.corp.local"],
"deny_hosts": ["169.254.169.254", "169.254.0.0/16"] },
"fs": { "allow_read_roots": ["C:/openhook/inbox"] },
"shell": { "allow_commands": [], "allow_any": false }
}
shell.execandscript.runare disabled by default; each executable or interpreter must be named.- Filesystem access is confined to explicit roots, checked after resolving
... - HTTP targets match allow/deny lists supporting exact names,
*.wildcardsand CIDR ranges. - Hostnames are resolved and the resulting IP re-checked, so a DNS name pointing at a blocked range is still refused.
- Cloud metadata endpoints are blocked out of the box; redirects are not followed unless you opt in.
- Credentials are stored as SHA-256 hashes (passwords use scrypt): enrollment tokens, agent tokens, and scoped API keys.
What OpenHook will not do: stop an operator who deliberately sets allow_any: true on cmd.exe from handing the cloud full control of that machine. Keep policies narrow.
Identity and access
Single sign-on against any OIDC provider โ Entra ID, Okta, Google Workspace, Keycloak, Auth0 โ with authorization-code flow and PKCE.
| viewer | operator | admin | owner | |
|---|---|---|---|---|
| Read tasks and agents | โ | โ | โ | โ |
| Submit and cancel tasks | โ | โ | โ | |
| Manage agents, keys, users, audit | โ | โ | ||
| System settings | โ |
API keys are separate credentials for machines, scoped to tasks:read, tasks:write, agents:read, agents:write, with optional expiry and a per-key rate limit.
Reliability & observability
Leases
A claimed task carries a lease; if the agent dies, the task is requeued โ or failed, once max_attempts is spent โ rather than lost.
Cancellation
Cancellation reaches a running task within seconds and kills its process tree.
Idempotency
Pass idempotency_key and a repeated submission returns the original task, even across replicas.
Graceful drain
On SIGTERM, readiness flips false, parked agents are released to reconnect elsewhere, and in-flight work finishes before exit.
Metrics
/metrics serves Prometheus text: queue depth, task durations, queue wait, lease expiries, auth failures, agent counts, live replicas.
Health checks
/healthz is liveness, /readyz is readiness including a database check. Logs are structured JSON with automatic credential redaction.
Deployment
docker compose up -d # single node, SQLite
helm install openhook deploy/helm/openhook -f my-values.yaml
| SQLite (default) | Postgres | |
|---|---|---|
| Replicas | Exactly one | As many as you like |
| Setup | None | A managed database |
| Rolling deploys | Brief gap | Zero gap |
Full platform recipes for AWS, Azure, GCP and Kubernetes live in deploy/README.md. One requirement that bites people: agents hold long polls open for up to 25 seconds, so every proxy, ingress and load balancer in front must allow idle connections longer than that.
Run OpenHook in your own cloud
Self-hosted, auditable, and outbound-only from day one.
Documentation
Guides for installing agents, managing tasks, and integrating the OpenHook API.