npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bitmacro/relay-agent

v0.2.9

Published

REST API agent for managing Nostr relays without touching the terminal

Readme

@bitmacro/relay-agent

BitMacro relay-agent

CI npm version Docker GHCR License: MIT TypeScript Node Hono Vitest tsup strfry

→ 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-token

Use --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:latest

Or 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-agent

Prerequisites: 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-agent

Standalone (from relay-agent dir)

cd relay-agent && docker compose up -d

Smoke 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/stats

REST 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)

  • [x] DELETE /:relayId/policy/allow/:pubkey
  • [x] DELETE /:relayId/policy/block/:pubkey
  • [x] 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, set strfryDb to the LMDB directory inside the agent container, e.g.:
    "/app/nostr/<relayId>/data"
    where <relayId> matches your logical id (public, private, paid). Mount the host folder to that path (see docker-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"

  1. 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.

  2. LMDB "Resource temporarily unavailable" — relay and relay-agent share the same db. Increase maxreaders in your relay's strfry.conf (e.g. ./nostr/private/strfry.conf):

    dbParams {
      maxreaders = 512
    }

    Then restart the relay: docker restart relay_private Stale 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 contains data.mdb):

    # Debian/Ubuntu: lmdb-utils provides mdb_stat — not mdb_reader_check
    sudo mdb_stat -e -rr /home/server/nostr/public/data

    Double -r checks the reader table and removes stale entries. Then start the relay container(s), then relay-agent.

  3. Verify db path — relay-agent mounts ./nostr/private/data:/app/nostr/private/data so it matches production strfry.conf with db="./data/" (same layout as relay_private). If you see mdb_env_open: No such file or directory, the mount path or db= in strfry.conf does not match. Check your main docker-compose.yml:

    grep -A5 relay_private docker-compose.yml
  4. 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.mdb is 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.