CodeRelay is a local-first bridge for controlling Codex from messaging channels first, and over time from a mobile web console.
It is designed for workflows where Codex should keep running on your machine while approved actions, thread control, and status updates can be reached from external clients such as Discord, Telegram, or Feishu.
CodeRelay is an early-stage open-source project.
The current focus is:
- a shared bridge core for sessions, approvals, and event flow
- Codex integration through
codex app-server - IM-based control through channel adapters
The web control surface is a later expansion on top of the same runtime.
- Local-first execution: Codex stays on the machine you control
- Clear adapter boundaries: one bridge core, multiple channel integrations
- Security by default: secrets and runtime state stay out of the repository
- Thread-oriented workflows: conversations can be resumed and handed off across devices
Today the repository includes:
- a shared bridge runtime for sessions, approvals, event publishing, and workspace bindings
- a Codex adapter layer built around
codex app-server - channel adapters for Discord, Telegram, and Feishu
- an IM bridge server application that composes the runtime and adapters
Telegram / Discord / Feishu / Web
|
Channel Adapters
|
Bridge Core
- session registry
- approval broker
- event bus
- workspace bindings
|
Codex Adapter
|
codex app-server
apps/
im-bridge-server/ # Bootstrap app for the IM bridge daemon
packages/
shared/ # Shared types and contracts
bridge-core/ # Session, approvals, event bus
codex-adapter/ # Codex integration layer
channel-telegram/ # Telegram adapter
channel-discord/ # Discord adapter
channel-feishu/ # Feishu adapter
- macOS, with the Codex desktop app installed
- Node.js
20+ npm- a Discord server for testing
- a Discord application with a bot user
- Clone the repository and install dependencies.
git clone <your-repo-url>
cd CodeRelay
npm install- Create a local environment file.
cp apps/im-bridge-server/.env.example apps/im-bridge-server/.env.local-
Fill in the required values in
apps/im-bridge-server/.env.local. -
Start the bridge server.
npm run dev- In Discord, test the bot with:
/coderelay help/coderelay ask prompt:hello@CodexBot helloifDISCORD_MESSAGE_MODEis enabled/coderelay-doctor
In the Discord Developer Portal:
- Create a new application
- Open the
Botsection - Add a bot user
- Copy or regenerate the bot token
- If you want ordinary
@CodexBot ...or reply-to-bot prompts, enableMESSAGE CONTENT INTENT
Use that value as:
DISCORD_BOT_TOKEN=your-bot-tokenInstall the bot into your Discord server with these scopes:
botapplications.commands
Recommended permissions:
View ChannelsSend MessagesUse Slash CommandsRead Message History
Enable Discord Developer Mode, then:
- right-click the server and copy
Server ID - right-click the channel and copy
Channel ID
Use those values as:
DISCORD_GUILD_ID=your-server-id
DISCORD_ALLOWED_CHANNEL_IDS=your-channel-idIf you want to allow more than one channel:
DISCORD_ALLOWED_CHANNEL_IDS=channel-id-1,channel-id-2The quickest way to configure the project is to start from:
cp apps/im-bridge-server/.env.example apps/im-bridge-server/.env.localFor a Discord-first setup, these values are the important ones:
CODERELAY_BOT_NAME=CodeRelay
CODERELAY_PORT=4317
CODERELAY_CODEX_COMMAND=/Applications/Codex.app/Contents/Resources/codex
CODERELAY_DEFAULT_WORKSPACE=/absolute/path/to/your/workspace
CODERELAY_CODEX_MODE=app-server
CODERELAY_CODEX_APPROVAL_POLICY=on-request
TELEGRAM_ENABLED=false
DISCORD_ENABLED=true
DISCORD_BOT_TOKEN=your-bot-token
DISCORD_GUILD_ID=your-server-id
DISCORD_ALLOWED_CHANNEL_IDS=your-channel-id
DISCORD_MESSAGE_MODE=off
DISCORD_UI_LANGUAGE=en-USNotes:
CODERELAY_DEFAULT_WORKSPACEmust be an absolute path to the workspace you want Codex to operate in- on macOS, the default Codex binary path is usually
/Applications/Codex.app/Contents/Resources/codex - on Windows, prefer pointing
CODERELAY_CODEX_COMMANDto the runnable Codex CLI copy under%USERPROFILE%\\.codex\\.sandbox-bin\\codex.exe, for exampleC:\\Users\\<you>\\.codex\\.sandbox-bin\\codex.exe - on Windows, avoid
C:\\Program Files\\WindowsApps\\...\\codex.exeif it fails withEPERMorAccess is deniedwhen CodeRelay tries to spawncodex app-server - for most local single-user setups,
CODERELAY_CODEX_APPROVAL_POLICY=on-requestis the best balance between safety and fewer interruptions DISCORD_MESSAGE_MODE=offkeeps Discord in slash-command-only modeDISCORD_MESSAGE_MODE=mentionallows prompts only when you explicitly mention@CodexBotDISCORD_MESSAGE_MODE=mention-or-replyalso allows prompts when you reply to a bot message in an allowed channel- set
DISCORD_UI_LANGUAGE=zh-CNif you want Discord command descriptions and fixed system replies in Chinese - if port
4317is already in use, choose another value such as4318
Start the bridge with:
npm run devOn a healthy startup, you should see logs indicating:
- the IM bridge server is ready
- Discord is enabled
- Codex mode is
app-server - persisted session bindings were restored, if any were found
- the bot logged in successfully
- slash commands were registered for your guild
- CodeRelay now binds the local HTTP port before starting channel adapters. If the configured port is already in use, the process exits before a second Discord bot instance can start handling interactions.
- Discord login and slash command registration automatically retry a few transient network failures such as
ECONNRESET. - The bridge runtime keeps lightweight per-session progress metadata so future clients can surface recent agent output and approval-wait state more cleanly.
If Discord shows Unknown interaction or Interaction has already been acknowledged, the most common cause is that more than one local CodeRelay process is connected to the same bot at the same time.
Recommended checks:
- keep only one local CodeRelay instance running per Discord bot
- if the configured port is already in use, stop the old process before starting a new one
- on macOS, you can inspect the listening process with
lsof -nP -iTCP:4318 -sTCP:LISTENand stop it withkill <PID>
/coderelay help/coderelay ask prompt:<text> [attachment]@CodexBot <text>whenDISCORD_MESSAGE_MODEis enabled/coderelay continue/coderelay current/coderelay handoff/coderelay health/coderelay-doctor/coderelay approvals/coderelay approve [id]/coderelay deny [id]/coderelay progress/coderelay policy/coderelay set-policy policy:<mode>/coderelay projects [limit]/coderelay recent [limit]/coderelay threads [limit]/coderelay new [title]/coderelay rename title:<text>/coderelay resume id:<query>/coderelay hide id:<query>/coderelay archive id:<query>/coderelay unhide id:<query>/coderelay pin target:<query>/coderelay unpin target:<query>/coderelay use-project query:<name-or-path>/coderelay interrupt
CodeRelay does not create a new thread for every Discord message.
The current behavior is:
- one Discord session is keyed by
channelId + userId - repeated
/coderelay askcalls continue on the same current thread - a new thread is created only when:
- the session has not talked to Codex before
/coderelay newis used/coderelay resumeis used to switch to another thread
The current session-to-thread binding is persisted under .coderelay/session-bindings.json in the configured workspace, so the same Discord session can continue on the same thread after a server restart. Startup logs now report how many bindings were restored from that file. If a restored binding points to a thread that no longer exists in the local Codex app-server, CodeRelay now reports that mismatch explicitly instead of silently creating a replacement thread.
Use /coderelay recent to view recent user and Codex messages from the current thread directly in Discord.
Use /coderelay health to check the Discord channel, current workspace, current thread, message mode, approval policy, pending approvals, and whether Codex thread listing is reachable.
Use /coderelay-doctor for a deeper diagnosis view that adds mention/reply intake readiness, current channel permission checks, Codex executable discovery, app-server reachability, default workspace status, bridge-side approval smoothing mode, the current session binding state, binding reachability on this machine, restore source, last restore time, and the latest pending approval summary.
Use /coderelay progress to view the current thread status, active turn start time, last tracked bridge event, latest pending approval summary, last user message, last agent output time, latest tracked agent output snippet, and approval wait state.
Use /coderelay resume id:<query> to resume a thread by list index, thread ID or prefix, title keyword, or workspace keyword. Discord also offers autocomplete suggestions while you type the id field.
Use /coderelay hide id:<query> or /coderelay archive id:<query> to remove old threads from /coderelay threads and /coderelay resume candidates. Use /coderelay unhide id:<query> to show a hidden thread again.
Use /coderelay pin target:<query> to pin a frequently used thread or project. Use /coderelay unpin target:<query> to remove that preference. Thread and project preferences are stored locally under .coderelay/thread-preferences.json.
If DISCORD_MESSAGE_MODE=mention, CodeRelay also accepts prompts from ordinary channel messages that mention @CodexBot. If DISCORD_MESSAGE_MODE=mention-or-reply, replying to a bot message in an allowed channel also sends a prompt. Other channel messages are ignored. In these ordinary-message flows, Discord attachments are downloaded into .coderelay/inbox/... inside the active workspace before the prompt is forwarded. Images are passed to Codex as local image inputs, while ordinary files are passed as local file paths in the prompt text.
Use /coderelay continue after interruptions or long pauses to ask Codex to report the current status and continue the current thread. Use /coderelay rename title:<text> to rename the current thread for easier thread lists and resume searches.
Use /coderelay approvals to list pending approvals. If a button is no longer convenient, use /coderelay approve [id] or /coderelay deny [id]. When id is omitted, CodeRelay applies the latest pending request for the current Discord session. If you do provide id, it can be an approval list index or approval ID prefix.
When CODERELAY_CODEX_APPROVAL_POLICY=on-request, the bridge now auto-approves a conservative set of low-risk read-only actions such as common rg, find, git status, git diff, npm test, and local read permission requests. More sensitive commands, file writes, network access, and broad permission escalations still require approval.
Recent thread progress is now persisted locally under .coderelay/session-progress.json, so /coderelay progress, /coderelay health, and /coderelay continue recover more context after a restart. If no cached progress exists yet, CodeRelay falls back to recent thread history to rebuild the latest user/assistant summary.
Long Discord answers are now split into multiple messages instead of being hard-truncated, and CodeRelay tries to keep fenced code blocks valid across chunk boundaries.
Use /coderelay projects to list project/workspace paths discovered from recent threads, and /coderelay use-project to switch the current Discord session to another project. Switching projects clears the current thread binding for that session so the next /coderelay ask starts in the selected workspace.
Use /coderelay policy to inspect the current approval mode and /coderelay set-policy to reduce or increase approval prompts at runtime. The runtime change applies to future turns, while the env value remains the startup default.
CodeRelay is intended to be run as a local service, not as a shared public relay.
Repository rules:
- keep secrets in local env files only
- commit example configuration only
- do not commit tokens, logs, pairing state, or machine-specific runtime data
MIT. See LICENSE.