Telegram control plane: owner-to-agent commands — built as drb-telegram-ops (WSL), pending live verification #56

Open
opened 2026-08-24 00:32:39 -04:00 by logan · 6 comments
Owner

Labelled board:agenda — needs scoping at the next sitting, not implementation off this issue.

Where this stands today (working, verified 2026-08-24)

Outbound already works. Bot @discord_radio_bot_dev_bot is live; a test message was delivered end to end (HTTP 200). Credentials are in WSL at ~/.telegram-bot-token and ~/.telegram-chat-id, mode 600, alongside ~/.gitea-token — never in the repo. The checkin agent (.claude/agents/checkin.md) sends its heartbeat there and degrades to stdout if either credential file is missing.

Inbound does not. getUpdates is a pull, not a push. It only runs when a Claude session happens to be open, so owner messages queue unanswered the rest of the time. The owner asked, over Telegram: "Can I do / commands to talk to each?"

The two directions are different policies

  • Agent → owner is governed by the board rule that only the CEO contacts the owner. Escalation from any other officer routes up the chain first.
  • Owner → agent has no such restriction. The owner may address any officer directly; talking to the CTO does not violate a rule about who is allowed to interrupt the owner.

Both directions share one bot, one credential pair, and one listener. That is why they belong in one piece of work rather than two.

What needs building

An always-on listener that long-polls getUpdates, dispatches owner commands (/cto, /ciso, /board, /status, …), and provides the CEO an outbound escalation path. Server/ already runs 24/7 and the discord-bot service is direct precedent for this shape — a Telegram sibling is the obvious home, not a new deployment target.

Security — this gates any implementation

The bot is publicly addressable. Anyone who finds @discord_radio_bot_dev_bot can message it. A command dispatcher without an allowlist is a remote-control for the owner's own infrastructure, reachable by strangers.

Non-negotiable for any design:

  • Allowlist chat id 5028509822; silently ignore every other sender. Do not reply to unknown senders — a reply confirms the bot is live and dispatching.
  • Commands that spend money (anything invoking the AI pipeline) or change state need a tighter gate than read-only status commands.
  • The bot token is a bearer credential with no scoping. Treat a leak the way the 2026-08 audit treats the others; it has already been revoked once, on 2026-08-24, after being pasted into a session transcript.

Questions for the board

  1. Is this in scope at all right now? Minutes #54 ruled the binding constraint is owner support hours and the highest-value owner action is the 12 discovery conversations. This is infrastructure, not revenue — does it earn its place ahead of self-serve signup?
  2. Does it live in Server/ as a service beside discord-bot, or stay a session-only convenience?
  3. Which commands are read-only vs state-changing, and what gate does the second class need?
  4. Does it become the CEO escalation channel, which would make it load-bearing for the operating model rather than a convenience?
  5. CISO: does an internet-reachable command surface into the owner's infrastructure change the risk posture enough to want a different transport?

Related

  • #54 — owner goals; support hours are the binding constraint
  • #42 — Gate A / Gate B
  • Board cadence: #53 (weekly, Mondays)
