This repository runs one private, general-purpose Telegram bot for Codex work. It keeps its own allowlist, state, attachments, queue, and user service in this checkout. Starting or stopping this service does not start, stop, select, archive, or delete sessions owned by other bots.
- Node.js 22 or newer
- A local Codex login that the SDK can use
- A Telegram bot token from BotFather
- Linux with
systemd --userfor the background-service commands
Install dependencies and run the automated checks:
npm install
npm testRun setup in a local interactive terminal:
npm run setupEnter the BotFather token only at the hidden local prompt. Do not put it in a Telegram message, a shell argument, command history, documentation, or a Codex prompt. Setup drains old Telegram updates, asks you to send a fresh private text message, and displays the sender before it writes credentials.
Confirm that setup shows @viocity1110 and the expected numeric user ID and private chat ID. The username helps you inspect the prompt, but authorization uses the numeric user ID and private chat ID stored in the untracked .env. This guide does not record either numeric value. Type the exact lowercase word yes only after both values match.
Setup refuses to replace any existing .env, including an empty file, unless you pass --replace:
npm run setup -- --replaceReplacement requires a second exact yes. The resulting .env has mode 0600.
Telegram cannot approve a filesystem path. Add each project from the local terminal. The registry resolves the supplied path to its canonical path, accepts Git repositories by default, and stores an opaque project ID internally.
npm run project:add -- portfolio /home/harvy/my-portfolio-astro
npm run project:add -- notes /home/harvy/notes --allow-non-git
npm run project:rename -- portfolio personal-site
npm run project:remove -- personal-siteProject names are case-insensitive. A name containing spaces must arrive as one shell argument, for example "Client Site". The --allow-non-git flag grants the bot access to a non-Git directory and deserves the same review as approving a repository. You cannot remove a project while an active or archived session refers to it.
Use /projects in Telegram to see approved project names. The bot cannot add or change the local allowlist.
Use /new to choose an approved project and create a named session. /sessions or /switch selects an existing active session, and /session shows the selected session, project, thread state, and network policy. Use /rename to change its display name.
Import an existing Codex thread from the local terminal, then select it in Telegram:
npm run session:import -- --project portfolio --name "Existing portfolio" --thread <thread-id>The import command resolves the project name to its opaque ID and keeps the supplied Codex thread ID in local state. Do not paste a private thread ID into Telegram.
/archive and /close remove the selected session from the active list without deleting its thread record or attachments. /restore returns an archived session. /delete requires an archived session and confirmation, then removes its local session record and stored attachments. Archive before deleting when you may need to resume the work.
Send ordinary text to the selected session. Telegram photos and documents up to 20 MB are supported. The bot stores immutable originals under its private attachment directory and gives Codex disposable copies from staging. It removes staging copies after the turn; stored originals survive restarts and archives until session deletion.
A photo or document without a caption means: inspect and summarize the attachment. The bot treats the content as untrusted data and forces that turn into a read-only sandbox. It does not authorize project changes or other actions.
A caption becomes the prompt for the attachment turn. A caption can authorize actions and uses the normal workspace-write sandbox, so state the requested action and scope. /files selects stored attachments for the next text prompt.
New and imported sessions start with command network access and live web search off. Settings belong to each session and are captured when a job enters the queue.
| Command | Command network after | Web search after |
|---|---|---|
/net on |
on | unchanged |
/net off |
off | off |
/web on |
on | on |
/web off |
unchanged | off |
/web on enables both network access and web search. /net off disables both. Check /session after a change.
This bot runs one Codex job at a time in FIFO order. Switching sessions does not retarget queued jobs; each job keeps the session, project, attachments, and network settings captured when it was queued. /status shows pending and running work.
/cancel aborts the running turn or clears pending interactive input. Later jobs stay queued and start in FIFO order. A failed or cancelled job does not poison the rest of the queue.
State and queued work survive service restarts. Startup returns pending jobs to the worker. If the process stopped during a running Codex turn, startup marks that job interrupted and does not replay a turn that may already have changed files. Inspect the project and send the request again when appropriate.
Telegram delivery records the next unsent response part. A recoverable delivery retry continues from that part instead of intentionally resending completed parts.
The FIFO guarantee covers this bot process only. Another bot or Codex process can work at the same time. Separate projects do not interfere through this service, but two processes editing the same project can conflict at the filesystem or Git level. This bot does not manage or stop those other bot sessions.
Install the service only after local setup and project review:
npm run service:install
npm run service:status
npm run service:logsThe installer displays the exact unit path, writes ~/.config/systemd/user/codextg.service with mode 0600, reloads the user manager, and enables and starts codextg. It asks for an exact yes before replacing different unit content. The fixed codextg namespace is ownership-verified before service actions and reserved by this checkout; commands refuse a different fragment, working directory, executable, or any drop-in. A changed installation also restarts the unit so an already-active earlier process cannot remain. The installer does not install a cron job, desktop autostart entry, or another fallback when systemd --user is unavailable.
Use these lifecycle commands as needed:
npm run service:start
npm run service:stop
npm run service:restart
npm run service:status
npm run service:logsAn enabled user service starts with your systemd user manager, normally after login. Running it before login or after logout requires user lingering, which is a separate machine-administration choice such as loginctl enable-linger "$USER". The installer does not enable lingering.
Stopping codextg drains this bot's polling, album timers, worker, and state writes before releasing its lock. It has no process-control connection to another bot service.
- Run
npm run service:statusfirst.SYSTEMD_UNAVAILABLEmeans this machine lacks a usable systemd user manager; no alternative startup mechanism was installed. - Run
npm run service:logsfor the latest 200 entries since today. This query reads unit-name history, so it can include earlier same-name entries after an intentional takeover of the reserved namespace. The application does not intentionally log raw Telegram updates or Codex response contents. Redaction is targeted at configured secrets and Telegram credential URLs, not blanket privacy filtering: the local journal may still contain error details, filesystem paths, and identifiers. Treat all output as private operational data. - A second copy of this service cannot acquire the same state lock. Stop the existing
codextgservice before starting this checkout by hand. - A project add failure may mean the path is missing, is not a directory, resolves to an approved canonical path, or is outside Git without
--allow-non-git. - Telegram cannot download a photo or document larger than 20 MB. Send a smaller file or provide a locally approved path through another workflow.
- An interrupted job needs human review before retry because the previous Codex turn may have changed the project.
- Only the configured numeric user ID in the configured private chat can control this bot. Messages from usernames, groups, channels, or other IDs stay unauthorized.
- The BotFather token remains in the hidden setup prompt and local
.env; the systemd command line contains no token. - Only local administration can approve canonical project paths or import Codex thread IDs.
- Attachment contents are untrusted. Captionless turns stay read-only; captions and ordinary text can authorize workspace writes.
- Network access and web search start off and change only through
/netand/webfor the selected session. - This bot owns only this checkout's
.env,.data, attachment store, queue, andcodextguser service. It does not alter another bot's state or sessions.
Automated tests inject Telegram, Codex, filesystem, and service-process boundaries. They do not use your token, contact Telegram, install a unit, or start a real user service.
After reviewing this checkout, complete these steps yourself:
- Run
npm run setup, enter the token in the hidden prompt, send a fresh private text message, verify@viocity1110plus both numeric IDs, and typeyes. - Create a temporary Git repository outside active projects and approve it with
npm run project:add. - Run
npm run service:install, then inspectnpm run service:statusandnpm run service:logs. - In Telegram, create
Smoke test, request a read-only directory summary, and send a captionless photo. Confirm the repository does not change. - Send a small document with a caption, queue two harmless prompts, verify FIFO responses, cancel a long harmless prompt, and confirm the next job starts.
- Restart the service, return to
Smoke test, and continue the same thread. Verify/net on,/web on,/web off, and/net offthrough/session. - Archive, restore, archive again, and delete the smoke session. Remove the temporary project from the allowlist.
Keep tokens, numeric Telegram IDs, Codex thread IDs, .env, and private project paths out of screenshots and verification reports.