TECHFILE.md 20 KB

Tech Stack Analysis — discordo-plus

Analyzed: 2026-03-20 | Analyst: Claude (automated)


CURRENT STACK

Layer Package Version in Use Latest Stable Status
Language Go 1.26 (module min) 1.26.1 Current
TUI Framework github.com/ayn2op/tview v0.0.0-20260318094340 v0.0.0-20260320 (pseudo) Current
Terminal Backend github.com/gdamore/tcell/v3 v3.1.2 v3.1.2 Current
Discord API github.com/diamondburned/arikawa/v3 v3.6.1-0.20260311 (pseudo) same series Current
Discord State github.com/diamondburned/ningen/v3 v3.0.1-0.20260306 (pseudo) same series Current
Markdown Parser github.com/yuin/goldmark v1.7.17 v1.7.17 Current
Syntax Highlight github.com/alecthomas/chroma/v2 v2.23.1 v2.23.1 Current
Config github.com/BurntSushi/toml v1.6.0 v1.6.0 Current
Unicode Segmentation github.com/rivo/uniseg v0.4.7 v0.4.7 Current
Fuzzy Search github.com/sahilm/fuzzy v0.1.1 unknown Current
WebSocket github.com/gorilla/websocket v1.5.3 v1.5.3 Current (slow dev)
Compression github.com/klauspost/compress v1.18.4 v1.18.4 Current
Brotli github.com/andybalholm/brotli v1.2.0 v1.2.0 Current
Clipboard github.com/ayn2op/clipboard v0.0.0-20260308 (pseudo) same Current
Keyring github.com/zalando/go-keyring v0.2.6 v0.2.6 Current
Notifications github.com/gen2brain/beeep v0.11.2 v0.11.x Current
File Dialog github.com/ncruces/zenity v0.10.14 v0.10.14 Current
System Open github.com/skratchdot/open-golang v0.0.0-20200116 same (no updates) Unmaintained
QR Code github.com/skip2/go-qrcode v0.0.0-20200617 same (no updates) Stale
UUID github.com/google/uuid v1.6.0 v1.6.0 Current
x/sys golang.org/x/sys v0.42.0 v0.42.0 Current
x/term golang.org/x/term v0.41.0 v0.41.0 Current

POSITIVES

  • Go 1.26 module minimum is the most recent stable release (released Feb 10, 2026), making this one of the most current Go projects possible.
  • tcell/v3 at v3.1.2 is the latest release of the terminal backend.
  • chroma/v2 at v2.23.1 is current; syntax highlighting is up to date.
  • BurntSushi/toml at v1.6.0 is the latest release.
  • klauspost/compress at v1.18.4 is current, including the latest gzhttp improvements.
  • golang.org/x/sys and golang.org/x/term are both pinned to their latest releases as of the audit date.
  • ayn2op/tview is an actively developed custom fork tracked at a specific commit; the fork shows multiple commits per day as of this writing — active and in sync with the project's needs.
  • zalando/go-keyring at v0.2.6 is current; token storage via the OS keyring is a sound security pattern (avoids plaintext storage).
  • Config defaults are embedded via //go:embed config.toml, which eliminates runtime file-not-found failures for the default configuration.
  • The guildstate.go implementation uses a mutex correctly around both read and write operations; the state file is saved synchronously after each change, not in a goroutine.
  • Build flags -trimpath -ldflags=-s are used in CI, producing a smaller binary and stripping debug symbols and file paths — correct for distribution builds.
  • The xdotool dependency for geometry detection is used with graceful degradation: if xdotool fails or is absent, terminalGeometry() returns an empty string and mpv runs without geometry arguments. This is the correct pattern for an optional dependency.

CRITICAL

No security vulnerabilities or EOL technologies requiring immediate attention were found. The Go version, TUI stack, and core Discord libraries are current. See RECOMMENDED UPGRADES for lower-priority items.


RECOMMENDED UPGRADES

1. skratchdot/open-golang: Unmaintained (Last commit: January 2020)

internal/ui/chat/messages_list.go, internal/notifications/notifications.go — This package has had no development activity in over six years. It has a single pseudo-version, 28 total commits, and no indication of future maintenance. It wraps xdg-open (Linux), open (macOS), and start (Windows) to open files and URLs in the system default application. While it continues to function, it receives no security patches and no bug fixes, and any Go toolchain evolution that changes CGO or exec handling could break it silently.

Two approaches are available: (a) replace with a direct exec.Command("xdg-open", path) call gated on runtime.GOOS, which eliminates the dependency entirely — the usage in this project is straightforward enough that three lines of stdlib code cover it; or (b) adopt golang.design/x/clipboard or simply inline the platform-specific invocations. Given the project already has a platform-detection pattern (the clipboard package uses build tags for Wayland vs default), inlining is the cleaner path.

