@nordbyte/nordrelay 0.6.0 → 0.8.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.
Files changed (62) hide show
  1. package/.env.example +52 -0
  2. package/README.md +171 -50
  3. package/dist/access-control.js +6 -1
  4. package/dist/activity-events.js +2 -2
  5. package/dist/adapter-conformance.js +61 -0
  6. package/dist/bot-preferences.js +1 -0
  7. package/dist/bot.js +95 -37
  8. package/dist/channel-adapter.js +44 -11
  9. package/dist/channel-command-catalog.js +94 -0
  10. package/dist/channel-command-core.js +60 -0
  11. package/dist/channel-command-service.js +230 -1
  12. package/dist/channel-mirror-registry.js +84 -0
  13. package/dist/channel-peer-prompt.js +95 -0
  14. package/dist/channel-prompt-engine.js +177 -0
  15. package/dist/channel-runtime.js +12 -5
  16. package/dist/channel-turn-lifecycle.js +73 -0
  17. package/dist/codex-state.js +114 -78
  18. package/dist/config-metadata.js +82 -8
  19. package/dist/config.js +79 -7
  20. package/dist/context-key.js +42 -0
  21. package/dist/discord-bot.js +173 -342
  22. package/dist/discord-command-surface.js +11 -73
  23. package/dist/index.js +29 -0
  24. package/dist/metrics.js +48 -0
  25. package/dist/peer-auth.js +85 -0
  26. package/dist/peer-client.js +288 -0
  27. package/dist/peer-context.js +21 -0
  28. package/dist/peer-identity.js +127 -0
  29. package/dist/peer-readiness.js +77 -0
  30. package/dist/peer-runtime-service.js +658 -0
  31. package/dist/peer-server.js +220 -0
  32. package/dist/peer-store.js +307 -0
  33. package/dist/peer-types.js +52 -0
  34. package/dist/relay-runtime-helpers.js +210 -0
  35. package/dist/relay-runtime.js +79 -274
  36. package/dist/remote-prompt.js +98 -0
  37. package/dist/settings-wizard-test.js +216 -0
  38. package/dist/slack-artifacts.js +165 -0
  39. package/dist/slack-bot.js +1461 -0
  40. package/dist/slack-channel-runtime.js +147 -0
  41. package/dist/slack-command-surface.js +46 -0
  42. package/dist/slack-diagnostics.js +116 -0
  43. package/dist/slack-rate-limit.js +139 -0
  44. package/dist/telegram-command-menu.js +3 -53
  45. package/dist/telegram-general-commands.js +14 -0
  46. package/dist/telegram-preference-commands.js +23 -127
  47. package/dist/user-management-crypto.js +38 -0
  48. package/dist/user-management-normalize.js +188 -0
  49. package/dist/user-management-types.js +1 -0
  50. package/dist/user-management.js +193 -196
  51. package/dist/web-api-contract.js +16 -0
  52. package/dist/web-dashboard-access-routes.js +62 -0
  53. package/dist/web-dashboard-assets.js +1 -0
  54. package/dist/web-dashboard-pages.js +26 -4
  55. package/dist/web-dashboard-peer-routes.js +225 -0
  56. package/dist/web-dashboard-ui.js +1 -0
  57. package/dist/web-dashboard.js +46 -0
  58. package/dist/web-state.js +2 -2
  59. package/dist/webui-assets/dashboard.css +193 -0
  60. package/dist/webui-assets/dashboard.js +870 -57
  61. package/package.json +5 -2
  62. package/plugins/nordrelay/scripts/nordrelay.mjs +468 -11
package/.env.example CHANGED
@@ -56,6 +56,41 @@ DISCORD_QUIET_HOURS=
56
56
  # Optional Discord override for automatic artifact summaries/uploads.
57
57
  DISCORD_AUTO_SEND_ARTIFACTS=
58
58
 
59
+ # Slack
60
+ # Slack bot settings. Slack is opt-in and uses the same NordRelay users, groups, and permissions as Telegram and Discord.
61
+ # Start the Slack bot adapter.
62
+ SLACK_ENABLED=false
63
+ # Slack bot token.
64
+ SLACK_BOT_TOKEN=
65
+ # Slack app-level token for Socket Mode.
66
+ SLACK_APP_TOKEN=
67
+ # Slack signing secret for HTTP Events mode.
68
+ SLACK_SIGNING_SECRET=
69
+ # Use Slack Socket Mode instead of an HTTP events receiver.
70
+ SLACK_SOCKET_MODE=true
71
+ # HTTP port used when Slack Socket Mode is disabled.
72
+ SLACK_PORT=3000
73
+ # Optional comma-separated Slack team/workspace allow-list.
74
+ SLACK_ALLOWED_TEAM_IDS=
75
+ # Optional comma-separated Slack channel allow-list before user/group checks.
76
+ SLACK_ALLOWED_CHANNEL_IDS=
77
+ # Read regular Slack text messages as prompts.
78
+ SLACK_MESSAGE_CONTENT_ENABLED=true
79
+ # Slash command configured in Slack.
80
+ SLACK_COMMAND=/nordrelay
81
+ # Optional Slack override for CLI mirror mode. Uses the NordRelay default when unset.
82
+ # Options: off, status, final, full
83
+ SLACK_CLI_MIRROR_MODE=
84
+ # Optional Slack override for mirrored edit interval.
85
+ SLACK_CLI_MIRROR_MIN_UPDATE_MS=
86
+ # Optional Slack override for completion notifications.
87
+ # Options: off, minimal, all
88
+ SLACK_NOTIFY_MODE=
89
+ # Optional Slack quiet hours override. Use HH-HH, off, or leave blank for default.
90
+ SLACK_QUIET_HOURS=
91
+ # Optional Slack override for automatic artifact summaries/uploads.
92
+ SLACK_AUTO_SEND_ARTIFACTS=
93
+
59
94
  # Agents
60
95
  # Agent access. Codex is enabled by default; Pi, Hermes, OpenClaw, and Claude Code are opt-in.
61
96
  # Allow Codex sessions.
@@ -264,6 +299,23 @@ NORDRELAY_VERSION_CACHE_TTL_MS=3600000
264
299
  # Installed agent CLI version cache TTL.
265
300
  NORDRELAY_CLI_VERSION_CACHE_TTL_MS=60000
266
301
 
