A self-hosted integration hub. Run it on your own server, mint one API key, and connect as many of your sites as you like through a single local API.
- Nothing runs on anyone else's server. No accounts, no cloud, no phone-home.
- The key is yours alone. It carries 512 bits of entropy, so it is globally unique by sheer randomness — there is no central registry to collide with. It is verified only by your own install, in constant time, against a stored hash. The plaintext key is shown once and never persisted.
- One engine, your workflows. A connection is a list of steps —
fetch(call any of your sites/APIs),transform(reshape data),when(conditional gate). Push data into a connection and read the result back out.
cd packages/connect
npm install(Or, once published, npm install -g @bruncsoft/connect.)
# 1. Mint your key (printed once — store it now)
node bin/connect.js init
# 2. Run the hub (defaults to 127.0.0.1:8787)
node bin/connect.js start --port 8787All requests authenticate with the key:
KEY=bsc_... # the key from `init`
H="Authorization: Bearer $KEY"
# register a site
curl -X POST localhost:8787/sites -H "$H" -H 'Content-Type: application/json' \
-d '{"name":"My Shop","base_url":"https://shop.example.com"}'
# define a connection: forward only when the value changed
curl -X POST localhost:8787/connections -H "$H" -H 'Content-Type: application/json' -d '{
"name": "stock sync",
"steps": [
{ "type": "when", "left": "{{input.sku}}", "op": "not_equals", "right": "{{state.last.sku}}" },
{ "type": "fetch", "url": "https://shop.example.com/api/stock", "method": "POST", "body": { "sku": "{{input.sku}}" } },
{ "type": "transform", "output": { "sku": "{{input.sku}}", "synced_at": "{{meta.now}}" } }
]
}'
# push data in, read the latest result out
curl -X POST "localhost:8787/c/<slug>/in" -H "$H" -H 'Content-Type: application/json' -d '{"sku":"A-1"}'
curl "localhost:8787/c/<slug>/out" -H "$H"| Method | Path | Description |
|---|---|---|
GET |
/ |
Public health check ({ service, version, initialised }). |
GET/POST/DELETE |
/sites · /sites/:slug |
Manage connected sites. |
GET/POST |
/connections · /connections/:slug |
Define / inspect connections. |
POST |
/c/:slug/in |
Run the connection over the posted JSON. |
GET |
/c/:slug/out |
Latest successful result. |
GET |
/c/:slug/runs |
Recent run history. |
fetch—{ "type":"fetch", "url", "method"?, "headers"?, "body"? }→{ status, body }.transform—{ "type":"transform", "output": <any JSON with {{placeholders}}> }.when—{ "type":"when", "left", "op"?, "right" }(op:equals|not_equals|contains, defaultnot_equals). If false the run stops silently — nothing is stored.
{{input.*}} what you pushed · {{prev.*}} previous step output · {{steps.N.*}} step
by index · {{state.last.*}} previous successful result · {{meta.now}} timestamp.
bruncsoft-connect init mint the key (once)
bruncsoft-connect start [--port N] [--host H] [--restrict-private]
bruncsoft-connect key:rotate issue a new key, retire the old one
Options: --data DIR (default ./data), --port (8787), --host (127.0.0.1),
--restrict-private (block fetch to private/loopback addresses — off by default,
since a self-hosted hub usually wants to reach your internal sites).
- The key is 512-bit CSPRNG, stored only as a SHA-256 hash, compared in constant time,
and carries a checksum that rejects typos offline. Rotate with
key:rotate. data/connect.dbis created0600(owner-only) and holds the key hash.- Bind to
127.0.0.1and put it behind your own HTTPS reverse proxy; if you bind to a public interface, firewall it. Request bodies are capped at 256 KB;fetchresponses at 512 KB with a 10 s timeout andredirect: error. - Run history never stores request headers or tokens.
npm test