Telegram control plane: owner-to-agent commands — built as drb-telegram-ops (WSL), pending live verification #56
Closed
opened 2026-08-24 00:32:39 -04:00 by logan
·
7 comments
No Branch/Tag Specified
Labels
Clear labels
area/ai-pipeline
area/auth
area/billing
area/correlation
area/discord
area/frontend
area/governance
area/growth
area/infra-deploy
area/legal
area/map
area/mqtt
area/node-ota
area/osint
area/recording-audio
area/sdr-radio
area/transcription
board
board:agenda
minutes:draft
minutes:final
role:ceo
role:cfo
role:cio
role:ciso
role:cmo
role:coo
role:cto
Scene/entity extraction, summarizer, AI providers, embeddings
AuthN/Z, roles, tenancy/org scoping, API keys, credentials
Plans, entitlements, AI spend accounting and caps
Incident correlation, linking, lifecycle, severity, corr_debug
Discord bot, radio bots, voice streams, bot tokens, webhooks
Web UI: Next.js app and the edge-node local dashboard
Board process, minutes, unattended runner, agent tooling
Go-to-market, outreach, cold email, discovery conversations
CI/CD, VM, registry, Firestore rules/indexes deploy, Terraform/Caddy
ToS, retention, licences/DAAs, CAN-SPAM, E&O, name suppression
Live map: pins, paths, tiles, legend, geocoding
Node-C2 MQTT, checkins, commands, node sweeper, dynsec
Node install, enrollment, OTA updater, image skew
External feeds: cameras, 511, ADS-B, AIS, outages, and their licences
Call capture, boundaries, PulseAudio, ring buffer, trimming, attribution
OP25 decode, SDR hardware/tuning, conventional, second SDR, mesh radio
STT, transcript correction, place verification, name accuracy
Board process: minutes and board-filed items
Needs board scoping at the next sitting; CEO removes the label once scoped
One exec pre-read, closed by CEO
CEO ruling; open until enacted
Filed by the CEO
Filed by the CFO
Filed by the CIO
Filed by the CISO
Filed by the CMO
Filed by the COO
Filed by the CTO
kind
behavior-bug
Wrong product behavior; the instrument reads it correctly
kind
measurement-bug
Corrupts the recording/instrument (debug data, evals, health checks), not behavior
priority
p0
Drop everything: prod broken, live exposure, or owner-ruled P0
priority
p1
Next up: blocks a gate, a customer, or core incident quality
priority
p2
Normal: real defect or planned work, schedule in turn
priority
p3
Low: minor, cosmetic, or someday
status
blocked
Waiting on an external party, spend, or another issue
status
in-progress
Branch or PR open against it
status
needs-decision
Waiting on an owner or board ruling before work
status
needs-info
Root cause or repro unknown; needs a reading first
type
bug
Something built is wrong: code, data, or a measurement
type
chore
Maintenance, refactor, tests, pinning, cleanup: no user-visible change
type
decision
A ruling to make or record: board minutes, owner calls, legal reviews
type
docs
Docs, runbooks, CLAUDE.md, skills, SOPs
type
feature
New capability or an extension of an existing one
type
security
Security, privacy, credential or tenant-isolation exposure
type
tracking
Tracker, checklist, initiative or punch-list spanning several changes
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: logan/server-26#56
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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_botis live; a test message was delivered end to end (HTTP 200). Credentials are in WSL at~/.telegram-bot-tokenand~/.telegram-chat-id, mode 600, alongside~/.gitea-token— never in the repo. Thecheckinagent (.claude/agents/checkin.md) sends its heartbeat there and degrades to stdout if either credential file is missing.Inbound does not.
getUpdatesis 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
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 thediscord-botservice 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_botcan 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:
5028509822; silently ignore every other sender. Do not reply to unknown senders — a reply confirms the bot is live and dispatching.Questions for the board
Server/as a service besidediscord-bot, or stay a session-only convenience?Related
Scoped by board minutes #62 (2026-08-24). Label
board:agendaremoved. Owner: CTO, sign-off CISO, by 2026-08-31.Answering the questions in the body:
/status, read-only: deployed SHA vsorigin/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.
discord-botinServer/is the obvious shape, but a session-only convenience is acceptable for these two commands./statusis read-only./logis 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.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.
Scope correction — owner ruling 2026-08-25, and the build that followed
This issue was scoped toward a Telegram sibling service inside
Server/, besidediscord-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
Server/service, deploys with the stackVersion 5C/drb-telegram-ops/, runs in WSL on the authoring machine. Not aServer/service, not a deploy target, not in the compose stackclaude.exe -pin the Version 5C root. Zero reimplementationWhy
Server/was the wrong homeThe officers, the board procedure and the skills are filesystem definitions in
.claude/. They are written againstH:/…paths, they reach Gitea throughwsl -e bash -lc, and.claude/settings.jsongrants permissions in those exact terms. A container inServer/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.exethe 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-leadand 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):
Bashstays enabled because theboardandgiteaskills file issues with curl through it, andBashcan 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
--disallowedToolsis honoured at all under the configured permission mode is verified at runtime, not assumed —/doctorruns a live probe that attempts aWriteand reports whether it was blocked.Security notes against this issue's own requirements
from_user.id, not chat id. This issue specified chat id5028509822. 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.app/internal/auth.py; flag it and it will be made.--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— spawnsclaude.exe, parses thestream-jsonevent 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
pipand noensurepip—sudo apt-get install -y python3-pip python3-venvfirst.make setup, fill.env. Reuse the existing credential at~/.telegram-bot-tokenrather than minting a new bot.make doctor— confirms the WSL→claude.exespawn 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.
Telegram control plane: owner-to-agent commands and CEO escalation need an always-on listenerto Telegram control plane: owner-to-agent commands — built as drb-telegram-ops (WSL), pending live verificationStray 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.pyand three empty__init__.py. There is nobot.py, no Dockerfile, no requirements file, andhandlers/is empty, so it cannot run and is not wired intodocker-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-26would 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 retiredrb-telegram-ops/. Until then it is a tripwire — every future run'sgit statusshows an untracked directory inServer/and has to re-derive that it is dead.Found uncommitted on the authoring machine, unattended run 2026-08-28.
Server/drb-telegram-bot/exists as an untracked directory in theserver-26working 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:
auth.pyand the allowlist inconfig.pyare the security boundary of the whole feature and have had no review.No action taken on the files. Flagged for the next sitting.
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:agendaand says explicitly that the control plane needs scoping at a sitting, not implementation off the issue. Pushing it tomainwould ship code the board has not authorised, and CI deploysmainon 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:
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.
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
Serverrepo, not covered by.gitignore, and therefore one carelessgit add -Aaway 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 emptyNo
bot.pyentrypoint, no tests, no Dockerfile, no requirements file, and nodocker-compose.ymlservice. It does not run and cannot be deployed.Checked for credential exposure: clean. No hardcoded token literals anywhere in the tree;
config.pyreads 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-tghelper, which is not this.Closing per owner (2026-10-27): the Telegram control plane was built for the old WSL box and isn't in use.