302
+ # Peers
303
+ # Optional NordRelay-to-NordRelay federation. Pairing is explicit, authenticated, scoped, and TLS-protected.
304
+ # Expose the dedicated authenticated NordRelay peer API.
305
+ NORDRELAY_PEER_ENABLED=false
306
+ # Human-readable name shown to paired NordRelay instances.
307
+ NORDRELAY_PEER_NAME=
308
+ # Bind host for the peer API. Use 127.0.0.1 for local-only or a LAN/interface IP when explicitly exposing peers.
309
+ NORDRELAY_PEER_HOST=127.0.0.1
310
+ # Port for the peer API.
311
+ NORDRELAY_PEER_PORT=31979
312
+ # Optional public URL other instances should use for this node.
313
+ NORDRELAY_PEER_PUBLIC_URL=
314
+ # Serve the peer API over HTTPS with an automatically generated local certificate.
315
+ NORDRELAY_PEER_TLS_ENABLED=true
316
+ # Reject plaintext peer serving on non-loopback hosts.
317
+ NORDRELAY_PEER_REQUIRE_TLS=true
318
+
267
319
  # Voice
268
320
  # Optional voice transcription settings.
269
321
  # 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 and Discord, keeps independent sessions per chat, thread, forum topic, or DM, streams replies and tool activity back to the active channel, supports files, photos, voice input, model controls, session browsing, retry/abort, and CLI handback.
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, Slack, 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
 
@@ -8,48 +8,62 @@ The repo is both a local Codex marketplace and a standalone Node app. The plugin
8
8
 
9
9
  Session control:
10
10
 
11
- - Independent coding-agent sessions per Telegram private chat, group chat, and forum topic.
12
- - `/agent` switches a Telegram context between enabled agents such as Codex, Pi, Hermes, OpenClaw, and Claude Code.
13
- - Persistent Telegram context metadata in the active workspace under `.nordrelay/contexts.json`.
11
+ - Independent coding-agent sessions per Telegram private chat, group chat, forum topic, Discord DM/channel/thread, Slack DM/channel/thread, WebUI, and peer target.
12
+ - `/agent` switches a chat context between enabled agents such as Codex, Pi, Hermes, OpenClaw, and Claude Code.
13
+ - Persistent channel context metadata in the active workspace under `.nordrelay/contexts.json`.
14
14
  - `/new` starts a fresh thread, with workspace selection when known workspaces are available.
15
15
  - `/session` shows thread id, workspace, launch profile, launch behavior, model, reasoning, fast mode, context usage, token totals, and subscription limit remaining percentages.
16
16
  - `/sessions` opens a paginated browser for recent sessions from the selected agent.
17
17
  - `/sessions <query>` filters recent sessions by id, title, workspace, model, or first message.
18
- - `/sync` manually refreshes the active Telegram session from local CLI state when the selected agent supports state watching.
18
+ - `/sync` manually refreshes the active chat session from local CLI state when the selected agent supports state watching.
19
19
  - `/pin`, `/unpin`, and `/pinned` keep important threads at the top of Telegram session browsing.
20
20
  - `/switch <session-id>` switches directly to an existing session.
21
21
  - `/attach <session-id>` binds an existing agent session to the current chat or topic.
22
22
  - Existing thread metadata is imported on switch/attach, including model, reasoning effort, sandbox mode, and approval policy.
23
23
  - Codex session usage is read from local rollout JSONL files, including context-used percent, total input/output tokens, 5h limit remaining, and weekly limit remaining.
24
24
  - `/handback` returns a ready-to-run CLI command for continuing in the native agent CLI.
25
- - `/retry` resends the last prompt for the current Telegram context.
26
- - `/queue`, inline run/top/up/down/cancel buttons, `/cancel <queue-id>`, and `/clearqueue` manage queued prompts for a busy Telegram context.
25
+ - `/retry` resends the last prompt for the current chat context.
26
+ - `/queue`, inline run/top/up/down/cancel buttons, `/cancel <queue-id>`, and `/clearqueue` manage queued prompts for a busy chat context.
27
27
  - `/queue later <minutes> <prompt>` schedules a prompt for later execution, and `/queue inspect <queue-id>` shows full queue metadata.
28
28
  - `/abort`, `/stop`, and the inline Abort button cancel the active agent turn.
29
- - Busy prompts are queued per Telegram context instead of being dropped.
30
- - If the attached thread is currently active in the local agent CLI, Telegram prompts are queued until that CLI task finishes.
31
- - Active Codex, Pi, Hermes, OpenClaw, and Claude Code CLI/API turns are mirrored into Telegram with configurable `off`, `status`, `final`, or `full` modes.
32
- - `/mirror` controls CLI mirroring per Telegram context.
29
+ - Busy prompts are queued per chat context instead of being dropped.
30
+ - If the attached thread is currently active in the local agent CLI, chat prompts are queued until that CLI task finishes.
31
+ - Active Codex, Pi, Hermes, OpenClaw, and Claude Code CLI/API turns are mirrored into Telegram, Discord, and Slack with configurable `off`, `status`, `final`, or `full` modes.
32
+ - `/mirror` controls CLI mirroring per chat context.
33
33
  - Queues survive connector restarts and are resumed automatically when the external CLI turn becomes idle.
34
- - `/notify` controls completion/status notifications and quiet hours per Telegram context.
34
+ - `/notify` controls completion/status notifications and quiet hours per chat context.
35
35
  - `/workspaces` lists allowed workspaces and shows workspace guardrail warnings.
36
- - `/status`, `/health`, and `/version` report connector runtime health from Telegram.
36
+ - `/status`, `/health`, and `/version` report connector runtime health from chat adapters.
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, user/group authorization, Telegram rate-limit, mirror, voice, session, queue, and progress details.
39
+ - `/diagnostics` reports redacted runtime, config, user/group authorization, channel rate-limit, mirror, voice, session, queue, and progress details.
40
40
  - `/support` exports a redacted diagnostics ZIP with config, health, versions, agent paths, recent logs, audit events, update jobs, state backend, and OS/Node/npm info.
41
41
  - `/lock`, `/unlock`, and `/locks` provide a team write-lock for shared sessions so one user can operate while others watch.
42
- - `/audit` shows recent prompt, queue, lock, command, authentication, permission-denied, user, group, Telegram-link, Telegram-chat, and web-session audit events for admins.
42
+ - `/audit` shows recent prompt, queue, lock, command, authentication, permission-denied, user, group, Telegram-link, Telegram-chat, Discord-link, Discord-channel, Slack-link, Slack-channel, and web-session audit events for admins.
43
43
 
44
44
  Adapter architecture:
45
45
 
46
46
  - Telegram supports text, typing, streaming edits, inline buttons, files, photos, voice, forum topics, and polling/webhook transport.
47
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
+ - Slack supports text, typing/status, streaming edits, Block Kit buttons, files, images, audio transcription, channels, DMs, threads, Socket Mode, and HTTP Events mode.
49
+ - Slack startup and `/diagnostics` include readiness checks for token/transport configuration, registered channels, Slack API auth probes, channel visibility probes, file-upload readiness notes, and rate-limit counters.
50
+ - `/channels` shows available and planned messaging adapters for Telegram, Discord, Slack, WhatsApp, and Matrix.
49
51
  - Codex, Pi, Hermes, OpenClaw, and Claude Code are implemented as agent adapters.
