Package Exports
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@bitmacro/relay-agent) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@bitmacro/relay-agent

→ Web UI: relay-panel.bitmacro.io
→ BitMacro Ecosystem: bitmacro.io
Manage your Nostr relay without touching the terminal.
relay-agent is a REST API agent that runs on your relay server and translates HTTP requests into strfry CLI commands. It is part of the BitMacro Relay Manager ecosystem.
| Project | Description | License |
|---|---|---|
| relay-agent | This repo — REST API for strfry | MIT |
| @bitmacro/relay-connect | BitMacro Connect SDK (NIP-46 / NIP-07) | MIT |
| relay-connect-web | Connect UI + /signer proxy (Next.js) |
MIT |
| relay-api | Central hub (Supabase, proxy) | Private |
| relay-panel | Relay management UI | BSL 1.1 |
Quick Start
Via npx
npx @bitmacro/relay-agent --port 7800 --token your-secret-tokenUse --version or --help to check version or CLI options.
Via Docker
Multi-arch image (amd64, arm64) at ghcr.io/bitmacro/relay-agent. Includes strfry binary. Mount your strfry data volume:
docker pull ghcr.io/bitmacro/relay-agent:latest
docker run -p 7800:7800 \
-e RELAY_AGENT_TOKEN=your-secret-token \
-v /path/to/strfry-db:/app/strfry-db \
-v /path/to/whitelist.txt:/app/whitelist.txt \
ghcr.io/bitmacro/relay-agent:latestOr build locally: docker build -t relay-agent .
Multiple relays (v0.2): One agent, N relays via RELAY_INSTANCES. Use docker-compose.relay-agent.yml (fragment) or docker-compose.yml (standalone).
Server deployment (v0.2 multi-relay)
# 1. Clone relay-agent into a subdir next to your docker-compose.yml
git clone https://github.com/bitmacro/relay-agent.git relay-agent
# 2. Configure .env (single token for all relays)
echo "RELAY_AGENT_TOKEN=your-secret-token" >> .env
# 3. Build and start (requires relay_private, relay_public, relay_paid, network bitmacro in parent compose)
docker compose -f docker-compose.yml -f relay-agent/docker-compose.relay-agent.yml build relay-agent
docker compose -f docker-compose.yml -f relay-agent/docker-compose.relay-agent.yml up -d relay-agentPrerequisites: nostr/{public,private,paid}/ must have strfry.conf, whitelist.txt, data/.
Operational Commands
v0.2 multi-relay
# Build and start
docker compose -f docker-compose.yml -f relay-agent/docker-compose.relay-agent.yml up -d --build relay-agent
# View logs
docker compose -f docker-compose.yml -f relay-agent/docker-compose.relay-agent.yml logs -f relay-agent
# Stop
docker compose -f docker-compose.yml -f relay-agent/docker-compose.relay-agent.yml stop relay-agentStandalone (from relay-agent dir)
cd relay-agent && docker compose up -dSmoke Test
# v0.2: health lists relay IDs and version
curl http://localhost:7810/health
# {"status":"ok","version":"0.2.7","relayIds":["public","private","paid"],...}
# Per-relay: GET /:relayId/health includes the same package version field.
# v0.2: stats for a specific relay (replace TOKEN and relay id)
curl -H "Authorization: Bearer TOKEN" http://localhost:7810/private/statsREST API Endpoints
v0.2 multi-relay (RELAY_INSTANCES set)
| Method | Path | Description | Auth |
|---|---|---|---|
GET |
/health |
List active relay IDs | no |
GET |
/:relayId/health |
Health for relay | no |
GET |
/:relayId/stats |
Relay statistics | Bearer |
GET |
/:relayId/events |
List events | Bearer |
DELETE |
/:relayId/events/:id |
Delete event | Bearer |
GET |
/:relayId/policy |
Policy entries | Bearer |
GET |
/:relayId/policy/blocked |
List blocked pubkeys (! lines in whitelist) |
Bearer |
POST |
/:relayId/policy/block |
Block pubkey | Bearer |
POST |
/:relayId/policy/allow |
Allow pubkey ({ "pubkey", "label"? }) |
Bearer |
DELETE |
/:relayId/policy/allow/:pubkey |
Remove pubkey from allow list (plain line) | Bearer |
DELETE |
/:relayId/policy/block/:pubkey |
Remove !pubkey block line |
Bearer |
GET |
/:relayId/users |
List pubkeys | Bearer |
relayId = logical ID from RELAY_INSTANCES (e.g. public, private, paid). Must match agent_relay_id in relay_configs.
v0.1 single-relay (no RELAY_INSTANCES)
| Method | Path | Description |
|---|---|---|
GET |
/health |
Health check |
GET |
/stats, /events, /policy, /policy/blocked, /users |
Same as above, no prefix |
DELETE |
/policy/allow/:pubkey, /policy/block/:pubkey |
Remove allow / block line (same whitelist file) |
Query parameters for GET /events
| Param | Type | Description |
|---|---|---|
kinds |
comma-separated | e.g. 1,3 |
authors |
comma-separated | pubkeys |
since |
unix timestamp | |
until |
unix timestamp | |
limit |
number | max events to return |
Authentication
All endpoints except /health require:
Authorization: Bearer <your-token>API Roadmap
Alta prioridade (esta PR)
-
DELETE /:relayId/policy/allow/:pubkey -
DELETE /:relayId/policy/block/:pubkey -
GET /:relayId/policy/blocked
Média prioridade
-
DELETE /:relayId/events— apagar eventos por filtro{ kinds?, authors?, since?, until? } -
GET /:relayId/events/count— contagem sem payload completo -
POST /:relayId/policy/allow/batch— body{ pubkeys: string[] }, máx. 100
v1.0 Lightning
-
GET /:relayId/subscribers— lista pubkeys com expiração de acesso pago -
POST /:relayId/invoice— gerar invoice LNbits para nova subscrição
Environment Variables
v0.2 multi-relay
| Variable | Default | Description |
|---|---|---|
RELAY_INSTANCES |
— | JSON array of {id, token, strfryConfig, strfryDb, whitelistPath?} — see strfry.conf vs relay-agent |
RELAY_AGENT_TOKEN |
— | Not used when RELAY_INSTANCES is set |
STRFRY_BIN |
strfry |
Path to strfry binary |
PORT |
7800 |
HTTP server port |
ALLOWED_ORIGINS |
— | Comma-separated extra CORS origins |
strfry.conf vs relay-agent (v0.2)
The agent runs strfry with cwd = dirname(strfryDb). The db = … line in strfry.conf is resolved relative to that directory.
- In each relay’s
strfry.conf, use:db = "./data/"
- In
RELAY_INSTANCES, setstrfryDbto the LMDB directory inside the agent container, e.g.:where"/app/nostr/<relayId>/data"
<relayId>matches your logical id (public,private,paid). Mount the host folder to that path (seedocker-compose.relay-agent.yml).
Then ./data/ resolves to /app/nostr/<relayId>/data — the same files the relay uses if the host nostr/<relayId>/data is mounted consistently.
Relay containers (strfry relay) often use working_dir: /app and mount the same host data/ at /app/data, still with db = "./data/". That is a different in-container path but the same host directory as the agent mount.
Breaking change: Older setups used db = "./strfry-db/" and strfryDb ending in strfry-db. Migrate to db = "./data/" and strfryDb ending in /data, and align Docker volume targets on both the agent and relay services.
v0.1 single-relay
| Variable | Default | Description |
|---|---|---|
RELAY_AGENT_TOKEN |
— | Required. Bearer token for API auth |
STRFRY_BIN |
strfry |
Path to strfry binary |
STRFRY_DB_PATH |
./strfry-db |
Path to strfry database directory |
STRFRY_CONFIG |
— | Path to strfry config file |
WHITELIST_PATH |
/etc/strfry/whitelist.txt |
Path to whitelist file |
PORT |
7800 |
HTTP server port |
ALLOWED_ORIGINS |
— | Comma-separated extra CORS origins |
Compatibility
| relay-agent | strfry | Mode |
|---|---|---|
| 0.1.x | 1.0.x | Single-relay |
| 0.2.x | 1.0.x | Multi-relay (RELAY_INSTANCES) |
Architecture
relay-panel
│ HTTP + JWT
▼
relay-api (Vercel)
│ HTTP REST + Bearer JWT
▼
relay-agent (this repo)
│ child_process spawn()
▼
strfry (local C++ process / LMDB)The relay-agent is stateless — it has no database. State lives in Supabase, managed by relay-api. The relay-agent only translates HTTP calls into strfry CLI commands.
Troubleshooting
503 "relay unavailable"
Capture the error — run logs in one terminal, then curl in another:
# v0.2 docker compose -f docker-compose.yml -f relay-agent/docker-compose.relay-agent.yml logs -f relay-agent curl -H "Authorization: Bearer TOKEN" "http://localhost:7810/private/events?limit=3"
The strfry stderr will appear in the logs.
LMDB "Resource temporarily unavailable" — relay and relay-agent share the same db. Increase
maxreadersin your relay's strfry.conf (e.g../nostr/private/strfry.conf):dbParams { maxreaders = 512 }Then restart the relay:
docker restart relay_privateStale reader slots (after crashes / SIGABRT) can still wedge the relay until cleared. Stop every container that mounts that LMDB directory (relay_*+relay-agent), then on the host (path = host dir that containsdata.mdb):# Debian/Ubuntu: lmdb-utils provides mdb_stat — not mdb_reader_check sudo mdb_stat -e -rr /home/server/nostr/public/data
Double
-rchecks the reader table and removes stale entries. Then start the relay container(s), thenrelay-agent.Verify db path — relay-agent mounts
./nostr/private/data:/app/nostr/private/dataso it matches productionstrfry.confwithdb="./data/"(same layout asrelay_private). If you seemdb_env_open: No such file or directory, the mount path ordb=instrfry.confdoes not match. Check your maindocker-compose.yml:grep -A5 relay_private docker-compose.yml
Test strfry inside container (v0.2):
docker compose -f docker-compose.yml -f relay-agent/docker-compose.relay-agent.yml run --rm relay-agent sh -c 'ls -la /app/nostr/private/data && /app/strfry --config /app/nostr/private/strfry.conf scan "{}" | head -3'
If
data.mdbis missing or strfry fails, fix the volume path.
Security
- Run on a private network. The relay-agent should run on the operator's server and never be exposed directly to the internet.
- Access is controlled by the relay-api, which proxies requests with a shared Bearer token.
- Use a strong, random token in production. Rotate it if compromised.
Contributing
See CONTRIBUTING.md for setup and PR guidelines.
Issues and PRs are welcome — bug fixes, UI improvements, and new relay-agent integrations.
This project is maintained by BitMacro.
Contributors
License
MIT. See LICENSE.