@bitmacro/relay-agent 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,12 @@
8
8
 
9
9
  `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](https://bitmacro.io) ecosystem.
10
10
 
11
+ | Project | Description | License |
12
+ |---------|-------------|---------|
13
+ | **relay-agent** | This repo — REST API for strfry | MIT |
14
+ | [relay-api](https://github.com/bitmacro/relay-api) | Central hub (Supabase, proxy) | Private |
15
+ | [relay-panel](https://github.com/bitmacro/relay-panel) | Frontend | BSL 1.1 |
16
+
11
17
  ---
12
18
 
13
19
  ## Quick Start
@@ -18,6 +24,8 @@
18
24
  npx @bitmacro/relay-agent --port 7800 --token your-secret-token
19
25
  ```
20
26
 
27
+ Use `--version` or `--help` to check version or CLI options.
28
+
21
29
  ### Via Docker
22
30
 
23
31
  Multi-arch image (amd64, arm64) at `ghcr.io/bitmacro/relay-agent`. Includes strfry binary. Mount your strfry data volume:
@@ -135,7 +143,7 @@ Authorization: Bearer <your-token>
135
143
  | `STRFRY_CONFIG` | — | Path to strfry config file (for explicit db path) |
136
144
  | `WHITELIST_PATH` | `/etc/strfry/whitelist.txt` | Path to whitelist file |
137
145
  | `PORT` | `7800` | HTTP server port |
138
- | `ALLOWED_ORIGINS` | — | Comma-separated extra CORS origins (defaults include `https://admin.bitmacro.io`, `http://localhost:3000`) |
146
+ | `ALLOWED_ORIGINS` | — | Comma-separated extra CORS origins (defaults include `relay-panel.bitmacro.cloud`, `relay-panel.bitmacro.pro`, `http://localhost:3000`) |
139
147
 
140
148
  ---
141
149
 
@@ -150,16 +158,19 @@ Authorization: Bearer <your-token>
150
158
  ## Architecture
151
159
 
152
160
  ```
153
- bitmacro-api (Vercel)
161
+ relay-panel
162
+ │ HTTP + JWT
163
+
164
+ relay-api (Vercel)
154
165
  │ HTTP REST + Bearer JWT
155
166
 
156
- relay-agent this package
167
+ relay-agent (this repo)
157
168
  │ child_process spawn()
158
169
 
159
170
  strfry (local C++ process / LMDB)
160
171
  ```
161
172
 
162
- The relay-agent is **stateless** — it has no database. State lives in Supabase, managed by bitmacro-api. The relay-agent only translates HTTP calls into strfry CLI commands.
173
+ 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.
163
174
 
164
175
  ---
165
176
 
@@ -200,7 +211,7 @@ The relay-agent is **stateless** — it has no database. State lives in Supabase
200
211
  ## Security
201
212
 
202
213
  - **Run on a private network.** The relay-agent should run on the operator's server and **never be exposed directly to the internet**.
203
- - Access is controlled by the bitmacro-api, which proxies requests with a shared Bearer token.
214
+ - Access is controlled by the relay-api, which proxies requests with a shared Bearer token.
204
215
  - Use a strong, random token in production. Rotate it if compromised.
205
216
 
206
217
  ---
@@ -2,7 +2,7 @@
2
2
 
3
3
  // bin/relay-agent.ts
4
4
  import { parseArgs } from "util";
5
- import { readFileSync } from "fs";
5
+ import { readFileSync, realpathSync } from "fs";
6
6
  import { dirname as dirname2, join } from "path";
7
7
 
8
8
  // node_modules/hono/dist/compose.js
@@ -2986,6 +2986,25 @@ async function readWhitelist() {
2986
2986
  const content = await readFile(WHITELIST_PATH, "utf-8");
2987
2987
  return content.split("\n").map((l) => l.trim()).filter(Boolean);
2988
2988
  }
2989
+ var PUBKEY_HEX_REGEX = /^[0-9a-f]{64}$/;
2990
+ function isValidPubkey(s) {
2991
+ return PUBKEY_HEX_REGEX.test(s.toLowerCase());
2992
+ }
2993
+ async function getPolicyEntries() {
2994
+ const lines = await readWhitelist();
2995
+ const entries = [];
2996
+ for (const line of lines) {
2997
+ if (line.startsWith("#") || !line) continue;
2998
+ if (line.startsWith("!")) {
2999
+ const pubkey2 = line.slice(1).toLowerCase();
3000
+ if (isValidPubkey(pubkey2)) entries.push({ pubkey: pubkey2, status: "blocked" });
3001
+ continue;
3002
+ }
3003
+ const pubkey = line.toLowerCase();
3004
+ if (isValidPubkey(pubkey)) entries.push({ pubkey, status: "allowed" });
3005
+ }
3006
+ return entries;
3007
+ }
2989
3008
  async function writeWhitelist(lines) {
2990
3009
  const dir = dirname(WHITELIST_PATH);
2991
3010
  if (!existsSync(dir)) {
@@ -3084,6 +3103,14 @@ statsRoutes.get("/stats", async (c) => {
3084
3103
  // src/routes/policy.ts
3085
3104
  var PUBKEY_REGEX = /^[0-9a-f]{64}$/;
3086
3105
  var policyRoutes = new Hono2();
3106
+ policyRoutes.get("/policy", async (c) => {
3107
+ try {
3108
+ const entries = await getPolicyEntries();
3109
+ return c.json({ entries });
3110
+ } catch {
3111
+ return c.json({ error: "relay unavailable" }, 503);
3112
+ }
3113
+ });
3087
3114
  policyRoutes.post("/policy/block", async (c) => {
3088
3115
  try {
3089
3116
  const body = await c.req.json();
@@ -3137,12 +3164,18 @@ usersRoutes.get("/users", async (c) => {
3137
3164
 
3138
3165
  // src/index.ts
3139
3166
  var DEFAULT_ORIGINS = [
3140
- "https://admin.bitmacro.io",
3167
+ "https://relay-panel.bitmacro.cloud",
3168
+ "https://relay-panel.bitmacro.pro",
3141
3169
  "http://localhost:3000"
3142
3170
  ];
3143
3171
  var EXTRA_ORIGINS = (process.env.ALLOWED_ORIGINS ?? "").split(",").map((s) => s.trim()).filter(Boolean);
3144
3172
  var ALLOWED_ORIGINS = [...DEFAULT_ORIGINS, ...EXTRA_ORIGINS];
3145
3173
  var app = new Hono2();
3174
+ app.use("*", async (c, next) => {
3175
+ const start = Date.now();
3176
+ await next();
3177
+ console.log(`[relay-agent] ${c.req.method} ${c.req.path} ${c.res.status} ${Date.now() - start}ms`);
3178
+ });
3146
3179
  app.use("*", cors({ origin: ALLOWED_ORIGINS }));
3147
3180
  app.use("*", async (c, next) => {
3148
3181
  if (c.req.path === "/health") return next();
@@ -3168,7 +3201,8 @@ process.on("uncaughtException", (err) => {
3168
3201
  process.on("unhandledRejection", (reason, promise) => {
3169
3202
  console.error("[relay-agent] unhandledRejection:", reason, promise);
3170
3203
  });
3171
- var __dirname = dirname2(process.argv[1] ?? ".");
3204
+ var scriptPath = process.argv[1] ? realpathSync(process.argv[1]) : ".";
3205
+ var __dirname = dirname2(scriptPath);
3172
3206
  function getVersion() {
3173
3207
  for (const rel of ["../../package.json", "../package.json"]) {
3174
3208
  try {