50
52
  - `/agents` shows available/planned agent adapters and whether Codex, Pi, Hermes, OpenClaw, and Claude Code are enabled.
53
+ - Agent and chat adapters expose a shared conformance matrix so command coverage and feature support can be tested and surfaced consistently.
51
54
  - 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
55
 
56
+ Peer federation:
57
+
58
+ - Optional NordRelay-to-NordRelay pairing lets one instance operate agents on trusted Ubuntu, macOS, Windows, LAN, or remote hosts.
59
+ - Peer serving is disabled by default and uses a dedicated API port separate from the dashboard.
60
+ - 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.
61
+ - Peer scopes restrict which remote WebUI/API actions are allowed, including read, prompt, queue, file, diagnostic, log, and session permissions.
62
+ - Peer records can also restrict allowed agent ids, allowed workspace roots, and per-peer workspace aliases such as `app=/srv/app`.
63
+ - 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.
64
+ - Telegram, Discord, and Slack expose `/peers` and `/target` so a linked user can choose whether prompts run locally or on a paired NordRelay instance.
65
+ - Remote prompts stream text, tool status, turn completion, and errors back to the originating Telegram, Discord, or Slack context.
66
+
53
67
  Codex runtime:
54
68
 
55
69
  - Uses `@openai/codex-sdk` to start, resume, and stream Codex threads.
@@ -59,18 +73,18 @@ Codex runtime:
59
73
  - Supports launch profiles through `/launch_profiles` and `/launch`.
60
74
  - Built-in launch profiles include Default, Read Only, Review, and optional Full Access.
61
75
  - Custom launch profiles can be configured with `CODEX_LAUNCH_PROFILES_JSON`.
62
- - Unsafe `danger-full-access` profiles require `ENABLE_UNSAFE_LAUNCH_PROFILES=true` and Telegram confirmation.
63
- - Review or unsafe launch profiles require an inline Telegram approval before each prompt is executed.
76
+ - Unsafe `danger-full-access` profiles require `ENABLE_UNSAFE_LAUNCH_PROFILES=true` and channel confirmation.
77
+ - Review or unsafe launch profiles require an inline channel approval before each prompt is executed.
64
78
  - Fast mode can be toggled with `/fast` and mirrors Codex's `fast_default_opt_out` setting from `~/.codex/config.toml`.
65
- - Active Telegram sessions periodically sync model, reasoning, workspace, launch metadata, and fast-mode defaults from local agent state where supported.
66
- - Active local Codex CLI tasks are detected from rollout JSONL files so Telegram does not race the CLI on the same thread.
79
+ - Active chat sessions periodically sync model, reasoning, workspace, launch metadata, and fast-mode defaults from local agent state where supported.
80
+ - Active local Codex CLI tasks are detected from rollout JSONL files so chat adapters do not race the CLI on the same thread.
67
81
  - `/diagnostics` includes rollout path, activity status, stale/idle reason, line count, and last update time.
68
82
  - Optional per-turn token usage footer with `SHOW_TURN_TOKEN_USAGE=true`.
69
83
 
70
84
  Pi runtime:
71
85
 
72
86
  - Pi support is opt-in with `NORDRELAY_PI_ENABLED=true`.
73
- - The default Telegram agent is selected with `NORDRELAY_DEFAULT_AGENT=codex`, `pi`, `hermes`, `openclaw`, or `claude-code`.
87
+ - The default chat agent is selected with `NORDRELAY_DEFAULT_AGENT=codex`, `pi`, `hermes`, `openclaw`, or `claude-code`.
74
88
  - Pi sessions are driven through official `pi --mode rpc` JSONL commands and events.
75
89
  - Existing Pi sessions are discovered from `~/.pi/agent/sessions/` or `PI_SESSION_DIR`.
76
90
  - `/sessions`, `/switch`, `/attach`, `/new`, `/session`, `/handback`, `/model`, `/reasoning`, `/abort`, `/stop`, `/retry`, `/queue`, files, photos, and voice input work for Pi contexts.
@@ -78,15 +92,15 @@ Pi runtime:
78
92
  - Pi thinking levels use `/reasoning` and support `off`, `minimal`, `low`, `medium`, `high`, and `xhigh`.
79
93
  - Pi token and context stats are read through `get_session_stats` when an RPC session is active.
80
94
  - Pi launch profiles expose CLI safety modes such as default, read-only tools, no tools, offline, and safe offline.
81
- - Pi external CLI activity is detected from Pi session JSONL files so Telegram/WebUI prompts queue while the same Pi session is busy.
82
- - Pi CLI turns can be mirrored into Telegram/WebUI with status, tool activity, final answers, activity timelines, diagnostics, and generated artifact discovery.
95
+ - Pi external CLI activity is detected from Pi session JSONL files so chat/WebUI prompts queue while the same Pi session is busy.
96
+ - Pi CLI turns can be mirrored into chat adapters and the WebUI with status, tool activity, final answers, activity timelines, diagnostics, and generated artifact discovery.
83
97
  - Pi provider auth checks report the environment variables expected for the selected provider.
84
98
  - Codex-only subscription limit percentages remain Codex-specific; Pi reports token/context stats when available.
85
99
 
86
100
  Hermes runtime:
87
101
 
88
102
  - Hermes support is opt-in with `NORDRELAY_HERMES_ENABLED=true`.
89
- - The default Telegram agent can be set with `NORDRELAY_DEFAULT_AGENT=hermes`.
103
+ - The default chat agent can be set with `NORDRELAY_DEFAULT_AGENT=hermes`.
90
104
  - Hermes turns are executed through the Hermes API Server `/v1/runs` endpoint and streamed through `/v1/runs/{run_id}/events`.
91
105
  - `/abort` and `/stop` use the Hermes run stop endpoint when a NordRelay-started Hermes run is active.
92
106
  - Existing Hermes sessions are discovered from `~/.hermes/state.db`, or from `HERMES_STATE_DB_PATH` when configured.
@@ -94,14 +108,14 @@ Hermes runtime:
94
108
  - Hermes model selection uses `/v1/models` when the API Server is reachable and falls back to the selected/default model.
95
109
  - Hermes reasoning uses `/reasoning` and supports `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`.
96
110
  - Hermes launch profiles include `default`, `safe`, `readonly`, and `yolo`; profiles map to run instructions and Hermes approval responses.
97
- - Hermes external activity is detected from `state.db`, so Telegram/WebUI prompts queue while the same Hermes session has an unfinished CLI turn.
98
- - Hermes CLI/API turns can be mirrored into Telegram/WebUI with status, tool activity, final answers, activity timelines, diagnostics, and generated artifact discovery.
111
+ - Hermes external activity is detected from `state.db`, so chat/WebUI prompts queue while the same Hermes session has an unfinished CLI turn.
112
+ - Hermes CLI/API turns can be mirrored into chat adapters and the WebUI with status, tool activity, final answers, activity timelines, diagnostics, and generated artifact discovery.
99
113
  - `/auth` checks that the Hermes API Server is reachable and that `HERMES_API_KEY` is usable when configured.