Fix: Replace the open.Start() call in messages_list.go (image fallback, line ~1281) and open.Start() in the notifications file with a small platform-gated function using os/exec. Remove the github.com/skratchdot/open-golang require line from go.mod. Total effort: low (one usage in each of two files; the call signature is open.Start(path string) error).

Implementation Risk: Low. The replacement is a one-for-one substitution using stdlib. Test on Linux (xdg-open), macOS (open), and Windows (cmd /c start) paths separately. The gorilla/websocket indirect dependency is completely unrelated.


2. skip2/go-qrcode: Stale (Last commit: June 2020)

internal/ui/login/qr/model.go — This package generates the QR code displayed during Discord QR login. It has not been updated since June 2020, has a single pseudo-version, and receives no maintenance. It has 1.5k+ stars but development appears permanently stopped.

A maintained alternative is github.com/yeqown/go-qrcode/v2 or github.com/skip2/go-qrcode's most active fork. For this use case (terminal QR code output), the output is rendered as ASCII art in the TUI, so any spec-compliant QR library would work without API changes.

Fix: Evaluate github.com/yeqown/go-qrcode/v2 as a replacement. The current usage in qr/model.go likely calls qrcode.New(data, qrcode.Medium) and then renders it — confirm the API surface before migrating. Alternatively, pin a specific commit of a fork with recent activity. Effort: low to medium (QR rendering is self-contained to the login/qr package).

Implementation Risk: Low-medium. QR code generation is not security-sensitive in this context (the QR code is an externally-provided token URL from Discord). The main risk is API differences between the current library and replacements; test QR login flow after migration.


3. gorilla/websocket: Archived upstream, slow development

go.mod (indirect, pulled in via arikawa) — gorilla/websocket is the WebSocket implementation used by the arikawa Discord library. The original maintainer archived the repository in 2022, though community maintainers subsequently revived it. Development is slow and the most recent release (v1.5.3, June 2024) is now nearly two years old. The package has no critical CVEs at the time of this audit, but its maintenance trajectory is uncertain.

This is an indirect dependency — discordo does not import gorilla/websocket directly. The fix requires arikawa to migrate, not discordo. The upstream arikawa project (diamondburned/arikawa) would need to adopt an alternative such as coder/websocket or nhooyr.io/websocket. This is worth tracking but cannot be resolved within this fork without forking arikawa as well.

Fix: Monitor the arikawa upstream for movement on this issue. If arikawa does not migrate within 12 months, consider patching the discordo-plus fork of arikawa (already commented out as a local replace directive in go.mod) to use coder/websocket.

Implementation Risk: High if attempted. WebSocket replacement in arikawa would require careful handling of connection lifecycle, reconnect logic, and all Discord gateway event flows. Not recommended as an immediate action.


4. ✅ FIXED — goldmark: One patch version behind (v1.7.16 vs v1.7.17)

go.mod — goldmark v1.7.17 was released March 19, 2026 (one day before this audit). The project uses v1.7.16. This is a minimal gap; patch releases in goldmark are typically bug fixes.

Fix: Run go get github.com/yuin/goldmark@v1.7.17 and go mod tidy. Review the v1.7.17 changelog on GitHub for any rendering-related fixes that might affect Discord markdown output.

Implementation Risk: Negligible. Patch-level goldmark releases are non-breaking by convention. Run go test ./... after updating.


5. ✅ FIXED — CI uses go-version: stable — no pinned Go toolchain

.github/workflows/ci.yml — The CI workflow uses go-version: stable, which automatically tracks the latest stable Go release. This means CI silently advances whenever Go publishes a new version. This can introduce unexpected breakage from toolchain changes (vet rules, new lints, behavior changes) without any explicit decision being made.

Fix: Pin the CI Go version to go-version: "1.26" (or "1.26.x" to allow patch updates). This makes toolchain upgrades explicit and reviewable. Update the pin when upgrading is intentional.

Implementation Risk: None from pinning. The risk is the current behavior: an unexpected toolchain bump mid-CI could cause false test failures or missed vet issues during a period where CI is temporarily broken.


STRATEGIC RECOMMENDATIONS

6. Plan for arikawa/ningen version pinning strategy

go.mod — Both diamondburned/arikawa/v3 and diamondburned/ningen/v3 are pinned to pre-release pseudo-versions (commit hashes, not semver tags). This is necessary because the upstream does not publish stable semver tags frequently. However, it means dependency updates are entirely manual and opaque — there is no conventional way to check for "newer versions" using go list -m or Dependabot, since these are pseudo-versions.

