@nordbyte/nordrelay 0.5.2 → 0.7.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 +80 -11
- package/README.md +154 -22
- package/dist/access-control.js +7 -1
- package/dist/activity-events.js +44 -0
- package/dist/audit-log.js +40 -2
- package/dist/bot-preferences.js +1 -0
- package/dist/bot-rendering.js +10 -7
- package/dist/bot.js +535 -11
- package/dist/channel-actions.js +7 -2
- package/dist/channel-adapter.js +40 -7
- package/dist/channel-command-catalog.js +88 -0
- package/dist/channel-command-service.js +369 -0
- package/dist/channel-mirror-registry.js +77 -0
- package/dist/channel-peer-prompt.js +95 -0
- package/dist/channel-runtime.js +12 -5
- package/dist/channel-turn-service.js +237 -0
- package/dist/codex-state.js +114 -78
- package/dist/config-metadata.js +93 -13
- package/dist/config.js +103 -8
- package/dist/context-key.js +87 -5
- package/dist/discord-artifacts.js +165 -0
- package/dist/discord-bot.js +2073 -0
- package/dist/discord-channel-runtime.js +133 -0
- package/dist/discord-command-surface.js +57 -0
- package/dist/discord-rate-limit.js +141 -0
- package/dist/index.js +36 -5
- package/dist/job-store.js +127 -0
- package/dist/metrics.js +87 -0
- package/dist/peer-auth.js +85 -0
- package/dist/peer-client.js +256 -0
- package/dist/peer-context.js +21 -0
- package/dist/peer-identity.js +127 -0
- package/dist/peer-runtime-service.js +636 -0
- package/dist/peer-server.js +220 -0
- package/dist/peer-store.js +294 -0
- package/dist/peer-types.js +52 -0
- package/dist/relay-external-activity-monitor.js +47 -6
- package/dist/relay-runtime-helpers.js +208 -0
- package/dist/relay-runtime.js +897 -394
- package/dist/remote-prompt.js +98 -0
- package/dist/runtime-cache.js +57 -0
- package/dist/session-locks.js +10 -7
- package/dist/support-bundle.js +1 -0
- package/dist/telegram-access-commands.js +15 -2
- package/dist/telegram-access-middleware.js +16 -3
- package/dist/telegram-agent-commands.js +25 -0
- package/dist/telegram-artifact-commands.js +46 -0
- package/dist/telegram-command-menu.js +3 -53
- package/dist/telegram-diagnostics-command.js +5 -50
- package/dist/telegram-general-commands.js +16 -6
- package/dist/telegram-operational-commands.js +14 -6
- package/dist/telegram-preference-commands.js +23 -127
- package/dist/telegram-queue-commands.js +74 -4
- package/dist/telegram-support-command.js +7 -0
- package/dist/telegram-update-commands.js +27 -0
- package/dist/user-management.js +208 -0
- package/dist/web-api-contract.js +17 -0
- package/dist/web-dashboard-access-routes.js +74 -1
- package/dist/web-dashboard-artifact-routes.js +3 -3
- package/dist/web-dashboard-assets.js +2 -0
- package/dist/web-dashboard-pages.js +109 -13
- package/dist/web-dashboard-peer-routes.js +204 -0
- package/dist/web-dashboard-runtime-routes.js +53 -8
- package/dist/web-dashboard-session-routes.js +27 -20
- package/dist/web-dashboard-ui.js +2 -0
- package/dist/web-dashboard.js +160 -6
- package/dist/web-state.js +33 -2
- package/dist/webui-assets/dashboard.css +75 -1
- package/dist/webui-assets/dashboard.js +779 -55
- package/package.json +5 -2
- package/plugins/nordrelay/scripts/nordrelay.mjs +578 -19
package/.env.example
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# NordRelay runtime config example.
|
|
2
|
-
# Access is managed with NordRelay users, groups, linked
|
|
2
|
+
# Access is managed with NordRelay users, groups, linked chat identities, and enabled group/guild channels.
|
|
3
3
|
# Create the first admin with `nordrelay init` or `nordrelay user create-admin`.
|
|
4
4
|
|
|
5
5
|
# Telegram
|
|
6
|
-
#
|
|
6
|
+
# Telegram bot and transport settings.
|
|
7
|
+
# Start the Telegram bot adapter.
|
|
8
|
+
TELEGRAM_ENABLED=true
|
|
7
9
|
# BotFather token.
|
|
8
10
|
TELEGRAM_BOT_TOKEN=123456789:replace-me
|
|
9
11
|
# polling or webhook.
|
|
@@ -20,6 +22,40 @@ TELEGRAM_WEBHOOK_PATH=/telegram/webhook
|
|
|
20
22
|
# Optional Telegram webhook secret token.
|
|
21
23
|
TELEGRAM_WEBHOOK_SECRET=
|
|
22
24
|
|
|
25
|
+
# Discord
|
|
26
|
+
# Discord bot settings. Discord is opt-in and uses the same NordRelay users, groups, and permissions as Telegram.
|
|
27
|
+
# Start the Discord bot adapter.
|
|
28
|
+
DISCORD_ENABLED=false
|
|
29
|
+
# Discord bot token.
|
|
30
|
+
DISCORD_BOT_TOKEN=
|
|
31
|
+
# Discord application/client id used for slash command registration.
|
|
32
|
+
DISCORD_CLIENT_ID=
|
|
33
|
+
# Comma-separated guild ids for instant guild slash-command registration.
|
|
34
|
+
DISCORD_GUILD_IDS=
|
|
35
|
+
# Optional comma-separated guild allow-list.
|
|
36
|
+
DISCORD_ALLOWED_GUILD_IDS=
|
|
37
|
+
# Optional comma-separated channel allow-list before user/group checks.
|
|
38
|
+
DISCORD_ALLOWED_CHANNEL_IDS=
|
|
39
|
+
# Read regular Discord text messages as prompts. Requires enabling the privileged intent in Discord.
|
|
40
|
+
DISCORD_MESSAGE_CONTENT_ENABLED=true
|
|
41
|
+
# slash, message, or both.
|
|
42
|
+
# Options: slash, message, both
|
|
43
|
+
DISCORD_COMMAND_MODE=both
|
|
44
|
+
# Register Discord slash commands on startup when client id is configured.
|
|
45
|
+
DISCORD_AUTO_REGISTER_COMMANDS=true
|
|
46
|
+
# Optional Discord override for CLI mirror mode. Uses the NordRelay default when unset.
|
|
47
|
+
# Options: off, status, final, full
|
|
48
|
+
DISCORD_CLI_MIRROR_MODE=
|
|
49
|
+
# Optional Discord override for mirrored edit interval.
|
|
50
|
+
DISCORD_CLI_MIRROR_MIN_UPDATE_MS=
|
|
51
|
+
# Optional Discord override for completion notifications.
|
|
52
|
+
# Options: off, minimal, all
|
|
53
|
+
DISCORD_NOTIFY_MODE=
|
|
54
|
+
# Optional Discord quiet hours override. Use HH-HH, off, or leave blank for default.
|
|
55
|
+
DISCORD_QUIET_HOURS=
|
|
56
|
+
# Optional Discord override for automatic artifact summaries/uploads.
|
|
57
|
+
DISCORD_AUTO_SEND_ARTIFACTS=
|
|
58
|
+
|
|
23
59
|
# Agents
|
|
24
60
|
# Agent access. Codex is enabled by default; Pi, Hermes, OpenClaw, and Claude Code are opt-in.
|
|
25
61
|
# Allow Codex sessions.
|
|
@@ -161,15 +197,27 @@ ENABLE_TELEGRAM_REACTIONS=false
|
|
|
161
197
|
TELEGRAM_RATE_LIMIT_MIN_INTERVAL_MS=80
|
|
162
198
|
# Minimum edit interval.
|
|
163
199
|
TELEGRAM_EDIT_MIN_INTERVAL_MS=1200
|
|
164
|
-
# off, status, final, or full.
|
|
200
|
+
# Default mirror mode for chat adapters: off, status, final, or full.
|
|
165
201
|
# Options: off, status, final, full
|
|
166
|
-
|
|
167
|
-
#
|
|
168
|
-
|
|
169
|
-
# off, minimal, or all.
|
|
202
|
+
NORDRELAY_CLI_MIRROR_MODE=status
|
|
203
|
+
# Default minimum mirrored edit interval.
|
|
204
|
+
NORDRELAY_CLI_MIRROR_MIN_UPDATE_MS=4000
|
|
205
|
+
# Default completion notifications: off, minimal, or all.
|
|
170
206
|
# Options: off, minimal, all
|
|
171
|
-
|
|
172
|
-
# HH-HH or blank.
|
|
207
|
+
NORDRELAY_NOTIFY_MODE=minimal
|
|
208
|
+
# Default quiet hours. Use HH-HH, off, or leave blank.
|
|
209
|
+
NORDRELAY_QUIET_HOURS=
|
|
210
|
+
# Default automatic artifact summaries/uploads for chat adapters.
|
|
211
|
+
NORDRELAY_AUTO_SEND_ARTIFACTS=false
|
|
212
|
+
# Optional Telegram override for CLI mirror mode. Uses the NordRelay default when unset.
|
|
213
|
+
# Options: off, status, final, full
|
|
214
|
+
TELEGRAM_CLI_MIRROR_MODE=
|
|
215
|
+
# Optional Telegram override for mirrored edit interval.
|
|
216
|
+
TELEGRAM_CLI_MIRROR_MIN_UPDATE_MS=
|
|
217
|
+
# Optional Telegram override for completion notifications.
|
|
218
|
+
# Options: off, minimal, all
|
|
219
|
+
TELEGRAM_NOTIFY_MODE=
|
|
220
|
+
# Optional Telegram quiet hours override. Use HH-HH, off, or leave blank for default.
|
|
173
221
|
TELEGRAM_QUIET_HOURS=
|
|
174
222
|
# Additional comma-separated regex patterns.
|
|
175
223
|
TELEGRAM_REDACT_PATTERNS=
|
|
@@ -191,8 +239,8 @@ ARTIFACT_MAX_INBOX_DIRS=30
|
|
|
191
239
|
ARTIFACT_IGNORE_DIRS=
|
|
192
240
|
# Extra ignored glob patterns.
|
|
193
241
|
ARTIFACT_IGNORE_GLOBS=
|
|
194
|
-
#
|
|
195
|
-
TELEGRAM_AUTO_SEND_ARTIFACTS=
|
|
242
|
+
# Optional Telegram override for automatic artifact summaries/uploads.
|
|
243
|
+
TELEGRAM_AUTO_SEND_ARTIFACTS=
|
|
196
244
|
|
|
197
245
|
# Workspace
|
|
198
246
|
# State and workspace guardrails.
|
|
@@ -207,11 +255,32 @@ NORDRELAY_STATE_BACKEND=json
|
|
|
207
255
|
NORDRELAY_AUDIT_MAX_EVENTS=1000
|
|
208
256
|
# Write-lock TTL.
|
|
209
257
|
NORDRELAY_SESSION_LOCK_TTL_MS=1800000
|
|
258
|
+
# Stale-while-refresh TTL for expensive dashboard API snapshots.
|
|
259
|
+
NORDRELAY_DASHBOARD_CACHE_TTL_MS=10000
|
|
260
|
+
# Maximum persisted unified jobs retained for the WebUI jobs view.
|
|
261
|
+
NORDRELAY_UNIFIED_JOB_MAX_ITEMS=1000
|
|
210
262
|
# NPM version cache TTL.
|
|
211
263
|
NORDRELAY_VERSION_CACHE_TTL_MS=3600000
|
|
212
264
|
# Installed agent CLI version cache TTL.
|
|
213
265
|
NORDRELAY_CLI_VERSION_CACHE_TTL_MS=60000
|
|
214
266
|
|
|
267
|
+
# Peers
|
|
268
|
+
# Optional NordRelay-to-NordRelay federation. Pairing is explicit, authenticated, scoped, and TLS-protected.
|
|
269
|
+
# Expose the dedicated authenticated NordRelay peer API.
|
|
270
|
+
NORDRELAY_PEER_ENABLED=false
|
|
271
|
+
# Human-readable name shown to paired NordRelay instances.
|
|
272
|
+
NORDRELAY_PEER_NAME=
|
|
273
|
+
# Bind host for the peer API. Use 127.0.0.1 for local-only or a LAN/interface IP when explicitly exposing peers.
|
|
274
|
+
NORDRELAY_PEER_HOST=127.0.0.1
|
|
275
|
+
# Port for the peer API.
|
|
276
|
+
NORDRELAY_PEER_PORT=31979
|
|
277
|
+
# Optional public URL other instances should use for this node.
|
|
278
|
+
NORDRELAY_PEER_PUBLIC_URL=
|
|
279
|
+
# Serve the peer API over HTTPS with an automatically generated local certificate.
|
|
280
|
+
NORDRELAY_PEER_TLS_ENABLED=true
|
|
281
|
+
# Reject plaintext peer serving on non-loopback hosts.
|
|
282
|
+
NORDRELAY_PEER_REQUIRE_TLS=true
|
|
283
|
+
|
|
215
284
|
# Voice
|
|
216
285
|
# Optional voice transcription settings.
|
|
217
286
|
# Whisper fallback API key.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# NordRelay
|
|
2
2
|
|
|
3
|
-
NordRelay is a remote control plane for coding agents across messaging channels. The current implementation connects Codex, Pi, Hermes, OpenClaw, and Claude Code coding-agent sessions to Telegram, keeps independent sessions per chat
|
|
3
|
+
NordRelay is a remote control plane for coding agents across messaging channels and paired NordRelay instances. The current implementation connects Codex, Pi, Hermes, OpenClaw, and Claude Code coding-agent sessions to Telegram, Discord, the WebUI, and trusted peer nodes, keeps independent sessions per chat, thread, forum topic, DM, or remote target, streams replies and tool activity back to the active channel, supports files, photos, voice input, model controls, session browsing, retry/abort, CLI handback, and scoped multi-host control.
|
|
4
4
|
|
|
5
5
|
The repo is both a local Codex marketplace and a standalone Node app. The plugin lives in `plugins/nordrelay/`; the full bot runtime lives in `src/` and uses `@openai/codex-sdk` for Codex, Pi RPC mode for Pi, the Hermes API Server for Hermes, the OpenClaw Gateway WebSocket RPC surface for OpenClaw, and the Claude Agent SDK for Claude Code.
|
|
6
6
|
|
|
@@ -43,11 +43,23 @@ Session control:
|
|
|
43
43
|
|
|
44
44
|
Adapter architecture:
|
|
45
45
|
|
|
46
|
-
- Telegram
|
|
47
|
-
-
|
|
46
|
+
- Telegram supports text, typing, streaming edits, inline buttons, files, photos, voice, forum topics, and polling/webhook transport.
|
|
47
|
+
- Discord supports text, typing, streaming edits, buttons, files, photos, voice/audio transcription, guild channels, threads, DMs, message commands, and slash commands.
|
|
48
|
+
- `/channels` shows available and planned messaging adapters for Telegram, Discord, WhatsApp, Slack, and Matrix.
|
|
48
49
|
- Codex, Pi, Hermes, OpenClaw, and Claude Code are implemented as agent adapters.
|
|
49
50
|
- `/agents` shows available/planned agent adapters and whether Codex, Pi, Hermes, OpenClaw, and Claude Code are enabled.
|
|
50
|
-
- Shared command-action renderers and a channel runtime contract keep inbound commands, outbound messages, typing, files, inline actions, and streaming-ready delivery separate from
|
|
51
|
+
- Shared command-action renderers and a channel runtime contract keep inbound commands, outbound messages, typing, files, inline actions, and streaming-ready delivery separate from channel-specific API calls.
|
|
52
|
+
|
|
53
|
+
Peer federation:
|
|
54
|
+
|
|
55
|
+
- Optional NordRelay-to-NordRelay pairing lets one instance operate agents on trusted Ubuntu, macOS, Windows, LAN, or remote hosts.
|
|
56
|
+
- Peer serving is disabled by default and uses a dedicated API port separate from the dashboard.
|
|
57
|
+
- Pairing requires an explicit one-time invitation code, Ed25519 node identity verification, a per-peer shared secret, request HMAC signatures, timestamp/nonce replay protection, and TLS fingerprint pinning.
|
|
58
|
+
- Peer scopes restrict which remote WebUI/API actions are allowed, including read, prompt, queue, file, diagnostic, log, and session permissions.
|
|
59
|
+
- Peer records can also restrict allowed agent ids, allowed workspace roots, and per-peer workspace aliases such as `app=/srv/app`.
|
|
60
|
+
- The WebUI has a Peers page plus a local/remote target selector; dashboard pages, SSE streaming, queue actions, artifact downloads, health checks, and the global session browser proxy through the selected peer when allowed.
|
|
61
|
+
- Telegram and Discord expose `/peers` and `/target` so a linked user can choose whether prompts run locally or on a paired NordRelay instance.
|
|
62
|
+
- Remote prompts stream text, tool status, turn completion, and errors back to the originating Telegram or Discord context.
|
|
51
63
|
|
|
52
64
|
Codex runtime:
|
|
53
65
|
|
|
@@ -159,19 +171,33 @@ Telegram output:
|
|
|
159
171
|
- Old artifact and inbox turn directories are pruned automatically with configurable retention.
|
|
160
172
|
- Optional Telegram message reactions can acknowledge work start and completion with `ENABLE_TELEGRAM_REACTIONS=true`.
|
|
161
173
|
|
|
174
|
+
Discord input and output:
|
|
175
|
+
|
|
176
|
+
- Enable Discord with `DISCORD_ENABLED=true` and `DISCORD_BOT_TOKEN`. If a requested chat adapter is missing its token, NordRelay disables that adapter and keeps running as long as another chat adapter is usable.
|
|
177
|
+
- Set `DISCORD_CLIENT_ID` to let NordRelay register slash commands automatically.
|
|
178
|
+
- `DISCORD_COMMAND_MODE=both` supports slash commands and `/command` text messages. Set it to `slash` if the bot should not read message commands.
|
|
179
|
+
- `DISCORD_MESSAGE_CONTENT_ENABLED=true` lets regular Discord messages become prompts. The matching privileged intent must also be enabled in the Discord Developer Portal.
|
|
180
|
+
- Discord DMs, guild channels, and threads get independent NordRelay contexts.
|
|
181
|
+
- Discord attachments are staged like Telegram uploads; images are passed as image inputs and audio files are transcribed before prompting.
|
|
182
|
+
- Discord buttons cover session picks, model/reasoning/launch picks, queue actions, artifact actions, update jobs, and abort where Discord component limits allow.
|
|
183
|
+
- Discord slash commands mirror the Telegram command surface where Discord supports it: `/agent`, `/auth`, `/login`, `/logout`, `/session`, `/sessions`, `/new`, `/switch`, `/attach`, `/handback`, `/workspaces`, `/pin`, `/unpin`, `/pinned`, `/model`, `/reasoning`, `/fast`, `/launch`, `/launch_profiles`, `/queue`, `/stop`, `/retry`, `/sync`, `/progress`, `/activity`, `/audit`, `/artifacts`, `/logs`, `/version`, `/diagnostics`, `/restart`, `/update`, `/lock`, `/unlock`, `/mirror`, `/notify`, `/voice`, `/link`, `/whoami`, and `/register_channel`.
|
|
184
|
+
|
|
162
185
|
Authentication and safety:
|
|
163
186
|
|
|
164
187
|
- WebUI login is required for every dashboard page, API route, SSE stream, artifact download, and health endpoint.
|
|
165
|
-
- Access is managed through NordRelay users, groups, permissions, web sessions, and linked
|
|
166
|
-
- 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
|
|
188
|
+
- Access is managed through NordRelay users, groups, permissions, web sessions, linked Telegram identities, and linked Discord identities.
|
|
189
|
+
- Built-in groups are `Admin`, `User`, and `Read Only`; custom groups can be created in the WebUI and can restrict allowed agents, workspace roots, Telegram chats, and Discord channels.
|
|
167
190
|
- The last active admin cannot be disabled or demoted, and web sessions are revoked when passwords or group memberships change.
|
|
168
191
|
- Admins can review and revoke active WebUI sessions from the Users page.
|
|
169
192
|
- Telegram private chats require a linked active NordRelay user.
|
|
170
193
|
- Telegram group and forum chats must be registered before use; admins can run `/register_chat` in the chat or enable chats in the WebUI.
|
|
194
|
+
- Discord DMs require a linked active NordRelay user.
|
|
195
|
+
- Discord guild channels and threads must be registered before use; admins can run `/register_channel` in the channel or enable channels in the WebUI.
|
|
171
196
|
- `/whoami` shows the linked NordRelay account and groups.
|
|
172
197
|
- `/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`.
|
|
173
|
-
-
|
|
174
|
-
-
|
|
198
|
+
- `/link <code>` also links a Discord account when the code was created as a Discord link code.
|
|
199
|
+
- WebUI login and chat-account link attempts are rate-limited to reduce brute-force risk.
|
|
200
|
+
- User, group, Telegram-link, Telegram-chat, Discord-link, Discord-channel, web-session, login, and permission-denied events are written to the audit log.
|
|
175
201
|
- `/auth` reports Codex authentication, Pi provider environment health, Hermes API Server reachability, OpenClaw Gateway reachability, or Claude Code CLI auth for the selected agent.
|
|
176
202
|
- `/login` starts Telegram-managed CLI auth for Codex, Hermes, or Claude Code when enabled.
|
|
177
203
|
- `/logout` signs out of CLI auth for Codex, Hermes, or Claude Code; Codex logout is disabled while `CODEX_API_KEY` is in use.
|
|
@@ -185,18 +211,25 @@ Operations:
|
|
|
185
211
|
- Plugin command/skill starts, stops, restarts, and inspects the connector process.
|
|
186
212
|
- Manual process commands support `start`, `stop`, `restart`, `status`, `update`, and `foreground`.
|
|
187
213
|
- Telegram admin commands support `/logs`, `/diagnostics`, `/support`, `/restart`, and `/update` for NordRelay and agent CLIs.
|
|
214
|
+
- `nordrelay peer identity`, `list`, `invite`, `add`, `test`, and `revoke` manage secure peer federation from the CLI.
|
|
188
215
|
- `nordrelay update`, `/update`, and the WebUI update button detect 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 if the connector is running.
|
|
189
216
|
- `/update agents`, `/update <agent>`, `/update install <agent>`, `/update jobs`, `/update log <id>`, `/update cancel <id>`, and `/update input <id> <text>` manage Codex, Pi, Hermes, OpenClaw, and Claude Code updater or installer jobs from Telegram.
|
|
190
217
|
- `/logs` renders redacted connector, NordRelay update, and agent update logs with local-time timestamps, levels, file path, last-modified time, and highlighted warnings/errors.
|
|
191
218
|
- Logs can be emitted as timestamped plain text or JSON records with `CONNECTOR_LOG_FORMAT`.
|
|
192
219
|
- Telegram sends/edits/documents are routed through a rate-limit queue that honors Telegram retry-after responses.
|
|
220
|
+
- Mirror, notification, quiet-hour, and automatic artifact-delivery defaults are configured through channel-neutral `NORDRELAY_*` settings, with Telegram and Discord override keys when a channel should differ.
|
|
221
|
+
- The WebUI Tasks page includes a unified Jobs view for active WebUI turns, external CLI turns, queued prompts, agent update/install jobs, self-updates, and diagnostics bundle exports, with log, cancel, and retry actions where supported.
|
|
222
|
+
- Unified Jobs are persisted across restarts and retain recent prompt, queue, update, connector-update, and support-bundle history for WebUI inspection.
|
|
223
|
+
- The WebUI Metrics page reports queue state, active/completed/failed turns, job counts, average prompt duration, and Telegram/Discord rate-limit counters.
|
|
224
|
+
- Expensive dashboard views such as version checks, adapter health, and diagnostics use a short stale-while-refresh server cache so the UI can render recent data while fresh checks run in the background.
|
|
193
225
|
- Context metadata, queues, and preferences are written atomically with backup recovery.
|
|
194
226
|
- Context metadata, queues, preferences, audit events, and locks can use JSON files or the optional SQLite state backend with `NORDRELAY_STATE_BACKEND=sqlite`.
|
|
195
227
|
- Runtime config, state, and logs are written under `~/.nordrelay/`.
|
|
196
228
|
- `nordrelay init` creates a private runtime config, `nordrelay doctor` validates host prerequisites, and `nordrelay web` starts the connector plus a full local WebUI dashboard.
|
|
229
|
+
- On first WebUI startup without an admin account, NordRelay shows a setup wizard for creating the first admin; remote setup requires the one-time token printed in the server console.
|
|
197
230
|
- The WebUI has responsive header/sidebar/footer navigation, live chat streaming, session controls, queue/artifact/log/diagnostic views, and settings management.
|
|
198
231
|
- The WebUI supports light and dark themes, tabbed settings groups, paginated session browsing, and chat uploads for images, documents, and audio transcription.
|
|
199
|
-
- The WebUI exposes REST and SSE endpoints for chat streaming, sessions, settings, queue, artifacts, logs, health, diagnostics, and redacted diagnostics bundle export.
|
|
232
|
+
- The WebUI exposes REST and SSE endpoints for chat streaming, sessions, settings, queue, artifacts, logs, health, diagnostics, peers, and redacted diagnostics bundle export.
|
|
200
233
|
- The dashboard can bind to `127.0.0.1` or `0.0.0.0`; user login and session cookies are mandatory in both modes.
|
|
201
234
|
- Telegram can run with long polling or an HTTP webhook via `TELEGRAM_TRANSPORT=webhook`.
|
|
202
235
|
- Version freshness checks are cached with `NORDRELAY_VERSION_CACHE_TTL_MS`, and installed agent CLI version checks are cached with `NORDRELAY_CLI_VERSION_CACHE_TTL_MS`, to keep `/version` and adapter health responsive.
|
|
@@ -218,12 +251,16 @@ nordrelay start
|
|
|
218
251
|
```
|
|
219
252
|
|
|
220
253
|
npm is the fastest install path and is the recommended default for normal use. `nordrelay init` writes the private runtime config to `~/.nordrelay/nordrelay.env`.
|
|
254
|
+
If you start `nordrelay web` before creating an admin, the dashboard opens a first-run setup wizard. Remote setup requires the one-time setup token printed in the NordRelay console.
|
|
221
255
|
|
|
222
256
|
Non-interactive setup is also supported:
|
|
223
257
|
|
|
224
258
|
```bash
|
|
225
259
|
nordrelay init \
|
|
226
260
|
--token 123456789:replace-me \
|
|
261
|
+
--enable-discord \
|
|
262
|
+
--discord-token "discord-bot-token" \
|
|
263
|
+
--discord-client-id "discord-client-id" \
|
|
227
264
|
--admin-email you@example.com \
|
|
228
265
|
--admin-name "Your Name" \
|
|
229
266
|
--admin-password "replace-with-a-long-password" \
|
|
@@ -231,6 +268,7 @@ nordrelay init \
|
|
|
231
268
|
```
|
|
232
269
|
|
|
233
270
|
`--telegram-user-id` is optional, but linking the first admin during setup is the fastest way to use Telegram immediately.
|
|
271
|
+
Use `--discord-user-id <id>` with `nordrelay user create-admin` or `nordrelay user link-discord` to link Discord directly.
|
|
234
272
|
|
|
235
273
|
Source checkout setup:
|
|
236
274
|
|
|
@@ -253,10 +291,23 @@ Create the Telegram bot:
|
|
|
253
291
|
5. Create the first admin user with `nordrelay init` or `nordrelay user create-admin`.
|
|
254
292
|
6. Link Telegram from the WebUI, with `nordrelay user link-telegram`, or by creating a link code and sending `/link <code>` to the bot.
|
|
255
293
|
|
|
294
|
+
Create the Discord bot:
|
|
295
|
+
|
|
296
|
+
1. Open the Discord Developer Portal and create an application.
|
|
297
|
+
2. Add a bot user and copy the bot token into `DISCORD_BOT_TOKEN`.
|
|
298
|
+
3. Copy the application client ID into `DISCORD_CLIENT_ID`.
|
|
299
|
+
4. Enable the Message Content intent if you want regular Discord messages to become prompts.
|
|
300
|
+
5. Invite the bot with application-command, message-send, message-read, attachment, and thread permissions.
|
|
301
|
+
6. Link Discord from the WebUI, with `nordrelay user link-discord`, or by creating a Discord link code and sending `/link <code>` to the bot.
|
|
302
|
+
7. In guild channels, run `/register_channel` once from an admin-linked Discord account.
|
|
303
|
+
|
|
256
304
|
Minimal private-bot `~/.nordrelay/nordrelay.env`:
|
|
257
305
|
|
|
258
306
|
```dotenv
|
|
259
307
|
TELEGRAM_BOT_TOKEN=123456789:replace-me
|
|
308
|
+
DISCORD_ENABLED=false
|
|
309
|
+
DISCORD_BOT_TOKEN=
|
|
310
|
+
DISCORD_CLIENT_ID=
|
|
260
311
|
NORDRELAY_CODEX_ENABLED=true
|
|
261
312
|
NORDRELAY_PI_ENABLED=false
|
|
262
313
|
NORDRELAY_HERMES_ENABLED=false
|
|
@@ -267,14 +318,44 @@ CODEX_SANDBOX_MODE=workspace-write
|
|
|
267
318
|
CODEX_APPROVAL_POLICY=never
|
|
268
319
|
```
|
|
269
320
|
|
|
270
|
-
User and
|
|
321
|
+
User and chat access management:
|
|
271
322
|
|
|
272
323
|
- `nordrelay init` creates the first admin user and writes `~/.nordrelay/users.json`.
|
|
273
324
|
- `nordrelay user create-admin --email you@example.com --name "Your Name"` creates another admin.
|
|
274
325
|
- `nordrelay user create --email dev@example.com --name "Dev" --group user` creates a normal user.
|
|
275
326
|
- `nordrelay user link-telegram --email you@example.com --telegram-user-id 123456789` links a Telegram account directly.
|
|
276
|
-
- `nordrelay user link-
|
|
277
|
-
-
|
|
327
|
+
- `nordrelay user link-discord --email you@example.com --discord-user-id 123456789012345678` links a Discord account directly.
|
|
328
|
+
- `nordrelay user link-code --email you@example.com` creates a short-lived Telegram code that the user sends as `/link <code>` to the Telegram bot.
|
|
329
|
+
- `nordrelay user discord-link-code --email you@example.com` creates a short-lived Discord code that the user sends as `/link <code>` to the Discord bot.
|
|
330
|
+
- Telegram group chats are disabled until an admin enables them from the WebUI or runs `/register_chat` inside the group.
|
|
331
|
+
- Discord guild channels are disabled until an admin enables them from the WebUI or runs `/register_channel` inside the channel.
|
|
332
|
+
|
|
333
|
+
Peer setup:
|
|
334
|
+
|
|
335
|
+
1. On each host that should accept peer connections, set `NORDRELAY_PEER_ENABLED=true` in `~/.nordrelay/nordrelay.env`.
|
|
336
|
+
2. Keep `NORDRELAY_PEER_TLS_ENABLED=true` and `NORDRELAY_PEER_REQUIRE_TLS=true` for LAN or internet use.
|
|
337
|
+
3. Use `NORDRELAY_PEER_HOST=127.0.0.1` for local-only testing, a LAN/interface IP for trusted local networks, or keep the peer API behind a TLS reverse proxy/VPN for internet access.
|
|
338
|
+
4. Set `NORDRELAY_PEER_PUBLIC_URL=https://host.example:31979` when other hosts cannot reach the bind address directly.
|
|
339
|
+
5. Restart NordRelay on the accepting host and create an invitation:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
nordrelay peer invite --name workstation --scopes inspect,sessions.read,sessions.write,prompt.send,prompt.abort,queue.read,queue.write,files.read,files.write,diagnostics.read,logs.read
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
6. On the controlling host, run the printed command:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
nordrelay peer add https://workstation.example:31979 --code one-time-code
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
7. Confirm the connection:
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
nordrelay peer list
|
|
355
|
+
nordrelay peer test <peer-id>
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Use `--workspace-aliases app=/srv/app,demo=/home/me/demo` on invites when a controller should be able to start remote sessions with short workspace names. Use the WebUI Peers page for the same invite, pair, enable/disable, test, alias, global-session, and revoke workflow. Use `/peers` from Telegram or Discord to inspect paired nodes and `/target <peer-id>` or `/target local` to choose where subsequent prompts run.
|
|
278
359
|
|
|
279
360
|
Codex authentication:
|
|
280
361
|
|
|
@@ -361,6 +442,9 @@ nordrelay restart
|
|
|
361
442
|
nordrelay stop
|
|
362
443
|
nordrelay foreground
|
|
363
444
|
nordrelay web
|
|
445
|
+
nordrelay peer list
|
|
446
|
+
nordrelay peer invite
|
|
447
|
+
nordrelay peer add https://peer.example:31979 --code one-time-code
|
|
364
448
|
```
|
|
365
449
|
|
|
366
450
|
Source checkout process commands:
|
|
@@ -375,6 +459,7 @@ node plugins/nordrelay/scripts/nordrelay.mjs foreground
|
|
|
375
459
|
node plugins/nordrelay/scripts/nordrelay.mjs user list
|
|
376
460
|
node plugins/nordrelay/scripts/nordrelay.mjs doctor
|
|
377
461
|
node plugins/nordrelay/scripts/nordrelay.mjs web
|
|
462
|
+
node plugins/nordrelay/scripts/nordrelay.mjs peer list
|
|
378
463
|
```
|
|
379
464
|
|
|
380
465
|
NPM shortcuts:
|
|
@@ -422,6 +507,7 @@ The dashboard is a second NordRelay client next to Telegram. It can:
|
|
|
422
507
|
- Control the active session model, reasoning/thinking, fast mode, and launch profile directly from the chat view.
|
|
423
508
|
- Abort turns, hand sessions back to the native CLI, and inspect the active session.
|
|
424
509
|
- Manage queued prompts with pause/resume, run, cancel, reorder buttons, and drag-and-drop prioritization.
|
|
510
|
+
- Inspect unified jobs across queued prompts, active turns, mirrored CLI work, agent updates, self-updates, and support-bundle exports.
|
|
425
511
|
- Browse, preview, download, ZIP, and delete artifacts.
|
|
426
512
|
- Inspect the activity timeline for WebUI and mirrored CLI turns.
|
|
427
513
|
- Edit all supported runtime settings from tabbed Settings groups with option selects, validation feedback, and restart actions.
|
|
@@ -429,6 +515,7 @@ The dashboard is a second NordRelay client next to Telegram. It can:
|
|
|
429
515
|
- Inspect a per-agent capability matrix showing model, reasoning, launch, fast mode, attachments, activity, usage, auth, login/logout, and handback support.
|
|
430
516
|
- Check NordRelay and agent CLI versions, then start Codex, Pi, Hermes, OpenClaw, or Claude Code updates from outdated rows or installs from not-installed rows with live output, cancel, delete-log, and stdin response controls.
|
|
431
517
|
- Build dashboard CSS and client JavaScript from modular source assets through esbuild, then serve them as authenticated static assets instead of inline HTML.
|
|
518
|
+
- Pair, test, enable/disable, and revoke NordRelay peers, then switch the dashboard target between the local instance and paired remote instances.
|
|
432
519
|
|
|
433
520
|
Dashboard API endpoints are served under `/api/*`. Streaming uses `GET /api/events`.
|
|
434
521
|
|
|
@@ -461,6 +548,8 @@ Run NordRelay behind your reverse proxy so the public URL forwards to `http://12
|
|
|
461
548
|
- `/channels` shows available and planned messaging adapters.
|
|
462
549
|
- `/agents` shows available and planned coding-agent adapters.
|
|
463
550
|
- `/agent` selects the active agent for this Telegram context.
|
|
551
|
+
- `/peers` shows configured NordRelay peer instances.
|
|
552
|
+
- `/target local|<peer-id>` selects whether prompts for this chat run locally or on a paired peer.
|
|
464
553
|
- `/link <code>` links the Telegram account to a NordRelay user.
|
|
465
554
|
- `/whoami` shows the linked NordRelay user, groups, and permissions.
|
|
466
555
|
- `/register_chat` enables the current Telegram group or forum chat for NordRelay when the linked user has user-management permission.
|
|
@@ -524,6 +613,19 @@ Run NordRelay behind your reverse proxy so the public URL forwards to `http://12
|
|
|
524
613
|
- `/update` updates through npm or git depending on the detected install type, then restarts only on success. Requires `updates.run`.
|
|
525
614
|
- `/update agents`, `/update <agent>`, `/update install <agent>`, `/update jobs`, `/update log <id>`, `/update cancel <id>`, and `/update input <id> <text>` manage agent CLI update and install jobs. Requires `updates.run`.
|
|
526
615
|
|
|
616
|
+
## Discord Commands
|
|
617
|
+
|
|
618
|
+
Discord supports slash commands and `/command` text messages for the shared command set. The primary differences from Telegram are:
|
|
619
|
+
|
|
620
|
+
- `/register_channel` replaces `/register_chat` for guild channels and threads.
|
|
621
|
+
- `/prompt <text>` is available for slash-command-only deployments where regular message content is disabled.
|
|
622
|
+
- `/link <code>` consumes Discord link codes created in the WebUI or with `nordrelay user discord-link-code`.
|
|
623
|
+
- `/queue`, `/sessions`, `/agent`, `/model`, `/reasoning`, `/launch`, `/artifacts`, `/update`, and `/stop` use Discord buttons where component limits allow.
|
|
624
|
+
- `/peers` and `/target local|<peer-id>` use the same paired-instance target selection as Telegram.
|
|
625
|
+
- `/artifacts latest`, `/artifacts zip latest`, `/artifacts images`, `/artifacts docs`, `/artifacts search <text>`, and `/artifacts delete <turn-id>` are available in Discord.
|
|
626
|
+
- Unsafe launch profiles require explicit confirmation with `/launch <profile-id> confirm`.
|
|
627
|
+
- Discord does not support Telegram reactions or Telegram webhook transport; typing, message edits, attachments, files, DMs, guild channels, and threads are supported.
|
|
628
|
+
|
|
527
629
|
## Command Examples
|
|
528
630
|
|
|
529
631
|
Switching to an existing thread:
|
|
@@ -704,7 +806,8 @@ Voice transcription uses `OPENAI_API_KEY`, not `CODEX_API_KEY`.
|
|
|
704
806
|
|
|
705
807
|
Telegram:
|
|
706
808
|
|
|
707
|
-
- `
|
|
809
|
+
- `TELEGRAM_ENABLED`: starts the Telegram adapter. Defaults to `true`.
|
|
810
|
+
- `TELEGRAM_BOT_TOKEN`: BotFather token. Required for the Telegram adapter to start.
|
|
708
811
|
- `TELEGRAM_RATE_LIMIT_MIN_INTERVAL_MS`: minimum interval for normal Telegram API sends. Defaults to `80`.
|
|
709
812
|
- `TELEGRAM_EDIT_MIN_INTERVAL_MS`: minimum interval for Telegram message edits. Defaults to `1200`.
|
|
710
813
|
- `TELEGRAM_TRANSPORT`: `polling` or `webhook`. Defaults to `polling`.
|
|
@@ -713,19 +816,46 @@ Telegram:
|
|
|
713
816
|
- `TELEGRAM_WEBHOOK_PORT`: local bind port for webhook mode. Defaults to `8080`.
|
|
714
817
|
- `TELEGRAM_WEBHOOK_PATH`: webhook request path. Defaults to `/telegram/webhook`.
|
|
715
818
|
- `TELEGRAM_WEBHOOK_SECRET`: optional Telegram webhook secret token.
|
|
716
|
-
- `
|
|
717
|
-
- `
|
|
718
|
-
- `
|
|
719
|
-
- `
|
|
819
|
+
- `NORDRELAY_CLI_MIRROR_MODE`: default CLI mirror mode for chat adapters: `off`, `status`, `final`, or `full`. Defaults to `status`.
|
|
820
|
+
- `NORDRELAY_CLI_MIRROR_MIN_UPDATE_MS`: default minimum interval for mirrored CLI status edits. Defaults to `4000`.
|
|
821
|
+
- `NORDRELAY_NOTIFY_MODE`: default notification mode for chat adapters: `off`, `minimal`, or `all`. Defaults to `minimal`.
|
|
822
|
+
- `NORDRELAY_QUIET_HOURS`: optional default quiet-hour range in `HH-HH` format, for example `22-7`; use `off` in a channel override to disable inherited quiet hours.
|
|
823
|
+
- `NORDRELAY_AUTO_SEND_ARTIFACTS`: default automatic artifact summaries/uploads for chat adapters. Defaults to `false`.
|
|
824
|
+
- `TELEGRAM_CLI_MIRROR_MODE`, `TELEGRAM_CLI_MIRROR_MIN_UPDATE_MS`, `TELEGRAM_NOTIFY_MODE`, `TELEGRAM_QUIET_HOURS`, and `TELEGRAM_AUTO_SEND_ARTIFACTS`: optional Telegram-specific overrides.
|
|
720
825
|
- `TELEGRAM_REDACT_PATTERNS`: comma-separated regular expressions for additional Telegram/log redaction.
|
|
721
826
|
|
|
827
|
+
Discord:
|
|
828
|
+
|
|
829
|
+
- `DISCORD_ENABLED`: starts the Discord adapter. Defaults to `false`.
|
|
830
|
+
- `DISCORD_BOT_TOKEN`: Discord bot token. Required for the Discord adapter to start.
|
|
831
|
+
- `DISCORD_CLIENT_ID`: Discord application/client id used for slash-command registration.
|
|
832
|
+
- `DISCORD_GUILD_IDS`: optional comma-separated guild ids for instant guild slash-command registration.
|
|
833
|
+
- `DISCORD_ALLOWED_GUILD_IDS`: optional guild allow-list before user/group permissions are checked.
|
|
834
|
+
- `DISCORD_ALLOWED_CHANNEL_IDS`: optional channel allow-list before user/group permissions are checked.
|
|
835
|
+
- `DISCORD_MESSAGE_CONTENT_ENABLED`: reads regular Discord text messages as prompts. Defaults to `true`.
|
|
836
|
+
- `DISCORD_COMMAND_MODE`: `slash`, `message`, or `both`. Defaults to `both`.
|
|
837
|
+
- `DISCORD_AUTO_REGISTER_COMMANDS`: registers slash commands on startup when `DISCORD_CLIENT_ID` is set. Defaults to `true`.
|
|
838
|
+
- `DISCORD_CLI_MIRROR_MODE`, `DISCORD_CLI_MIRROR_MIN_UPDATE_MS`, `DISCORD_NOTIFY_MODE`, `DISCORD_QUIET_HOURS`, and `DISCORD_AUTO_SEND_ARTIFACTS`: optional Discord-specific overrides for the channel-neutral defaults.
|
|
839
|
+
|
|
722
840
|
User management:
|
|
723
841
|
|
|
724
|
-
- Users, groups, Telegram identities, Telegram group-chat access, and web sessions are stored in `~/.nordrelay/users.json`.
|
|
725
|
-
- Manage users in the WebUI Users page or with `nordrelay user list`, `create-admin`, `create`, `reset-password`, `link-telegram`, and `link-code`.
|
|
842
|
+
- Users, groups, Telegram identities, Telegram group-chat access, Discord identities, Discord channel access, and web sessions are stored in `~/.nordrelay/users.json`.
|
|
843
|
+
- Manage users in the WebUI Users page or with `nordrelay user list`, `create-admin`, `create`, `reset-password`, `link-telegram`, `link-discord`, `link-code`, and `discord-link-code`.
|
|
726
844
|
- Built-in groups are `admin`, `user`, and `readonly`.
|
|
727
|
-
- 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 `
|
|
728
|
-
- Custom groups can also restrict access to specific agent ids, workspace roots,
|
|
845
|
+
- 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`, `audit.read`, `peers.read`, `peers.write`, and `peers.connect`.
|
|
846
|
+
- Custom groups can also restrict access to specific agent ids, workspace roots, Telegram chat ids, and Discord channel ids.
|
|
847
|
+
|
|
848
|
+
Peers:
|
|
849
|
+
|
|
850
|
+
- `NORDRELAY_PEER_ENABLED`: starts the dedicated peer API. Defaults to `false`.
|
|
851
|
+
- `NORDRELAY_PEER_NAME`: optional human-readable node name shown to paired instances.
|
|
852
|
+
- `NORDRELAY_PEER_HOST`: peer API bind host. Defaults to `127.0.0.1`.
|
|
853
|
+
- `NORDRELAY_PEER_PORT`: peer API port. Defaults to `31979`.
|
|
854
|
+
- `NORDRELAY_PEER_PUBLIC_URL`: optional URL other instances should use to reach this node.
|
|
855
|
+
- `NORDRELAY_PEER_TLS_ENABLED`: serves the peer API over HTTPS with an automatically generated local certificate. Defaults to `true`.
|
|
856
|
+
- `NORDRELAY_PEER_REQUIRE_TLS`: refuses plaintext peer serving on non-loopback hosts. Defaults to `true`.
|
|
857
|
+
- Peer identity, TLS certificate, peers, and invitations are stored under `~/.nordrelay/identity.json`, `~/.nordrelay/tls/`, and `~/.nordrelay/peers.json`.
|
|
858
|
+
- Peer invitations expire after at most 24 hours even if a longer lifetime is requested.
|
|
729
859
|
|
|
730
860
|
Agent selection:
|
|
731
861
|
|
|
@@ -734,7 +864,7 @@ Agent selection:
|
|
|
734
864
|
- `NORDRELAY_HERMES_ENABLED`: enables Hermes contexts through the Hermes API Server. Defaults to `false`.
|
|
735
865
|
- `NORDRELAY_OPENCLAW_ENABLED`: enables OpenClaw contexts through the OpenClaw Gateway. Defaults to `false`.
|
|
736
866
|
- `NORDRELAY_CLAUDE_CODE_ENABLED`: enables Claude Code contexts through the Claude Agent SDK. Defaults to `false`.
|
|
737
|
-
- `NORDRELAY_DEFAULT_AGENT`: `codex`, `pi`, `hermes`, `openclaw`, or `claude-code`, used for new
|
|
867
|
+
- `NORDRELAY_DEFAULT_AGENT`: `codex`, `pi`, `hermes`, `openclaw`, or `claude-code`, used for new chat contexts. Defaults to the first enabled agent.
|
|
738
868
|
- `NORDRELAY_STATE_BACKEND`: `json` or `sqlite`. JSON is the default; SQLite requires `better-sqlite3`.
|
|
739
869
|
- `NORDRELAY_AUDIT_MAX_EVENTS`: maximum audit events retained. Defaults to `1000`.
|
|
740
870
|
- `NORDRELAY_SESSION_LOCK_TTL_MS`: session write-lock TTL. Defaults to `1800000`.
|
|
@@ -885,12 +1015,14 @@ Unsafe profiles are intentionally gated. Telegram asks for confirmation before a
|
|
|
885
1015
|
- Link Telegram accounts only to active NordRelay users that should control agents remotely.
|
|
886
1016
|
- Enable Telegram group/forum chats only when the whole chat context is trusted for the permissions granted to linked users.
|
|
887
1017
|
- Review group permissions before granting `prompt.send`, `prompt.abort`, `files.write`, `settings.write`, `updates.run`, `system.restart`, or `users.write`.
|
|
1018
|
+
- Review peer scopes before granting `peers.write`, `peers.connect`, broad `prompt.send`, or unrestricted workspace roots to a paired instance.
|
|
888
1019
|
- Treat `danger-full-access` as equivalent to shell access on the host.
|
|
889
1020
|
- Treat uploaded files as untrusted input. They are staged inside the active workspace so the selected sandbox policy still matters.
|
|
890
1021
|
- 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.
|
|
891
1022
|
- In group chats, remember that any linked user with prompt permissions can prompt the selected agent in that chat context.
|
|
892
1023
|
- Use `TOOL_VERBOSITY=summary` or `errors-only` when command output may include sensitive data.
|
|
893
1024
|
- Review and unsafe launch profiles add a Telegram approve/deny gate before each turn starts.
|
|
1025
|
+
- Keep the peer API disabled unless needed. For internet use, expose it only through a firewall, VPN, or hardened reverse proxy; keep TLS enabled and revoke unused peers with `nordrelay peer revoke <peer-id>`.
|
|
894
1026
|
|
|
895
1027
|
## Troubleshooting
|
|
896
1028
|
|
package/dist/access-control.js
CHANGED
|
@@ -20,6 +20,9 @@ export const ALL_PERMISSIONS = [
|
|
|
20
20
|
"users.read",
|
|
21
21
|
"users.write",
|
|
22
22
|
"audit.read",
|
|
23
|
+
"peers.read",
|
|
24
|
+
"peers.write",
|
|
25
|
+
"peers.connect",
|
|
23
26
|
];
|
|
24
27
|
export const ADMIN_GROUP_ID = "admin";
|
|
25
28
|
export const USER_GROUP_ID = "user";
|
|
@@ -71,6 +74,8 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
71
74
|
["health", "inspect"],
|
|
72
75
|
["version", "inspect"],
|
|
73
76
|
["channels", "inspect"],
|
|
77
|
+
["peers", "peers.read"],
|
|
78
|
+
["target", "peers.connect"],
|
|
74
79
|
["agents", "inspect"],
|
|
75
80
|
["tasks", "inspect"],
|
|
76
81
|
["progress", "inspect"],
|
|
@@ -119,6 +124,7 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
119
124
|
["abort", "prompt.abort"],
|
|
120
125
|
["stop", "prompt.abort"],
|
|
121
126
|
["register_chat", "users.write"],
|
|
127
|
+
["register_channel", "users.write"],
|
|
122
128
|
["chat_access", "users.write"],
|
|
123
129
|
["link", "inspect"],
|
|
124
130
|
]);
|
|
@@ -147,7 +153,7 @@ export function permissionForCallbackData(callbackData) {
|
|
|
147
153
|
if (callbackData.startsWith("approval_") || callbackData.startsWith("codex_abort:") || callbackData.startsWith("agent_abort:")) {
|
|
148
154
|
return "prompt.abort";
|
|
149
155
|
}
|
|
150
|
-
if (callbackData.startsWith("queue_")) {
|
|
156
|
+
if (callbackData.startsWith("queue_") || callbackData.startsWith("peer_queue_")) {
|
|
151
157
|
return "queue.write";
|
|
152
158
|
}
|
|
153
159
|
if (callbackData.startsWith("artifact_delete")) {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export function activityCategoryForType(type) {
|
|
2
|
+
if (/^(prompt|cli_turn|voice|upload|attachment)/.test(type))
|
|
3
|
+
return "prompt";
|
|
4
|
+
if (/^(session|agent_switch|handback|model_|reasoning_|fast_|launch_)/.test(type))
|
|
5
|
+
return "session";
|
|
6
|
+
if (/^queue_/.test(type))
|
|
7
|
+
return "queue";
|
|
8
|
+
if (/^agent_(install|update)/.test(type))
|
|
9
|
+
return "agent-update";
|
|
10
|
+
if (/^(artifact|artifacts)/.test(type))
|
|
11
|
+
return "artifact";
|
|
12
|
+
if (/^(auth|login|logout)/.test(type))
|
|
13
|
+
return "auth";
|
|
14
|
+
if (/^(user_|group_|telegram_chat_|telegram_link|discord_channel_|discord_link|peer_|permission_|access_|lock_)/.test(type))
|
|
15
|
+
return "security";
|
|
16
|
+
if (/^(tool_|cli_tool)/.test(type))
|
|
17
|
+
return "tool";
|
|
18
|
+
return "system";
|
|
19
|
+
}
|
|
20
|
+
export function activityActorLabel(actor) {
|
|
21
|
+
if (!actor) {
|
|
22
|
+
return "system";
|
|
23
|
+
}
|
|
24
|
+
return actor.label || actor.username || actor.id || actor.channelUserId || actor.channel;
|
|
25
|
+
}
|
|
26
|
+
export function auditCategoryForAction(action) {
|
|
27
|
+
if (/^prompt_/.test(action))
|
|
28
|
+
return "prompt";
|
|
29
|
+
if (/^queue_/.test(action))
|
|
30
|
+
return "queue";
|
|
31
|
+
if (/^lock_/.test(action))
|
|
32
|
+
return "security";
|
|
33
|
+
if (/^auth_/.test(action))
|
|
34
|
+
return "auth";
|
|
35
|
+
if (/^(permission_|user_|group_|telegram_|discord_|peer_)/.test(action))
|
|
36
|
+
return "security";
|
|
37
|
+
if (/^(artifact|file)/.test(action))
|
|
38
|
+
return "artifact";
|
|
39
|
+
if (/^(model_|reasoning_|fast_|launch_|session_|handback)/.test(action))
|
|
40
|
+
return "session";
|
|
41
|
+
if (/^(tool_)/.test(action))
|
|
42
|
+
return "tool";
|
|
43
|
+
return "system";
|
|
44
|
+
}
|
package/dist/audit-log.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { auditCategoryForAction, } from "./activity-events.js";
|
|
2
3
|
import { createDocumentStore } from "./state-backend.js";
|
|
3
4
|
export class AuditLogStore {
|
|
4
5
|
store;
|
|
@@ -18,6 +19,7 @@ export class AuditLogStore {
|
|
|
18
19
|
id: randomUUID().replace(/-/g, "").slice(0, 12),
|
|
19
20
|
timestamp: new Date().toISOString(),
|
|
20
21
|
...event,
|
|
22
|
+
category: event.category ?? auditCategoryForAction(event.action),
|
|
21
23
|
channelId: event.channelId ?? "telegram",
|
|
22
24
|
};
|
|
23
25
|
payload.events.push(next);
|
|
@@ -27,8 +29,22 @@ export class AuditLogStore {
|
|
|
27
29
|
this.store.write(payload);
|
|
28
30
|
return next;
|
|
29
31
|
}
|
|
30
|
-
list(
|
|
31
|
-
|
|
32
|
+
list(options = 20) {
|
|
33
|
+
const resolved = typeof options === "number" ? { limit: options } : options;
|
|
34
|
+
const limit = Math.max(1, Math.min(500, resolved.limit ?? 20));
|
|
35
|
+
const since = normalizeSince(resolved.since);
|
|
36
|
+
return this.readPayload().events
|
|
37
|
+
.filter((event) => !resolved.channelId || resolved.channelId === "all" || event.channelId === resolved.channelId)
|
|
38
|
+
.filter((event) => !resolved.status || resolved.status === "all" || event.status === resolved.status)
|
|
39
|
+
.filter((event) => !resolved.action || resolved.action === "all" || event.action === resolved.action)
|
|
40
|
+
.filter((event) => !resolved.category || resolved.category === "all" || (event.category ?? auditCategoryForAction(event.action)) === resolved.category)
|
|
41
|
+
.filter((event) => !resolved.agentId || resolved.agentId === "all" || event.agentId === resolved.agentId)
|
|
42
|
+
.filter((event) => !resolved.threadId || event.threadId === resolved.threadId)
|
|
43
|
+
.filter((event) => !resolved.workspace || event.workspace === resolved.workspace)
|
|
44
|
+
.filter((event) => !resolved.actor || auditActorMatches(event, resolved.actor))
|
|
45
|
+
.filter((event) => !since || Date.parse(event.timestamp) >= since)
|
|
46
|
+
.slice(-limit)
|
|
47
|
+
.reverse();
|
|
32
48
|
}
|
|
33
49
|
readPayload() {
|
|
34
50
|
const payload = this.store.read();
|
|
@@ -41,6 +57,28 @@ export class AuditLogStore {
|
|
|
41
57
|
};
|
|
42
58
|
}
|
|
43
59
|
}
|
|
60
|
+
function normalizeSince(value) {
|
|
61
|
+
if (value === undefined || value === null || value === "") {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const time = typeof value === "number" ? value : Date.parse(value);
|
|
65
|
+
return Number.isFinite(time) ? time : null;
|
|
66
|
+
}
|
|
67
|
+
function auditActorMatches(event, query) {
|
|
68
|
+
const needle = query.trim().toLowerCase();
|
|
69
|
+
if (!needle) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
return [
|
|
73
|
+
event.actor?.id,
|
|
74
|
+
event.actor?.label,
|
|
75
|
+
event.actor?.username,
|
|
76
|
+
event.actor?.channelUserId,
|
|
77
|
+
event.actor?.channel,
|
|
78
|
+
event.actorId,
|
|
79
|
+
event.actorRole,
|
|
80
|
+
].some((value) => String(value ?? "").toLowerCase().includes(needle));
|
|
81
|
+
}
|
|
44
82
|
function isAuditEvent(value) {
|
|
45
83
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
46
84
|
return false;
|
package/dist/bot-preferences.js
CHANGED
|
@@ -117,6 +117,7 @@ function normalizePreferences(value) {
|
|
|
117
117
|
voiceBackend: isVoiceBackendPreference(candidate.voiceBackend) ? candidate.voiceBackend : undefined,
|
|
118
118
|
voiceLanguage: typeof candidate.voiceLanguage === "string" ? candidate.voiceLanguage : candidate.voiceLanguage === null ? null : undefined,
|
|
119
119
|
voiceTranscribeOnly: typeof candidate.voiceTranscribeOnly === "boolean" ? candidate.voiceTranscribeOnly : undefined,
|
|
120
|
+
targetPeerId: typeof candidate.targetPeerId === "string" ? candidate.targetPeerId : candidate.targetPeerId === null ? null : undefined,
|
|
120
121
|
});
|
|
121
122
|
}
|
|
122
123
|
function pruneEmptyPreferences(preferences) {
|