100
114
 
101
115
  OpenClaw runtime:
102
116
 
103
117
  - OpenClaw support is opt-in with `NORDRELAY_OPENCLAW_ENABLED=true`.
104
- - The default Telegram agent can be set with `NORDRELAY_DEFAULT_AGENT=openclaw`.
118
+ - The default chat agent can be set with `NORDRELAY_DEFAULT_AGENT=openclaw`.
105
119
  - OpenClaw turns are executed through the OpenClaw Gateway WebSocket RPC endpoint configured by `OPENCLAW_GATEWAY_URL`.
106
120
  - `/abort` and `/stop` call the OpenClaw Gateway cancel method when a NordRelay-started OpenClaw run is active.
107
121
  - Existing OpenClaw sessions are discovered from `openclaw sessions --all-agents --json`, or from the state directory configured with `OPENCLAW_HOME` or `OPENCLAW_STATE_DIR`.
@@ -109,22 +123,22 @@ OpenClaw runtime:
109
123
  - OpenClaw model selection uses the Gateway `models.list` method when reachable and falls back to `openclaw models list --json`.
110
124
  - OpenClaw thinking uses `/reasoning` and supports `off`, `minimal`, `low`, `medium`, `high`, and `xhigh`.
111
125
  - OpenClaw launch profiles include `default`, `safe`, `readonly`, `local`, and `deliver`; profiles map to Gateway run flags and additional instructions.
112
- - OpenClaw external activity is detected from OpenClaw session state, so Telegram/WebUI prompts queue while the same OpenClaw session has an unfinished CLI turn.
113
- - OpenClaw Gateway turns can be mirrored into Telegram/WebUI with status, tool activity, final answers, activity timelines, diagnostics, and generated artifact discovery.
126
+ - OpenClaw external activity is detected from OpenClaw session state, so chat/WebUI prompts queue while the same OpenClaw session has an unfinished CLI turn.
127
+ - OpenClaw Gateway turns can be mirrored into chat adapters and the WebUI with status, tool activity, final answers, activity timelines, diagnostics, and generated artifact discovery.
114
128
  - `/auth` checks that the OpenClaw Gateway is reachable and that `OPENCLAW_GATEWAY_TOKEN` or `OPENCLAW_GATEWAY_PASSWORD` is usable when configured.
115
129
 
116
130
  Claude Code runtime:
117
131
 
118
132
  - Claude Code support is opt-in with `NORDRELAY_CLAUDE_CODE_ENABLED=true`.
119
- - The default Telegram agent can be set with `NORDRELAY_DEFAULT_AGENT=claude-code`.
133
+ - The default chat agent can be set with `NORDRELAY_DEFAULT_AGENT=claude-code`.
120
134
  - Claude Code turns are executed through `@anthropic-ai/claude-agent-sdk`, using the host `claude` executable when available and the SDK bundled runtime otherwise.
121
135
  - Existing Claude Code sessions are discovered from `~/.claude/projects/`, or from `CLAUDE_CONFIG_DIR/projects` when configured.
122
136
  - `/sessions`, `/switch`, `/attach`, `/new`, `/session`, `/handback`, `/model`, `/reasoning`, `/abort`, `/stop`, `/retry`, `/queue`, files, photos, and voice input work for Claude Code contexts.
123
137
  - Claude Code model selection exposes common aliases and model ids; explicit values from existing sessions are preserved.
124
138
  - Claude Code effort uses `/reasoning` and supports `off`, `low`, `medium`, `high`, and `xhigh`.
125
139
  - Claude Code launch profiles include `default`, `accept-edits`, `plan`, `readonly`, `no-tools`, and optional `bypass-permissions`.
126
- - Claude Code external activity is detected from transcript JSONL files, so Telegram/WebUI prompts queue while the same Claude Code session has an unfinished CLI turn.
127
- - Claude Code SDK turns can be mirrored into Telegram/WebUI with status, tool activity, final answers, activity timelines, diagnostics, and generated artifact discovery.
140
+ - Claude Code external activity is detected from transcript JSONL files, so chat/WebUI prompts queue while the same Claude Code session has an unfinished CLI turn.
141
+ - Claude Code SDK turns can be mirrored into chat adapters and the WebUI with status, tool activity, final answers, activity timelines, diagnostics, and generated artifact discovery.
128
142
  - `/auth` checks the host Claude Code CLI auth state when `claude auth status` is available.
129
143
 
130
144
  Telegram input:
@@ -162,7 +176,7 @@ Telegram output:
162
176
 
163
177
  Discord input and output:
164
178
 
165
- - Enable Discord with `DISCORD_ENABLED=true` and `DISCORD_BOT_TOKEN`.
179
+ - 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.
166
180
  - Set `DISCORD_CLIENT_ID` to let NordRelay register slash commands automatically.
167
181
  - `DISCORD_COMMAND_MODE=both` supports slash commands and `/command` text messages. Set it to `slash` if the bot should not read message commands.
168
182
  - `DISCORD_MESSAGE_CONTENT_ENABLED=true` lets regular Discord messages become prompts. The matching privileged intent must also be enabled in the Discord Developer Portal.
@@ -171,22 +185,33 @@ Discord input and output:
171
185
  - Discord buttons cover session picks, model/reasoning/launch picks, queue actions, artifact actions, update jobs, and abort where Discord component limits allow.
172
186
  - 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`.
173
187
 
188
+ Slack input and output:
189
+
190
+ - Enable Slack with `SLACK_ENABLED=true`, `SLACK_BOT_TOKEN`, and `SLACK_APP_TOKEN` for Socket Mode. If Socket Mode is disabled, set `SLACK_SIGNING_SECRET` and expose the Slack HTTP receiver.
191
+ - `SLACK_MESSAGE_CONTENT_ENABLED=true` lets regular Slack messages become prompts. Keep it disabled if you only want slash-command control.
192
+ - Slack DMs, channels, and message threads get independent NordRelay contexts.
193
+ - Slack files are staged like Telegram/Discord uploads; images are passed as image inputs and audio files are transcribed before prompting.
194
+ - Slack Block Kit buttons cover session picks, model/reasoning/launch picks, queue actions, artifact actions, update jobs, and abort where Slack component limits allow.
195
+ - Slack slash/text commands mirror the shared command surface where Slack 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`.
196
+
174
197
  Authentication and safety:
175
198
 
176
199
  - WebUI login is required for every dashboard page, API route, SSE stream, artifact download, and health endpoint.