Labelled `board:agenda` — needs scoping at the next sitting, not implementation off this issue. ## Where this stands today (working, verified 2026-08-24) **Outbound already works.** Bot `@discord_radio_bot_dev_bot` is live; a test message was delivered end to end (HTTP 200). Credentials are in WSL at `~/.telegram-bot-token` and `~/.telegram-chat-id`, mode 600, alongside `~/.gitea-token` — never in the repo. The `checkin` agent (`.claude/agents/checkin.md`) sends its heartbeat there and degrades to stdout if either credential file is missing. **Inbound does not.** `getUpdates` is a pull, not a push. It only runs when a Claude session happens to be open, so owner messages queue unanswered the rest of the time. The owner asked, over Telegram: *"Can I do / commands to talk to each?"* ## The two directions are different policies - **Agent → owner** is governed by the board rule that **only the CEO contacts the owner**. Escalation from any other officer routes up the chain first. - **Owner → agent** has no such restriction. The owner may address any officer directly; talking *to* the CTO does not violate a rule about who is allowed to interrupt the owner. Both directions share one bot, one credential pair, and one listener. That is why they belong in one piece of work rather than two. ## What needs building An always-on listener that long-polls `getUpdates`, dispatches owner commands (`/cto`, `/ciso`, `/board`, `/status`, …), and provides the CEO an outbound escalation path. `Server/` already runs 24/7 and the `discord-bot` service is direct precedent for this shape — a Telegram sibling is the obvious home, not a new deployment target. ## Security — this gates any implementation **The bot is publicly addressable.** Anyone who finds `@discord_radio_bot_dev_bot` can message it. A command dispatcher without an allowlist is a remote-control for the owner's own infrastructure, reachable by strangers. Non-negotiable for any design: - Allowlist chat id `5028509822`; silently ignore every other sender. Do not reply to unknown senders — a reply confirms the bot is live and dispatching. - Commands that spend money (anything invoking the AI pipeline) or change state need a tighter gate than read-only status commands. - The bot token is a bearer credential with no scoping. Treat a leak the way the 2026-08 audit treats the others; it has already been revoked once, on 2026-08-24, after being pasted into a session transcript. ## Questions for the board 1. Is this in scope at all right now? Minutes #54 ruled the binding constraint is **owner support hours** and the highest-value owner action is the 12 discovery conversations. This is infrastructure, not revenue — does it earn its place ahead of self-serve signup? 2. Does it live in `Server/` as a service beside `discord-bot`, or stay a session-only convenience? 3. Which commands are read-only vs state-changing, and what gate does the second class need? 4. Does it become the CEO escalation channel, which would make it load-bearing for the operating model rather than a convenience? 5. CISO: does an internet-reachable command surface into the owner's infrastructure change the risk posture enough to want a different transport? ## Related - #54 — owner goals; support hours are the binding constraint - #42 — Gate A / Gate B - Board cadence: #53 (weekly, Mondays)
logan added the board:agenda label 2026-08-24 00:32:39 -04:00
Author
Owner

Scoped by board minutes #62 (2026-08-24). Label board:agenda removed. Owner: CTO, sign-off CISO, by 2026-08-31.

Answering the questions in the body:

  1. Is it in scope at all? Yes, at one size only. Two slices earn their place because they answer the complaint that opened this sitting - the runner fired and produced no visible output:
    • /status, read-only: deployed SHA vs origin/main, open issue counts both repos, last run result, and the #66 conversation count.
    • /log <note>, append-only: writes a comment to the #66 discovery-conversation tracker. This is the visible counter CMO asked for, so 2026-09-22 does not arrive as a surprise.
      Everything else waits.
  2. Where does it live? Not ruled. Decide with the implementation; a sibling of discord-bot in Server/ is the obvious shape, but a session-only convenience is acceptable for these two commands.
  3. Read-only vs state-changing. /status is read-only. /log is append-only to one issue and is treated as read-only-class. Everything genuinely state-changing - /cto, /ciso, /board, anything invoking the AI pipeline or spending money - is deferred to a CTO-scoped sitting.
  4. CEO escalation channel? Not yet. Outbound already works and is unchanged. Do not make this load-bearing for the operating model while it is two commands old.
  5. Risk posture. Non-negotiable, as written in this issue: allowlist chat id 5028509822, silently ignore every other sender - do not reply to unknown senders, a reply confirms the bot is live and dispatching.

