Discord admin page + hide admin-only bot pool from operators (#213 items 2, 4) #219
No Reviewers
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
No labels
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#219
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.
Refs #213 (items 4 and 2). Does not close it: items 1 (
/drb link+ web code generation) and 3 (node page "Join Discord" -> stream) are sibling slices.Pairs with: the sibling PRs for #213 item 1 (self-serve link code) and item 3 (node-page Join Discord). The "Generate link code" button from the mockup is deliberately absent here; item 1's PR should add it to the Servers tab (
drb-frontend/components/discord/ServersPanel.tsx,actionprop of the Panel).Commit 1 - regression (#213 item 4)
GET /tokensandPUT /tokens/{id}/preferbecame platform-admin only in #216. Fixed the three places that still offered them to operators:/tokens: admin only (operators go to/network)./networkhub: Bot Tokens card admin only.Commit 2 - Discord admin page (#213 item 2)
Settings > Discord (
/settings/discord), built fromdesign-canvas/discord-system-mockup.html"DRB admin". Platform admin only (tab hidden from org owners; every route behind it isrequire_admin_token). Polls every 10 s while visible.discord_streamsdoc, desired -> actual, the node's reported error, red after 2 min desired-on without connecting. Stop ->POST /discord/guilds/{id}/stop; Start (stopped row) and + Add radio channel ->POST /discord/streams; a "no bot in this server" refusal renders its invite links.DISCORD_UNLINKED_GUILD_ORG_ID, shown as a warning). Link / Change org ->PUT /discord/guilds/{id}/link.valid/check_error/ checked-at, guild membership with a dashed per-server invite chip for each server the bot is missing from,invite_url, lease (node, since, releasing), fan-out vsDISCORD_NODE_MAX_STREAMS. Refresh ->POST /tokens/{id}/refresh, Remove, Release all leases ->POST /tokens/flush, add with optional BYO org./tokensnow renders the same radio-bot table.Left out because there is no backend (not shown as dead controls): Alert routes tab, incident forum / roles / plan columns, per-channel talkgroup chips, "Alerts today" KPI (replaced by "Bots needing a check").
Backend (
routers/discord.py):GET /discord/admin/overview(platform admin): guilds, streams with node/bot names, orgs, systems, limits. No token strings, no Discord user ids, no linking-admin uid (tested).PUT /discord/guilds/{id}/link: digits-only id, optionalguild_namelabel, and re-linking a server to a different org stops its live channels. Without that, the reconciler keeps re-placing them with the stored oldorg_id, so one org's nodes keep serving a server that now belongs to another.POST /discord/streams:guild_id/channel_idmust be digits (the web form types them; the bot always sent snowflakes).Person names: none shown. Labels are server/channel/bot/org names; Discord user ids are dropped server-side.
Verification (real runs, this branch)
~/venvs/drb-5c/bin/python -m pytest -qin drb-c2-core:613 passed, 4 warnings in 25.96s(new:tests/test_discord_admin.py, 5 tests)tsc --noEmit(TypeScript 5.9.3, Node v20.20.2) on drb-frontend:tsc exit 0drb-qa-reviewpass.🤖 Generated with Claude Code
GET /tokens and PUT /tokens/{id}/prefer became platform-admin only in #216 (server-26#204 item 6). Three frontend surfaces still offered them to operators: - systems page "Preferred Bot Token" panel: now rendered for admins only. No non-admin listing endpoint was added: the write behind the picker is admin-only too, so a list would only feed a control that 403s on save. The panel also no longer turns a fetch error into "No tokens in pool." - /tokens page: admin-only (operators are sent to /network). - /network hub: the Bot Tokens card is shown to admins only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>Frontend, built from design-canvas/discord-system-mockup.html ("DRB admin"): - Settings > Discord (/settings/discord, platform admin only; the tab is hidden from org owners because every route behind it is admin-only). KPIs + tabs: * Radio channels: every discord_streams doc, desired -> actual, the node's real error, red after 2 min desired-on without connecting. Stop (POST /discord/guilds/{id}/stop), Start for a stopped channel and "+ Add radio channel" (POST /discord/streams), with the invite links a "no bot in this server" refusal carries. * Servers: linked servers plus servers seen running radio while unlinked (acting for DISCORD_UNLINKED_GUILD_ORG_ID); Link / Change org (PUT /discord/guilds/{id}/link). * Radio bots: valid / check_error, guild membership with a per-server invite link for each server the bot is missing from, lease (node, since, releasing), fan-out, Refresh (POST /tokens/{id}/refresh), Remove, Release all leases (POST /tokens/flush), add with optional BYO org. * How it works. - /tokens now renders the same radio-bot table (new token fields + refresh). - Left out, no backend yet: alert routes, incident forum/roles/plan columns, per-channel talkgroup chips, "Generate link code" (item 1). Backend (routers/discord.py): - GET /discord/admin/overview (platform admin): guilds, streams with node and bot names, orgs, systems, limits. No token strings, no Discord user ids, no linking-admin uid. - PUT /discord/guilds/{id}/link: digits-only id, optional guild_name label, and re-linking a server to a different org stops its live channels (the reconciler would otherwise keep re-placing them on the old org's nodes, using its stored org_id). - POST /discord/streams: guild_id/channel_id must be digits (the web form types them in; the bot always sent snowflakes). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>QA review fix pushed as
31ca63b:GET /discord/admin/overviewnow gives no fallback org to a server withunlinked_at(the field #218 sets), matchingresolve_guild_orgafter #218 merges. Before, such a server showed as "Unlinked · fallback org" and appeared in Add radio channel, which would 403. Also dropped two panel copy lines that were wrong (a "Needed" figure the page never shows; invite permissions also include View Channel). Re-run: pytest614 passed, tscexit 0.Note: #221 item 1 (no unlink) overlaps #218, which adds an org-owner unlink. Re-scope #221 once #218 merges.