The go.mod also contains commented-out replace directives pointing to local sibling directories (../tview, ../arikawa), which suggests the development workflow sometimes involves local patches to these dependencies. This is functional but should be documented explicitly to avoid confusion for future contributors.

Recommendation: Document the update process for pseudo-version dependencies in CLAUDE.md or a CONTRIBUTING file. Establish a cadence (e.g., monthly) for reviewing arikawa and ningen upstream commits and updating the pinned hashes. Consider whether a maintained fork of arikawa is needed long-term if upstream activity slows.

Implementation Risk: Low operational risk. This is a process recommendation, not a code change. The main risk of the status quo is missing security-relevant commits in arikawa or ningen without noticing.


7. xdotool geometry detection: X11-only with no Wayland fallback

internal/ui/chat/messages_list.go — The terminalGeometry() function calls xdotool getactivewindow getwindowgeometry --shell to position the mpv window to match the terminal window. This only works on X11. On Wayland, xdotool either fails silently or is not installed, in which case mpv launches without geometry arguments. This is documented in CLAUDE.md.

A Wayland-native approach does not have a clean library equivalent (Wayland compositors are fragmented on window geometry APIs). However, the user experience on Wayland is noticeably worse: mpv opens at a default position/size rather than overlaying the terminal.

Recommendation: Consider exposing viewer_args as a raw config array (e.g., image_viewer_args = ["--geometry=800x600"]) that users can set directly. This makes geometry configuration explicit and compositor-agnostic, and removes the xdotool runtime dependency entirely. The viewerArgs() function would then concatenate the configured args instead of detecting them. This is a small config and code change that significantly improves Wayland usability.

Implementation Risk: Low. The config field would be optional with a nil/empty default. The viewerArgs() function already returns a flat []string slice. The main consideration is backward compatibility for existing users who rely on the automatic geometry detection on X11 — offer viewer_args as an override rather than a replacement.


8. Token stored in keyring — DISCORDO_TOKEN env var bypasses keyring

internal/ui/root/model.go — The DISCORDO_TOKEN environment variable is checked first at startup and bypasses keyring storage entirely. This is intentional for CI/automated use cases, but environment variables are visible to all processes running as the same user (via /proc/PID/environ) on Linux. Users who set this variable in shell profiles (.bashrc, .zshrc) may inadvertently expose their Discord token to other user-space processes.

Recommendation: Add a warning in documentation (README or config.toml comments) that DISCORDO_TOKEN should only be used in transient, controlled environments (e.g., a systemd unit with EnvironmentFile= from a protected path) and not in shell profiles. The keyring path is the correct default for interactive use. This is a documentation/UX concern, not a code defect.

Implementation Risk: Zero — documentation only.


NO ACTION NEEDED

Package Version Status Notes
github.com/alecthomas/chroma/v2 v2.23.1 Current Latest release Jan 23, 2026
github.com/BurntSushi/toml v1.6.0 Current Latest release Dec 18, 2025
github.com/gdamore/tcell/v3 v3.1.2 Current Latest release Jan 26, 2026
github.com/klauspost/compress v1.18.4 Current Latest release Feb 9, 2026
github.com/andybalholm/brotli v1.2.0 Current Pure Go brotli; no open issues
github.com/rivo/uniseg v0.4.7 Current Latest release Feb 8, 2024; stable
github.com/zalando/go-keyring v0.2.6 Current Latest release Oct 25, 2024
github.com/gen2brain/beeep v0.11.2 Current Active maintenance, cross-platform
github.com/ncruces/zenity v0.10.14 Current Latest release Sep 4, 2024
github.com/google/uuid v1.6.0 Current Stable, actively maintained
github.com/google/go-cmp v0.6.0 Current Stable test utility
golang.org/x/sys v0.42.0 Current Latest release Mar 3, 2026
golang.org/x/term v0.41.0 Current Latest release Mar 10, 2026
golang.org/x/text v0.35.0 Current Maintained by Go team
golang.org/x/image v0.37.0 Current Maintained by Go team
golang.org/x/time v0.15.0 Current Maintained by Go team
github.com/ayn2op/tview v0.0.0-20260318094340 Current Active fork; multiple commits/week
github.com/ayn2op/clipboard v0.0.0-20260308203959 Current Active fork aligned with tview
github.com/diamondburned/arikawa/v3 v3.6.1-0.20260311 Current Pre-release pseudo-version; active
github.com/diamondburned/ningen/v3 v3.0.1-0.20260306 Current Pre-release pseudo-version; active
github.com/gorilla/websocket v1.5.3 Current (indirect) Latest tagged release; no CVEs
github.com/dlclark/regexp2 v1.11.5 Current (indirect) Chroma dependency
github.com/sahilm/fuzzy v0.1.1 Current Fuzzy search for pickers

