# 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. ✅ FIXED — xdotool geometry detection: X11-only with no Wayland fallback `internal/ui/chat/attachment_handler.go` — Added `image_viewer_args` config field (`[]string`). When set, `viewerArgs()` uses these args instead of auto-detecting via xdotool. The auto-detection remains the default for mpv when `image_viewer_args` is empty, preserving backward compatibility on X11. Wayland users can now set explicit geometry args in config. --- ### 8. ✅ FIXED — Token stored in keyring — DISCORDO_TOKEN env var bypasses keyring `internal/config/config.toml` — Added a prominent warning at the top of the default config file about `DISCORDO_TOKEN` security implications: environment variables are visible to other processes via `/proc`, users should prefer the keyring-based login flow. --- ## 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 (3 fixed) | | Strategic | 3 (2 fixed) | | 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.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` | Message rendering + keybinds | | 13a | `internal/ui/chat/attachment_handler.go` | Attachment download/view/save | | 13b | `internal/ui/chat/embed_renderer.go` | Embed types + line wrapping | | 13c | `internal/ui/chat/url_extractor.go` | URL extraction from messages | | 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) |