shhoook is a tiny HTTP server designed for quickly building minimal HTTP-based control layers around bash / CLI scripts.
The idea is simple: you describe an endpoint in a single JSON file (URI, method, auth, TTL, argv), and the server:
- listens strictly on an interface IP (
LISTEN_ADDRmust beIP:port); - matches requests by HTTP method + URI template;
- collects parameters from path / query / JSON body;
- substitutes
{placeholders}into command argv; - executes the command with a timeout;
- returns stdout/stderr as
text/plain.
It is suitable for micro-admin panels, orchestration of local operations, admin hooks, healthcheck/exec endpoints, and integrations (including n8n), where a full-featured service would be overkill.
./install-ubuntu.sh --token <your-token>docker compose run --rm gobuild
# the binary will appear at ./dist/shhoook
LISTEN_ADDR="10.8.0.1:8080" CONFIG_DIR="./conf" ./dist/shhoook
curl -sS http://10.8.0.1:8080/health
# ok| Variable | Purpose | Default |
|---|---|---|
| LISTEN_ADDR | IP:port to listen on | 10.8.0.1:8080 |
| CONFIG_DIR | Directory with *.json endpoints | ./conf |
LISTEN_ADDR must be exactly IP:port. Hostnames are not allowed.
The build is controlled via environment variables read by build.sh:
GOOS(default: linux)GOARCH(default: amd64)CGO_ENABLED(default: 0, static build)OUTPUT(default: shhoook)MAIN(default: .)SRC_DIR(default: /src)OUT_DIR(default: /out)
GOARCH=amd64 docker compose run --rm gobuilddocker compose run --rm -e GOARCH=arm64 gobuilddocker compose run --rm -e GOARCH=arm -e GOARM=7 gobuilddocker compose run --rm -e GOARCH=arm -e GOARM=7 gobuilddocker compose run --rm -e GOARCH=arm -e GOARM=6 gobuildEach endpoint is defined in a separate JSON file.
The server loads all *.json files from CONFIG_DIR (default: ./conf) and builds the endpoint list.
File: example/conf/run-hello.json
{
"uri": "/run/:name",
"method": "POST",
"auth": "X-Token:SECRET",
"ttl": "8s",
"error": 500,
"query": {
"who": "world"
},
"body": {
"msg": "hi"
},
"script": ["bash", "-lc", "echo name={name}; echo who={who}; echo msg={msg}"]
}| Field | Required | Description |
|---|---|---|
| uri | yes | URI template |
| method | yes | HTTP method |
| auth | yes | Header:Token |
| script | yes | Command argv |
| ttl | no | Execution timeout (8s default) |
| error | no | HTTP status code on error |
| query | no | Default query parameters |
| body | no | Default body parameters |
:name— a single path segment*rest— path tail (must be the last segment)
Examples:
/run/:id/run/:id/*rest
Parameters are merged in the following order:
- query defaults
- body defaults
- path variables
- URL query parameters
- JSON body parameters
The last value always wins.
You can use {placeholder} in script arguments:
"script": ["bash", "-lc", "echo user={user} id={id}"]If a parameter is missing, an empty string is substituted.
- Binds strictly to an IP address
- Minimal PATH
- Empty environment
- Execution timeouts
- stdout + stderr returned to the client
example/
conf/
run-hello.json
run-with-rest.json
autostart/
alpine/
ubuntu/
Autostart examples are provided in:
example/autostart/alpineexample/autostart/ubuntu
Goal: start shhoook after the required IP (e.g. 10.8.0.1) appears on a network interface.
The example directories mirror full system paths from the filesystem root.
Assumptions in examples:
- binary:
/usr/local/bin/shhoook - configs:
/etc/shhoook/conf - listen address:
10.8.0.1:8080
Supports waiting for an interface/IP before starting the service.
After copying and configuring the files, run:
chmod +x /etc/init.d/shhoook
rc-update add shhoook default
rc-service shhoook startUses ExecStartPre to wait for the interface/IP and applies systemd hardening.
After copying and configuring the files, run:
systemctl daemon-reload
systemctl enable --now shhoook.service
systemctl status shhoook.serviceCheck where it is listening:
sudo journalctl -u shhoook -n 50 --no-pager
ss -lntp | grep shhoook || trueShow recent service logs
sudo journalctl -u shhoook -n 50 --no-pagerCheck listening sockets
ss -lntp | grep shhoook || trueService status
sudo systemctl status shhoook.serviceQuick health check
curl -sS http://<LISTEN_IP>:<PORT>/healthShow recent service logs (syslog)
sudo logread -e shhoook | tail -n 50If logs are written to a file
sudo tail -n 50 /var/log/messages | grep -i shhoook || trueService status
sudo rc-service shhoook statusCheck listening sockets (preferred)
sudo ss -lntp | grep shhoook || trueIf ss is not available
sudo netstat -lntp 2>/dev/null | grep shhoook || trueCheck by port only
sudo ss -lnt | grep ':8080' || trueQuick health check
curl -sS http://<LISTEN_IP>:<PORT>/healthOn Alpine, ss is provided by iproute2:
sudo apk add --no-cache iproute2netstat comes from net-tools (legacy):
sudo apk add --no-cache net-toolsIf the service does not start, check:
- network interface name (
WAIT_IFACE) - assigned IPv4 address / prefix
LISTEN_ADDRresolved from the interface
shhoook is a tool for engineers who need a:
- deterministic,
- minimalistic,
- easily auditable
HTTP layer on top of shell scripts and CLI tools.
No frameworks. No magic. No runtime dependencies.
MIT