177
- - Access is managed through NordRelay users, groups, permissions, web sessions, linked Telegram identities, and linked Discord identities.
178
- - 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.
200
+ - Access is managed through NordRelay users, groups, permissions, web sessions, linked Telegram identities, linked Discord identities, and linked Slack identities.
201
+ - 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, Discord channels, and Slack channels.
179
202
  - The last active admin cannot be disabled or demoted, and web sessions are revoked when passwords or group memberships change.
180
203
  - Admins can review and revoke active WebUI sessions from the Users page.
181
204
  - Telegram private chats require a linked active NordRelay user.
182
205
  - Telegram group and forum chats must be registered before use; admins can run `/register_chat` in the chat or enable chats in the WebUI.
183
206
  - Discord DMs require a linked active NordRelay user.
184
207
  - Discord guild channels and threads must be registered before use; admins can run `/register_channel` in the channel or enable channels in the WebUI.
208
+ - Slack DMs require a linked active NordRelay user.
209
+ - Slack channels and threads must be registered before use; admins can run `/register_channel` in the channel or enable channels in the WebUI.
185
210
  - `/whoami` shows the linked NordRelay account and groups.
186
211
  - `/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`.
187
- - `/link <code>` also links a Discord account when the code was created as a Discord link code.
212
+ - `/link <code>` also links a Discord or Slack account when the code was created for that channel.
188
213
  - WebUI login and chat-account link attempts are rate-limited to reduce brute-force risk.
189
- - User, group, Telegram-link, Telegram-chat, Discord-link, Discord-channel, web-session, login, and permission-denied events are written to the audit log.
214
+ - User, group, Telegram-link, Telegram-chat, Discord-link, Discord-channel, Slack-link, Slack-channel, web-session, login, and permission-denied events are written to the audit log.
190
215
  - `/auth` reports Codex authentication, Pi provider environment health, Hermes API Server reachability, OpenClaw Gateway reachability, or Claude Code CLI auth for the selected agent.
191
216
  - `/login` starts Telegram-managed CLI auth for Codex, Hermes, or Claude Code when enabled.
192
217
  - `/logout` signs out of CLI auth for Codex, Hermes, or Claude Code; Codex logout is disabled while `CODEX_API_KEY` is in use.
@@ -200,15 +225,16 @@ Operations:
200
225
  - Plugin command/skill starts, stops, restarts, and inspects the connector process.
201
226
  - Manual process commands support `start`, `stop`, `restart`, `status`, `update`, and `foreground`.
202
227
  - Telegram admin commands support `/logs`, `/diagnostics`, `/support`, `/restart`, and `/update` for NordRelay and agent CLIs.
228
+ - `nordrelay peer identity`, `list`, `invite`, `add`, `test`, and `revoke` manage secure peer federation from the CLI.
203
229
  - `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.
204
230
  - `/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.
205
231
  - `/logs` renders redacted connector, NordRelay update, and agent update logs with local-time timestamps, levels, file path, last-modified time, and highlighted warnings/errors.
206
232
  - Logs can be emitted as timestamped plain text or JSON records with `CONNECTOR_LOG_FORMAT`.
207
233
  - Telegram sends/edits/documents are routed through a rate-limit queue that honors Telegram retry-after responses.
208
- - 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.
234
+ - Mirror, notification, quiet-hour, and automatic artifact-delivery defaults are configured through channel-neutral `NORDRELAY_*` settings, with Telegram, Discord, and Slack override keys when a channel should differ.
209
235
  - 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.
210
236
  - Unified Jobs are persisted across restarts and retain recent prompt, queue, update, connector-update, and support-bundle history for WebUI inspection.
211
- - The WebUI Metrics page reports queue state, active/completed/failed turns, job counts, average prompt duration, and Telegram/Discord rate-limit counters.
237
+ - The WebUI Metrics page reports queue state, active/completed/failed turns, job counts, average prompt duration, and Telegram/Discord/Slack rate-limit counters.
212
238
  - 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.
213
239
  - Context metadata, queues, and preferences are written atomically with backup recovery.
214
240
  - Context metadata, queues, preferences, audit events, and locks can use JSON files or the optional SQLite state backend with `NORDRELAY_STATE_BACKEND=sqlite`.
@@ -217,7 +243,7 @@ Operations:
217
243
  - 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.
218
244
  - The WebUI has responsive header/sidebar/footer navigation, live chat streaming, session controls, queue/artifact/log/diagnostic views, and settings management.
219
245
  - The WebUI supports light and dark themes, tabbed settings groups, paginated session browsing, and chat uploads for images, documents, and audio transcription.
220
- - The WebUI exposes REST and SSE endpoints for chat streaming, sessions, settings, queue, artifacts, logs, health, diagnostics, and redacted diagnostics bundle export.
246
+ - The WebUI exposes REST and SSE endpoints for chat streaming, sessions, settings, queue, artifacts, logs, health, diagnostics, peers, adapter conformance, and redacted diagnostics bundle export.
221
247
  - The dashboard can bind to `127.0.0.1` or `0.0.0.0`; user login and session cookies are mandatory in both modes.
222
248
  - Telegram can run with long polling or an HTTP webhook via `TELEGRAM_TRANSPORT=webhook`.
223
249
  - 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.
@@ -289,6 +315,16 @@ Create the Discord bot:
289
315
  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.
290
316
  7. In guild channels, run `/register_channel` once from an admin-linked Discord account.
291
317
 
318
+ Create the Slack app:
319
+
320
+ 1. Open Slack API Apps and create a new app for your workspace.
321
+ 2. Add a bot user and copy the bot token into `SLACK_BOT_TOKEN`.
322
+ 3. Enable Socket Mode and create an app-level token with `connections:write`; copy it into `SLACK_APP_TOKEN`.
323
+ 4. Add bot scopes for messages, files, channels, groups, IMs, MPIMs, commands, and chat write access.
324
+ 5. Create the slash command configured in `SLACK_COMMAND` (default `/nordrelay`) and install the app to your workspace.
325
+ 6. Link Slack from the WebUI, with `nordrelay user link-slack`, or by creating a Slack link code and sending `/link <code>` to the app.
326
+ 7. In Slack channels, run `/register_channel` once from an admin-linked Slack account.
327
+
292
328
  Minimal private-bot `~/.nordrelay/nordrelay.env`:
293
329
 
