Resolve desktop and brand icons to local file paths — for Waybar, Rofi, scripts, and anything else that needs a real icon file.
appicon resolve firefox
appicon resolve --json --format png --size 24 "VS Code"
appicon resolve --json firefox discord
appicon resolve --offline some-cached-app
appicon resolve --explain missing-app
appicon prefetch firefox discord
appicon prefetch --from-desktop
appicon prefetch --json --offline firefox
appicon override set my-browser firefox
appicon override suggest my-browser
appicon override list
appicon sources get --json
appicon sources set --file ./sources.json
appicon status
appicon cache stats
appicon cache prune
appicon mcp # stdio MCP for agents
appicon daemon # optional user socket daemon
appicon completion bash # print completion script
appicon man | man -l - # view man pageResolve order (default): file → overrides → XDG / .desktop → SVGL. Fully reorderable via sources.json / sources.yaml / --order — including opt-in simple-icons, dashboard-icons, github, BYOK (logo-dev, iconify, noun-project), glyph, and local packs. See docs/sources.md and docs/packs.md.
XDG, SVGL (cache-first), local packs, opt-in CDN/github/BYOK/glyph stages, PNG rasterization, --offline, cache prune, MCP, optional unix-socket daemon (not on Windows), and shell completions are implemented. Deferred ideas: docs/deferred.md.
Consumer contract: exit 0 / 1 (miss) / 2 (error); stable resolve --json fields (single object or {results:[…]} batch) — docs/consumer-contract.md, schemas docs/resolve-result.schema.json / docs/resolve-batch-result.schema.json. Misses are supported (callers keep glyphs). Auth-skipped BYOK stages show as stage(auth) in --explain tried. Treat appicon like optional peers such as zscroll / cava: never require the binary for a working bar.
Portability: Primary target is Linux (XDG, Flatpak/Snap roots, systemd daemon). The local xdg stage also discovers macOS .app bundle icons from XML Info.plist metadata and Windows .url shortcuts with an absolute IconFile; binary plist, Windows .lnk, and embedded EXE icon extraction are not supported. Config/cache fall back to OS user dirs when XDG_* are unset. Daemon refuses on Windows (daemon_supported=false in status).
PNG note: resolve --format png prefers resvg or rsvg-convert on PATH, otherwise a pure-Go oksvg fallback. Rasterized files are cached under $XDG_CACHE_HOME/appicon/raster/. Pixel size defaults to 48 and is capped at 512 (--size / MCP size values above that are clamped).
Theme note: --theme dark|light, APPICON_THEME, or GTK_THEME suffix (Adwaita:dark) prefer matching SVGL/CDN and XDG variants (name-dark / name-symbolic / name-light). Icon theme name is separate (APPICON_ICON_THEME).
Sources: $XDG_CONFIG_HOME/appicon/sources.json (or .yaml) — every stage is an ordered entry. Default without a file is file → overrides → xdg → svgl. Opt-in remotes are never enabled by default. BYOK stages take token_env / secret_env (env var names whose values hold secrets — never put keys or secret paths in config).
appicon sources list
appicon sources get --json
appicon pack install simple-icons # local clone + register
appicon pack install --name mine --subdir icons https://github.com/org/my-icons.git
appicon resolve --order glyph,svgl,xdg my-app
appicon resolve --order logo-dev,xdg shopify.com # needs LOGO_DEV_TOKEN
appicon statusExample — remaps and a personal pack before path/XDG/SVGL:
{
"sources": [
{ "type": "overrides" },
{ "type": "pack", "name": "mine", "path": "~/.local/share/appicon/packs/mine" },
{ "type": "file" },
{ "type": "xdg" },
{ "type": "svgl" },
{ "type": "simple-icons" }
]
}Local pack paths must be absolute or start with ~/; working-directory-relative
and named-user paths are rejected.
CDN stages (simple-icons / dashboard-icons) are separate from local pack install clones of the same upstreams. Do not point http-index at third-party CDNs unless you control the allowlist and accept their terms.
Long-tail query remaps live in $XDG_CONFIG_HOME/appicon/overrides.json (or .yaml):
appicon override set my-wm-class firefox
appicon override list --json
appicon override export --format yaml > overrides.yaml
appicon override import --merge --file overrides.yamlRun the same binary as a stdio MCP server — tools call internal/resolve (no extra download logic):
appicon mcp| Tool | Mirrors |
|---|---|
resolve |
appicon resolve --json (optional order, explain, queries batch; miss → path:null, not IsError) |
prefetch |
appicon prefetch (optional order, offline, theme, from_desktop, json) |
status |
appicon status --json |
sources_list / sources_get / sources_set |
appicon sources list|get|set |
pack_list / pack_path / pack_add / pack_install / pack_update / pack_install_bundle |
appicon pack … (pack_install: recipe or url, plus name/subdir/ref) |
cache_stats / cache_clear / cache_prune |
matching cache subcommands |
override_list / override_get / override_set / override_rm / override_suggest / override_export / override_import |
appicon override … |
version |
appicon version |
Example Cursor / Claude Desktop snippet:
{
"mcpServers": {
"appicon": {
"command": "appicon",
"args": ["mcp"]
}
}
}Agents should prefer MCP tools over shelling appicon when MCP is connected. Call resolve / sources_* / pack_* only — never invent CDN or SVGL URLs in other repos.
Long-lived resolve over $XDG_RUNTIME_DIR/appicon.sock (mode 0600). Same allowlists/cache as the CLI, including order, explain, and batch. resolve / prefetch dial the socket when present and fall back in-process (--local / APPICON_NO_DAEMON=1 skips dial).
appicon daemon # foreground
# or user systemd — see contrib/systemd/README.md
systemctl --user enable --now appicon.socketHome Manager (Linux): programs.appicon.daemon.enable = true.
# bash
eval "$(appicon completion bash)"
# or install: appicon completion bash > ~/.local/share/bash-completion/completions/appicon
# zsh
appicon completion zsh > "${fpath[1]}/_appicon" # then: compinit
# fish
appicon completion fish > ~/.config/fish/completions/appicon.fishappicon man | man -l -
# or: appicon man > /usr/local/share/man/man1/appicon.1ver=v0.3.0
arch=$(uname -m)
case "$arch" in
x86_64) arch=amd64 ;;
aarch64|arm64) arch=arm64 ;;
esac
curl -fsSL "https://github.com/bolens/appicon/releases/download/${ver}/appicon_${ver}_linux_${arch}.tar.gz" | tar -xz
install -m 755 appicon ~/.local/bin/appicon
appicon version # → v0.3.0Checksums: download SHA256SUMS (and optionally SHA256SUMS.sigstore.json) from the same release.
# checksums
sha256sum --check SHA256SUMS
# macOS
shasum -a 256 --check SHA256SUMS
# optional cosign keyless verify (Sigstore)
cosign verify-blob \
--bundle SHA256SUMS.sigstore.json \
--certificate-identity-regexp '^https://github.com/bolens/appicon/\.github/workflows/release\.yml@refs/tags/v' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
SHA256SUMS
# optional GitHub build provenance attestation
gh attestation verify "appicon_${ver}_linux_${arch}.tar.gz" --repo bolens/appiconOr: bash scripts/ci/verify-release.sh /path/to/downloaded/assets.
See SECURITY.md for reporting vulnerabilities and the trust model.
waybar-config pins this via make install-appicon.
Reference PKGBUILDs live under packaging/aur/:
| Package | Tracks |
|---|---|
appicon |
Tagged source release |
appicon-bin |
Prebuilt release tarball |
appicon-git |
Latest git commit on main |
Fill checksums (except -git) and push to aur.archlinux.org when ready.
nix flake lock # once
nix run github:bolens/appicon -- version
nix build github:bolens/appicon#appicon-bin # linux prebuilt (like AUR appicon-bin)
nix build github:bolens/appicon#appicon-git # source + unstable version (like AUR appicon-git)
# local: see nix/README.md for vendorHash + appicon / appicon-bin / appicon-gitHome Manager: programs.appicon.enable = true via homeManagerModules.default (overlay or set package to appicon / appicon-bin / appicon-git). Optional programs.appicon.daemon.enable = true for the user socket daemon on Linux.
From source:
git clone https://github.com/bolens/appicon.git
cd appicon
make build
./bin/appicon versionRemote assets live under $XDG_CACHE_HOME/appicon (default ~/.cache/appicon
on Linux and the OS user cache directory elsewhere). XDG hits return theme
paths directly and are not copied.
Optional query remaps: $XDG_CONFIG_HOME/appicon/overrides.json — manage with:
appicon override set steam_app_12345 "Some Game"
appicon override get steam_app_12345
appicon override list --json
appicon override export --format yaml
appicon override import --merge --file overrides.yaml
appicon override rm steam_app_12345
appicon override pathBrand logos from SVGL are third-party marks — cached for personal use; this project does not redistribute a logo pack.
Shell-out-only consumers (no SVGL URLs):
bash examples/rofi-appicon.sh
bash examples/walker-appicon.sh firefox
bash examples/notify-appicon.sh firefox "Hello" "Icon from appicon"make check-fast # go test + vet + gofmt
make check # + golangci-lint + govulncheck + gitleaks + actionlint + markdownlint + docs crosslinks
make buildCanonical map (update the listed source of truth when behavior changes): docs/README.md.
| Topic | Doc |
|---|---|
Exit codes / resolve --json |
docs/consumer-contract.md, docs/resolve-result.schema.json |
Stages / sources.json |
docs/sources.md |
| Local packs / recipes | docs/packs.md |
| Not a backlog | docs/deferred.md |
| Security / verify releases | SECURITY.md |
| Agents | AGENTS.md |
| Contributing | CONTRIBUTING.md |
| Changelog | CHANGELOG.md |
| Release playbook | docs/releasing.md |
| Nix / AUR / systemd | nix/README.md, packaging/aur/README.md, contrib/systemd/README.md |
Release preparation, tagging, verification, packaging refresh, and local installation: docs/releasing.md.
MIT for code only. See LICENSE.