A feature-rich TUI client for Discord
See the code
Concord is a feature-rich TUI client for Discord, written in Rust with ratatui.
Release builds include voice playback and stream broadcasting.
cargo install concord --locked
Cargo installations compile Concord locally and require the build dependencies listed below.
brew install concord
# or install the latest release from tap
brew install chojs23/tap/concord
npm install -g @chojs23/concord
# nixpkgs
nix profile install nixpkgs#concord-tui
# Latest GitHub release
nix profile install github:chojs23/concord
This package is maintained by a community contributor and is not yet available in Gentoo GURU.
Install the darwincereska overlay:
eselect repository add darwincereska git https://codeberg.org/darwincereska/gentoo-overlay.git
emaint sync -r darwincereska
emerge -av net-im/concord
Download an archive for Linux, macOS, or Windows from the latest GitHub release, or run the release installer:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/chojs23/concord/releases/latest/download/concord-installer.sh | sh
The installer places concord under $CARGO_HOME/bin.
macOS 13 or later is required. Linux release builds need these packages:
# Fedora
sudo dnf install alsa-lib libva mesa-libEGL pipewire-libs xdg-desktop-portal
# Debian or Ubuntu
sudo apt install libasound2 libegl1 libpipewire-0.3-0 libva-drm2 libva2 xdg-desktop-portal
# Arch Linux
sudo pacman -S alsa-lib libva mesa pipewire xdg-desktop-portal
Screen sharing requires the portal backend for your desktop environment. Concord uses native H.264 hardware encoding when available: VA API on Linux, VideoToolbox on macOS, and Media Foundation on Windows. If hardware encoding is unavailable, Concord falls back to software encoding.
Install the Rust stable toolchain and the native dependencies for your platform:
# macOS
xcode-select --install
brew install cmake pkg-config
brew install nasm # Intel only
# Fedora
sudo dnf install alsa-lib-devel clang-devel cmake gcc gcc-c++ libva-devel nasm pipewire-devel pkgconf-pkg-config
# Debian or Ubuntu
sudo apt install build-essential cmake libasound2-dev libclang-dev libpipewire-0.3-dev libva-dev nasm pkg-config
# Arch Linux
sudo pacman -S alsa-lib base-devel clang cmake libva mesa nasm pipewire pkgconf
Windows source builds require the MSVC Rust toolchain, Visual Studio 2022 Build Tools with Desktop development with C++, CMake, and NASM.
git clone https://github.com/chojs23/concord.git
cd concord
cargo build --release
To keep voice support without stream broadcasting:
cargo build --release --no-default-features --features voice-playback
To build without local voice playback, microphone, or stream broadcasting:
cargo build --release --no-default-features
The same feature flags can be passed to cargo install concord --locked.
CONCORD_TOKEN=your-token before launching Concord. It overrides every credentials.store mode (auto, keychain, plain).Email and QR code logins may trigger a CAPTCHA challenge on Discord's side. We cannot solve that, so I strongly recommend using token authentication.
By default, tokens are saved in the system keychain when available. In the
default auto mode, Concord falls back to its state directory when keychain
storage is unavailable. See the Security section below for details.
Space, Space); start the query with * to search servers instead/@mention autocomplete
Concord renders a practical subset of Discord-style Markdown in message bodies:
# H1, ## H2, ### H3> quoted text- item and * item**bold**, *italic*, __underline__, ~~strikethrough~~, and `inline code`
Image rendering is powered by ratatui-image. On startup, Concord queries the terminal to detect the best available graphics protocol. Supported protocols:
Check the compatibility matrix for terminals that support each protocol.
External media playback is off by default. You can enable it with Display options menu.
Video and audio playback, including live streams, uses mpv. Make sure mpv is installed and in your PATH.
YouTube playback depends on your local mpv setup, such as yt-dlp support.
To broadcast, join a voice channel, open its channel actions, choose
Share screen or use <leader>vs voice shortcut.
Linux screen capture depends on the active X11 or Wayland support.
discord-ipc socket and relays Rich Presence from
connected apps automatically, like the native client: the most recently
updated app becomes your activity, your custom status is kept alongside it,
and the activity clears when the app disconnectsdiscord-ipc socket that answers, so a running
native desktop client (or Vesktop/LegCord/arRPC) receives them instead of
concord; concord shows a warning toast when it detects thisshare_rich_presence under [presence] in config.toml<leader>n) with Unreads and Mentions tabs.⚠️ Keymap action names and default bindings may have breaking changes between releases.
All default key settings in this section can be customized. See Keymap options for the config and supported actions.
With default vim-style navigation:
| Key | Action |
|---|---|
1 2 3 4 | Focus pane |
Tab / Shift+Tab | Cycle focus forward / backward |
h / l, ← / → | Move focus left / right |
j / k, ↑ / ↓, Ctrl+n / Ctrl+p | Move down / up |
J, K / H, L | Scroll viewport |
Ctrl+d / Ctrl+u | Half-page scroll |
Alt+h/l/←/→ | Resize focused pane width |
gg / G | Jump or scroll to top / bottom |
Enter | Open or activate the selected item |
/ | Filter Guilds/Channels, search Messages/Members |
Space | Open leader shortcut window |
i | Text insert mode, or forum post composer |
Esc / q | Close popup, cancel mode, or go back |
q | Quit Concord |
Ctrl+n and Ctrl+p are fixed row movement keys. The default j and k
row movement keys are SelectNext and SelectPrevious and can be changed in
keymap.toml.
| Gesture | Action |
|---|---|
| Left click | Focus a pane, select an item, or use a popup control |
| Double left click | Activate the selected item like Enter |
| Right click | Open the clicked entry actions |
Press Space to open the leader shortcut window.
| Key sequence | Action |
|---|---|
Space, 1 | Toggle the Servers pane |
Space, 2 | Toggle the Channels pane |
Space, 4 | Toggle the Members pane |
Space, a | Open actions for the focused pane |
Space, p | Open my profile settings |
Space, o | Choose concord option category |
Space, n | Open the notification inbox |
Space, r | Clear and fully redraw the TUI |
Space, v | Open voice shortcuts |
Space, Space | Open the fuzzy channel switcher |
These shortcuts act on the selected message when the Messages pane is focused.
| Key | Action |
|---|---|
y | Copy message text |
r | Open the reaction picker |
R | Reply |
d | Delete |
e | Edit |
o | Open URL |
x | Play video or audio |
v | Open the attachment viewer |
Press Space, a to open actions for the focused pane. The menu shows the
available shortcuts and dims actions that do not apply to the current
selection.
The composer supports copied file attachments and editing the current draft in
$EDITOR. Pending uploads appear above the input before sending.
Set KLIPY_API_KEY before launching Concord, or add an opt-in key to
config.toml:
[klipy]
api_key = "your-klipy-key"
# Optional: read a different environment variable (takes precedence over api_key).
# api_key_env = "MY_KLIPY_KEY"
Create a key in the KLIPY Partner Panel with ads disabled. Testing keys allow 100 API requests per hour; request production access through the panel when needed. Configure content filtering in the panel. See the KLIPY integration requirements.
OpenGifPicker can be remapped in [keymap.composer] in keymap.toml.
Supports searching and selecting emoji with the picker. Press : to open the
picker, then type to filter.
When emojis_as_links is enabled, custom emoji your account cannot send
directly are inserted as Discord image links instead.
When the composer input starts with /, Concord shows available application
commands.
Concord reads config.toml, keymap.toml, and theme.toml from
$XDG_CONFIG_HOME/concord when XDG_CONFIG_HOME is set, or from the platform
config directory otherwise.
Machine-local UI state and fallback credentials are stored in
$XDG_STATE_HOME/concord, or in ~/.local/state/concord when
XDG_STATE_HOME is not set.
[display]
# Image protocol: auto, iterm2, kitty, sixel, or halfblocks.
image_protocol = "auto"
# Master switch that hides all image previews when true.
disable_image_preview = false
# Show user avatars next to messages and in profile views.
show_avatars = true
# Render inline image previews for attachments and embeds.
show_images = true
# Allow video and audio media to open in an external player.
media_playback = false
# Preview quality: efficient, balanced, high, or original.
image_preview_quality = "balanced"
# Attachment viewer quality: efficient, balanced, high, or original.
attachment_viewer_quality = "original"
# Which animated GIF and WebP previews keep playing: always, selected, or never.
# Each animated frame rebuilds a terminal graphics protocol, so "always" costs
# roughly 8% of a CPU core per animated preview on screen. Custom emoji animate
# regardless; theirs are small enough not to matter.
animate_previews = "always"
# Render custom Discord emoji as images when possible.
show_custom_emoji = true
# Crop avatars into circles instead of showing square images.
circular_avatars = false
# Show message times in 24-hour format (14:30) instead of 12-hour (02:30 PM).
hour_format_24 = true
[composer]
# Send custom emoji your account cannot use directly as image links.
emojis_as_links = false
[reactions]
# Up to 10 unicode emoji pinned at the top of the reaction picker.
# When unset or empty, Concord uses the built-in quick reactions.
# favorite_emojis = ["🔥", "👍", "❤️", "😂", "🎉", "😮", "😢", "🙏", "👀", "💯"]
[presence]
# Relay Rich Presence from local apps as your activity (most recent app wins,
# like the native client).
share_rich_presence = true
[translation]
# Translate selected messages and composer drafts when their target is set.
# Supported providers: deepl or libretranslate.
# provider = "deepl"
# message_target_language = "KO"
# composer_target_language = "EN"
# Optional full API endpoint override. DeepL defaults to its Free API endpoint.
# LibreTranslate defaults to http://127.0.0.1:5000/translate.
# endpoint = "https://api.deepl.com/v2/translate"
# Optional API key stored directly in this private config file.
# api_key = "your-api-key"
# Optional environment variable containing the API key. When set, its value
# overrides api_key. DeepL checks DEEPL_API_KEY by default.
# api_key_env = "DEEPL_API_KEY"
[credentials]
# Credential storage: auto, keychain, or plain.
# auto tries the system keychain first and falls back to the state file.
store = "auto"
[notifications]
# Show desktop notifications for Discord messages that pass notification rules.
desktop_notifications = true
# Optional notification icon to include in notifications. May not work on all platforms.
# When unset, no icon is used. It must either be a name of an icon (typically in /usr/share/icons)
# or a path to an icon.
# notification_icon = "/path/to/icon.svg"
# Optional WAV files for message, voice join/leave notification sounds.
# When unset, Concord uses built-in generated tones.
# notification_sound = "/path/to/message.wav"
# voice_join_sound = "/path/to/join.wav"
# voice_leave_sound = "/path/to/leave.wav"
[voice]
# Join or update Discord voice with Concord self-muted.
self_mute = false
# Join or update Discord voice with Concord self-deafened.
self_deaf = false
# Optional CPAL device IDs chosen from Voice Options. When omitted or no longer
# available, Concord uses the current system default input or output device.
# input_source = "CoreAudio:input-id"
# output_source = "CoreAudio:output-id"
# Allow microphone transmit while this session is joined and not self-muted.
allow_microphone_transmit = false
# Require holding the configured shortcut to transmit.
push_to_talk = false
# Global push-to-talk shortcut. The key works even when Concord is not focused.
# Modifiers can be combined before the key, for example "control+shift+F8".
push_to_talk_shortcut = "F8"
# Reduce steady background microphone noise.
noise_suppression = true
# Optional microphone capture buffer duration in milliseconds, from 10 to 60.
# When omitted, Concord requests 50ms on Linux and 10ms on other platforms,
# clamped to the device's supported range. It uses the host default only when
# the range is unknown or the platform fixed buffer cannot be opened.
# Setting a value forces that fixed duration.
# microphone_buffer_ms = 50
# Voice activity threshold in dB. Lower values transmit quieter input.
microphone_sensitivity = -30
# Microphone input volume percentage, from 0 to 200.
microphone_volume = 100
# Received voice playback volume percentage, from 0 to 200.
voice_output_volume = 100
macOS may require Input Monitoring permission for Concord or the terminal that started it. On Linux Wayland, the compositor manages global shortcuts, so Concord cannot guarantee that the shortcut input also reaches the focused application.
Press uppercase T in the Messages pane to translate the selected message.
While editing the message composer, press Ctrl-T to translate the current
draft. The composer shows the original above a translated divider and makes
the translated draft editable. Press Ctrl-T again to return to the editable
original.
[translation]
provider = "deepl"
message_target_language = "KO"
composer_target_language = "EN"
api_key = "your-deepl-api-key"
Both providers detect the source language automatically.
The default DeepL endpoint is the Free API. DeepL Pro users can set
endpoint = "https://api.deepl.com/v2/translate".
The DEEPL_API_KEY environment variable overrides api_key when present.
LibreTranslate defaults to a local server and does not require an API key:
[translation]
provider = "libretranslate"
message_target_language = "KO"
composer_target_language = "EN"
Set endpoint and api_key when using a hosted LibreTranslate instance.
You can use api_key_env instead when an environment-based secret is more
convenient.
See Keymap options for the config format and supported actions.
[keymap]
leader = "space"
StartComposer = "i"
OpenPaneFilter = "/"
ClosePopup = "q"
OpenDebugPanel = "`"
FocusGuildPane = "1"
FocusChannelPane = "2"
FocusMessagePane = "3"
FocusMemberPane = "4"
SelectNext = "j"
SelectPrevious = "k"
CycleFocusNext = { keys = ["tab", "l", "right"] }
CycleFocusPrevious = { keys = ["<S-tab>", "h", "left"] }
HalfPageDown = "<C-d>"
HalfPageUp = "<C-u>"
ScrollViewportDown = "J"
ScrollViewportUp = "K"
JumpTop = "gg"
JumpBottom = "G"
ScrollHorizontalLeft = "H"
ScrollHorizontalRight = "L"
ResizePaneLeft = { keys = ["<A-h>", "<A-left>"] }
ResizePaneRight = { keys = ["<A-l>", "<A-right>"] }
Quit = "q"
CopyMessage = "y"
TranslateMessage = "T"
ReactMessage = "r"
ReplyMessage = "R"
DeleteMessage = "d"
EditMessage = "e"
OpenMessageUrl = "o"
PlayMedia = "x"
ViewMessageAttachment = "v"
ToggleGuildPane = "<leader>1"
ToggleChannelPane = "<leader>2"
ToggleMemberPane = "<leader>4"
OpenFocusedPaneAction = "<leader>a"
OpenCurrentUserProfile = "<leader>p"
OpenOptions = "<leader>o"
OpenNotificationInbox = "<leader>n"
RefreshScreen = "<leader>r"
ChannelSwitcher = "<leader><leader>"
VoiceDeafen = "<leader>vd"
VoiceMute = "<leader>vm"
ToggleStream = "<leader>vs"
VoiceLeave = "<leader>vl"
[keymap.groups]
"<leader>v" = "Voice"
[keymap.notification_inbox_actions]
MarkRead = "r"
MarkAllRead = "a"
[keymap.guild_actions]
MarkAsRead = "m"
ToggleMute = "u"
LeaveServer = "l"
FolderSettings = "r"
[keymap.channel_actions]
JoinVoice = "e"
LeaveVoice = "l"
ToggleStream = "s"
WatchStream = "w"
VoiceParticipantAudio = "a"
ShowPinnedMessages = "p"
ShowThreads = "t"
MarkAsRead = "m"
ToggleMute = "u"
[keymap.message_actions]
CopyMessage = "y"
TranslateMessage = "T"
ReactMessage = "r"
ReplyMessage = "R"
DeleteMessage = "d"
EditMessage = "e"
OpenMessageUrl = "o"
RemoveMessageEmbeds = "D"
PlayMedia = "x"
ViewMessageAttachment = "v"
GoToReferencedMessage = "g"
ShowMessageProfile = "p"
PinMessage = "P"
OpenThread = "t"
ShowReactionUsers = "u"
OpenPollVotePicker = "c"
[keymap.member_actions]
ShowProfile = "p"
[keymap.thread_actions]
MarkAsRead = "m"
ToggleFollow = "f"
Close = "c"
Lock = "l"
Edit = "e"
CopyLink = "y"
ToggleMute = "u"
NotificationSettings = "n"
Pin = "P"
Delete = "d"
CopyId = "i"
[keymap.composer]
OpenEditor = "<C-e>"
PasteClipboard = "<C-v>"
InsertNewline = { keys = ["<C-j>", "<S-enter>", "<C-enter>", "<A-enter>"] }
Submit = "enter"
Close = "esc"
ClearInput = "<C-c>"
RemoveLastAttachment = "delete"
TranslateComposer = "<C-t>"
OpenGifPicker = "<C-g>"
DeletePreviousChar = "backspace"
DeletePreviousWord = { keys = ["<A-backspace>", "<C-backspace>", "<C-w>"] }
DeleteToLineStart = "<C-u>"
DeleteToLineEnd = "<C-k>"
MoveCursorUp = "up"
MoveCursorDown = "down"
MoveCursorWordLeft = "<C-left>"
MoveCursorLeft = "left"
MoveCursorWordRight = "<C-right>"
MoveCursorRight = "right"
MoveCursorHome = "home"
MoveCursorEnd = "end"
ToggleReplyPing = "<A-p>"
See Theme options for available groups, values, border shapes, and selection marker settings.
# Complete Highlight Group reference with Concord's built-in defaults.
# Copy this configuration to `theme.toml` in the Concord config directory,
# then keep only the groups you want to override.
#
# A group accepts `link`, `foreground`, `background`, `bold`, `italic`, `dim`,
# `underline`, and `strikethrough`. Colors accept `none`, `terminal_default`, a
# canonical lowercase ANSI name, or six-digit RGB such as `#39ff14`. `none`
# clears that color channel after inheritance. A linked group inherits fields
# it does not set. Use `link = "none"` to detach a built-in link.
# Border shapes are UI geometry rather than Highlight Group styles. An omitted
# surface uses `default`. These values reproduce Concord's built-in layout.
[ui.border]
default = "plain"
composer = "rounded"
message = "rounded"
forum = "rounded"
# Selection markers are UI glyphs. This value is shared by list panes and
# pickers, while `highlight.SelectionMarker` controls its style.
[ui.indicator]
selection = "▸ "
[highlight.Normal]
foreground = "terminal_default"
background = "terminal_default"
[highlight.Strong]
bold = true
[highlight.Emphasis]
italic = true
[highlight.Muted]
dim = true
[highlight.Title]
link = "Strong"
[highlight.Heading]
link = "Strong"
[highlight.Decoration]
link = "Muted"
[highlight.Hint]
link = "Muted"
[highlight.Description]
link = "Muted"
[highlight.Shortcut]
link = "Muted"
[highlight.Activity]
link = "Muted"
[highlight.ChannelTypeMarker]
link = "Muted"
[highlight.FieldLabel]
link = "Muted"
[highlight.SearchContext]
link = "Muted"
[highlight.Timestamp]
link = "Muted"
[highlight.Placeholder]
link = "Muted"
[highlight.Disabled]
link = "Muted"
[highlight.Loading]
link = "Muted"
[highlight.Edited]
link = "Muted"
italic = true
[highlight.Unavailable]
link = "Muted"
strikethrough = true
[highlight.LoginTitle]
link = "Title"
foreground = "cyan"
[highlight.LoginHint]
link = "Muted"
[highlight.PaneTitle]
link = "Title"
[highlight.ModalTitle]
link = "Title"
[highlight.ComposerTitle]
link = "Title"
[highlight.HeaderTitle]
link = "Title"
foreground = "cyan"
[highlight.HeaderLabel]
link = "Muted"
[highlight.MessageAuthor]
link = "Strong"
[highlight.MessageTimestamp]
link = "Timestamp"
[highlight.CategoryHeading]
link = "Heading"
[highlight.MemberGroupHeading]
link = "Heading"
[highlight.MessageSecondary]
link = "Muted"
[highlight.ForumSecondary]
link = "Muted"
[highlight.EmbedAuthor]
link = "Emphasis"
[highlight.EmbedTitle]
link = "Strong"
foreground = "blue"
[highlight.EmbedFieldName]
link = "Strong"
underline = true
[highlight.EmbedFooter]
link = "Muted"
italic = true
[highlight.CodeBlockBorder]
link = "Border"
dim = true
[highlight.ScrollbarTrack]
link = "ScrollbarThumb"
dim = true
[highlight.UnavailableEmoji]
link = "Unavailable"
[highlight.HeaderError]
link = "Error"
bold = true
[highlight.HeaderWarning]
link = "Warning"
bold = true
[highlight.Border]
foreground = "dark_gray"
[highlight.FocusBorder]
foreground = "cyan"
[highlight.Selection]
foreground = "cyan"
background = "none"
bold = true
dim = false
[highlight.SelectionBorder]
foreground = "green"
bold = true
[highlight.PaneBorder]
link = "Border"
[highlight.FocusedPaneBorder]
link = "FocusBorder"
bold = true
[highlight.LoginBorder]
link = "FocusBorder"
[highlight.ComposerBorder]
link = "Border"
[highlight.ActiveComposerBorder]
link = "FocusBorder"
bold = true
[highlight.ModalBorder]
link = "FocusBorder"
bold = true
[highlight.ComposerPickerBorder]
link = "FocusBorder"
[highlight.SelectedRow]
link = "Selection"
[highlight.SelectionMarker]
link = "Selection"
[highlight.ActiveField]
foreground = "cyan"
bold = true
[highlight.ActiveTab]
link = "Selection"
[highlight.MessageSelectedBorder]
link = "SelectionBorder"
[highlight.ForumBorder]
link = "FocusBorder"
[highlight.ForumSelectedBorder]
link = "SelectionBorder"
[highlight.ScrollbarThumb]
foreground = "#AAAAAA"
[highlight.UnreadNotice]
foreground = "cyan"
bold = true
[highlight.Editing]
foreground = "yellow"
[highlight.Reaction]
foreground = "cyan"
[highlight.SelfReaction]
foreground = "yellow"
[highlight.PresenceOnline]
foreground = "green"
[highlight.PresenceIdle]
foreground = "#B48C00"
[highlight.PresenceDnd]
foreground = "red"
[highlight.PresenceOffline]
link = "Normal"
dim = true
[highlight.VoiceDisabled]
foreground = "yellow"
[highlight.VoiceConnection]
foreground = "yellow"
bold = true
[highlight.FolderFallback]
foreground = "cyan"
[highlight.NavigationActive]
foreground = "green"
bold = true
[highlight.NavigationMentioned]
foreground = "#FFA500"
[highlight.NavigationNotified]
foreground = "terminal_default"
[highlight.NavigationUnread]
foreground = "terminal_default"
[highlight.MentionBadge]
foreground = "#FFA500"
[highlight.NotificationBadge]
foreground = "terminal_default"
[highlight.JoinedVoiceChannel]
foreground = "yellow"
bold = true
[highlight.VoiceSpeaking]
foreground = "green"
bold = true
[highlight.ReplyPingEnabled]
foreground = "cyan"
[highlight.Tag]
foreground = "cyan"
[highlight.RelationshipFriend]
foreground = "green"
[highlight.RelationshipIncoming]
foreground = "yellow"
[highlight.RelationshipOutgoing]
foreground = "yellow"
[highlight.RelationshipBlocked]
foreground = "red"
[highlight.RelationshipNone]
foreground = "terminal_default"
dim = true
[highlight.GaugeFill]
foreground = "cyan"
[highlight.MessageBody]
foreground = "terminal_default"
[highlight.MarkdownHeading1]
link = "Heading"
foreground = "cyan"
[highlight.MarkdownHeading2]
link = "Heading"
underline = true
[highlight.MarkdownHeading3]
link = "Heading"
[highlight.MarkdownQuote]
foreground = "dark_gray"
[highlight.MarkdownMarker]
foreground = "dark_gray"
[highlight.MessageAttachment]
foreground = "cyan"
[highlight.ImageOverflow]
link = "MessageAttachment"
bold = true
[highlight.InlineCode]
foreground = "#FFA500"
[highlight.InlineTimestamp]
foreground = "#DBDEE1"
background = "#3B3C43"
[highlight.MessageLink]
foreground = "cyan"
underline = true
[highlight.MentionSelf]
foreground = "yellow"
background = "#5C4C23"
[highlight.MentionOther]
foreground = "#C1CEF7"
background = "#28325C"
[highlight.MentionRole]
# Discord supplies the foreground and a darker role-color background by default.
# This group controls colored role mentions that do not notify the current user.
# Notifying role mentions use MentionSelf with the Discord role foreground.
[highlight.MentionPickerRole]
foreground = "magenta"
[highlight.EmbedGutter]
foreground = "red"
[highlight.EmbedLink]
foreground = "blue"
underline = true
[highlight.CommandName]
link = "MessageSecondary"
foreground = "#5865F2"
[highlight.SystemThreadName]
foreground = "cyan"
bold = true
[highlight.PollAnswerSelected]
foreground = "terminal_default"
bold = true
[highlight.PollWinner]
foreground = "terminal_default"
bold = true
[highlight.UnreadBanner]
foreground = "terminal_default"
background = "#5865F2"
[highlight.UnreadDivider]
foreground = "#ED4245"
[highlight.ForumPinnedBadge]
foreground = "yellow"
bold = true
[highlight.BotBadge]
link = "Normal"
background = "#5865F2"
bold = true
[highlight.Error]
foreground = "red"
[highlight.Warning]
foreground = "yellow"
[highlight.Success]
foreground = "green"
[highlight.Info]
foreground = "cyan"
Concord is designed to stay lightweight in normal terminal use. In observed typical use, it usually uses about 20-40 MB of memory.
Image-heavy screens can temporarily use more memory because compressed image bytes need to be decoded before they can be rendered in the terminal. When many images are loaded, memory can briefly rise to around 100-200 MB while decoding and then drop again as work completes and caches are pruned.
There are some path that did trigger a temporary block:
Other features have not caused blocks in my testing.
That said, Concord is not an official Discord client. Using unofficial clients, automated user accounts, or self-bots can violate Discord's TOS, so there is always some risk. Use it at your own discretion.
CONCORD_TOKEN environment variable lets you provide a token without writing it to disk. This avoids leaving plaintext on disk, but the token is visible in /proc/<pid>/environ and in process listings while Concord is running.credentials.store = "auto", Concord falls back to plain text credentials under Concord's state directory when keychain storage is unavailable. In keychain mode, Concord does not fall back to plain storage. Keep fallback credential files secure and do not share them. You can use a token from that file to log in to the official Discord client, so treat it like a password.0700 and the credential file with 0600 permissions.Any issues, pull requests, and feedback are welcome. See CONTRIBUTING.md for details.
Unicode emoji images are provided by Twemoji, Copyright 2020 Twitter, Inc. and other contributors, under the CC-BY 4.0 license.
Concord is licensed under GPL-3.0-only.
931 followers · starred May 2026
269 followers · starred May 2026
907 followers · starred May 2026
617 followers · starred Jun 2026
A feature-rich TUI client for Discord
See the code
Concord is a feature-rich TUI client for Discord, written in Rust with ratatui.
Release builds include voice playback and stream broadcasting.
cargo install concord --locked
Cargo installations compile Concord locally and require the build dependencies listed below.
brew install concord
# or install the latest release from tap
brew install chojs23/tap/concord
npm install -g @chojs23/concord
# nixpkgs
nix profile install nixpkgs#concord-tui
# Latest GitHub release
nix profile install github:chojs23/concord
This package is maintained by a community contributor and is not yet available in Gentoo GURU.
Install the darwincereska overlay:
eselect repository add darwincereska git https://codeberg.org/darwincereska/gentoo-overlay.git
emaint sync -r darwincereska
emerge -av net-im/concord
Download an archive for Linux, macOS, or Windows from the latest GitHub release, or run the release installer:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/chojs23/concord/releases/latest/download/concord-installer.sh | sh
The installer places concord under $CARGO_HOME/bin.
macOS 13 or later is required. Linux release builds need these packages:
# Fedora
sudo dnf install alsa-lib libva mesa-libEGL pipewire-libs xdg-desktop-portal
# Debian or Ubuntu
sudo apt install libasound2 libegl1 libpipewire-0.3-0 libva-drm2 libva2 xdg-desktop-portal
# Arch Linux
sudo pacman -S alsa-lib libva mesa pipewire xdg-desktop-portal
Screen sharing requires the portal backend for your desktop environment. Concord uses native H.264 hardware encoding when available: VA API on Linux, VideoToolbox on macOS, and Media Foundation on Windows. If hardware encoding is unavailable, Concord falls back to software encoding.
Install the Rust stable toolchain and the native dependencies for your platform:
# macOS
xcode-select --install
brew install cmake pkg-config
brew install nasm # Intel only
# Fedora
sudo dnf install alsa-lib-devel clang-devel cmake gcc gcc-c++ libva-devel nasm pipewire-devel pkgconf-pkg-config
# Debian or Ubuntu
sudo apt install build-essential cmake libasound2-dev libclang-dev libpipewire-0.3-dev libva-dev nasm pkg-config
# Arch Linux
sudo pacman -S alsa-lib base-devel clang cmake libva mesa nasm pipewire pkgconf
Windows source builds require the MSVC Rust toolchain, Visual Studio 2022 Build Tools with Desktop development with C++, CMake, and NASM.
git clone https://github.com/chojs23/concord.git
cd concord
cargo build --release
To keep voice support without stream broadcasting:
cargo build --release --no-default-features --features voice-playback
To build without local voice playback, microphone, or stream broadcasting:
cargo build --release --no-default-features
The same feature flags can be passed to cargo install concord --locked.
CONCORD_TOKEN=your-token before launching Concord. It overrides every credentials.store mode (auto, keychain, plain).Email and QR code logins may trigger a CAPTCHA challenge on Discord's side. We cannot solve that, so I strongly recommend using token authentication.
By default, tokens are saved in the system keychain when available. In the
default auto mode, Concord falls back to its state directory when keychain
storage is unavailable. See the Security section below for details.
Space, Space); start the query with * to search servers instead/@mention autocomplete
Concord renders a practical subset of Discord-style Markdown in message bodies:
# H1, ## H2, ### H3> quoted text- item and * item**bold**, *italic*, __underline__, ~~strikethrough~~, and `inline code`
Image rendering is powered by ratatui-image. On startup, Concord queries the terminal to detect the best available graphics protocol. Supported protocols:
Check the compatibility matrix for terminals that support each protocol.
External media playback is off by default. You can enable it with Display options menu.
Video and audio playback, including live streams, uses mpv. Make sure mpv is installed and in your PATH.
YouTube playback depends on your local mpv setup, such as yt-dlp support.
To broadcast, join a voice channel, open its channel actions, choose
Share screen or use <leader>vs voice shortcut.
Linux screen capture depends on the active X11 or Wayland support.
discord-ipc socket and relays Rich Presence from
connected apps automatically, like the native client: the most recently
updated app becomes your activity, your custom status is kept alongside it,
and the activity clears when the app disconnectsdiscord-ipc socket that answers, so a running
native desktop client (or Vesktop/LegCord/arRPC) receives them instead of
concord; concord shows a warning toast when it detects thisshare_rich_presence under [presence] in config.toml<leader>n) with Unreads and Mentions tabs.⚠️ Keymap action names and default bindings may have breaking changes between releases.
All default key settings in this section can be customized. See Keymap options for the config and supported actions.
With default vim-style navigation:
| Key | Action |
|---|---|
1 2 3 4 | Focus pane |
Tab / Shift+Tab | Cycle focus forward / backward |
h / l, ← / → | Move focus left / right |
j / k, ↑ / ↓, Ctrl+n / Ctrl+p | Move down / up |
J, K / H, L | Scroll viewport |
Ctrl+d / Ctrl+u | Half-page scroll |
Alt+h/l/←/→ | Resize focused pane width |
gg / G | Jump or scroll to top / bottom |
Enter | Open or activate the selected item |
/ | Filter Guilds/Channels, search Messages/Members |
Space | Open leader shortcut window |
i | Text insert mode, or forum post composer |
Esc / q | Close popup, cancel mode, or go back |
q | Quit Concord |
Ctrl+n and Ctrl+p are fixed row movement keys. The default j and k
row movement keys are SelectNext and SelectPrevious and can be changed in
keymap.toml.
| Gesture | Action |
|---|---|
| Left click | Focus a pane, select an item, or use a popup control |
| Double left click | Activate the selected item like Enter |
| Right click | Open the clicked entry actions |
Press Space to open the leader shortcut window.
| Key sequence | Action |
|---|---|
Space, 1 | Toggle the Servers pane |
Space, 2 | Toggle the Channels pane |
Space, 4 | Toggle the Members pane |
Space, a | Open actions for the focused pane |
Space, p | Open my profile settings |
Space, o | Choose concord option category |
Space, n | Open the notification inbox |
Space, r | Clear and fully redraw the TUI |
Space, v | Open voice shortcuts |
Space, Space | Open the fuzzy channel switcher |
These shortcuts act on the selected message when the Messages pane is focused.
| Key | Action |
|---|---|
y | Copy message text |
r | Open the reaction picker |
R | Reply |
d | Delete |
e | Edit |
o | Open URL |
x | Play video or audio |
v | Open the attachment viewer |
Press Space, a to open actions for the focused pane. The menu shows the
available shortcuts and dims actions that do not apply to the current
selection.
The composer supports copied file attachments and editing the current draft in
$EDITOR. Pending uploads appear above the input before sending.
Set KLIPY_API_KEY before launching Concord, or add an opt-in key to
config.toml:
[klipy]
api_key = "your-klipy-key"
# Optional: read a different environment variable (takes precedence over api_key).
# api_key_env = "MY_KLIPY_KEY"
Create a key in the KLIPY Partner Panel with ads disabled. Testing keys allow 100 API requests per hour; request production access through the panel when needed. Configure content filtering in the panel. See the KLIPY integration requirements.
OpenGifPicker can be remapped in [keymap.composer] in keymap.toml.
Supports searching and selecting emoji with the picker. Press : to open the
picker, then type to filter.
When emojis_as_links is enabled, custom emoji your account cannot send
directly are inserted as Discord image links instead.
When the composer input starts with /, Concord shows available application
commands.
Concord reads config.toml, keymap.toml, and theme.toml from
$XDG_CONFIG_HOME/concord when XDG_CONFIG_HOME is set, or from the platform
config directory otherwise.
Machine-local UI state and fallback credentials are stored in
$XDG_STATE_HOME/concord, or in ~/.local/state/concord when
XDG_STATE_HOME is not set.
[display]
# Image protocol: auto, iterm2, kitty, sixel, or halfblocks.
image_protocol = "auto"
# Master switch that hides all image previews when true.
disable_image_preview = false
# Show user avatars next to messages and in profile views.
show_avatars = true
# Render inline image previews for attachments and embeds.
show_images = true
# Allow video and audio media to open in an external player.
media_playback = false
# Preview quality: efficient, balanced, high, or original.
image_preview_quality = "balanced"
# Attachment viewer quality: efficient, balanced, high, or original.
attachment_viewer_quality = "original"
# Which animated GIF and WebP previews keep playing: always, selected, or never.
# Each animated frame rebuilds a terminal graphics protocol, so "always" costs
# roughly 8% of a CPU core per animated preview on screen. Custom emoji animate
# regardless; theirs are small enough not to matter.
animate_previews = "always"
# Render custom Discord emoji as images when possible.
show_custom_emoji = true
# Crop avatars into circles instead of showing square images.
circular_avatars = false
# Show message times in 24-hour format (14:30) instead of 12-hour (02:30 PM).
hour_format_24 = true
[composer]
# Send custom emoji your account cannot use directly as image links.
emojis_as_links = false
[reactions]
# Up to 10 unicode emoji pinned at the top of the reaction picker.
# When unset or empty, Concord uses the built-in quick reactions.
# favorite_emojis = ["🔥", "👍", "❤️", "😂", "🎉", "😮", "😢", "🙏", "👀", "💯"]
[presence]
# Relay Rich Presence from local apps as your activity (most recent app wins,
# like the native client).
share_rich_presence = true
[translation]
# Translate selected messages and composer drafts when their target is set.
# Supported providers: deepl or libretranslate.
# provider = "deepl"
# message_target_language = "KO"
# composer_target_language = "EN"
# Optional full API endpoint override. DeepL defaults to its Free API endpoint.
# LibreTranslate defaults to http://127.0.0.1:5000/translate.
# endpoint = "https://api.deepl.com/v2/translate"
# Optional API key stored directly in this private config file.
# api_key = "your-api-key"
# Optional environment variable containing the API key. When set, its value
# overrides api_key. DeepL checks DEEPL_API_KEY by default.
# api_key_env = "DEEPL_API_KEY"
[credentials]
# Credential storage: auto, keychain, or plain.
# auto tries the system keychain first and falls back to the state file.
store = "auto"
[notifications]
# Show desktop notifications for Discord messages that pass notification rules.
desktop_notifications = true
# Optional notification icon to include in notifications. May not work on all platforms.
# When unset, no icon is used. It must either be a name of an icon (typically in /usr/share/icons)
# or a path to an icon.
# notification_icon = "/path/to/icon.svg"
# Optional WAV files for message, voice join/leave notification sounds.
# When unset, Concord uses built-in generated tones.
# notification_sound = "/path/to/message.wav"
# voice_join_sound = "/path/to/join.wav"
# voice_leave_sound = "/path/to/leave.wav"
[voice]
# Join or update Discord voice with Concord self-muted.
self_mute = false
# Join or update Discord voice with Concord self-deafened.
self_deaf = false
# Optional CPAL device IDs chosen from Voice Options. When omitted or no longer
# available, Concord uses the current system default input or output device.
# input_source = "CoreAudio:input-id"
# output_source = "CoreAudio:output-id"
# Allow microphone transmit while this session is joined and not self-muted.
allow_microphone_transmit = false
# Require holding the configured shortcut to transmit.
push_to_talk = false
# Global push-to-talk shortcut. The key works even when Concord is not focused.
# Modifiers can be combined before the key, for example "control+shift+F8".
push_to_talk_shortcut = "F8"
# Reduce steady background microphone noise.
noise_suppression = true
# Optional microphone capture buffer duration in milliseconds, from 10 to 60.
# When omitted, Concord requests 50ms on Linux and 10ms on other platforms,
# clamped to the device's supported range. It uses the host default only when
# the range is unknown or the platform fixed buffer cannot be opened.
# Setting a value forces that fixed duration.
# microphone_buffer_ms = 50
# Voice activity threshold in dB. Lower values transmit quieter input.
microphone_sensitivity = -30
# Microphone input volume percentage, from 0 to 200.
microphone_volume = 100
# Received voice playback volume percentage, from 0 to 200.
voice_output_volume = 100
macOS may require Input Monitoring permission for Concord or the terminal that started it. On Linux Wayland, the compositor manages global shortcuts, so Concord cannot guarantee that the shortcut input also reaches the focused application.
Press uppercase T in the Messages pane to translate the selected message.
While editing the message composer, press Ctrl-T to translate the current
draft. The composer shows the original above a translated divider and makes
the translated draft editable. Press Ctrl-T again to return to the editable
original.
[translation]
provider = "deepl"
message_target_language = "KO"
composer_target_language = "EN"
api_key = "your-deepl-api-key"
Both providers detect the source language automatically.
The default DeepL endpoint is the Free API. DeepL Pro users can set
endpoint = "https://api.deepl.com/v2/translate".
The DEEPL_API_KEY environment variable overrides api_key when present.
LibreTranslate defaults to a local server and does not require an API key:
[translation]
provider = "libretranslate"
message_target_language = "KO"
composer_target_language = "EN"
Set endpoint and api_key when using a hosted LibreTranslate instance.
You can use api_key_env instead when an environment-based secret is more
convenient.
See Keymap options for the config format and supported actions.
[keymap]
leader = "space"
StartComposer = "i"
OpenPaneFilter = "/"
ClosePopup = "q"
OpenDebugPanel = "`"
FocusGuildPane = "1"
FocusChannelPane = "2"
FocusMessagePane = "3"
FocusMemberPane = "4"
SelectNext = "j"
SelectPrevious = "k"
CycleFocusNext = { keys = ["tab", "l", "right"] }
CycleFocusPrevious = { keys = ["<S-tab>", "h", "left"] }
HalfPageDown = "<C-d>"
HalfPageUp = "<C-u>"
ScrollViewportDown = "J"
ScrollViewportUp = "K"
JumpTop = "gg"
JumpBottom = "G"
ScrollHorizontalLeft = "H"
ScrollHorizontalRight = "L"
ResizePaneLeft = { keys = ["<A-h>", "<A-left>"] }
ResizePaneRight = { keys = ["<A-l>", "<A-right>"] }
Quit = "q"
CopyMessage = "y"
TranslateMessage = "T"
ReactMessage = "r"
ReplyMessage = "R"
DeleteMessage = "d"
EditMessage = "e"
OpenMessageUrl = "o"
PlayMedia = "x"
ViewMessageAttachment = "v"
ToggleGuildPane = "<leader>1"
ToggleChannelPane = "<leader>2"
ToggleMemberPane = "<leader>4"
OpenFocusedPaneAction = "<leader>a"
OpenCurrentUserProfile = "<leader>p"
OpenOptions = "<leader>o"
OpenNotificationInbox = "<leader>n"
RefreshScreen = "<leader>r"
ChannelSwitcher = "<leader><leader>"
VoiceDeafen = "<leader>vd"
VoiceMute = "<leader>vm"
ToggleStream = "<leader>vs"
VoiceLeave = "<leader>vl"
[keymap.groups]
"<leader>v" = "Voice"
[keymap.notification_inbox_actions]
MarkRead = "r"
MarkAllRead = "a"
[keymap.guild_actions]
MarkAsRead = "m"
ToggleMute = "u"
LeaveServer = "l"
FolderSettings = "r"
[keymap.channel_actions]
JoinVoice = "e"
LeaveVoice = "l"
ToggleStream = "s"
WatchStream = "w"
VoiceParticipantAudio = "a"
ShowPinnedMessages = "p"
ShowThreads = "t"
MarkAsRead = "m"
ToggleMute = "u"
[keymap.message_actions]
CopyMessage = "y"
TranslateMessage = "T"
ReactMessage = "r"
ReplyMessage = "R"
DeleteMessage = "d"
EditMessage = "e"
OpenMessageUrl = "o"
RemoveMessageEmbeds = "D"
PlayMedia = "x"
ViewMessageAttachment = "v"
GoToReferencedMessage = "g"
ShowMessageProfile = "p"
PinMessage = "P"
OpenThread = "t"
ShowReactionUsers = "u"
OpenPollVotePicker = "c"
[keymap.member_actions]
ShowProfile = "p"
[keymap.thread_actions]
MarkAsRead = "m"
ToggleFollow = "f"
Close = "c"
Lock = "l"
Edit = "e"
CopyLink = "y"
ToggleMute = "u"
NotificationSettings = "n"
Pin = "P"
Delete = "d"
CopyId = "i"
[keymap.composer]
OpenEditor = "<C-e>"
PasteClipboard = "<C-v>"
InsertNewline = { keys = ["<C-j>", "<S-enter>", "<C-enter>", "<A-enter>"] }
Submit = "enter"
Close = "esc"
ClearInput = "<C-c>"
RemoveLastAttachment = "delete"
TranslateComposer = "<C-t>"
OpenGifPicker = "<C-g>"
DeletePreviousChar = "backspace"
DeletePreviousWord = { keys = ["<A-backspace>", "<C-backspace>", "<C-w>"] }
DeleteToLineStart = "<C-u>"
DeleteToLineEnd = "<C-k>"
MoveCursorUp = "up"
MoveCursorDown = "down"
MoveCursorWordLeft = "<C-left>"
MoveCursorLeft = "left"
MoveCursorWordRight = "<C-right>"
MoveCursorRight = "right"
MoveCursorHome = "home"
MoveCursorEnd = "end"
ToggleReplyPing = "<A-p>"
See Theme options for available groups, values, border shapes, and selection marker settings.
# Complete Highlight Group reference with Concord's built-in defaults.
# Copy this configuration to `theme.toml` in the Concord config directory,
# then keep only the groups you want to override.
#
# A group accepts `link`, `foreground`, `background`, `bold`, `italic`, `dim`,
# `underline`, and `strikethrough`. Colors accept `none`, `terminal_default`, a
# canonical lowercase ANSI name, or six-digit RGB such as `#39ff14`. `none`
# clears that color channel after inheritance. A linked group inherits fields
# it does not set. Use `link = "none"` to detach a built-in link.
# Border shapes are UI geometry rather than Highlight Group styles. An omitted
# surface uses `default`. These values reproduce Concord's built-in layout.
[ui.border]
default = "plain"
composer = "rounded"
message = "rounded"
forum = "rounded"
# Selection markers are UI glyphs. This value is shared by list panes and
# pickers, while `highlight.SelectionMarker` controls its style.
[ui.indicator]
selection = "▸ "
[highlight.Normal]
foreground = "terminal_default"
background = "terminal_default"
[highlight.Strong]
bold = true
[highlight.Emphasis]
italic = true
[highlight.Muted]
dim = true
[highlight.Title]
link = "Strong"
[highlight.Heading]
link = "Strong"
[highlight.Decoration]
link = "Muted"
[highlight.Hint]
link = "Muted"
[highlight.Description]
link = "Muted"
[highlight.Shortcut]
link = "Muted"
[highlight.Activity]
link = "Muted"
[highlight.ChannelTypeMarker]
link = "Muted"
[highlight.FieldLabel]
link = "Muted"
[highlight.SearchContext]
link = "Muted"
[highlight.Timestamp]
link = "Muted"
[highlight.Placeholder]
link = "Muted"
[highlight.Disabled]
link = "Muted"
[highlight.Loading]
link = "Muted"
[highlight.Edited]
link = "Muted"
italic = true
[highlight.Unavailable]
link = "Muted"
strikethrough = true
[highlight.LoginTitle]
link = "Title"
foreground = "cyan"
[highlight.LoginHint]
link = "Muted"
[highlight.PaneTitle]
link = "Title"
[highlight.ModalTitle]
link = "Title"
[highlight.ComposerTitle]
link = "Title"
[highlight.HeaderTitle]
link = "Title"
foreground = "cyan"
[highlight.HeaderLabel]
link = "Muted"
[highlight.MessageAuthor]
link = "Strong"
[highlight.MessageTimestamp]
link = "Timestamp"
[highlight.CategoryHeading]
link = "Heading"
[highlight.MemberGroupHeading]
link = "Heading"
[highlight.MessageSecondary]
link = "Muted"
[highlight.ForumSecondary]
link = "Muted"
[highlight.EmbedAuthor]
link = "Emphasis"
[highlight.EmbedTitle]
link = "Strong"
foreground = "blue"
[highlight.EmbedFieldName]
link = "Strong"
underline = true
[highlight.EmbedFooter]
link = "Muted"
italic = true
[highlight.CodeBlockBorder]
link = "Border"
dim = true
[highlight.ScrollbarTrack]
link = "ScrollbarThumb"
dim = true
[highlight.UnavailableEmoji]
link = "Unavailable"
[highlight.HeaderError]
link = "Error"
bold = true
[highlight.HeaderWarning]
link = "Warning"
bold = true
[highlight.Border]
foreground = "dark_gray"
[highlight.FocusBorder]
foreground = "cyan"
[highlight.Selection]
foreground = "cyan"
background = "none"
bold = true
dim = false
[highlight.SelectionBorder]
foreground = "green"
bold = true
[highlight.PaneBorder]
link = "Border"
[highlight.FocusedPaneBorder]
link = "FocusBorder"
bold = true
[highlight.LoginBorder]
link = "FocusBorder"
[highlight.ComposerBorder]
link = "Border"
[highlight.ActiveComposerBorder]
link = "FocusBorder"
bold = true
[highlight.ModalBorder]
link = "FocusBorder"
bold = true
[highlight.ComposerPickerBorder]
link = "FocusBorder"
[highlight.SelectedRow]
link = "Selection"
[highlight.SelectionMarker]
link = "Selection"
[highlight.ActiveField]
foreground = "cyan"
bold = true
[highlight.ActiveTab]
link = "Selection"
[highlight.MessageSelectedBorder]
link = "SelectionBorder"
[highlight.ForumBorder]
link = "FocusBorder"
[highlight.ForumSelectedBorder]
link = "SelectionBorder"
[highlight.ScrollbarThumb]
foreground = "#AAAAAA"
[highlight.UnreadNotice]
foreground = "cyan"
bold = true
[highlight.Editing]
foreground = "yellow"
[highlight.Reaction]
foreground = "cyan"
[highlight.SelfReaction]
foreground = "yellow"
[highlight.PresenceOnline]
foreground = "green"
[highlight.PresenceIdle]
foreground = "#B48C00"
[highlight.PresenceDnd]
foreground = "red"
[highlight.PresenceOffline]
link = "Normal"
dim = true
[highlight.VoiceDisabled]
foreground = "yellow"
[highlight.VoiceConnection]
foreground = "yellow"
bold = true
[highlight.FolderFallback]
foreground = "cyan"
[highlight.NavigationActive]
foreground = "green"
bold = true
[highlight.NavigationMentioned]
foreground = "#FFA500"
[highlight.NavigationNotified]
foreground = "terminal_default"
[highlight.NavigationUnread]
foreground = "terminal_default"
[highlight.MentionBadge]
foreground = "#FFA500"
[highlight.NotificationBadge]
foreground = "terminal_default"
[highlight.JoinedVoiceChannel]
foreground = "yellow"
bold = true
[highlight.VoiceSpeaking]
foreground = "green"
bold = true
[highlight.ReplyPingEnabled]
foreground = "cyan"
[highlight.Tag]
foreground = "cyan"
[highlight.RelationshipFriend]
foreground = "green"
[highlight.RelationshipIncoming]
foreground = "yellow"
[highlight.RelationshipOutgoing]
foreground = "yellow"
[highlight.RelationshipBlocked]
foreground = "red"
[highlight.RelationshipNone]
foreground = "terminal_default"
dim = true
[highlight.GaugeFill]
foreground = "cyan"
[highlight.MessageBody]
foreground = "terminal_default"
[highlight.MarkdownHeading1]
link = "Heading"
foreground = "cyan"
[highlight.MarkdownHeading2]
link = "Heading"
underline = true
[highlight.MarkdownHeading3]
link = "Heading"
[highlight.MarkdownQuote]
foreground = "dark_gray"
[highlight.MarkdownMarker]
foreground = "dark_gray"
[highlight.MessageAttachment]
foreground = "cyan"
[highlight.ImageOverflow]
link = "MessageAttachment"
bold = true
[highlight.InlineCode]
foreground = "#FFA500"
[highlight.InlineTimestamp]
foreground = "#DBDEE1"
background = "#3B3C43"
[highlight.MessageLink]
foreground = "cyan"
underline = true
[highlight.MentionSelf]
foreground = "yellow"
background = "#5C4C23"
[highlight.MentionOther]
foreground = "#C1CEF7"
background = "#28325C"
[highlight.MentionRole]
# Discord supplies the foreground and a darker role-color background by default.
# This group controls colored role mentions that do not notify the current user.
# Notifying role mentions use MentionSelf with the Discord role foreground.
[highlight.MentionPickerRole]
foreground = "magenta"
[highlight.EmbedGutter]
foreground = "red"
[highlight.EmbedLink]
foreground = "blue"
underline = true
[highlight.CommandName]
link = "MessageSecondary"
foreground = "#5865F2"
[highlight.SystemThreadName]
foreground = "cyan"
bold = true
[highlight.PollAnswerSelected]
foreground = "terminal_default"
bold = true
[highlight.PollWinner]
foreground = "terminal_default"
bold = true
[highlight.UnreadBanner]
foreground = "terminal_default"
background = "#5865F2"
[highlight.UnreadDivider]
foreground = "#ED4245"
[highlight.ForumPinnedBadge]
foreground = "yellow"
bold = true
[highlight.BotBadge]
link = "Normal"
background = "#5865F2"
bold = true
[highlight.Error]
foreground = "red"
[highlight.Warning]
foreground = "yellow"
[highlight.Success]
foreground = "green"
[highlight.Info]
foreground = "cyan"
Concord is designed to stay lightweight in normal terminal use. In observed typical use, it usually uses about 20-40 MB of memory.
Image-heavy screens can temporarily use more memory because compressed image bytes need to be decoded before they can be rendered in the terminal. When many images are loaded, memory can briefly rise to around 100-200 MB while decoding and then drop again as work completes and caches are pruned.
There are some path that did trigger a temporary block:
Other features have not caused blocks in my testing.
That said, Concord is not an official Discord client. Using unofficial clients, automated user accounts, or self-bots can violate Discord's TOS, so there is always some risk. Use it at your own discretion.
CONCORD_TOKEN environment variable lets you provide a token without writing it to disk. This avoids leaving plaintext on disk, but the token is visible in /proc/<pid>/environ and in process listings while Concord is running.credentials.store = "auto", Concord falls back to plain text credentials under Concord's state directory when keychain storage is unavailable. In keychain mode, Concord does not fall back to plain storage. Keep fallback credential files secure and do not share them. You can use a token from that file to log in to the official Discord client, so treat it like a password.0700 and the credential file with 0600 permissions.Any issues, pull requests, and feedback are welcome. See CONTRIBUTING.md for details.
Unicode emoji images are provided by Twemoji, Copyright 2020 Twitter, Inc. and other contributors, under the CC-BY 4.0 license.
Concord is licensed under GPL-3.0-only.
931 followers · starred May 2026
269 followers · starred May 2026
907 followers · starred May 2026
617 followers · starred Jun 2026