294
330
  ```dotenv
@@ -296,6 +332,9 @@ TELEGRAM_BOT_TOKEN=123456789:replace-me
296
332
  DISCORD_ENABLED=false
297
333
  DISCORD_BOT_TOKEN=
298
334
  DISCORD_CLIENT_ID=
335
+ SLACK_ENABLED=false
336
+ SLACK_BOT_TOKEN=
337
+ SLACK_APP_TOKEN=
299
338
  NORDRELAY_CODEX_ENABLED=true
300
339
  NORDRELAY_PI_ENABLED=false
301
340
  NORDRELAY_HERMES_ENABLED=false
@@ -313,10 +352,40 @@ User and chat access management:
313
352
  - `nordrelay user create --email dev@example.com --name "Dev" --group user` creates a normal user.
314
353
  - `nordrelay user link-telegram --email you@example.com --telegram-user-id 123456789` links a Telegram account directly.
315
354
  - `nordrelay user link-discord --email you@example.com --discord-user-id 123456789012345678` links a Discord account directly.
355
+ - `nordrelay user link-slack --email you@example.com --slack-user-id U123 --slack-team-id T123` links a Slack account directly.
316
356
  - `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.
317
357
  - `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.
358
+ - `nordrelay user slack-link-code --email you@example.com` creates a short-lived Slack code that the user sends as `/link <code>` to the Slack app.
318
359
  - Telegram group chats are disabled until an admin enables them from the WebUI or runs `/register_chat` inside the group.
319
360
  - Discord guild channels are disabled until an admin enables them from the WebUI or runs `/register_channel` inside the channel.
361
+ - Slack channels are disabled until an admin enables them from the WebUI or runs `/register_channel` inside the channel.
362
+
363
+ Peer setup:
364
+
365
+ 1. On each host that should accept peer connections, set `NORDRELAY_PEER_ENABLED=true` in `~/.nordrelay/nordrelay.env`.
366
+ 2. Keep `NORDRELAY_PEER_TLS_ENABLED=true` and `NORDRELAY_PEER_REQUIRE_TLS=true` for LAN or internet use.
367
+ 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.
368
+ 4. Set `NORDRELAY_PEER_PUBLIC_URL=https://host.example:31979` when other hosts cannot reach the bind address directly.
369
+ 5. Restart NordRelay on the accepting host and create an invitation:
370
+
371
+ ```bash
372
+ 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
373
+ ```
374
+
375
+ 6. On the controlling host, run the printed command:
376
+
377
+ ```bash
378
+ nordrelay peer add https://workstation.example:31979 --code one-time-code
379
+ ```
380
+
381
+ 7. Confirm the connection:
382
+
383
+ ```bash
384
+ nordrelay peer list
385
+ nordrelay peer test <peer-id>
386
+ ```
387
+
388
+ 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, Discord, or Slack to inspect paired nodes and `/target <peer-id>` or `/target local` to choose where subsequent prompts run.
320
389
 
321
390
  Codex authentication:
322
391
 
@@ -403,6 +472,9 @@ nordrelay restart
403
472
  nordrelay stop
404
473
  nordrelay foreground
405
474
  nordrelay web
475
+ nordrelay peer list
476
+ nordrelay peer invite
477
+ nordrelay peer add https://peer.example:31979 --code one-time-code
406
478
  ```
407
479
 
408
480
  Source checkout process commands:
@@ -417,6 +489,7 @@ node plugins/nordrelay/scripts/nordrelay.mjs foreground
417
489
  node plugins/nordrelay/scripts/nordrelay.mjs user list
418
490
  node plugins/nordrelay/scripts/nordrelay.mjs doctor
419
491
  node plugins/nordrelay/scripts/nordrelay.mjs web
492
+ node plugins/nordrelay/scripts/nordrelay.mjs peer list
420
493
  ```
421
494
 
422
495
  NPM shortcuts:
@@ -472,6 +545,7 @@ The dashboard is a second NordRelay client next to Telegram. It can:
472
545
  - Inspect a per-agent capability matrix showing model, reasoning, launch, fast mode, attachments, activity, usage, auth, login/logout, and handback support.
473
546
  - 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.
474
547
  - Build dashboard CSS and client JavaScript from modular source assets through esbuild, then serve them as authenticated static assets instead of inline HTML.
548
+ - Pair, test, enable/disable, and revoke NordRelay peers, then switch the dashboard target between the local instance and paired remote instances.
475
549
 
476
550
  Dashboard API endpoints are served under `/api/*`. Streaming uses `GET /api/events`.
477
551
 
@@ -504,6 +578,8 @@ Run NordRelay behind your reverse proxy so the public URL forwards to `http://12
504
578
  - `/channels` shows available and planned messaging adapters.
505
579
  - `/agents` shows available and planned coding-agent adapters.
506
580
  - `/agent` selects the active agent for this Telegram context.
581
+ - `/peers` shows configured NordRelay peer instances.
582
+ - `/target local|<peer-id>` selects whether prompts for this chat run locally or on a paired peer.
507
583
  - `/link <code>` links the Telegram account to a NordRelay user.
508
584
  - `/whoami` shows the linked NordRelay user, groups, and permissions.
509
585
  - `/register_chat` enables the current Telegram group or forum chat for NordRelay when the linked user has user-management permission.
@@ -575,10 +651,24 @@ Discord supports slash commands and `/command` text messages for the shared comm
575
651
  - `/prompt <text>` is available for slash-command-only deployments where regular message content is disabled.
576
652
  - `/link <code>` consumes Discord link codes created in the WebUI or with `nordrelay user discord-link-code`.
577
653
  - `/queue`, `/sessions`, `/agent`, `/model`, `/reasoning`, `/launch`, `/artifacts`, `/update`, and `/stop` use Discord buttons where component limits allow.
654
+ - `/peers` and `/target local|<peer-id>` use the same paired-instance target selection as Telegram.
578
655
  - `/artifacts latest`, `/artifacts zip latest`, `/artifacts images`, `/artifacts docs`, `/artifacts search <text>`, and `/artifacts delete <turn-id>` are available in Discord.
579
656
  - Unsafe launch profiles require explicit confirmation with `/launch <profile-id> confirm`.
580
657
  - Discord does not support Telegram reactions or Telegram webhook transport; typing, message edits, attachments, files, DMs, guild channels, and threads are supported.
581
658
 
659
+ ## Slack Commands
660
+
661
+ Slack supports the configured slash command and `/command` text messages for the shared command set. The primary differences from Telegram are:
662
+
663
+ - `/register_channel` enables the current Slack channel or thread for NordRelay when the linked user has user-management permission.
664
+ - `/prompt <text>` is available through the configured slash command when regular message content is disabled.
665
+ - `/link <code>` consumes Slack link codes created in the WebUI or with `nordrelay user slack-link-code`.
666
+ - `/queue`, `/sessions`, `/agent`, `/model`, `/reasoning`, `/launch`, `/artifacts`, `/update`, and `/stop` use Slack buttons where Block Kit limits allow.
667
+ - `/peers` and `/target local|<peer-id>` use the same paired-instance target selection as Telegram and Discord.
668
+ - `/artifacts latest`, `/artifacts zip latest`, `/artifacts images`, `/artifacts docs`, `/artifacts search <text>`, and `/artifacts delete <turn-id>` are available in Slack.
669
+ - Unsafe launch profiles require explicit confirmation with `/launch <profile-id> confirm`.
670
+ - Slack does not support Telegram reactions or Telegram webhook transport; typing/status, message edits, attachments, files, DMs, channels, and threads are supported.
671
+
582
672
  ## Command Examples
583
673
 
584
674
  Switching to an existing thread:
@@ -760,7 +850,7 @@ Voice transcription uses `OPENAI_API_KEY`, not `CODEX_API_KEY`.
760
850
  Telegram:
761
851
 
762
852
  - `TELEGRAM_ENABLED`: starts the Telegram adapter. Defaults to `true`.
763
- - `TELEGRAM_BOT_TOKEN`: required BotFather token.
853
+ - `TELEGRAM_BOT_TOKEN`: BotFather token. Required for the Telegram adapter to start.
764
854
  - `TELEGRAM_RATE_LIMIT_MIN_INTERVAL_MS`: minimum interval for normal Telegram API sends. Defaults to `80`.
765
855
  - `TELEGRAM_EDIT_MIN_INTERVAL_MS`: minimum interval for Telegram message edits. Defaults to `1200`.
766
856
  - `TELEGRAM_TRANSPORT`: `polling` or `webhook`. Defaults to `polling`.
@@ -780,7 +870,7 @@ Telegram:
780
870
  Discord:
781
871
 
782
872
  - `DISCORD_ENABLED`: starts the Discord adapter. Defaults to `false`.
783
- - `DISCORD_BOT_TOKEN`: required Discord bot token when Discord is enabled.
873
+ - `DISCORD_BOT_TOKEN`: Discord bot token. Required for the Discord adapter to start.
784
874
  - `DISCORD_CLIENT_ID`: Discord application/client id used for slash-command registration.
785
875
  - `DISCORD_GUILD_IDS`: optional comma-separated guild ids for instant guild slash-command registration.
786
876
  - `DISCORD_ALLOWED_GUILD_IDS`: optional guild allow-list before user/group permissions are checked.
@@ -790,13 +880,39 @@ Discord:
790
880
  - `DISCORD_AUTO_REGISTER_COMMANDS`: registers slash commands on startup when `DISCORD_CLIENT_ID` is set. Defaults to `true`.
791
881
  - `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.
