@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 +16 -5
- package/dist/bin/relay-agent.mjs +37 -3
- package/dist/bin/relay-agent.mjs.map +1 -1
- package/dist/index.mjs +34 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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 `
|
|
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
|
-
|
|
161
|
+
relay-panel
|
|
162
|
+
│ HTTP + JWT
|
|
163
|
+
▼
|
|
164
|
+
relay-api (Vercel)
|
|
154
165
|
│ HTTP REST + Bearer JWT
|
|
155
166
|
▼
|
|
156
|
-
relay-agent
|
|
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
|
|
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
|
|
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
|
---
|
package/dist/bin/relay-agent.mjs
CHANGED
|
@@ -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://
|
|
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
|
|
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 {
|