This is the canonical first-run path for a brand-new user.
Goal: connect Telegram to your local OpenAI Codex Desktop session so you can keep talking to that session from anywhere.
"Codex Desktop" here means OpenAI's coding agent app — not the deprecated 2021 OpenAI Codex model. If you do not have it installed, get it before continuing.
Start with the base bridge. It is the main supported path. Live /call remains a second-stage feature.
For the base bridge, the only required secret is TELEGRAM_BOT_TOKEN.
- Node 22+
- OpenAI Codex Desktop installed locally
- a Telegram account
- the repo cloned locally and opened in Codex Desktop
tmuxonly if you want the optional terminal lane
flowchart TD
REPO["Clone repo"] --> CODEX["Open repo in Codex Desktop"]
CODEX --> SECRETS["Add TELEGRAM_BOT_TOKEN to .env"]
SECRETS --> DISCOVER["Configure bot and discover authorized chat id"]
DISCOVER --> CLAIM["Run bridge:claim from the intended Codex Desktop session"]
CLAIM --> START["Start telegram-daemon"]
START --> READY["Run bridge:capabilities"]
READY --> TALK["Talk to that Codex session from Telegram"]
If you want Codex Desktop to drive the setup with you, say:
Help me set up the base Telegram bridge in this repo. Check what already exists and tell me the next step without asking me to paste secrets into chat.
git clone https://github.com/jvogan/telegram-codex-bridge.git
cd telegram-codex-bridgeIf you have a fork, substitute its URL. Open that folder in Codex Desktop before you continue — Telegram should inherit the specific Codex Desktop session that is looking at this workspace.
Create a bot with BotFather, then keep the bot token ready for .env.
Install dependencies first:
npm ciThen copy the starter files:
cp bridge.config.example.toml bridge.config.toml
cp .env.example .envPut the bot token in .env:
TELEGRAM_BOT_TOKEN=...Do not paste secrets into chat. Keep them in .env or .env.local.
Leave the other keys blank until you decide to enable optional media providers or live /call.
npm run telegram:configureThis pushes the public bot name, description, and slash-command metadata to Telegram.
- Send
/startto the bot from Telegram. - Run:
npm run telegram:discoverBy default this prints exact private-chat IDs only. Re-run with --verbose if you need redacted webhook-host detail or private-chat labels while troubleshooting setup.
- Copy the matching private-chat ID into:
[telegram]
authorized_chat_id = "..."In bridge.config.toml:
- set
codex.workdirto the repo or workspace you want Telegram and Codex to work in - keep
bridge.mode = "shared-thread-resume"unless you deliberately want a bridge-owned thread - leave
bridge.codex_binary = ""first unless Codex auto-detection fails
Codex binary resolution order is:
bridge.codex_binaryCODEX_BINARYcodexonPATH- known platform-specific defaults such as the macOS Codex app bundle
From the exact Codex Desktop session you want Telegram to inherit:
npm run bridge:claimnpm run bridge:connect is an equivalent current-session claim flow.
npm run start:telegram
npm run bridge:capabilitiesThe base bridge is ready when the capability report shows all of these clearly:
TELEGRAM_BOT_TOKEN: presentAuthorized chat: ...Telegram daemon: runningDesktop thread binding: readyforshared-thread-resume
If daemon startup says Codex Desktop could not be found automatically, set bridge.codex_binary, export CODEX_BINARY, or make codex available on PATH, then start the daemon again.
Once the readiness report looks right, send a normal Telegram message to the bot. That message should now continue the bound Codex Desktop thread.
If the report does not look right, check these next:
npm run bridge:ctl -- status.bridge-data/telegram-daemon.log- troubleshooting.md
Add these only if you want them:
OPENAI_API_KEY- OpenAI ASR
- OpenAI image generation
- live
/call - create one at the OpenAI API keys page
ELEVENLABS_API_KEY- TTS fallback or override
GOOGLE_GENAI_API_KEY- image fallback or override
Live /call also requires:
REALTIME_CONTROL_SECRET[realtime].enabled = truenpm run start:gatewaynpm run bridge:ctl -- call arm
Treat /call as experimental. Only enable it after the base bridge is already working end to end.
Full guide: calling-openai-realtime.md
The optional fallback lane gives the bot safe extra capacity while the bound desktop turn is busy. It is disabled by default and is not for arbitrary repo edits:
[bridge.fallback_lane]
enabled = true
routing = "when_desktop_busy_safe"
allow_workspace_writes = falseUse /fallback status to inspect it, /fallback enable to turn it on for this bridge state, and /fallback reset if the fallback thread needs to start fresh.
The optional terminal lane is also experimental. It is disabled by default and starts as a bridge-owned read-only tmux Codex worker for explicit /terminal work:
[terminal_lane]
enabled = true
backend = "tmux"
profile = "public-safe"
sandbox = "read-only"
approval_policy = "never"
model = "gpt-5.5"
reasoning_effort = "low"
daemon_owned = truenpm run bridge:capabilities
npm run bridge:ctl -- terminal init
tmux attach -t telegram-codex-bridge-terminal
npm run bridge:ctl -- terminal statusIt does not route normal Telegram messages until the user explicitly sends /terminal chat on. In chat mode, normal text/document and safe web-research work can use the terminal lane, while image generation, voice/ASR/TTS, live calls, and desktop-control requests stay on the primary bridge path. bridge:capabilities reports the config gates; bridge:ctl -- terminal status and /terminal status inspect the live terminal lane.
If you want stronger terminal powers, ask Codex to unlock terminal superpowers in this repo or run:
npm run bridge:ctl -- terminal unlock-superpowersThat prints the config gates for workspace-write tmux or user-owned iTerm2/Terminal.app adoption.
If you are unsure whether you need an extra key, check faq.md.
Open setup-with-codex.md. This repo includes explicit setup instructions for Codex in AGENTS.md.