792
882
 
883
+ Slack:
884
+
885
+ - `SLACK_ENABLED`: starts the Slack adapter. Defaults to `false`.
886
+ - `SLACK_BOT_TOKEN`: Slack bot token. Required for the Slack adapter to start.
887
+ - `SLACK_APP_TOKEN`: Slack app-level token for Socket Mode. Required when `SLACK_SOCKET_MODE=true`.
888
+ - `SLACK_SIGNING_SECRET`: Slack signing secret for HTTP Events mode. Required when `SLACK_SOCKET_MODE=false`.
889
+ - `SLACK_SOCKET_MODE`: uses Slack Socket Mode instead of an HTTP Events receiver. Defaults to `true`.
890
+ - `SLACK_PORT`: HTTP receiver port when Socket Mode is disabled. Defaults to `3000`.
891
+ - `SLACK_ALLOWED_TEAM_IDS`: optional Slack workspace allow-list before user/group permissions are checked.
892
+ - `SLACK_ALLOWED_CHANNEL_IDS`: optional channel allow-list before user/group permissions are checked.
893
+ - `SLACK_MESSAGE_CONTENT_ENABLED`: reads regular Slack text messages as prompts. Defaults to `true`.
894
+ - `SLACK_COMMAND`: slash command configured in Slack. Defaults to `/nordrelay`.
895
+ - `SLACK_CLI_MIRROR_MODE`, `SLACK_CLI_MIRROR_MIN_UPDATE_MS`, `SLACK_NOTIFY_MODE`, `SLACK_QUIET_HOURS`, and `SLACK_AUTO_SEND_ARTIFACTS`: optional Slack-specific overrides for the channel-neutral defaults.
896
+
793
897
  User management:
794
898
 
795
- - Users, groups, Telegram identities, Telegram group-chat access, Discord identities, Discord channel access, and web sessions are stored in `~/.nordrelay/users.json`.
796
- - 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`.
899
+ - Users, groups, Telegram identities, Telegram group-chat access, Discord identities, Discord channel access, Slack identities, Slack channel access, and web sessions are stored in `~/.nordrelay/users.json`.
900
+ - Manage users in the WebUI Users page or with `nordrelay user list`, `create-admin`, `create`, `reset-password`, `link-telegram`, `link-discord`, `link-slack`, `link-code`, `discord-link-code`, and `slack-link-code`.
797
901
  - Built-in groups are `admin`, `user`, and `readonly`.
798
- - 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`.
799
- - Custom groups can also restrict access to specific agent ids, workspace roots, Telegram chat ids, and Discord channel ids.
902
+ - 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`.
903
+ - Custom groups can also restrict access to specific agent ids, workspace roots, Telegram chat ids, Discord channel ids, and Slack channel ids.
904
+
905
+ Peers:
906
+
907
+ - `NORDRELAY_PEER_ENABLED`: starts the dedicated peer API. Defaults to `false`.
908
+ - `NORDRELAY_PEER_NAME`: optional human-readable node name shown to paired instances.
909
+ - `NORDRELAY_PEER_HOST`: peer API bind host. Defaults to `127.0.0.1`.
910
+ - `NORDRELAY_PEER_PORT`: peer API port. Defaults to `31979`.
911
+ - `NORDRELAY_PEER_PUBLIC_URL`: optional URL other instances should use to reach this node.
912
+ - `NORDRELAY_PEER_TLS_ENABLED`: serves the peer API over HTTPS with an automatically generated local certificate. Defaults to `true`.
913
+ - `NORDRELAY_PEER_REQUIRE_TLS`: refuses plaintext peer serving on non-loopback hosts. Defaults to `true`.
914
+ - Peer identity, TLS certificate, peers, and invitations are stored under `~/.nordrelay/identity.json`, `~/.nordrelay/tls/`, and `~/.nordrelay/peers.json`.
915
+ - Peer invitations expire after at most 24 hours even if a longer lifetime is requested.
800
916
 
801
917
  Agent selection:
802
918
 
@@ -824,8 +940,8 @@ Codex:
824
940
  - `CODEX_CLI_PATH`: optional explicit path to the Codex CLI executable.
825
941
  - `CODEX_USE_BUNDLED_CLI`: set `true` to force the SDK-bundled Codex CLI instead of the host `codex` executable.
826
942
  - `CODEX_MODEL`: default model for new threads.
827
- - `CODEX_SYNC_INTERVAL_MS`: periodic local Codex-state sync interval for active Telegram sessions. Defaults to `10000`; set `0` to disable.
828
- - `CODEX_EXTERNAL_BUSY_CHECK_MS`: how often queued Telegram prompts re-check an active local Codex CLI task. Defaults to `5000`.
943
+ - `CODEX_SYNC_INTERVAL_MS`: periodic local Codex-state sync interval for active chat sessions. Defaults to `10000`; set `0` to disable.
944
+ - `CODEX_EXTERNAL_BUSY_CHECK_MS`: how often queued chat prompts re-check an active local Codex CLI task. Defaults to `5000`.
829
945
  - `CODEX_EXTERNAL_BUSY_STALE_MS`: maximum age for an unclosed rollout task before it is treated as stale instead of active. Defaults to `300000`.
830
946
  - `CODEX_SANDBOX_MODE`: default sandbox mode, one of `read-only`, `workspace-write`, `danger-full-access`.
831
947
  - `CODEX_APPROVAL_POLICY`: default approval policy, one of `never`, `on-request`, `on-failure`, `untrusted`.
@@ -948,7 +1064,7 @@ ENABLE_UNSAFE_LAUNCH_PROFILES=true
948
1064
  CODEX_LAUNCH_PROFILES_JSON=[{"id":"host-full","label":"Host Full Access","sandboxMode":"danger-full-access","approvalPolicy":"never"}]
949
1065
  ```
