ayn2op/discordo
A lightweight, secure, and feature-rich Discord terminal (TUI) client.
Single-maintainer risk — review before adopting
weakest axiscopyleft license (GPL-3.0) — review compatibility; top contributor handles 96% of recent commits…
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 3d ago
- ✓3 active contributors
- ✓GPL-3.0 licensed
Show all 8 evidence items →Show less
- ✓CI configured
- ⚠Small team — 3 contributors active in recent commits
- ⚠Single-maintainer risk — top contributor 96% of recent commits
- ⚠GPL-3.0 is copyleft — check downstream compatibility
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Concerns → Mixed if: relicense under MIT/Apache-2.0 (rare for established libs)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/ayn2op/discordo)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/ayn2op/discordo on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: ayn2op/discordo
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/ayn2op/discordo shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Single-maintainer risk — review before adopting
- Last commit 3d ago
- 3 active contributors
- GPL-3.0 licensed
- CI configured
- ⚠ Small team — 3 contributors active in recent commits
- ⚠ Single-maintainer risk — top contributor 96% of recent commits
- ⚠ GPL-3.0 is copyleft — check downstream compatibility
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live ayn2op/discordo
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/ayn2op/discordo.
What it runs against: a local clone of ayn2op/discordo — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in ayn2op/discordo | Confirms the artifact applies here, not a fork |
| 2 | License is still GPL-3.0 | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 33 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of ayn2op/discordo. If you don't
# have one yet, run these first:
#
# git clone https://github.com/ayn2op/discordo.git
# cd discordo
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of ayn2op/discordo and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "ayn2op/discordo(\\.git)?\\b" \\
&& ok "origin remote is ayn2op/discordo" \\
|| miss "origin remote is not ayn2op/discordo (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(GPL-3\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"GPL-3\\.0\"" package.json 2>/dev/null) \\
&& ok "license is GPL-3.0" \\
|| miss "license drift — was GPL-3.0 at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "main.go" \\
&& ok "main.go" \\
|| miss "missing critical file: main.go"
test -f "internal/ui/root/model.go" \\
&& ok "internal/ui/root/model.go" \\
|| miss "missing critical file: internal/ui/root/model.go"
test -f "internal/ui/chat/model.go" \\
&& ok "internal/ui/chat/model.go" \\
|| miss "missing critical file: internal/ui/chat/model.go"
test -f "internal/http/client.go" \\
&& ok "internal/http/client.go" \\
|| miss "missing critical file: internal/http/client.go"
test -f "internal/config/config.go" \\
&& ok "internal/config/config.go" \\
|| miss "missing critical file: internal/config/config.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 33 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~3d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/ayn2op/discordo"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Discordo is a lightweight, secure Discord terminal client written in Go that provides a TUI (text user interface) alternative to Discord's native applications. It enables users to browse guilds, read channels, send messages, and manage attachments directly in the terminal using keyboard shortcuts and a custom theme system, targeting users who prefer CLI-based workflows or resource-constrained environments. Monolithic Go application with internal packages split by domain: cmd/root.go is the entry point; internal/ui/{chat,login}/ contains TUI screens; internal/{config,cache,keyring,http,notifications}/ handle persistence, auth, and Discord API calls; internal/{clipboard,markdown,logger}/ provide utilities. State is managed per-screen (internal/ui/chat/state.go) with event-driven model updates (internal/ui/chat/model.go).
👥Who it's for
Terminal-focused Discord users (Linux/macOS/Windows developers, sysadmins, power users) who want a lightweight Discord client that runs in their shell with custom keybindings, clipboard integration, and desktop notifications—especially those already comfortable with TUI applications like tmux or vim.
🌱Maturity & risk
Early-stage but actively developed: the README explicitly states 'Heavily work-in-progress, expect breaking changes,' and the codebase targets Go 1.26.0 with CI/CD configured (ci.yml). No visible mature release tags or extensive test suite (only config_test.go visible), suggesting foundation work is ongoing rather than production-hardened.
Single maintainer (ayn2op) with ~176KB of Go code and active dependencies on Discord's arikawa library (a third-party wrapper), clipboard/notification libraries (beeep, zenity), and a forked tview (terminal UI kit). Reliance on these forks (ayn2op/tview, ayn2op/clipboard) and the WIP status mean breaking changes are expected; clipboard support requires wayland-specific runtime dependencies (wl-clipboard).
Active areas of work
Active development on the TUI client: recent commits updated Go to 1.26.0, added clipboard integration across platforms (clipboard_default.go, clipboard_wayland.go), desktop notifications (desktop_toast.go, desktop_toast_darwin.go), and QR code login support (skip2/go-qrcode dependency). The CI/CD workflow (ci.yml) is set up for automated builds.
🚀Get running
Clone, install Go 1.26+, build and run: git clone https://github.com/ayn2op/discordo && cd discordo && go build . then execute ./discordo to open the login TUI, or set DISCORDO_TOKEN="<token>" ./discordo to login via environment variable.
Daily commands:
go build . outputs a binary; run with ./discordo (TUI login) or DISCORDO_TOKEN=<token> ./discordo (programmatic login). Configuration loads from $XDG_CONFIG_HOME/discordo/config.toml (Linux), $HOME/Library/Application Support/discordo/config.toml (macOS), or %AppData%/discordo/config.toml (Windows).
🗺️Map of the codebase
main.go— Application entry point; every contributor must understand how the CLI is initialized and root command is invoked.internal/ui/root/model.go— Root Bubble Tea model managing the entire TUI state machine and view transitions; core to the application architecture.internal/ui/chat/model.go— Chat UI model handling message rendering, guild/channel navigation, and user interactions; the primary user-facing component.internal/http/client.go— HTTP client wrapping Discord API calls; all network communication flows through this critical abstraction.internal/config/config.go— Configuration and theme management; defines settings that affect behavior across the entire application.internal/keyring/keyring.go— Secure token storage mechanism; essential for understanding authentication and security practices.
🛠️How to make changes
Add a new keyboard shortcut
- Define the keybinding in the appropriate keybinds file matching the UI context (
internal/ui/chat/keybinds.go or internal/ui/login/keybinds.go) - Register the key mapping in the config keybinds system (
internal/config/keybinds.go) - Implement the handler in the corresponding model's Update method (
internal/ui/chat/model.go or internal/ui/login/model.go)
Add a new message formatting feature
- Extend the markdown renderer with new formatting rules (
internal/markdown/renderer.go) - Add color/styling definitions to the theme system if needed (
internal/config/theme.go) - Update the message rendering in the chat UI to use the new formatter (
internal/ui/chat/messages_list.go)
Add a new Discord API call
- Implement the API method in the HTTP client following Discord REST conventions (
internal/http/client.go) - Add the HTTP headers if custom auth or parameters are needed (
internal/http/headers.go) - Call the new API method from the appropriate UI model's Update or Init method (
internal/ui/chat/model.go or internal/ui/login/model.go) - Define a corresponding message type in the model's msg.go file to handle the response (
internal/ui/chat/msg.go or internal/ui/login/msg.go)
Add a new UI screen or modal
- Create a new directory under internal/ui with model.go, msg.go, and keybinds.go (
internal/ui/newscreen/model.go) - Implement the Bubble Tea Model interface (Init, Update, View) (
internal/ui/newscreen/model.go) - Add a message type to transition to this screen from the root model (
internal/ui/root/msg.go) - Register the screen in the root model's Update method for state management (
internal/ui/root/model.go)
🔧Why these technologies
- Bubble Tea (Go TUI framework) — Provides reactive, event-driven UI architecture with easy state management; ideal for terminal applications
- Arikawa (Discord API library) — Type-safe Go bindings for Discord API and gateway; eliminates manual JSON unmarshaling and reduces boilerplate
- TCell (terminal library) — Cross-platform terminal rendering with mouse/color support; underlying layer for sophisticated TUI components
- System keyrings (via keyring.go) — Secure credential storage avoiding plaintext config files; leverages OS-native secure storage (macOS Keychain, Linux Secret Service)
- Chroma (syntax highlighter) — Renders code blocks in messages with syntax highlighting; enhances readability for developer-heavy Discord communities
⚖️Trade-offs already made
-
TUI-only (no GUI/web interface)
- Why: Reduces surface area for security vulnerabilities; lightweight footprint suitable for headless/SSH use cases
- Consequence: Users must be comfortable with terminal interfaces; limits accessibility for non-technical users
-
Local in-memory caching instead of persistent database
- Why: Simplifies deployment (no external dependencies); faster access for frequently-viewed data
- Consequence: Data is lost on client restart; cannot sync state across multiple machines running the client
-
Synchronous HTTP calls with retry logic instead of background async sync
- Why: Predictable behavior; easier to reason about state; matches user expectations (no surprise delays)
- Consequence: Network latency blocks UI updates; slow connections may cause apparent freezes
-
Custom clipboard implementation (including Wayland support)
- Why: Ensures clipboard access works reliably on modern Linux desktops without relying on external tools
- Consequence: More code to maintain; platform-specific branches increase testing burden
🚫Non-goals (don't propose these)
- Does not support voice/video calling (Discord audio/video gateway)
- Does not provide server administration features (role management, bans, etc.)
- Does not support all Discord bot features (application commands, interactions are not prioritized)
- Not a mobile client (terminal-only, desktop/server Linux/macOS/Windows)
- Does not implement Discord's thread features (last-minute scope based on code inspection)
🪤Traps & gotchas
Clipboard runtime dependency: Wayland users must have wl-clipboard installed separately; the code has platform-specific stubs (clipboard_wayland.go) but doesn't auto-install it. Config path priority: uses XDG_CONFIG_HOME or falls back to hardcoded ~/.config (Linux); if XDG_CONFIG_HOME is unset and ~ is weird, config won't load as expected. Discord API rate limits: arikawa handles this, but no explicit backoff/retry logic visible in client.go—rapid message sends may silently fail. Forked dependencies: ayn2op/tview and ayn2op/clipboard are pinned forks (checked in go.mod), so upstream TUI or clipboard fixes won't auto-merge. Token via env var: DISCORDO_TOKEN is only used at startup; changes require restart.
🏗️Architecture
💡Concepts to learn
- TUI (Terminal User Interface) — Discordo's entire interface is a TUI built with tcell and tview, not a CLI; understanding widget trees, event loops, and screen stacking is essential to modifying the UI
- Discord API Authentication (Token-based) — Discordo authenticates via Bearer tokens stored in system keyring; understanding token lifetime, refresh logic, and QR-based login flow is critical for auth features
- Platform-specific abstractions (clipboard, notifications) — Code uses build tags and conditional compilation (clipboard_wayland.go vs clipboard_default.go) to handle OS differences; this pattern repeats for notifications and editors throughout the codebase
- Keyring/credential storage integration — Discordo uses zalando/go-keyring to secure tokens in the OS credential store rather than plaintext; prevents token exposure in config files
- WebSocket event streaming (Arikawa) — The arikawa library manages persistent WebSocket connections to Discord Gateway; Discordo's real-time message updates, typing indicators, and presence rely on proper session/reconnection handling
- TOML configuration with serde — User keybinds, themes, and settings are loaded from TOML (BurntSushi/toml); understanding the struct tags and validation in config.go is needed to extend configuration options
- Markdown to terminal rendering — Messages are rendered from Discord's Markdown to terminal-safe output via yuin/goldmark and internal/markdown/renderer.go; handling Discord-specific syntax (user mentions, code blocks, embeds) requires custom renderers
🔗Related repos
diamondburned/arikawa— The Discord API client library Discordo depends on; fork or contribute upstream improvements here for API coveragerivo/tview— Original TUI widget library that ayn2op/tview forks; track upstream for bug fixes and new widgets to backportmatterbridge/matterbridge— Alternative chat bridge/client that supports Discord among other protocols; shows patterns for multi-protocol TUI clientsmuesli/dua— Exemplary Go TUI project using tcell/tview patterns; good reference for state management and screen transitions in terminal appsBetaM0d/discordo-old— Original Discordo implementation (if it exists) or predecessor projects for understanding evolution of Discord TUI clients
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add unit tests for internal/config package
The config package (internal/config/) handles critical functionality like theme loading, keybind parsing, and editor detection across Unix/non-Unix platforms. Currently only config_test.go exists but internal/config/keybinds.go, internal/config/theme.go, and platform-specific editor detection (editor_unix.go, editor_default.go) lack test coverage. This is high-value for a security-focused Discord client where config parsing could expose vulnerabilities.
- [ ] Add tests for internal/config/keybinds.go covering keybind parsing and conflict detection
- [ ] Add tests for internal/config/theme.go covering theme application and color validation
- [ ] Add platform-specific tests for internal/config/editor_unix.go and editor_default.go to ensure correct editor detection
- [ ] Expand internal/config/config_test.go with test cases for malformed TOML and edge cases in config.toml defaults
Add clipboard backend tests and cross-platform validation
The clipboard package (internal/clipboard/) has platform-specific implementations (clipboard_wayland.go, clipboard_default.go) but lacks unit tests. Given that clipboard operations handle user data and the Wayland implementation is separate, missing tests create risk of copy/paste failures on different Linux environments. New contributor could add comprehensive tests covering both backends.
- [ ] Add unit tests in internal/clipboard/clipboard_test.go for clipboard read/write operations
- [ ] Add platform-specific tests for internal/clipboard/clipboard_wayland.go mocking D-Bus interactions
- [ ] Add tests for internal/clipboard/clipboard_default.go testing fallback behavior
- [ ] Add CI integration test to validate clipboard works across mock environments
Add integration tests for HTTP client headers and authentication in internal/http
The internal/http package (client.go, headers.go, props.go, transport.go) handles Discord API authentication and request signing, which is security-critical. Currently no test files exist in this package. A new contributor should add tests verifying correct header injection, token handling, and transport security without exposing credentials in logs.
- [ ] Create internal/http/client_test.go with tests for HTTP client initialization and default headers
- [ ] Add tests in internal/http/headers_test.go validating User-Agent, authorization header injection, and header sanitization
- [ ] Add tests for internal/http/props_test.go covering request property serialization
- [ ] Add tests for internal/http/transport_test.go ensuring TLS/compression settings and rate-limit header handling
🌿Good first issues
- Add unit tests for internal/config/config.go beyond config_test.go: test theme parsing, keybind validation, and platform-specific config path resolution to improve reliability
- Improve markdown rendering for code blocks in internal/ui/chat/messages_list.go: currently uses yuin/goldmark but doesn't syntax-highlight code or handle discord spoiler tags (||text||)
- Add missing error recovery for clipboard operations in internal/clipboard/clipboard.go: gracefully fall back to manual copy if wl-clipboard is missing on Wayland instead of panicking
⭐Top contributors
Click to expand
Top contributors
- @ayn2op — 96 commits
- @xqrs — 3 commits
- @kenperkins — 1 commits
📝Recent commits
Click to expand
Recent commits
e50ce44— fix(chat): use traversal-resistant file api (ayn2op)164795e— feat(config): add keybinds.guilds_tree.collapse_all keybind (ayn2op)be527a1— build(deps): upgrade to latest (ayn2op)b3fe7e3— Revert "fix: limit mouse handling to button events" (ayn2op)00d259f— build(deps): upgrade tview to latest (ayn2op)7b8a3d0— refactor(chat): remove gateway event wrapper (ayn2op)5f7b024— build(deps): upgrade chroma to 2.40.0 (ayn2op)fdd2ec9— fix: disable terminal focus events (ayn2op)ef3924d— fix: limit mouse handling to button events (ayn2op)eaaf87c— build(deps): upgrade tview to latest (ayn2op)
🔒Security observations
Failed to generate security analysis.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.