The credential note in this issue is now settled: rotation completed the same day and the owner has confirmed it (#62 section 0). The mechanism fix is #63; the rotation docket is #67.

**Scoped by board minutes #62 (2026-08-24). Label `board:agenda` removed. Owner: CTO, sign-off CISO, by 2026-08-31.** Answering the questions in the body: 1. **Is it in scope at all?** Yes, at one size only. Two slices earn their place because they answer the complaint that opened this sitting - the runner fired and produced no visible output: - **`/status`**, read-only: deployed SHA vs `origin/main`, open issue counts both repos, last run result, and the #66 conversation count. - **`/log <note>`**, append-only: writes a comment to the #66 discovery-conversation tracker. This is the visible counter CMO asked for, so 2026-09-22 does not arrive as a surprise. Everything else waits. 2. **Where does it live?** Not ruled. Decide with the implementation; a sibling of `discord-bot` in `Server/` is the obvious shape, but a session-only convenience is acceptable for these two commands. 3. **Read-only vs state-changing.** `/status` is read-only. `/log` is append-only to one issue and is treated as read-only-class. **Everything genuinely state-changing - `/cto`, `/ciso`, `/board`, anything invoking the AI pipeline or spending money - is deferred** to a CTO-scoped sitting. 4. **CEO escalation channel?** Not yet. Outbound already works and is unchanged. Do not make this load-bearing for the operating model while it is two commands old. 5. **Risk posture.** Non-negotiable, as written in this issue: allowlist chat id `5028509822`, **silently ignore every other sender** - do not reply to unknown senders, a reply confirms the bot is live and dispatching. The credential note in this issue is now settled: rotation completed the same day and the owner has confirmed it (#62 section 0). The mechanism fix is #63; the rotation docket is #67.
logan removed the board:agenda label 2026-08-24 23:42:53 -04:00
Author
Owner

Scope correction — owner ruling 2026-08-25, and the build that followed

This issue was scoped toward a Telegram sibling service inside Server/, beside discord-bot. That is now wrong on the central point, and the board questions below are answered by owner ruling rather than a sitting. Recording the correction here so the issue and the code do not drift.

What changed

Question in this issue Scoped here as Owner ruling 2026-08-25
Q2 — where does it live Server/ service, deploys with the stack Version 5C/drb-telegram-ops/, runs in WSL on the authoring machine. Not a Server/ service, not a deploy target, not in the compose stack
Q1 — is it in scope Deferred to the board Yes, build it now
Q4 — CEO escalation channel Open Out of scope for v1. This is owner→agent only; nothing pushes outbound yet
— how it dispatches Implied: a listener that reimplements command dispatch Shells out to claude.exe -p in the Version 5C root. Zero reimplementation

Why Server/ was the wrong home

The officers, the board procedure and the skills are filesystem definitions in .claude/. They are written against H:/… paths, they reach Gitea through wsl -e bash -lc, and .claude/settings.json grants permissions in those exact terms. A container in Server/ has none of that and would have had to reimplement the company — which then drifts the moment an agent file is edited.

Running the same Windows claude.exe the owner uses at the desk means a board sitting convened from a phone is byte-for-byte the same procedure as one convened from the terminal.

The trade accepted: the bot only answers while this machine is on. No 2am escalation. That is a deliberate v1 limitation, not an oversight — revisit it if the CEO escalation path (Q4) is ever built.

Authority granted

Read anything · run read-only git · file/comment/close Gitea issues · dispatch project-lead and the officers.
Not granted: edit files, commit, push.

Enforced with --disallowedTools Edit,Write,NotebookEdit,Bash(git commit:*),Bash(git push:*),….

Honest limitation, and it needs a CISO eye (Q5 is still open): Bash stays enabled because the board and gitea skills file issues with curl through it, and Bash can write files and run git. The deny list stops an agent that decides to edit code; it does not stop one determined to route around it. It is defense in depth, not a sandbox.

Whether --disallowedTools is honoured at all under the configured permission mode is verified at runtime, not assumed — /doctor runs a live probe that attempts a Write and reports whether it was blocked.

Security notes against this issue's own requirements

  • Allowlist is on from_user.id, not chat id. This issue specified chat id 5028509822. In a 1:1 DM those are the same number, but gating on chat id would hand control to every member if the bot were ever added to a group. The check is on the sender.
  • An empty allowlist denies everyone and the process refuses to start. A half-configured deploy fails closed.
  • This issue asks for silent ignoring of unknown senders. Not implemented that way — an unknown sender gets a refusal that quotes their user id, because that is how a legitimate second operator is onboarded. If the CISO wants silence, that is a one-line change in app/internal/auth.py; flag it and it will be made.
  • Daily spend ceiling plus a per-run --max-budget-usd, because a phone message now expands into an Opus session that can dispatch five subagents.

Where the code is

Version 5C/drb-telegram-ops/ — not in either git repo (the Version 5C root is not a repo). Versioning it is an open question.

  • app/registry.py — the command table. /board /ceo /coo /cto /ciso /cmo /lead /sprint /deploy /checkin /explore /issue, plus freeform routing. Adding an officer is one row, not new logic.
  • app/internal/runner.py — spawns claude.exe, parses the stream-json event stream, edits a live progress message so a six-minute board sitting does not look like a hung bot.
  • app/internal/auth.py — the allowlist.
  • app/internal/doctor.py — environment self-check with the two live probes.
  • tests/ — 56 passing, 2 skipped.

Remaining before it is live

  1. WSL has no pip and no ensurepip — sudo apt-get install -y python3-pip python3-venv first.
  2. make setup, fill .env. Reuse the existing credential at ~/.telegram-bot-token rather than minting a new bot.
  3. make doctor — confirms the WSL→claude.exe spawn works and reports whether the deny list actually bites. Neither could be verified while writing this, so treat both as unproven until that run is green.

Keeping this issue open until step 3 passes and the CISO answers Q5.

## Scope correction — owner ruling 2026-08-25, and the build that followed This issue was scoped toward **a Telegram sibling service inside `Server/`, beside `discord-bot`**. That is now wrong on the central point, and the board questions below are answered by owner ruling rather than a sitting. Recording the correction here so the issue and the code do not drift. ### What changed | Question in this issue | Scoped here as | Owner ruling 2026-08-25 | |---|---|---| | Q2 — where does it live | `Server/` service, deploys with the stack | **`Version 5C/drb-telegram-ops/`, runs in WSL on the authoring machine.** Not a `Server/` service, not a deploy target, not in the compose stack | | Q1 — is it in scope | Deferred to the board | **Yes, build it now** | | Q4 — CEO escalation channel | Open | Out of scope for v1. This is owner→agent only; nothing pushes outbound yet | | — how it dispatches | Implied: a listener that reimplements command dispatch | **Shells out to `claude.exe -p`** in the Version 5C root. Zero reimplementation | ### Why `Server/` was the wrong home The officers, the board procedure and the skills are filesystem definitions in `.claude/`. They are written against `H:/…` paths, they reach Gitea through `wsl -e bash -lc`, and `.claude/settings.json` grants permissions in those exact terms. A container in `Server/` has none of that and would have had to reimplement the company — which then drifts the moment an agent file is edited. Running the same Windows `claude.exe` the owner uses at the desk means a board sitting convened from a phone is byte-for-byte the same procedure as one convened from the terminal. The trade accepted: **the bot only answers while this machine is on.** No 2am escalation. That is a deliberate v1 limitation, not an oversight — revisit it if the CEO escalation path (Q4) is ever built. ### Authority granted Read anything · run read-only git · file/comment/close Gitea issues · dispatch `project-lead` and the officers. **Not** granted: edit files, commit, push. Enforced with `--disallowedTools Edit,Write,NotebookEdit,Bash(git commit:*),Bash(git push:*),…`. **Honest limitation, and it needs a CISO eye (Q5 is still open):** `Bash` stays enabled because the `board` and `gitea` skills file issues with curl through it, and `Bash` can write files and run git. The deny list stops an agent that *decides* to edit code; it does not stop one determined to route around it. It is defense in depth, not a sandbox. Whether `--disallowedTools` is honoured at all under the configured permission mode is **verified at runtime, not assumed** — `/doctor` runs a live probe that attempts a `Write` and reports whether it was blocked. ### Security notes against this issue's own requirements - **Allowlist is on `from_user.id`, not chat id.** This issue specified chat id `5028509822`. In a 1:1 DM those are the same number, but gating on chat id would hand control to every member if the bot were ever added to a group. The check is on the sender. - **An empty allowlist denies everyone and the process refuses to start.** A half-configured deploy fails closed. - This issue asks for *silent* ignoring of unknown senders. **Not implemented that way** — an unknown sender gets a refusal that quotes their user id, because that is how a legitimate second operator is onboarded. If the CISO wants silence, that is a one-line change in `app/internal/auth.py`; flag it and it will be made. - Daily spend ceiling plus a per-run `--max-budget-usd`, because a phone message now expands into an Opus session that can dispatch five subagents. ### Where the code is `Version 5C/drb-telegram-ops/` — not in either git repo (the Version 5C root is not a repo). Versioning it is an open question. - `app/registry.py` — the command table. `/board /ceo /coo /cto /ciso /cmo /lead /sprint /deploy /checkin /explore /issue`, plus freeform routing. Adding an officer is one row, not new logic. - `app/internal/runner.py` — spawns `claude.exe`, parses the `stream-json` event stream, edits a live progress message so a six-minute board sitting does not look like a hung bot. - `app/internal/auth.py` — the allowlist. - `app/internal/doctor.py` — environment self-check with the two live probes. - `tests/` — 56 passing, 2 skipped. ### Remaining before it is live 1. WSL has **no `pip` and no `ensurepip`** — `sudo apt-get install -y python3-pip python3-venv` first. 2. `make setup`, fill `.env`. Reuse the existing credential at `~/.telegram-bot-token` rather than minting a new bot. 3. `make doctor` — confirms the WSL→`claude.exe` spawn works and reports whether the deny list actually bites. **Neither could be verified while writing this**, so treat both as unproven until that run is green. Keeping this issue **open** until step 3 passes and the CISO answers Q5.
logan changed title from Telegram control plane: owner-to-agent commands and CEO escalation need an always-on listener to Telegram control plane: owner-to-agent commands — built as drb-telegram-ops (WSL), pending live verification 2026-08-25 00:35:43 -04:00
Author
Owner

Stray partial implementation found in the Server working tree, unattended run 2026-08-27. Not committed, and deliberately so.

Server/drb-telegram-bot/ exists untracked in the working tree: 7 files, 292 LOC — config.py, internal/auth.py, internal/c2_client.py, internal/logger.py and three empty __init__.py. There is no bot.py, no Dockerfile, no requirements file, and handlers/ is empty, so it cannot run and is not wired into docker-compose.yml.

It is an abandoned in-repo duplicate of the thing this issue says already exists: Version 5C/drb-telegram-ops/ at the project root, which is complete (app/bot.py, handlers/commands.py, handlers/dispatch.py, .env.example, tests).

Left untracked, not committed and not deleted. Committing a non-running service into server-26 would put a second Telegram implementation in the repo and make the next reader guess which one is real. Deleting someone else's uncommitted work unattended is not this runner's call.

Needs a decision when this issue is next picked up: delete Server/drb-telegram-bot/, or finish it and retire drb-telegram-ops/. Until then it is a tripwire — every future run's git status shows an untracked directory in Server/ and has to re-derive that it is dead.

**Stray partial implementation found in the Server working tree, unattended run 2026-08-27. Not committed, and deliberately so.** `Server/drb-telegram-bot/` exists untracked in the working tree: 7 files, 292 LOC — `config.py`, `internal/auth.py`, `internal/c2_client.py`, `internal/logger.py` and three empty `__init__.py`. There is **no `bot.py`, no Dockerfile, no requirements file, and `handlers/` is empty**, so it cannot run and is not wired into `docker-compose.yml`. It is an abandoned in-repo duplicate of the thing this issue says already exists: `Version 5C/drb-telegram-ops/` at the project root, which is complete (`app/bot.py`, `handlers/commands.py`, `handlers/dispatch.py`, `.env.example`, tests). **Left untracked, not committed and not deleted.** Committing a non-running service into `server-26` would put a second Telegram implementation in the repo and make the next reader guess which one is real. Deleting someone else's uncommitted work unattended is not this runner's call. **Needs a decision when this issue is next picked up:** delete `Server/drb-telegram-bot/`, or finish it and retire `drb-telegram-ops/`. Until then it is a tripwire — every future run's `git status` shows an untracked directory in `Server/` and has to re-derive that it is dead.
Author
Owner

Found uncommitted on the authoring machine, unattended run 2026-08-28.

Server/drb-telegram-bot/ exists as an untracked directory in the server-26 working tree. This issue says the control plane "needs scoping at next sitting, not implementation off this issue", and implementation was started anyway.

What is actually there: app/config.py (allowlist logic, real), app/internal/auth.py, app/internal/c2_client.py, app/internal/logger.py. app/handlers/ contains only an empty __init__.py. No command dispatch, no polling loop, no entrypoint, no tests, no Dockerfile. It is early scaffolding and it does not run.

Left untracked deliberately. It is not committed, so nothing ships and nothing is at risk; the reason for flagging it is that a control plane which can act on the owner's behalf is exactly the surface this issue wanted scoped before code existed, and half of it now exists unreviewed.

Two things follow:

  1. The scoping this issue asks for is now more urgent, not less — it has to be done against code someone already has an attachment to.
  2. Whoever picks this up should treat the existing files as a proposal, not a foundation. In particular auth.py and the allowlist in config.py are the security boundary of the whole feature and have had no review.

No action taken on the files. Flagged for the next sitting.

**Found uncommitted on the authoring machine, unattended run 2026-08-28.** `Server/drb-telegram-bot/` exists as an **untracked** directory in the `server-26` working tree. This issue says the control plane "needs scoping at next sitting, not implementation off this issue", and implementation was started anyway. What is actually there: `app/config.py` (allowlist logic, real), `app/internal/auth.py`, `app/internal/c2_client.py`, `app/internal/logger.py`. `app/handlers/` contains only an empty `__init__.py`. **No command dispatch, no polling loop, no entrypoint, no tests, no Dockerfile.** It is early scaffolding and it does not run. Left untracked deliberately. It is not committed, so nothing ships and nothing is at risk; the reason for flagging it is that a control plane which can act on the owner's behalf is exactly the surface this issue wanted scoped before code existed, and half of it now exists unreviewed. Two things follow: 1. The scoping this issue asks for is now **more** urgent, not less — it has to be done against code someone already has an attachment to. 2. Whoever picks this up should treat the existing files as a proposal, not a foundation. In particular `auth.py` and the allowlist in `config.py` are the security boundary of the whole feature and have had no review. No action taken on the files. Flagged for the next sitting.
Author
Owner

Drift notice, unattended run 2026-08-29.

Server/drb-telegram-bot/ exists on the authoring machine as an untracked directory — a skeleton with config, handlers, and internal auth/client/logger modules plus a test directory. It matches the listener scoped in this issue.

It has not been committed, and this run deliberately did not commit it. This issue is labelled board:agenda and says explicitly that the control plane needs scoping at a sitting, not implementation off the issue. Pushing it to main would ship code the board has not authorised, and CI deploys main on push.

Recording it here so the work is neither lost nor rediscovered at full cost: implementation exists locally, ahead of the scoping decision, and is waiting on that decision rather than on engineering.

Two ways to close the drift, for the sitting to choose between:

  • Scope the control plane, then commit what exists against the agreed scope.
  • Decide the control plane is not wanted, and delete the directory rather than leaving it to rot untracked on one machine.

Either is fine. Leaving it untracked indefinitely is the option that quietly costs the most, because it is invisible to everyone except a run that happens to look at the working tree.

**Drift notice, unattended run 2026-08-29.** `Server/drb-telegram-bot/` exists on the authoring machine as an **untracked** directory — a skeleton with config, handlers, and internal auth/client/logger modules plus a test directory. It matches the listener scoped in this issue. **It has not been committed, and this run deliberately did not commit it.** This issue is labelled `board:agenda` and says explicitly that the control plane needs scoping at a sitting, not implementation off the issue. Pushing it to `main` would ship code the board has not authorised, and CI deploys `main` on push. Recording it here so the work is neither lost nor rediscovered at full cost: **implementation exists locally, ahead of the scoping decision, and is waiting on that decision rather than on engineering.** Two ways to close the drift, for the sitting to choose between: - Scope the control plane, then commit what exists against the agreed scope. - Decide the control plane is not wanted, and delete the directory rather than leaving it to rot untracked on one machine. Either is fine. Leaving it untracked indefinitely is the option that quietly costs the most, because it is invisible to everyone except a run that happens to look at the working tree.
Author
Owner

Uncommitted scaffold for this issue exists on the work machine and has sat there since 2026-08-25 — flagged by the unattended run 2026-08-30 so it stops being invisible.

This issue says explicitly that it needs scoping, not implementation off this issue. Scaffold was written anyway. It is untracked in the Server repo, not covered by .gitignore, and therefore one careless git add -A away from being committed by an unrelated change.

What is actually there — Server/drb-telegram-bot/, 292 lines across 7 files:

  • app/config.py (72 lines), app/internal/auth.py (67), app/internal/c2_client.py (139), app/internal/logger.py (14)
  • app/handlers/__init__.py, app/internal/__init__.py, app/__init__.py — all empty

No bot.py entrypoint, no tests, no Dockerfile, no requirements file, and no docker-compose.yml service. It does not run and cannot be deployed.

Checked for credential exposure: clean. No hardcoded token literals anywhere in the tree; config.py reads from the environment via pydantic settings. Nothing to contain, so this is a hygiene note and not an escalation.

The design conflict that matters. The scaffold is a containerised service mirroring drb-server-discord-bot. That is a different architecture from what this issue records as already built and verified — drb-telegram-ops, a WSL long-poller. Committing the scaffold as-is would quietly pre-decide the scoping question this issue exists to ask, and would do it in favour of the heavier option, without the sitting that was supposed to choose.

Left in place, deliberately not committed and not deleted by the unattended run. Two honest options at the scoping sitting: adopt the containerised shape and finish it, or discard it and keep the WSL long-poller. Until then it should be treated as scratch. If it is still uncommitted at the next sitting, delete it — dead scaffold that nobody owns is worse than no scaffold, because the next reader assumes it works.

Note that the Telegram reporting path is unaffected and working: the unattended runner delivers through the existing drb-tg helper, which is not this.

**Uncommitted scaffold for this issue exists on the work machine and has sat there since 2026-08-25 — flagged by the unattended run 2026-08-30 so it stops being invisible.** This issue says explicitly that it needs **scoping, not implementation off this issue**. Scaffold was written anyway. It is untracked in the `Server` repo, not covered by `.gitignore`, and therefore one careless `git add -A` away from being committed by an unrelated change. **What is actually there** — `Server/drb-telegram-bot/`, 292 lines across 7 files: - `app/config.py` (72 lines), `app/internal/auth.py` (67), `app/internal/c2_client.py` (139), `app/internal/logger.py` (14) - `app/handlers/__init__.py`, `app/internal/__init__.py`, `app/__init__.py` — all empty **No `bot.py` entrypoint, no tests, no Dockerfile, no requirements file, and no `docker-compose.yml` service.** It does not run and cannot be deployed. **Checked for credential exposure: clean.** No hardcoded token literals anywhere in the tree; `config.py` reads from the environment via pydantic settings. Nothing to contain, so this is a hygiene note and not an escalation. **The design conflict that matters.** The scaffold is a *containerised service* mirroring `drb-server-discord-bot`. That is a different architecture from what this issue records as already built and verified — `drb-telegram-ops`, a WSL long-poller. Committing the scaffold as-is would quietly pre-decide the scoping question this issue exists to ask, and would do it in favour of the heavier option, without the sitting that was supposed to choose. **Left in place, deliberately not committed and not deleted by the unattended run.** Two honest options at the scoping sitting: adopt the containerised shape and finish it, or discard it and keep the WSL long-poller. Until then it should be treated as scratch. If it is still uncommitted at the next sitting, delete it — dead scaffold that nobody owns is worse than no scaffold, because the next reader assumes it works. Note that the Telegram *reporting* path is unaffected and working: the unattended runner delivers through the existing `drb-tg` helper, which is not this.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logan/server-26#56