@nordbyte/nordrelay 0.4.0 → 0.5.0
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/.env.example +155 -64
- package/README.md +80 -58
- package/dist/access-control.js +126 -114
- package/dist/agent-feature-matrix.js +42 -0
- package/dist/agent-updates.js +312 -0
- package/dist/bot-rendering.js +838 -0
- package/dist/bot.js +130 -1371
- package/dist/channel-actions.js +372 -0
- package/dist/channel-runtime.js +89 -0
- package/dist/config-metadata.js +238 -0
- package/dist/config.js +0 -58
- package/dist/index.js +8 -0
- package/dist/operations.js +33 -8
- package/dist/relay-runtime.js +159 -31
- package/dist/session-format.js +72 -3
- package/dist/settings-service.js +2 -117
- package/dist/telegram-access-commands.js +123 -0
- package/dist/telegram-access-middleware.js +129 -0
- package/dist/telegram-channel-runtime.js +132 -0
- package/dist/telegram-command-menu.js +54 -0
- package/dist/telegram-output.js +216 -0
- package/dist/telegram-update-commands.js +88 -0
- package/dist/user-management.js +708 -0
- package/dist/web-api-contract.js +56 -0
- package/dist/web-dashboard-assets.js +33 -0
- package/dist/web-dashboard-ui.js +14 -14
- package/dist/web-dashboard.js +649 -369
- package/dist/webui-assets/dashboard.css +919 -0
- package/dist/webui-assets/dashboard.js +1611 -0
- package/package.json +6 -3
- package/plugins/nordrelay/.codex-plugin/plugin.json +1 -1
- package/plugins/nordrelay/commands/remote.md +1 -1
- package/plugins/nordrelay/scripts/nordrelay.mjs +283 -87
- package/plugins/nordrelay/skills/telegram-remote/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -36,9 +36,9 @@ Session control:
|
|
|
36
36
|
- `/status`, `/health`, and `/version` report connector runtime health from Telegram.
|
|
37
37
|
- `/tasks` and `/progress` show the current turn status, queue length, active tool, elapsed time, and last error.
|
|
38
38
|
- `/activity` shows a compact timeline of recent rollout events for the active thread, with filters and export.
|
|
39
|
-
- `/diagnostics` reports redacted runtime, config,
|
|
39
|
+
- `/diagnostics` reports redacted runtime, config, user/group authorization, Telegram rate-limit, mirror, voice, session, queue, and progress details.
|
|
40
40
|
- `/lock`, `/unlock`, and `/locks` provide a team write-lock for shared sessions so one user can operate while others watch.
|
|
41
|
-
- `/audit` shows recent prompt, queue, lock, and
|
|
41
|
+
- `/audit` shows recent prompt, queue, lock, command, authentication, permission-denied, user, group, Telegram-link, Telegram-chat, and web-session audit events for admins.
|
|
42
42
|
|
|
43
43
|
Adapter architecture:
|
|
44
44
|
|
|
@@ -46,6 +46,7 @@ Adapter architecture:
|
|
|
46
46
|
- `/channels` shows available and planned messaging adapters for Discord, WhatsApp, Slack, and Matrix.
|
|
47
47
|
- Codex, Pi, Hermes, OpenClaw, and Claude Code are implemented as agent adapters.
|
|
48
48
|
- `/agents` shows available/planned agent adapters and whether Codex, Pi, Hermes, OpenClaw, and Claude Code are enabled.
|
|
49
|
+
- Shared command-action renderers and a channel runtime contract keep inbound commands, outbound messages, typing, files, inline actions, and streaming-ready delivery separate from Telegram-specific API calls.
|
|
49
50
|
|
|
50
51
|
Codex runtime:
|
|
51
52
|
|
|
@@ -159,13 +160,17 @@ Telegram output:
|
|
|
159
160
|
|
|
160
161
|
Authentication and safety:
|
|
161
162
|
|
|
162
|
-
-
|
|
163
|
-
-
|
|
164
|
-
- `
|
|
165
|
-
-
|
|
166
|
-
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
163
|
+
- WebUI login is required for every dashboard page, API route, SSE stream, artifact download, and health endpoint.
|
|
164
|
+
- Access is managed through NordRelay users, groups, permissions, web sessions, and linked Telegram identities.
|
|
165
|
+
- Built-in groups are `Admin`, `User`, and `Read Only`; custom groups can be created in the WebUI and can restrict allowed agents, workspace roots, and Telegram chats.
|
|
166
|
+
- The last active admin cannot be disabled or demoted, and web sessions are revoked when passwords or group memberships change.
|
|
167
|
+
- Admins can review and revoke active WebUI sessions from the Users page.
|
|
168
|
+
- Telegram private chats require a linked active NordRelay user.
|
|
169
|
+
- Telegram group and forum chats must be registered before use; admins can run `/register_chat` in the chat or enable chats in the WebUI.
|
|
170
|
+
- `/whoami` shows the linked NordRelay account and groups.
|
|
171
|
+
- `/link <code>` links a Telegram account to a NordRelay user after a link code is created in the WebUI or with `nordrelay user link-code`.
|
|
172
|
+
- WebUI login and Telegram link attempts are rate-limited to reduce brute-force risk.
|
|
173
|
+
- User, group, Telegram-link, Telegram-chat, web-session, login, and permission-denied events are written to the audit log.
|
|
169
174
|
- `/auth` reports Codex authentication, Pi provider environment health, Hermes API Server reachability, OpenClaw Gateway reachability, or Claude Code CLI auth for the selected agent.
|
|
170
175
|
- `/login` starts Telegram-managed CLI auth for Codex, Hermes, or Claude Code when enabled.
|
|
171
176
|
- `/logout` signs out of CLI auth for Codex, Hermes, or Claude Code; Codex logout is disabled while `CODEX_API_KEY` is in use.
|
|
@@ -178,19 +183,20 @@ Operations:
|
|
|
178
183
|
|
|
179
184
|
- Plugin command/skill starts, stops, restarts, and inspects the connector process.
|
|
180
185
|
- Manual process commands support `start`, `stop`, `restart`, `status`, and `foreground`.
|
|
181
|
-
- Telegram admin commands support `/logs`, `/diagnostics`, `/restart`, and `/update
|
|
186
|
+
- Telegram admin commands support `/logs`, `/diagnostics`, `/restart`, and `/update` for NordRelay and agent CLIs.
|
|
182
187
|
- `/update` detects the install type: npm installs update with `npm install -g @nordbyte/nordrelay@latest`; source checkouts pull `origin/main`, install dependencies, run check, tests, and build, then restart.
|
|
183
|
-
- `/
|
|
188
|
+
- `/update agents`, `/update <agent>`, `/update jobs`, `/update log <id>`, `/update cancel <id>`, and `/update input <id> <text>` manage Codex, Pi, Hermes, OpenClaw, and Claude Code updater jobs from Telegram.
|
|
189
|
+
- `/logs` renders redacted connector, NordRelay update, and agent update logs with local-time timestamps, levels, file path, last-modified time, and highlighted warnings/errors.
|
|
184
190
|
- Logs can be emitted as timestamped plain text or JSON records with `CONNECTOR_LOG_FORMAT`.
|
|
185
191
|
- Telegram sends/edits/documents are routed through a rate-limit queue that honors Telegram retry-after responses.
|
|
186
192
|
- Context metadata, queues, and preferences are written atomically with backup recovery.
|
|
187
193
|
- Context metadata, queues, preferences, audit events, and locks can use JSON files or the optional SQLite state backend with `NORDRELAY_STATE_BACKEND=sqlite`.
|
|
188
194
|
- Runtime config, state, and logs are written under `~/.nordrelay/`.
|
|
189
|
-
- `nordrelay init` creates a private runtime config, `nordrelay doctor` validates host prerequisites, and `nordrelay web` starts a full local WebUI dashboard.
|
|
195
|
+
- `nordrelay init` creates a private runtime config, `nordrelay doctor` validates host prerequisites, and `nordrelay web` starts the connector plus a full local WebUI dashboard.
|
|
190
196
|
- The WebUI has responsive header/sidebar/footer navigation, live chat streaming, session controls, queue/artifact/log/diagnostic views, and settings management.
|
|
191
197
|
- The WebUI supports light and dark themes, tabbed settings groups, paginated session browsing, and chat uploads for images, documents, and audio transcription.
|
|
192
198
|
- The WebUI exposes REST and SSE endpoints for chat streaming, sessions, settings, queue, artifacts, logs, health, and diagnostics.
|
|
193
|
-
-
|
|
199
|
+
- The dashboard can bind to `127.0.0.1` or `0.0.0.0`; user login and session cookies are mandatory in both modes.
|
|
194
200
|
- Telegram can run with long polling or an HTTP webhook via `TELEGRAM_TRANSPORT=webhook`.
|
|
195
201
|
- Version freshness checks are cached with `NORDRELAY_VERSION_CACHE_TTL_MS` to keep `/version` responsive.
|
|
196
202
|
- CI includes typecheck, tests, package dry run, npm audit, and a separate secret-scan workflow.
|
|
@@ -205,6 +211,7 @@ Recommended npm setup:
|
|
|
205
211
|
```bash
|
|
206
212
|
npm install -g @nordbyte/nordrelay
|
|
207
213
|
nordrelay init
|
|
214
|
+
nordrelay user list
|
|
208
215
|
nordrelay doctor
|
|
209
216
|
nordrelay start
|
|
210
217
|
```
|
|
@@ -214,9 +221,16 @@ npm is the fastest install path and is the recommended default for normal use. `
|
|
|
214
221
|
Non-interactive setup is also supported:
|
|
215
222
|
|
|
216
223
|
```bash
|
|
217
|
-
nordrelay init
|
|
224
|
+
nordrelay init \
|
|
225
|
+
--token 123456789:replace-me \
|
|
226
|
+
--admin-email you@example.com \
|
|
227
|
+
--admin-name "Your Name" \
|
|
228
|
+
--admin-password "replace-with-a-long-password" \
|
|
229
|
+
--telegram-user-id 123456789
|
|
218
230
|
```
|
|
219
231
|
|
|
232
|
+
`--telegram-user-id` is optional, but linking the first admin during setup is the fastest way to use Telegram immediately.
|
|
233
|
+
|
|
220
234
|
Source checkout setup:
|
|
221
235
|
|
|
222
236
|
Install dependencies and build the runtime:
|
|
@@ -235,14 +249,13 @@ Create the Telegram bot:
|
|
|
235
249
|
2. Run `/newbot`.
|
|
236
250
|
3. Choose a display name and bot username.
|
|
237
251
|
4. Copy the bot token into `TELEGRAM_BOT_TOKEN` in `~/.nordrelay/nordrelay.env`.
|
|
238
|
-
5.
|
|
239
|
-
6.
|
|
252
|
+
5. Create the first admin user with `nordrelay init` or `nordrelay user create-admin`.
|
|
253
|
+
6. Link Telegram from the WebUI, with `nordrelay user link-telegram`, or by creating a link code and sending `/link <code>` to the bot.
|
|
240
254
|
|
|
241
255
|
Minimal private-bot `~/.nordrelay/nordrelay.env`:
|
|
242
256
|
|
|
243
257
|
```dotenv
|
|
244
258
|
TELEGRAM_BOT_TOKEN=123456789:replace-me
|
|
245
|
-
TELEGRAM_ADMIN_USER_IDS=123456789
|
|
246
259
|
NORDRELAY_CODEX_ENABLED=true
|
|
247
260
|
NORDRELAY_PI_ENABLED=false
|
|
248
261
|
NORDRELAY_HERMES_ENABLED=false
|
|
@@ -253,13 +266,14 @@ CODEX_SANDBOX_MODE=workspace-write
|
|
|
253
266
|
CODEX_APPROVAL_POLICY=never
|
|
254
267
|
```
|
|
255
268
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
```dotenv
|
|
259
|
-
TELEGRAM_ALLOWED_USER_IDS=234567890,345678901
|
|
260
|
-
```
|
|
269
|
+
User and Telegram access management:
|
|
261
270
|
|
|
262
|
-
|
|
271
|
+
- `nordrelay init` creates the first admin user and writes `~/.nordrelay/users.json`.
|
|
272
|
+
- `nordrelay user create-admin --email you@example.com --name "Your Name"` creates another admin.
|
|
273
|
+
- `nordrelay user create --email dev@example.com --name "Dev" --group user` creates a normal user.
|
|
274
|
+
- `nordrelay user link-telegram --email you@example.com --telegram-user-id 123456789` links a Telegram account directly.
|
|
275
|
+
- `nordrelay user link-code --email you@example.com` creates a short-lived code that the user sends as `/link <code>` to the bot.
|
|
276
|
+
- Group chats are disabled until an admin enables them from the WebUI or runs `/register_chat` inside the group.
|
|
263
277
|
|
|
264
278
|
Codex authentication:
|
|
265
279
|
|
|
@@ -270,6 +284,7 @@ Codex authentication:
|
|
|
270
284
|
Pi setup:
|
|
271
285
|
|
|
272
286
|
- Install Pi from https://pi.dev/ and confirm `pi --help` works on the host.
|
|
287
|
+
- npm installs should use the current package name: `npm install -g @earendil-works/pi-coding-agent`.
|
|
273
288
|
- Set `NORDRELAY_PI_ENABLED=true` in `~/.nordrelay/nordrelay.env`.
|
|
274
289
|
- Keep `NORDRELAY_DEFAULT_AGENT=codex` to start chats in Codex, or set `NORDRELAY_DEFAULT_AGENT=pi` to start chats in Pi.
|
|
275
290
|
- Optional: set `PI_SESSION_DIR` if your Pi sessions are not stored in `~/.pi/agent/sessions/`.
|
|
@@ -331,7 +346,7 @@ Where Codex exposes namespaced plugin commands, this also works:
|
|
|
331
346
|
/nordrelay:remote
|
|
332
347
|
```
|
|
333
348
|
|
|
334
|
-
The
|
|
349
|
+
The command is only a process-manager shortcut; Telegram contains the actual controls.
|
|
335
350
|
|
|
336
351
|
Manual process commands:
|
|
337
352
|
|
|
@@ -354,6 +369,7 @@ node plugins/nordrelay/scripts/nordrelay.mjs status
|
|
|
354
369
|
node plugins/nordrelay/scripts/nordrelay.mjs restart
|
|
355
370
|
node plugins/nordrelay/scripts/nordrelay.mjs stop
|
|
356
371
|
node plugins/nordrelay/scripts/nordrelay.mjs foreground
|
|
372
|
+
node plugins/nordrelay/scripts/nordrelay.mjs user list
|
|
357
373
|
node plugins/nordrelay/scripts/nordrelay.mjs doctor
|
|
358
374
|
node plugins/nordrelay/scripts/nordrelay.mjs web
|
|
359
375
|
```
|
|
@@ -374,6 +390,7 @@ Runtime files:
|
|
|
374
390
|
- Log file: `~/.nordrelay/nordrelay.log`
|
|
375
391
|
- Home override: `NORDRELAY_HOME=/custom/path`
|
|
376
392
|
- Local dashboard: `nordrelay web --host 127.0.0.1 --port 31878`
|
|
393
|
+
- `nordrelay start` and `nordrelay status` print the configured WebUI URL.
|
|
377
394
|
|
|
378
395
|
## WebUI Dashboard
|
|
379
396
|
|
|
@@ -383,6 +400,8 @@ Start the local WebUI:
|
|
|
383
400
|
nordrelay web
|
|
384
401
|
```
|
|
385
402
|
|
|
403
|
+
If the connector is not already running, `nordrelay web` starts it automatically before binding the dashboard.
|
|
404
|
+
|
|
386
405
|
Open:
|
|
387
406
|
|
|
388
407
|
```text
|
|
@@ -403,7 +422,10 @@ The dashboard is a second NordRelay client next to Telegram. It can:
|
|
|
403
422
|
- Browse, preview, download, ZIP, and delete artifacts.
|
|
404
423
|
- Inspect the activity timeline for WebUI and mirrored CLI turns.
|
|
405
424
|
- Edit all supported runtime settings from tabbed Settings groups with option selects, validation feedback, and restart actions.
|
|
406
|
-
- View filtered logs, structured diagnostics, enabled channels, and agent adapters.
|
|
425
|
+
- View filtered connector/update/agent-update logs, structured diagnostics, enabled channels, and agent adapters.
|
|
426
|
+
- Inspect a per-agent capability matrix showing model, reasoning, launch, fast mode, attachments, activity, usage, auth, login/logout, and handback support.
|
|
427
|
+
- Check NordRelay and agent CLI versions, then start Codex, Pi, Hermes, OpenClaw, or Claude Code updates from outdated version rows with live output, cancel, delete-log, and stdin response controls for interactive updaters.
|
|
428
|
+
- Build dashboard CSS and client JavaScript from modular source assets through esbuild, then serve them as authenticated static assets instead of inline HTML.
|
|
407
429
|
|
|
408
430
|
Dashboard API endpoints are served under `/api/*`. Streaming uses `GET /api/events`.
|
|
409
431
|
|
|
@@ -412,13 +434,9 @@ Dashboard auth:
|
|
|
412
434
|
```dotenv
|
|
413
435
|
NORDRELAY_DASHBOARD_HOST=127.0.0.1
|
|
414
436
|
NORDRELAY_DASHBOARD_PORT=31878
|
|
415
|
-
NORDRELAY_DASHBOARD_TOKEN=replace-with-random-token
|
|
416
|
-
# or:
|
|
417
|
-
NORDRELAY_DASHBOARD_USER=admin
|
|
418
|
-
NORDRELAY_DASHBOARD_PASSWORD=replace-with-random-password
|
|
419
437
|
```
|
|
420
438
|
|
|
421
|
-
|
|
439
|
+
The dashboard always requires NordRelay email/password login. Login cookies use `SameSite=Strict`, and every dashboard route, API endpoint, SSE stream, artifact download, and health endpoint requires an authenticated active user with the matching permission.
|
|
422
440
|
|
|
423
441
|
Webhook mode:
|
|
424
442
|
|
|
@@ -431,7 +449,7 @@ TELEGRAM_WEBHOOK_PATH=/telegram/webhook
|
|
|
431
449
|
TELEGRAM_WEBHOOK_SECRET=replace-with-random-secret
|
|
432
450
|
```
|
|
433
451
|
|
|
434
|
-
Run NordRelay behind your reverse proxy so the public URL forwards to `http://127.0.0.1:8080/telegram/webhook`.
|
|
452
|
+
Run NordRelay behind your reverse proxy so the public URL forwards to `http://127.0.0.1:8080/telegram/webhook`. Dashboard health checks are available to authenticated WebUI sessions through `/healthz` and `/api/health`.
|
|
435
453
|
|
|
436
454
|
## Telegram Commands
|
|
437
455
|
|
|
@@ -440,6 +458,9 @@ Run NordRelay behind your reverse proxy so the public URL forwards to `http://12
|
|
|
440
458
|
- `/channels` shows available and planned messaging adapters.
|
|
441
459
|
- `/agents` shows available and planned coding-agent adapters.
|
|
442
460
|
- `/agent` selects the active agent for this Telegram context.
|
|
461
|
+
- `/link <code>` links the Telegram account to a NordRelay user.
|
|
462
|
+
- `/whoami` shows the linked NordRelay user, groups, and permissions.
|
|
463
|
+
- `/register_chat` enables the current Telegram group or forum chat for NordRelay when the linked user has user-management permission.
|
|
443
464
|
- `/new` starts a new thread. If the selected agent knows multiple workspaces, Telegram shows a workspace picker.
|
|
444
465
|
- `/session` shows current thread details.
|
|
445
466
|
- `/sessions` opens a paginated recent-session picker.
|
|
@@ -463,7 +484,7 @@ Run NordRelay behind your reverse proxy so the public URL forwards to `http://12
|
|
|
463
484
|
- `/cancel <queue-id>` removes one queued prompt; the queue id is the short code shown in messages such as `Queued prompt 332kmt`.
|
|
464
485
|
- `/clearqueue` clears queued prompts for this Telegram context.
|
|
465
486
|
- `/activity [all|tools|errors|user|agent|tasks] [limit] [since 1h] [export]` shows or exports rollout activity for the active thread.
|
|
466
|
-
- `/audit [limit]` shows recent audit events.
|
|
487
|
+
- `/audit [limit]` shows recent audit events. Requires `audit.read`.
|
|
467
488
|
- `/lock` locks writes for this Telegram session to the current user.
|
|
468
489
|
- `/unlock` releases the current session write lock.
|
|
469
490
|
- `/locks` lists active write locks.
|
|
@@ -490,12 +511,14 @@ Run NordRelay behind your reverse proxy so the public URL forwards to `http://12
|
|
|
490
511
|
- `/status` reports connector runtime status.
|
|
491
512
|
- `/health` reports runtime health, auth, PIDs, Codex CLI, Pi CLI, Hermes CLI, OpenClaw CLI, Claude Code CLI, and state DB.
|
|
492
513
|
- `/version` reports connector, Codex CLI, Pi CLI, Hermes CLI, OpenClaw CLI, and Claude Code CLI paths plus installed/latest NordRelay, Codex, Pi, Hermes, OpenClaw, and Claude Code versions with status icons.
|
|
493
|
-
- `/logs [lines]` shows a redacted, timestamped connector log tail.
|
|
494
|
-
- `/logs update [lines]` shows the self-update log.
|
|
495
|
-
- `/logs
|
|
496
|
-
- `/
|
|
497
|
-
- `/
|
|
498
|
-
- `/
|
|
514
|
+
- `/logs [lines]` shows a redacted, timestamped connector log tail. Requires `logs.read`.
|
|
515
|
+
- `/logs update [lines]` shows the self-update log. Requires `logs.read`.
|
|
516
|
+
- `/logs agent [lines]` shows the aggregate agent updater log. Requires `logs.read`.
|
|
517
|
+
- `/logs all [lines]` shows connector, self-update, and agent update logs together. Requires `logs.read`.
|
|
518
|
+
- `/diagnostics` shows redacted connector diagnostics. Requires `logs.read`.
|
|
519
|
+
- `/restart` restarts the connector process. Requires `system.restart`.
|
|
520
|
+
- `/update` updates through npm or git depending on the detected install type, then restarts only on success. Requires `updates.run`.
|
|
521
|
+
- `/update agents`, `/update <agent>`, `/update jobs`, `/update log <id>`, `/update cancel <id>`, and `/update input <id> <text>` manage agent CLI update jobs. Requires `updates.run`.
|
|
499
522
|
|
|
500
523
|
## Command Examples
|
|
501
524
|
|
|
@@ -678,12 +701,6 @@ Voice transcription uses `OPENAI_API_KEY`, not `CODEX_API_KEY`.
|
|
|
678
701
|
Telegram:
|
|
679
702
|
|
|
680
703
|
- `TELEGRAM_BOT_TOKEN`: required BotFather token.
|
|
681
|
-
- `TELEGRAM_ADMIN_USER_IDS`: required comma-separated Telegram user ids allowed to use admin commands. Admin ids are automatically allowed to use the bot.
|
|
682
|
-
- `TELEGRAM_ALLOWED_USER_IDS`: optional comma-separated non-admin Telegram user ids allowed to use the bot.
|
|
683
|
-
- `TELEGRAM_READONLY_USER_IDS`: comma-separated Telegram user ids that can inspect status and sessions but cannot run prompts or mutating commands.
|
|
684
|
-
- `TELEGRAM_ALLOWED_CHAT_IDS`: comma-separated chat ids allowed to use the bot. Group ids may be negative.
|
|
685
|
-
- `TELEGRAM_ALLOW_ANY_CHAT`: allows all chats when `true`. Keep `false` unless you intentionally want an open bot.
|
|
686
|
-
- `TELEGRAM_ROLE_POLICIES_JSON`: optional JSON object mapping roles to permissions. Permissions are `inspect`, `sessions`, `prompt`, `files`, `settings`, `auth`, and `admin`.
|
|
687
704
|
- `TELEGRAM_RATE_LIMIT_MIN_INTERVAL_MS`: minimum interval for normal Telegram API sends. Defaults to `80`.
|
|
688
705
|
- `TELEGRAM_EDIT_MIN_INTERVAL_MS`: minimum interval for Telegram message edits. Defaults to `1200`.
|
|
689
706
|
- `TELEGRAM_TRANSPORT`: `polling` or `webhook`. Defaults to `polling`.
|
|
@@ -698,11 +715,13 @@ Telegram:
|
|
|
698
715
|
- `TELEGRAM_QUIET_HOURS`: optional quiet-hour range in `HH-HH` format, for example `22-7`.
|
|
699
716
|
- `TELEGRAM_REDACT_PATTERNS`: comma-separated regular expressions for additional Telegram/log redaction.
|
|
700
717
|
|
|
701
|
-
|
|
718
|
+
User management:
|
|
702
719
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
720
|
+
- Users, groups, Telegram identities, Telegram group-chat access, and web sessions are stored in `~/.nordrelay/users.json`.
|
|
721
|
+
- Manage users in the WebUI Users page or with `nordrelay user list`, `create-admin`, `create`, `reset-password`, `link-telegram`, and `link-code`.
|
|
722
|
+
- Built-in groups are `admin`, `user`, and `readonly`.
|
|
723
|
+
- Group permissions include `inspect`, `sessions.read`, `sessions.write`, `prompt.send`, `prompt.abort`, `files.read`, `files.write`, `settings.read`, `settings.write`, `auth.manage`, `diagnostics.read`, `logs.read`, `logs.clear`, `queue.read`, `queue.write`, `updates.run`, `system.restart`, `users.read`, `users.write`, and `audit.read`.
|
|
724
|
+
- Custom groups can also restrict access to specific agent ids, workspace roots, and Telegram chat ids.
|
|
706
725
|
|
|
707
726
|
Agent selection:
|
|
708
727
|
|
|
@@ -721,9 +740,6 @@ Dashboard:
|
|
|
721
740
|
|
|
722
741
|
- `NORDRELAY_DASHBOARD_HOST`: dashboard bind host. Defaults to `127.0.0.1`.
|
|
723
742
|
- `NORDRELAY_DASHBOARD_PORT`: dashboard bind port. Defaults to `31878`.
|
|
724
|
-
- `NORDRELAY_DASHBOARD_TOKEN`: optional dashboard bearer/login token. Required when binding to `0.0.0.0` unless basic auth is configured.
|
|
725
|
-
- `NORDRELAY_DASHBOARD_USER`: optional dashboard basic-auth user.
|
|
726
|
-
- `NORDRELAY_DASHBOARD_PASSWORD`: optional dashboard basic-auth password. Required with `NORDRELAY_DASHBOARD_USER`.
|
|
727
743
|
- `NORDRELAY_ENV_FILE`: optional explicit env-file path used by the wrapper and edited by the dashboard settings page. Defaults to `~/.nordrelay/nordrelay.env`.
|
|
728
744
|
|
|
729
745
|
Codex:
|
|
@@ -820,6 +836,7 @@ NordRelay wrapper:
|
|
|
820
836
|
- `NORDRELAY_HOME`: config/state/log directory override. Defaults to `~/.nordrelay`.
|
|
821
837
|
- `NORDRELAY_SOURCE_ROOT`: runtime source root override. Useful when the plugin is launched from Codex cache.
|
|
822
838
|
- `NORDRELAY_UPDATE_METHOD`: optional `auto`, `npm`, or `git` self-update method override. Auto uses git when the runtime root has a `.git` directory and npm otherwise.
|
|
839
|
+
- Agent updates from the dashboard and Telegram use each agent's native updater where possible: `codex update`, `pi update pi`, `hermes update --yes`, `openclaw update --yes`, and `claude update`.
|
|
823
840
|
- `NORDRELAY_KEEP_PENDING_UPDATES`: set true to avoid dropping pending Telegram updates on start.
|
|
824
841
|
- `NORDRELAY_FORWARD_TOOL_OUTPUT`: backward-compatible alias that sets `TOOL_VERBOSITY=all` when `TOOL_VERBOSITY` is unset.
|
|
825
842
|
- `NORDRELAY_STATE_FILE`: internal state-file path passed by the wrapper.
|
|
@@ -859,14 +876,14 @@ Unsafe profiles are intentionally gated. Telegram asks for confirmation before a
|
|
|
859
876
|
|
|
860
877
|
## Security Notes
|
|
861
878
|
|
|
862
|
-
-
|
|
863
|
-
-
|
|
864
|
-
-
|
|
865
|
-
-
|
|
879
|
+
- Create the first admin user during setup and keep that account protected with a strong password.
|
|
880
|
+
- Link Telegram accounts only to active NordRelay users that should control agents remotely.
|
|
881
|
+
- Enable Telegram group/forum chats only when the whole chat context is trusted for the permissions granted to linked users.
|
|
882
|
+
- Review group permissions before granting `prompt.send`, `prompt.abort`, `files.write`, `settings.write`, `updates.run`, `system.restart`, or `users.write`.
|
|
866
883
|
- Treat `danger-full-access` as equivalent to shell access on the host.
|
|
867
884
|
- Treat uploaded files as untrusted input. They are staged inside the active workspace so the selected sandbox policy still matters.
|
|
868
885
|
- Keep `CODEX_API_KEY`, `HERMES_API_KEY`, `OPENCLAW_GATEWAY_TOKEN`, `OPENCLAW_GATEWAY_PASSWORD`, and `OPENAI_API_KEY` in `~/.nordrelay/nordrelay.env` or host secret management.
|
|
869
|
-
- In group chats, remember that any
|
|
886
|
+
- In group chats, remember that any linked user with prompt permissions can prompt the selected agent in that chat context.
|
|
870
887
|
- Use `TOOL_VERBOSITY=summary` or `errors-only` when command output may include sensitive data.
|
|
871
888
|
- Review and unsafe launch profiles add a Telegram approve/deny gate before each turn starts.
|
|
872
889
|
|
|
@@ -1023,19 +1040,24 @@ npm run build
|
|
|
1023
1040
|
- `plugins/nordrelay/`: Codex plugin bundle with manifest, skill, command, icon, and process wrapper.
|
|
1024
1041
|
- `plugins/nordrelay/scripts/nordrelay.mjs`: process manager for `start`, `stop`, `restart`, `status`, and `foreground`.
|
|
1025
1042
|
- `src/index.ts`: runtime entrypoint, config load, auth check, state-file writes, polling lifecycle, shutdown.
|
|
1026
|
-
- `src/bot.ts`: Telegram
|
|
1043
|
+
- `src/bot.ts`: Telegram prompt/session runtime, streaming, file/photo/voice handling, artifacts, and error handling.
|
|
1044
|
+
- `src/telegram-access-commands.ts`, `src/telegram-update-commands.ts`, and `src/telegram-command-menu.ts`: focused Telegram command groups for access linking, update jobs, and command menu registration.
|
|
1045
|
+
- `src/channel-adapter.ts`, `src/channel-runtime.ts`, and `src/channel-actions.ts`: channel descriptors, generic command routing, outbound delivery contracts, and channel-neutral command responses.
|
|
1046
|
+
- `src/config-metadata.ts`: shared setting metadata used by the WebUI settings page and generated `.env.example`.
|
|
1047
|
+
- `src/webui/`: focused WebUI source assets for core runtime state/API helpers, overview rendering, live events, chat/session workflows, admin pages, and CSS sections.
|
|
1027
1048
|
- `src/bot-preferences.ts`: per-context mirror, notification, quiet-hour, and voice preference persistence.
|
|
1028
1049
|
- `src/telegram-rate-limit.ts`: centralized Telegram API send/edit/document rate limiting and retry-after tracking.
|
|
1029
1050
|
- `src/persistence.ts`: atomic JSON/text writes with backup recovery.
|
|
1030
1051
|
- `src/redaction.ts`: common secret redaction and custom redaction pattern support.
|
|
1031
1052
|
- `src/workspace-policy.ts`: workspace allow/warn root evaluation.
|
|
1032
|
-
- `src/access-control.ts`:
|
|
1053
|
+
- `src/access-control.ts`: user/group permission definitions and command/callback/WebUI permission mapping.
|
|
1033
1054
|
- `src/codex-session.ts`: Codex SDK service for new/resumed threads, streaming events, abort, model, reasoning, launch profiles, and handback.
|
|
1034
1055
|
- `src/pi-session.ts`: Pi RPC service for JSONL RPC sessions, streaming events, abort, model, thinking, launch profiles, and handback.
|
|
1035
1056
|
- `src/hermes-session.ts`: Hermes API Server service for streamed runs, stop, model, reasoning, launch profiles, attachments, and handback.
|
|
1036
1057
|
- `src/openclaw-session.ts`: OpenClaw Gateway service for streamed runs, cancel, model, thinking, launch profiles, attachments, and handback.
|
|
1037
1058
|
- `src/claude-code-session.ts`: Claude Agent SDK service for streamed runs, abort, model, effort, launch profiles, attachments, and handback.
|
|
1038
1059
|
- `src/session-registry.ts`: per-chat/topic session registry and persisted context metadata.
|
|
1060
|
+
- `test/agent-adapter-contract.test.ts`: shared adapter contract coverage for descriptors, capability flags, reasoning options, launch profiles, and `AgentSessionService` method parity.
|
|
1039
1061
|
- `src/session-format.ts`: compact Telegram rendering for session details, token usage, and limits.
|
|
1040
1062
|
- `src/codex-state.ts`: reader for Codex `~/.codex/state_*.sqlite` thread, workspace, model, reasoning, sandbox, and approval metadata.
|
|
1041
1063
|
- `src/pi-state.ts`: reader for Pi session JSONL files, activity timelines, diagnostics, and external busy detection.
|
package/dist/access-control.js
CHANGED
|
@@ -1,11 +1,68 @@
|
|
|
1
|
-
|
|
1
|
+
import { permissionForWebRequestFromContract } from "./web-api-contract.js";
|
|
2
|
+
export const ALL_PERMISSIONS = [
|
|
2
3
|
"inspect",
|
|
3
|
-
"sessions",
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
4
|
+
"sessions.read",
|
|
5
|
+
"sessions.write",
|
|
6
|
+
"prompt.send",
|
|
7
|
+
"prompt.abort",
|
|
8
|
+
"files.read",
|
|
9
|
+
"files.write",
|
|
10
|
+
"settings.read",
|
|
11
|
+
"settings.write",
|
|
12
|
+
"auth.manage",
|
|
13
|
+
"diagnostics.read",
|
|
14
|
+
"logs.read",
|
|
15
|
+
"logs.clear",
|
|
16
|
+
"queue.read",
|
|
17
|
+
"queue.write",
|
|
18
|
+
"updates.run",
|
|
19
|
+
"system.restart",
|
|
20
|
+
"users.read",
|
|
21
|
+
"users.write",
|
|
22
|
+
"audit.read",
|
|
23
|
+
];
|
|
24
|
+
export const ADMIN_GROUP_ID = "admin";
|
|
25
|
+
export const USER_GROUP_ID = "user";
|
|
26
|
+
export const READONLY_GROUP_ID = "readonly";
|
|
27
|
+
export const BUILTIN_GROUPS = [
|
|
28
|
+
{
|
|
29
|
+
id: ADMIN_GROUP_ID,
|
|
30
|
+
name: "Admin",
|
|
31
|
+
description: "Full access to every NordRelay feature, user management, updates, and system controls.",
|
|
32
|
+
permissions: ALL_PERMISSIONS,
|
|
33
|
+
system: true,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: USER_GROUP_ID,
|
|
37
|
+
name: "User",
|
|
38
|
+
description: "Normal read/write use of agents, sessions, prompts, files, and personal agent auth.",
|
|
39
|
+
permissions: [
|
|
40
|
+
"inspect",
|
|
41
|
+
"sessions.read",
|
|
42
|
+
"sessions.write",
|
|
43
|
+
"prompt.send",
|
|
44
|
+
"prompt.abort",
|
|
45
|
+
"files.read",
|
|
46
|
+
"files.write",
|
|
47
|
+
"settings.read",
|
|
48
|
+
"auth.manage",
|
|
49
|
+
"queue.read",
|
|
50
|
+
"queue.write",
|
|
51
|
+
],
|
|
52
|
+
system: true,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: READONLY_GROUP_ID,
|
|
56
|
+
name: "Read Only",
|
|
57
|
+
description: "Read-only access to status, sessions, activity, and artifacts.",
|
|
58
|
+
permissions: [
|
|
59
|
+
"inspect",
|
|
60
|
+
"sessions.read",
|
|
61
|
+
"files.read",
|
|
62
|
+
"settings.read",
|
|
63
|
+
],
|
|
64
|
+
system: true,
|
|
65
|
+
},
|
|
9
66
|
];
|
|
10
67
|
const COMMAND_PERMISSIONS = new Map([
|
|
11
68
|
["start", "inspect"],
|
|
@@ -15,138 +72,93 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
15
72
|
["version", "inspect"],
|
|
16
73
|
["channels", "inspect"],
|
|
17
74
|
["agents", "inspect"],
|
|
18
|
-
["diagnostics", "admin"],
|
|
19
75
|
["tasks", "inspect"],
|
|
20
76
|
["progress", "inspect"],
|
|
21
|
-
["activity", "
|
|
22
|
-
["audit", "admin"],
|
|
23
|
-
["mirror", "settings"],
|
|
24
|
-
["notify", "settings"],
|
|
25
|
-
["workspaces", "sessions"],
|
|
26
|
-
["voice", "inspect"],
|
|
27
|
-
["agent", "settings"],
|
|
28
|
-
["session", "sessions"],
|
|
29
|
-
["sessions", "sessions"],
|
|
30
|
-
["switch", "sessions"],
|
|
31
|
-
["pinned", "sessions"],
|
|
32
|
-
["pin", "sessions"],
|
|
33
|
-
["unpin", "sessions"],
|
|
34
|
-
["attach", "sessions"],
|
|
35
|
-
["handback", "sessions"],
|
|
36
|
-
["new", "sessions"],
|
|
37
|
-
["sync", "sessions"],
|
|
38
|
-
["lock", "sessions"],
|
|
39
|
-
["unlock", "sessions"],
|
|
40
|
-
["locks", "sessions"],
|
|
41
|
-
["queue", "inspect"],
|
|
42
|
-
["cancel", "prompt"],
|
|
43
|
-
["clearqueue", "prompt"],
|
|
44
|
-
["retry", "prompt"],
|
|
45
|
-
["abort", "prompt"],
|
|
46
|
-
["stop", "prompt"],
|
|
47
|
-
["artifacts", "files"],
|
|
48
|
-
["launch", "settings"],
|
|
49
|
-
["launch_profiles", "settings"],
|
|
50
|
-
["launch-profiles", "settings"],
|
|
51
|
-
["fast", "settings"],
|
|
52
|
-
["model", "settings"],
|
|
53
|
-
["reasoning", "settings"],
|
|
54
|
-
["effort", "settings"],
|
|
77
|
+
["activity", "sessions.read"],
|
|
55
78
|
["auth", "inspect"],
|
|
56
|
-
["
|
|
57
|
-
["
|
|
58
|
-
["
|
|
59
|
-
["
|
|
60
|
-
["
|
|
79
|
+
["voice", "inspect"],
|
|
80
|
+
["whoami", "inspect"],
|
|
81
|
+
["diagnostics", "diagnostics.read"],
|
|
82
|
+
["logs", "logs.read"],
|
|
83
|
+
["audit", "audit.read"],
|
|
84
|
+
["restart", "system.restart"],
|
|
85
|
+
["update", "updates.run"],
|
|
86
|
+
["workspaces", "sessions.read"],
|
|
87
|
+
["session", "sessions.read"],
|
|
88
|
+
["sessions", "sessions.read"],
|
|
89
|
+
["pinned", "sessions.read"],
|
|
90
|
+
["locks", "sessions.read"],
|
|
91
|
+
["queue", "queue.read"],
|
|
92
|
+
["artifacts", "files.read"],
|
|
93
|
+
["agent", "settings.write"],
|
|
94
|
+
["mirror", "settings.write"],
|
|
95
|
+
["notify", "settings.write"],
|
|
96
|
+
["launch", "settings.write"],
|
|
97
|
+
["launch_profiles", "settings.write"],
|
|
98
|
+
["launch-profiles", "settings.write"],
|
|
99
|
+
["fast", "settings.write"],
|
|
100
|
+
["model", "settings.write"],
|
|
101
|
+
["reasoning", "settings.write"],
|
|
102
|
+
["effort", "settings.write"],
|
|
103
|
+
["login", "auth.manage"],
|
|
104
|
+
["logout", "auth.manage"],
|
|
105
|
+
["new", "sessions.write"],
|
|
106
|
+
["switch", "sessions.write"],
|
|
107
|
+
["attach", "sessions.write"],
|
|
108
|
+
["handback", "sessions.write"],
|
|
109
|
+
["sync", "sessions.write"],
|
|
110
|
+
["pin", "sessions.write"],
|
|
111
|
+
["unpin", "sessions.write"],
|
|
112
|
+
["lock", "sessions.write"],
|
|
113
|
+
["unlock", "sessions.write"],
|
|
114
|
+
["retry", "prompt.send"],
|
|
115
|
+
["clearqueue", "queue.write"],
|
|
116
|
+
["cancel", "queue.write"],
|
|
117
|
+
["abort", "prompt.abort"],
|
|
118
|
+
["stop", "prompt.abort"],
|
|
119
|
+
["register_chat", "users.write"],
|
|
120
|
+
["chat_access", "users.write"],
|
|
121
|
+
["link", "inspect"],
|
|
61
122
|
]);
|
|
62
|
-
export function createDefaultRolePolicies() {
|
|
63
|
-
return {
|
|
64
|
-
admin: new Set(ALL_PERMISSIONS),
|
|
65
|
-
operator: new Set(["inspect", "sessions", "prompt", "files", "settings", "auth"]),
|
|
66
|
-
readonly: new Set(["inspect", "sessions"]),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
export function parseRolePoliciesJson(raw) {
|
|
70
|
-
const policies = createDefaultRolePolicies();
|
|
71
|
-
if (!raw) {
|
|
72
|
-
return policies;
|
|
73
|
-
}
|
|
74
|
-
let parsed;
|
|
75
|
-
try {
|
|
76
|
-
parsed = JSON.parse(raw);
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
throw new Error(`Invalid TELEGRAM_ROLE_POLICIES_JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
80
|
-
}
|
|
81
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
82
|
-
throw new Error("TELEGRAM_ROLE_POLICIES_JSON must be an object keyed by role");
|
|
83
|
-
}
|
|
84
|
-
for (const [role, rawPermissions] of Object.entries(parsed)) {
|
|
85
|
-
if (!isTelegramRole(role)) {
|
|
86
|
-
throw new Error(`Invalid TELEGRAM_ROLE_POLICIES_JSON role: ${role}`);
|
|
87
|
-
}
|
|
88
|
-
policies[role] = parsePermissionList(rawPermissions, role);
|
|
89
|
-
}
|
|
90
|
-
if (!policies.admin.has("admin")) {
|
|
91
|
-
policies.admin.add("admin");
|
|
92
|
-
}
|
|
93
|
-
return policies;
|
|
94
|
-
}
|
|
95
|
-
export function hasTelegramPermission(policies, role, permission) {
|
|
96
|
-
return policies[role].has(permission) || policies[role].has("admin");
|
|
97
|
-
}
|
|
98
123
|
export function permissionForCommand(command) {
|
|
99
124
|
if (!command) {
|
|
100
|
-
return
|
|
125
|
+
return null;
|
|
101
126
|
}
|
|
102
|
-
return COMMAND_PERMISSIONS.get(command.toLowerCase()) ??
|
|
127
|
+
return COMMAND_PERMISSIONS.get(command.toLowerCase()) ?? null;
|
|
103
128
|
}
|
|
104
129
|
export function permissionForCallbackData(callbackData) {
|
|
105
130
|
if (!callbackData) {
|
|
106
|
-
return
|
|
131
|
+
return null;
|
|
107
132
|
}
|
|
108
133
|
if (callbackData === "noop_page") {
|
|
109
134
|
return "inspect";
|
|
110
135
|
}
|
|
111
136
|
if (/^(sess_|ws_)/.test(callbackData)) {
|
|
112
|
-
return "sessions";
|
|
137
|
+
return "sessions.write";
|
|
113
138
|
}
|
|
114
139
|
if (/^(launch_|launchconfirm_|model_|effort_|agent_)/.test(callbackData)) {
|
|
115
|
-
return "settings";
|
|
140
|
+
return "settings.write";
|
|
141
|
+
}
|
|
142
|
+
if (callbackData.startsWith("upd_")) {
|
|
143
|
+
return "updates.run";
|
|
116
144
|
}
|
|
117
145
|
if (callbackData.startsWith("approval_") || callbackData.startsWith("codex_abort:") || callbackData.startsWith("agent_abort:")) {
|
|
118
|
-
return "prompt";
|
|
146
|
+
return "prompt.abort";
|
|
119
147
|
}
|
|
120
148
|
if (callbackData.startsWith("queue_")) {
|
|
121
|
-
return "
|
|
149
|
+
return "queue.write";
|
|
150
|
+
}
|
|
151
|
+
if (callbackData.startsWith("artifact_delete")) {
|
|
152
|
+
return "files.write";
|
|
122
153
|
}
|
|
123
154
|
if (callbackData.startsWith("artifact_")) {
|
|
124
|
-
return "files";
|
|
155
|
+
return "files.read";
|
|
125
156
|
}
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
export function isTelegramRole(value) {
|
|
129
|
-
return value === "admin" || value === "operator" || value === "readonly";
|
|
157
|
+
return null;
|
|
130
158
|
}
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
return new Set(ALL_PERMISSIONS);
|
|
134
|
-
}
|
|
135
|
-
if (!Array.isArray(rawPermissions)) {
|
|
136
|
-
throw new Error(`TELEGRAM_ROLE_POLICIES_JSON.${role} must be an array or "*"`);
|
|
137
|
-
}
|
|
138
|
-
const permissions = new Set();
|
|
139
|
-
for (const rawPermission of rawPermissions) {
|
|
140
|
-
if (rawPermission === "*") {
|
|
141
|
-
return new Set(ALL_PERMISSIONS);
|
|
142
|
-
}
|
|
143
|
-
if (typeof rawPermission !== "string" || !isTelegramPermission(rawPermission)) {
|
|
144
|
-
throw new Error(`Invalid TELEGRAM_ROLE_POLICIES_JSON permission for ${role}: ${String(rawPermission)}`);
|
|
145
|
-
}
|
|
146
|
-
permissions.add(rawPermission);
|
|
147
|
-
}
|
|
148
|
-
return permissions;
|
|
159
|
+
export function permissionForWebRequest(method, pathname) {
|
|
160
|
+
return permissionForWebRequestFromContract(method, pathname);
|
|
149
161
|
}
|
|
150
|
-
function
|
|
162
|
+
export function isPermission(value) {
|
|
151
163
|
return ALL_PERMISSIONS.includes(value);
|
|
152
164
|
}
|