Runtime Requirements

External Tools

Tool Purpose Required? Platform Constraint
mpv Image attachment viewer No (configurable) Cross-platform; default choice
xdotool Terminal geometry detection for mpv window positioning No X11 only; gracefully skipped if absent
vim / $EDITOR Editing config file via E keybind No (falls back gracefully) Any
libx11-dev X11 clipboard support Build-time only on Linux Linux X11
Secret Service (dbus) Keyring token storage on Linux Recommended Linux (GNOME Keyring / KDE Wallet)

Terminal Requirements

  • OSC 8 hyperlink support: required for clickable attachment URLs. Supported in: kitty, foot, wezterm, alacritty (recent), VTE-based terminals. Not supported in: vanilla xterm, most SSH clients.
  • Mouse support: optional, enabled via mouse = true in config. screen.EnableMouse() is called when configured.
  • Paste bracket support: always enabled (screen.EnablePaste()).
  • Focus events: always enabled (screen.EnableFocus()).
  • True color (24-bit): required for accurate theme color rendering; supported by most modern terminals.

Platform Notes

  • Linux is the primary target platform. All features are supported.
  • macOS: supported in CI; image viewer defaults may need adjustment (image_viewer = "open" or "default"). Desktop notifications use AppleScript via beeep. Suspend (ctrl+z) works via unix build tag.
  • Windows: CI builds and tests pass. Suspend functionality is a no-op (suspend_default.go). Clipboard relies on Windows APIs. Keyring uses Windows Credential Manager. Some terminal features (OSC 8, mouse) depend on terminal emulator (Windows Terminal supports most).
  • Wayland: clipboard has a dedicated implementation (clipboard_wayland.go). xdotool geometry detection does not work; mpv opens without terminal-matching geometry.

Build Configuration

  • Module path: github.com/ayn2op/discordo
  • Go minimum: go 1.26.0 (go.mod directive)
  • Build command (CI): go build -trimpath -ldflags=-s .
    • -trimpath: removes local filesystem paths from the binary
    • -ldflags=-s: strips the symbol table, reducing binary size
  • Test command: go test -v ./...
  • CI matrix: Ubuntu (x64 + ARM), Windows (x64 + ARM), macOS (ARM + Intel)
  • Linux CI requires libx11-dev for clipboard compilation (sudo apt install libx11-dev)
  • Binary output: named discordo by CI artifact convention; project installs as discordo-plus
  • No custom build tags required at the module level; platform-specific files use Go's filename-based build tag convention (_unix, _darwin, _default)

Summary

Severity Total
Critical 0
Recommended Upgrades 5
Strategic 3
No Action 22

The stack is in excellent health. The Go module is pinned to the latest stable release (1.26), core TUI and Discord libraries are current or actively tracking upstream at pseudo-version granularity, and all major stdlib-adjacent dependencies are up to date. The two primary concerns are unmaintained third-party packages (skratchdot/open-golang, skip2/go-qrcode) that have had no development activity for 5+ years — both are low-risk to replace with stdlib code or maintained alternatives. The gorilla/websocket situation is an inherited risk through arikawa and cannot be resolved without upstream action or forking arikawa. The goldmark patch-version gap is trivial.


Files Reviewed

# File Type
1 go.mod Manifest
2 go.sum Lock file (presence verified)
3 main.go Entry point
4 cmd/root.go App initialization
5 .github/workflows/ci.yml CI configuration
6 internal/config/config.go Config struct and loader
7 internal/config/keybinds.go Keybind definitions
8 internal/config/theme.go Theme/style TOML unmarshaling
9 internal/config/editor_unix.go Editor command construction
10 internal/consts/consts.go App name, cache directory
11 internal/ui/root/model.go Root model, help overlay, edit config
12 internal/ui/root/suspend_unix.go Unix SIGTSTP suspend
13 internal/ui/chat/messages_list.go Core message rendering (1500+ lines)
14 internal/ui/chat/attachments_picker.go Attachment picker UI
15 internal/ui/chat/guildstate.go Guild expand/collapse persistence
16 internal/ui/chat/guilds_tree.go Guild/channel tree widget
17 internal/markdown/renderer.go Discord markdown AST renderer
18 internal/clipboard/clipboard.go Clipboard abstraction
19 internal/keyring/keyring.go Keyring token access
20 internal/notifications/notifications.go Desktop notification dispatch
21 internal/http/transport.go Custom HTTP transport (gzip + brotli)
22 internal/logger/logger.go slog setup
23 images.plan Feature plan document (historical)
24 CLAUDE.md Project context (via system prompt)