Production-oriented TypeScript/Node service that exposes Codex task management through a Telegram bot. It accepts commands from authorized Telegram users, persists task state in Postgres, runs codex exec --json behind a CodexRunner abstraction, and supports both webhook mode for deployment and polling mode for local development.
- Authorized-user-only Telegram access
- Secure workspace alias allowlist, never raw paths from chat
- Persistent async task queue with Postgres
- Resume-style workspace chat discovery from the Codex state SQLite database
- Task lifecycle states:
queued,running,waiting_for_approval,completed,failed,canceled - Webhook-first HTTP service with polling mode for local development
- Structured
pinologging with message redaction - In-memory Telegram rate limiting plus persistent audit trail
- Diff capture and diff summary after successful runs
- Dockerfile, Compose, tests, ESLint, Prettier, Vitest
CodexRunnerinterface ready for a future Codex SDK implementation
src/
app/ composition root
bot/ Telegram update parsing, command handling, outbound client
codex/ runner interface + codex CLI implementation
config/ env parsing + workspace alias policy
core/ task orchestration + background queue
db/ Postgres schema and repository layer
logging/ pino logger factory
security/ auth, rate limiting, redaction
server/ Fastify webhook server + polling loop
types/ domain model types
utils/ shared helpers
tests/ unit/integration-style tests
/start/help/ask <workspace> <prompt>/chats/chats <workspace>/status <task_id>/tasks/logs <task_id>/result <task_id>/diff <task_id>/resume <task_id>/cancel <task_id>/health
Example:
/ask repo Summarize the current branch changes and propose next steps
Copy .env.example to .env and set values for your environment.
Important variables:
TELEGRAM_BOT_TOKEN: Telegram bot tokenAUTHORIZED_USER_IDS: comma-separated Telegram numeric user IDsWORKSPACE_ALIASES: JSON object mapping alias to absolute workspace pathCODEX_STATE_DB_PATH: path to the Codex state SQLite database used for/chatsBOT_MODE:webhookin production,pollingfor local developmentWEBHOOK_URL: public HTTPS webhook endpoint in webhook modeWEBHOOK_SECRET: shared secret validated on the webhook routeDB_HOST,DB_PORT,DB_NAME,DB_USER,DB_PASSWORD: Postgres connection settingsDATABASE_URL: optional full Postgres connection string overrideCODEX_BINARY: Codex CLI executableCODEX_ARGS: defaultexec --json
Workspace example:
WORKSPACE_ALIASES={"repo":"/workspaces/repo","infra":"/workspaces/infra"}Only these aliases are accepted from Telegram. Raw paths are never accepted.
/chats does not read from the bot Postgres task tables. It reads Codex thread history from CODEX_STATE_DB_PATH and filters by the resolved workspace cwd, which makes it behave more like codex resume.
Postgres example:
DB_HOST=127.0.0.1
DB_PORT=5432
DB_NAME=telegram_agent
DB_USER=telegram_agent
DB_PASSWORD=replace_me
DB_SSL=falsenpm install
cp .env.example .env
npm run build
npm test
npm run devUse polling mode locally:
BOT_MODE=pollingWebhook mode is the default production target. Put the service behind HTTPS and configure Telegram to reach:
POST /telegram/webhook
Start with Docker Compose:
cp .env.example .env
docker compose up --build -dRecommended production setup:
- Run behind an HTTPS reverse proxy
- Mount only the allowed workspaces into the container
- Use a non-shared Telegram bot token per environment
- Put Postgres on persistent storage and back it up normally
- Restrict inbound access to
/healthif exposed publicly
- Unauthorized Telegram users receive only
Unauthorized. - Telegram requests are rate-limited per user/chat pair
- Webhook mode validates
X-Telegram-Bot-Api-Secret-Token - Task prompts, logs, and outbound messages pass through redaction
- Audit records are written for task creation, start, completion, resume, and cancellation
- Workspace access is alias-based only
- Codex execution is isolated behind
CodexRunner, which simplifies future migration and review
Each task stores:
- identifiers and Telegram ownership metadata
- workspace alias and prompt
- lifecycle timestamps
- summary, result, diff, and diff summary
- structured task logs
- audit trail entries
npm run lint
npm run build
npm testThe test suite covers:
- auth enforcement
- secret redaction
- task lifecycle transitions
- Telegram update and command handling with a fake client
- Replace the CLI-backed runner with a Codex SDK implementation behind the same interface
- Add explicit approval commands if your Codex workflow requires them
- Move from single-node Postgres queueing to a dedicated job system if you need higher throughput
- Add outbound Telegram notifications when task status changes