950
1066
 
951
- Unsafe profiles are intentionally gated. Telegram asks for confirmation before applying them.
1067
+ Unsafe profiles are intentionally gated. Chat adapters ask for confirmation before applying them.
952
1068
 
953
1069
  ## Security Notes
954
1070
 
@@ -956,12 +1072,14 @@ Unsafe profiles are intentionally gated. Telegram asks for confirmation before a
956
1072
  - Link Telegram accounts only to active NordRelay users that should control agents remotely.
957
1073
  - Enable Telegram group/forum chats only when the whole chat context is trusted for the permissions granted to linked users.
958
1074
  - Review group permissions before granting `prompt.send`, `prompt.abort`, `files.write`, `settings.write`, `updates.run`, `system.restart`, or `users.write`.
1075
+ - Review peer scopes before granting `peers.write`, `peers.connect`, broad `prompt.send`, or unrestricted workspace roots to a paired instance.
959
1076
  - Treat `danger-full-access` as equivalent to shell access on the host.
960
1077
  - Treat uploaded files as untrusted input. They are staged inside the active workspace so the selected sandbox policy still matters.
961
1078
  - 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.
962
1079
  - In group chats, remember that any linked user with prompt permissions can prompt the selected agent in that chat context.
963
1080
  - Use `TOOL_VERBOSITY=summary` or `errors-only` when command output may include sensitive data.
964
1081
  - Review and unsafe launch profiles add a Telegram approve/deny gate before each turn starts.
1082
+ - 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>`.
965
1083
 
966
1084
  ## Troubleshooting
967
1085
 
@@ -1118,7 +1236,10 @@ npm run build
1118
1236
  - `src/index.ts`: runtime entrypoint, config load, auth check, state-file writes, polling lifecycle, shutdown.
1119
1237
  - `src/bot.ts`: Telegram prompt/session runtime, streaming, file/photo/voice handling, artifacts, and error handling.
1120
1238
  - `src/telegram-general-commands.ts`, `src/telegram-agent-commands.ts`, `src/telegram-preference-commands.ts`, `src/telegram-access-commands.ts`, `src/telegram-diagnostics-command.ts`, `src/telegram-update-commands.ts`, `src/telegram-support-command.ts`, and `src/telegram-command-menu.ts`: focused Telegram command groups for start/help/adapters, agent/auth controls, per-chat preferences, access linking, diagnostics/log/version commands, update jobs, diagnostics bundle export, and command menu registration.
1121
- - `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.
1239
+ - `src/channel-adapter.ts`, `src/channel-runtime.ts`, `src/channel-command-core.ts`, `src/channel-actions.ts`, and `src/adapter-conformance.ts`: channel descriptors, shared command dispatch/coverage, outbound delivery contracts, channel-neutral responses, and generated feature/command conformance matrices.
1240
+ - `src/discord-bot.ts` and `src/slack-bot.ts`: Discord and Slack bridge runtimes built on the shared channel command core, channel runtimes, rate limiters, access checks, streaming replies, attachments, mirrors, and queue controls.
1241
+ - `src/slack-diagnostics.ts`: Slack readiness probes for token/transport config, auth, registered channel visibility, file-upload readiness, and rate-limit reporting.
1242
+ - `src/user-management.ts`, `src/user-management-types.ts`, `src/user-management-normalize.ts`, and `src/user-management-crypto.ts`: user/group/session/channel-access store with separated DTOs, payload normalization, password/token helpers, and public snapshots.
1122
1243
  - `src/config-metadata.ts`: shared setting metadata used by the WebUI settings page and generated `.env.example`.
1123
1244
  - `src/support-bundle.ts` and `src/zip-writer.ts`: redacted diagnostics bundle creation with a dependency-free ZIP writer.
1124
1245
  - `src/relay-queue-service.ts`, `src/relay-artifact-service.ts`, and `src/relay-external-activity-monitor.ts`: Web runtime queue operations, artifact preview/export/persistence, and external CLI activity mirroring.
@@ -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"],
@@ -148,7 +153,7 @@ export function permissionForCallbackData(callbackData) {
148
153
  if (callbackData.startsWith("approval_") || callbackData.startsWith("codex_abort:") || callbackData.startsWith("agent_abort:")) {
149
154
  return "prompt.abort";
150
155
  }
151
- if (callbackData.startsWith("queue_")) {
156
+ if (callbackData.startsWith("queue_") || callbackData.startsWith("peer_queue_")) {
152
157
  return "queue.write";
153
158
  }
154
159
  if (callbackData.startsWith("artifact_delete")) {
@@ -11,7 +11,7 @@ export function activityCategoryForType(type) {
11
11
  return "artifact";
12
12
  if (/^(auth|login|logout)/.test(type))
13
13
  return "auth";
14
- if (/^(user_|group_|telegram_chat_|telegram_link|discord_channel_|discord_link|permission_|access_|lock_)/.test(type))
14
+ if (/^(user_|group_|telegram_chat_|telegram_link|discord_channel_|discord_link|slack_channel_|slack_link|peer_|permission_|access_|lock_)/.test(type))
15
15
  return "security";
16
16
  if (/^(tool_|cli_tool)/.test(type))
17
17
  return "tool";
@@ -32,7 +32,7 @@ export function auditCategoryForAction(action) {
32
32
  return "security";
33
33
  if (/^auth_/.test(action))
34
34
  return "auth";
35
- if (/^(permission_|user_|group_|telegram_|discord_)/.test(action))
35
+ if (/^(permission_|user_|group_|telegram_|discord_|slack_|peer_)/.test(action))
36
36
  return "security";
37
37
  if (/^(artifact|file)/.test(action))
38
38
  return "artifact";