English | 简体中文
imtty is a control plane for a local Codex session running inside tmux.
It lets you interact with a persistent local coding agent from Telegram without turning the system into a remote shell, cloud IDE, or autonomous agent platform.
All execution happens locally. Telegram is only a control surface.
imtty provides a lightweight bridge between Telegram and locally running Codex sessions.
It is:
- a Telegram-based control surface for local Codex
- a
tmux-backed session manager - an event-driven bridge between Telegram and Codex app-server
- a single-owner workflow for staying connected to local project sessions
imtty is intentionally not:
- a remote development environment
- a cloud-based agent system
- a multi-user collaboration platform
- a general-purpose remote shell
- a file sync or long-term storage system
- a general observability dashboard
- continue persistent Codex sessions from Telegram
- approve or reject state-changing actions remotely
- send text, images, PDFs, text/code files, and zip archives as temporary context
- switch between whitelisted local project sessions
- resume recent Codex threads for a project
- inspect pending approvals, option inputs, and recent structured events
- set model, reasoning effort, and plan mode for the next real turn
- use an optional Mini App for session, project, approval, and reading workflows
- use optional local voice transcription through
ffmpegandwhisper.cpp
The system is split into three layers:
- Control plane: Telegram chat and Telegram Mini App
- Orchestration plane: the Go bridge, routing, session mapping, and event handling
- Execution plane:
tmuxand Codex running locally on the Mac
Telegram never owns execution state. It observes state, sends user intent, and authorizes transitions.
- Codex runs only inside
tmux. - Telegram never executes commands directly.
tmuxis used for persistence, not as an output API.- Terminal output is not parsed; structured Codex app-server events are the source of truth.
- All actions that mutate state require explicit approval.
- One Telegram chat binds to one active session at a time.
- Local desktop attach protection can block remote writes and
/kill.
Verified local baseline:
- macOS
- Go
1.26.1 tmux 3.6acodex-cli 0.125.0- Telegram bot token
- public HTTPS webhook URL, for example through Cloudflare Tunnel
Optional voice input:
ffmpegwhisper.cppwhisper-cli- local GGML whisper model
cp config.toml.example config.tomlMinimum required fields:
telegram_bot_token = "BOT_TOKEN"
telegram_webhook_secret = "SECRET"
[projects]
demo = "/absolute/path/to/your/project"Optional Mini App fields:
telegram_owner_id = 123456789
mini_app_base_url = "https://imtty.example.com"
[project_browse_roots]
workspace = "/absolute/path/to/your/workspace"config.toml is ignored by git. Commit config.toml.example, not your local config.
go run ./cmd/imtty-bridgeHealth check:
curl -s http://127.0.0.1:8080/healthzA named Cloudflare Tunnel is the recommended long-running setup:
cloudflared tunnel login
cloudflared tunnel create imtty
cloudflared tunnel route dns imtty imtty.example.com
cloudflared tunnel run imttyExample ~/.cloudflared/config.yml:
tunnel: imtty
credentials-file: /Users/<you>/.cloudflared/<tunnel-id>.json
ingress:
- hostname: imtty.example.com
service: http://127.0.0.1:8080
- service: http_status:404Set the Telegram webhook:
curl -X POST "https://api.telegram.org/bot${IMTTY_TELEGRAM_BOT_TOKEN}/setWebhook" \
-H 'Content-Type: application/json' \
-d '{
"url": "https://imtty.example.com/telegram/webhook",
"secret_token": "'"${IMTTY_TELEGRAM_WEBHOOK_SECRET}"'"
}'Verify:
curl -s "https://api.telegram.org/bot${IMTTY_TELEGRAM_BOT_TOKEN}/getWebhookInfo"
curl -s http://127.0.0.1:8080/healthz/projects
/open demo
hello
/pending
The Mini App is a lightweight companion UI inside Telegram for state that chat cannot represent cleanly:
- active session and project switching
- recent thread resume
- pending approval and option-input inspection
- dynamic Codex model discovery, including GPT-5.6 and model-specific reasoning levels
- separate current-session controls from allowlisted global Codex defaults
- structured replay of recent execution state
- explicit approve, reject, and control actions
It does not replace Telegram chat, terminal interaction, or Codex itself. It is a constrained state surface, not a standalone dashboard.
The frontend lives in web/mini-app/. Built assets are checked in under web/mini-app/dist/ so the Go bridge can serve them directly.
Voice input is optional and disabled by default.
When enabled, Telegram voice messages are downloaded to a local temporary directory, converted with ffmpeg, transcribed locally with whisper.cpp, and submitted to the active Codex session as plain text.
Voice data is not persisted, and voice input does not implement voice commands or voice approvals.
- Only whitelisted projects can be opened.
- Telegram webhook requests must include the configured secret token.
- Mini App requests must pass Telegram
initDatavalidation and owner checks. - Images, documents, zip archive extracts, and voice files are stored only as temporary local files.
- The bridge refuses remote writes and
/killwhen the sametmuxsession has a writable local desktop attach. - Codex permission prompts are never auto-approved by the bridge.
/list
/projects
/project_add <name> <abs-path>
/project_remove <name>
/open <project> [thread-id]
/close
/kill
/clear
/status
/pending
/last [n]
/model [model-id]
/reasoning [effort]
/plan_mode [default|plan]
Run all Go tests:
GOPATH=/tmp/imtty-go GOMODCACHE=/tmp/imtty-go/pkg/mod GOCACHE=/tmp/imtty-go-build go test ./...Build the Mini App:
cd web/mini-app
npm ci
npm run build