// POWERLINERS — RUST POWERLINE

powerliners — 134/137 DONE Rust port of powerline-status · single static binary · sub-millisecond render · drop-in for tmux / zsh / bash / vim / ipython · 624 upstream Python fn names allowlist enforced by audit test · 462 parity tests against live upstream · 11 real port bugs surfaced + fixed

>_POWERLINERS — RUST POWERLINE

A Rust port of Python's powerline-status — the canonical statusline / prompt renderer used by tmux, zsh, bash, vim, ipython. The Python implementation pays a ~100 ms interpreter-startup tax on every render; powerliners is a single static binary with sub-millisecond startup.

134/137
files at DONE tier
2,436
lib tests passing
462
parity tests vs upstream
11
port bugs surfaced + fixed
624
upstream py fns
0
python runtime deps
v0.2.18
current tag

Why a port?

Targets

tmux

statusline + window-list + continuation lines

zsh

PS1 / RPROMPT segments, precmd integration

bash

PS1 / PROMPT_COMMAND segments

vim

statusline + tabline (without `+python3` dependency)

ipython

REPL prompt (via shell hook, not embedded)

Port discipline

Every fn name landed in src/ported/ must exist in upstream powerline Python source. Enforced by tests/ported_fn_names_match_py.rs against docs/powerline_py_functions.txt (currently 624 names extracted from github.com/powerline/powerline). Maintainer-approved Rust-only exemptions live in tests/data/fake_fn_allowlist.txt.

# regenerate the upstream allowlist
./scripts/regen_py_functions.sh

# audit will fail with a list of any invented fn names
cargo test --test ported_fn_names_match_py

This pattern mirrors the zshrs ported_fn_names_match_c.rs enforcement — the audit test is load-bearing and must not be weakened.

Status

Port is structurally complete at the function level. 134 of 137 upstream Python files are at DONE tier per the citation-density classifier (scripts/gen_port_checklist.py); the 3 remaining NEAR files are Python lipstick — zero-fn class shells (renderers/shell/readline.py and renderers/shell/zsh.py each hold two escape-marker string constants and a class alias; bindings/i3/powerline-i3.py's single render fn is already 100% ported per docs/port_report.html). No behavioral surface left to port — the classifier's py_fn_total == 0 denominator just can't promote 0/0 → DONE. The Rust port spans ~66,043 LOC across src/ported/ with 2,436 lib tests + 2 audit tests + 462 parity tests passing. Parity tests pipe identical inputs through the upstream Python interpreter (via the vendored powerline-status) and the Rust port, asserting byte/value-identical results — 11 real port bugs were surfaced by this harness and fixed in-tree (ThreadedSegment.daemon, Spec.did_type, encoding locale lookup, _clear_special_values UB, Spec.ident regex, Spec.context_message recursion, Spec.tuple bounds, Spec.unknown_msg/_spec dispatch, NON_PRINTABLE_RE inversion, Spec.printable/unsigned). Every fn name under src/ported/ traces to one of 624 upstream Python fn names (or to tests/data/fake_fn_allowlist.txt for maintainer-approved exemptions); every ported body carries // py:NNN line-citations against upstream powerline/. End-to-end statusline rendering against a real .json theme tree runs through the Powerline orchestrator chain (Powerline class + Theme + Renderer base + segment dispatcher) in both powerline-render and powerline-daemon via the shared render_runtime::render_once entry — byte-identical output with upstream python3 powerline-render is asserted across 45 daemon-parity fixtures (tests/daemon_parity.rs) on top of the 462 unit-parity tests. Watch tags for milestone bumps.

Install

# source + build
git clone https://github.com/MenkeTechnologies/powerliners.git
cd powerliners
cargo build
ls target/debug/powerliners target/debug/powerline-{render,lint,config,daemon}

Five binaries land in target/debug/: powerliners (demo CLI for ported leaf utilities), powerline-render, powerline-lint, powerline-config, and powerline-daemon. The daemon lifecycle (UNIX socket bind, daemonize, pidfile lock, accept loop, EOF shutdown) is fully functional and the render path is wired end-to-end through render_runtime::render_once — same code path as powerline-render, with per-ext Configs caching and is_stale() mtime-reload (py:851-866). powerline-render / powerline-config shim through the ported scripts/ module mains and drive real theme JSON tree resolution; powerline-lint still has the markedjson loader + Spec checks behind the argparse, with the full orchestrator-integrated lint pipeline as the remaining substrate.

CLI reference

The suite ships five binaries. powerline is the hot-path client; the rest are management / fallback tools. All are 1:1 ports of the upstream scripts/ and client/ entry points.

binaryrole
powerlineNative Rust client (port of client/powerline.c). Packs argv + cwd + env into the wire request, sends it to the daemon over a Unix socket, copies the reply to stdout. On connect-fail it execvps powerline-render as a daemon-less fallback.
powerline-daemonLong-lived render server. Binds the per-UID socket, daemonizes, holds a pidfile lock, runs the accept loop, and renders each request through render_runtime::render_once against the cached theme tree.
powerline-renderOne-shot direct render through the same orchestrator path as the daemon. Used by shell hooks when no daemon is running and as the client's fallback exec.
powerline-configtmux / shell / vim integration helper — emits setup, deduces the $POWERLINE_COMMAND binding, extracts the bundled vim plugin.
powerline-lintConfig linter — markedjson loader + Spec checks behind an argparse front end.

powerline <EXT> — extensions

The first positional after the optional --socket PATH selects the extension to render. Stock extensions (from man powerline):

EXTrenders
tmuxtmux statusline segments
shellshell prompt — next positional is left / right / continuation
vimvim statusline / tabline
ipythonIPython prompt / continuation
wmi3 / awesome / qtile widget binding

render-request flags

Everything after <EXT> is forwarded to the daemon and parsed by parse_client_argv (src/ported/scripts/powerline_daemon.rs). Each long flag has the upstream short alias:

flagmeaning
-w / --widthstatusline width hint (columns)
-r / --renderer-moduleoverride the renderer module
-c / --config-overrideper-request config override (repeatable)
-t / --theme-overrideper-request theme override (repeatable)
-R / --renderer-argrenderer arg key=value (repeatable)
-p / --config-pathextra config-path root (repeatable)
-m / --modecurrent mode — shorthand for -R mode=VALUE, drives mode_translations
--last-exit-codelast command exit code (int_or_sig) — for the last_status segment
--last-pipe-statusspace-separated pipe exit codes — for last_pipe_status
--jobnumbackground-job count — for the jobnum segment
--socketsocket path (must precede <EXT> on the client)

powerline-daemon — process flags

flageffect
-q / --quietdouble-fork + detach silently (the canonical login-time start)
-f / --foregroundstay in the foreground (don't daemonize)
-s / --socket PATHbind a custom socket path instead of the per-UID default
-k / --killkill the running daemon for this UID
-r / --replacereplace an already-running daemon

powerline-config — subcommands

invocationdoes
powerline-config tmux setupemit the tmux integration (sources the version-matched powerline-base.conf variant from the per-user cache)
powerline-config shell commandprint the deduced $POWERLINE_COMMAND binary, or exit 1 if none
powerline-config shell uses COMPONENT [-s SHELL]exit 0/1 depending on whether a component (e.g. tmux, prompt) is enabled for the shell — checks POWERLINE_NO_* env gates
powerline-config vim source-pathextract the bundled powerline.vim plugin and print its cache path (non-port convenience action)

Environment variables

variableeffect
POWERLINE_COMMANDThe command shell integrations invoke to render. Set to powerline (this binary); existing setups that hardcode the upstream Python shim keep working because the wire format is identical.
POWERLINE_CONFIG_PATHSColon-separated config-root search list (config.json + themes / colorschemes / segments JSON). Mirrors upstream search order.
POWERLINE_CONFIG_OVERRIDESTheme-override key=value pairs applied per extension.
POWERLINERS_ICONSIcon tier: unset/default → Nerd Font glyphs, =unicode → text-presentation Unicode fallback, =ascii → label-style ASCII. Read once per process (see src/extensions/icons.rs).
$XDG_CACHE_HOMERoot for the extracted tmux conf (powerliners/tmux/) and vim plugin (powerliners/vim/); default ~/.cache.
$FUSEVM_JIT_CACHE · ${ZSHRS,STRYKE,AWKRS}_RKYV_CACHE / _HOME · $XDG_DATA_HOMEOverride roots for the four cache-size segments — see the resolution chain in Bundled segments below.
$KUBECONFIG · $AWS_PROFILE / $AWS_REGION / $AWS_DEFAULT_REGION · $CLOUDSDK_*Honored by the kubecontext, aws.context, and gcp.context segments respectively.

Per-UID socket path: /tmp/powerline-ipc-$UID on macOS / BSD, abstract \0powerline-ipc-$UID on Linux (no filesystem path — invisible to ls / fuser). Pidfile lock at /tmp/powerline-ipc-$UID.pid.

Shell & tmux integration

The wire format is byte-compatible with upstream's C client, so existing .tmux.conf / .zshrc invocations work unchanged once $POWERLINE_COMMAND resolves to this powerline binary.

tmux (~/.tmux.conf)

set -g status-interval 2
set -g status-left  "#(powerline tmux left)"
set -g status-right "#(powerline tmux right)"

# or the install-method-agnostic helper:
run-shell "powerline-config tmux setup"
run-shell -b "powerline-daemon -q &>/dev/null || exit 0"

zsh (~/.zshrc)

powerline-daemon -q   # start once at login
function powerline_precmd() {
    PS1="$(powerline shell left --last-exit-code=$? --jobnum=$(jobs -p | wc -l))"
}
precmd_functions+=(powerline_precmd)

verify against a config root

Point the daemon at a copy of your real config and confirm the rendered tmux markup matches upstream before swapping anything:

# spawn on a throwaway socket
POWERLINE_CONFIG_PATHS=~/.config/powerline \
  ./target/release/powerline-daemon --foreground --socket /tmp/powerliners-probe &

# render the right side via the wire protocol
powerline --socket /tmp/powerliners-probe tmux right

# compare against upstream Python (powerline-status installed)
powerline-render tmux right -p ~/.config/powerline

Custom segments

Upstream Python lets you drop a .py file in ~/.config/powerline/segments/ and reference it by dotted path; __import__ makes it callable. The Rust binary has no dynamic import, so the dispatch layer in src/extensions/exec_segment.rs provides two equivalent surfaces that fall through to a subprocess.

Pattern A — explicit exec adapter

Reference the built-in exec adapter directly in theme JSON when you want the script path, args, and format string spelled out inline:

{
  "function": "exec",
  "args": {
    "command": "/usr/local/bin/cpu_temp.sh",
    "args": ["--unit", "C"],
    "format": "%s°C",
    "highlight_groups": ["cpu_load"]
  }
}

Pattern B — dotted-path filesystem dispatch

Reference your segment by dotted path and drop the script under <config_path>/segments/. The daemon resolves myseg.cpu_temp<config_path>/segments/myseg/cpu_temp.{sh,py,rb,pl,lua,js,executable} (first hit wins; order in src/extensions/exec_segment.rs::SCRIPT_EXTENSIONS):

{ "function": "myseg.cpu_temp" }
mkdir -p ~/.config/powerline/segments/myseg
cat > ~/.config/powerline/segments/myseg/cpu_temp.sh <<'EOF'
#!/bin/sh
echo "$(osx-cpu-temp | sed 's/°C//')"
EOF
chmod +x ~/.config/powerline/segments/myseg/cpu_temp.sh

Dotted-path resolution honors the full config-path cascade (POWERLINE_CONFIG_PATHS, --config-path, ~/.config/).

Output protocol

Both patterns parse the script's stdout on the first non-whitespace byte:

stdout starts withtreated aswrapped how
[ (valid JSON array)verbatim segment listused directly — full control over highlight_groups, gradient_level, divider_highlight_group, multi-chunk output
anything elseplain text[{"contents": <trimmed-stdout>, "highlight_groups": [...]}]format template applies (%s → contents, %% → literal %)

Highlight-group caveat (Rust-port divergence)

The current Rust gen_segment_getter (src/ported/segment.rs) uses the segment's function_name as the only highlight group for "type": "function" segments — it ignores the theme's "highlight_groups" override (upstream Python respects it). Practical fix: name your colorscheme group to match the function name ("exec" for Pattern A, "cpu_temp" for Pattern B), or emit explicit highlight_groups inline via a JSON-array stdout.

Migration & rollback

Drop-in replacement for the Python powerline-daemon — same wire format, same EOF\0\0 shutdown sentinel, same socket path. The C client shipped with powerline-status talks to this daemon unchanged.

stepcommand
1. stop the Python daemonpowerline-daemon -k
2. put the Rust binary ahead in $PATHln -sf "$(pwd)/target/release/powerline-daemon" /usr/local/bin/powerline-daemon
3. restart tmuxtmux kill-server && tmux new-session
4. confirm resolutionls -la $(which powerline-daemon) — must report the symlink
rollbackpowerline-daemon -k; rm /usr/local/bin/powerline-daemon; powerline-daemon -q

The Python C client at ~/.local/bin/powerline does not need replacing — it speaks the same wire protocol to whichever daemon owns the socket. No config, .tmux.conf, or shell-rc edits.

Vim setup

Vim statusline + tabline rendering ships in the same powerline binary — no +python3 requirement, no pip install powerline-status. The bundled powerline.vim plugin is embedded via include_str! and extracted to ~/.cache/powerliners/vim/powerline.vim on first source. Works on vim 7.4+, vim 9, and neovim (vim8-compatible syntax only — no vim9script lock-in).

Step 1 — source the plugin from .vimrc

One-line install — extracts and sources the plugin on every launch:

if executable('powerline-config')
  execute 'source' trim(system('powerline-config vim source-path'))
endif

powerline-config vim source-path extracts the bundled plugin and prints its path; the :execute 'source' … then sources it. Caches in $XDG_CACHE_HOME/powerliners/vim/ (default ~/.cache/powerliners/vim/) so subsequent launches re-source from disk.

Or the manual two-step (pin the path, skip the per-launch fork):

# one-time extraction
$ powerline-config vim source-path
/Users/<you>/.cache/powerliners/vim/powerline.vim

" .vimrc
set runtimepath+=~/.cache/powerliners/vim
source ~/.cache/powerliners/vim/powerline.vim

Step 2 — pre-start the daemon

The plugin shells out via system('powerline vim left …') on every statusline refresh. With the daemon running that's a microsecond Unix-socket round-trip; without it every refresh fork-execs powerline-render (~30 ms cold per call). Start the daemon once at shell login (.zshrc / .bash_profile):

powerline-daemon -q

The -q flag double-forks and detaches; vim sees the socket immediately. No per-vim daemon — one process per UID handles tmux, shell prompts, and every running vim simultaneously.

Step 3 — verify

Open a fresh vim session and check:

:echo &statusline
" expected: a %#Pl_…# markup string ending in segment chunks

:PowerlinersRefresh
" manually re-runs the refresh; useful for debugging

:messages
" no errors; if 'powerline: write() to daemon failed' shows up,
" the daemon isn't reachable — re-run `powerline-daemon -q`

The plugin sets laststatus=2 automatically so the statusline shows even in single-window sessions.

What the plugin wires up

Triggered autocmds (augroup powerliners):

eventfires when
VimEnterinitial render on launch
WinEnter / BufWinEnter / BufEnter / TabEnterrefresh on window / buffer / tab context switch
ModeChanged (vim ≥ 8.2.2871)mode transitions (normal → insert …)
CursorMoved / CursorMovedI (legacy fallback)every cursor move on ancient vim — upgrade if possible

Per-request keys sent to the renderer (matches upstream's powerline.bindings.vim so theme JSON written for Python upstream renders identically): mode (mode(1)), bufnr (bufnr('%')), winnr (winnr()), buf_name (expand('%:p'), omitted when empty).

Override the binary name

If you've installed under a non-default name or want to test a build from target/release/, set the global before sourcing:

let g:powerliners_binary = expand('~/code/powerliners/target/release/powerline')
if executable(g:powerliners_binary)
  execute 'source' trim(system(g:powerliners_binary . '-config vim source-path'))
endif

Customize the theme

The vim statusline pulls from ~/.config/powerline/themes/vim/default.json plus ~/.config/powerline/colorschemes/vim/default.json. Bundled defaults live under src/ported/config_files/themes/vim/ and src/ported/config_files/colorschemes/vim/; copy any of them into ~/.config/powerline/ and edit. Same JSON shape as upstream — segments listed under segments.left / segments.right, inheritance via extends, per-mode highlight overrides via mode_translations.

Troubleshooting

symptomcause / fix
empty statuslineset laststatus=2 got stomped by something else in .vimrc; re-set after sourcing the plugin
garbled escape codesterminal doesn't support truecolor; renderer falls back to cterm but vim must run in a 256-color tty ($TERM=xterm-256color)
colors don't match terminalcolorscheme JSON missing your custom palette — copy colorschemes/vim/default.json into ~/.config/powerline/colorschemes/vim/
powerline: write() to daemon faileddaemon isn't running; powerline-daemon -q
refresh stutters / flickersthe legacy CursorMoved fallback is firing on every keystroke (vim < 8.2.2871); upgrade vim

Bundled segments

src/extensions/ ships net-new segments above upstream powerline-status, dispatched as standard built-ins via the daemon's ADAPTERS table. All accept a format theme arg and ship NF-default icons with Unicode + ASCII fallbacks (controlled by POWERLINERS_ICONS).

segmentsummary
powerliners.gpu.gpu_usage_percentVendor-dispatched GPU compute % (nvidia-smi → rocm-smi → intel_gpu_top → ioreg fallback).
powerliners.gpu.gpu_vramGPU VRAM USED/TOTAL via the same dispatch chain.
powerliners.disk.disk_usageFilesystem USED/TOTAL for any mount.
powerliners.disk.disk_usage_percentFilesystem percent-used.
powerliners.disk.disk_ioLive read/write throughput for any device (device arg, default auto). Format uses positional %s substitution — first %s → read rate, second %s → write rate (default <disk> <io> R %s W %s). Args: short (default true), recv_max/sent_max (gradient ceilings, default 100 MiB/s).
powerliners.thermal.thermalCPU/GPU temp + fan RPM. /sys/class/hwmon on Linux, powermetrics/istats on macOS.
powerliners.vcs.git_statusSingle-probe git status — branch, ahead/behind, dirty/staged/untracked, stash count, action (rebase/merge/cherry-pick), tag at HEAD. github-remote detection prepends the octocat glyph.
powerliners.docker.containersContainer counts via docker ps. Tokens: {running} / {total} / {images} / {stopped}. Falls through silently when the daemon is unreachable.
powerliners.k8s.kubecontextCurrent kubectl context + active namespace. Honors $KUBECONFIG cascade; hide_default arg suppresses the namespace fragment when it equals the configured default.
powerliners.proc.process_countPOSIX process tally via ps -eo stat=. Tokens: {total}/{running}/{sleeping}/{zombie}/{dwait}/{stopped}. warn_zombie flips the highlight group when defunct processes are present.
powerliners.github.ci_statusCurrent branch's HEAD check-runs via gh api repos/:o/:r/commits/:sha/check-runs, disk-cached by SHA (default ttl_secs=30). Tokens: {icon}/{state}/{passed}/{failed}/{running}/{total}. State-picked highlight group plus information:regular neutral fallback so the chunk renders in any colorscheme.
powerliners.aws.contextActive AWS profile + region. Pure-fs probe of $AWS_PROFILE/$AWS_REGION/$AWS_DEFAULT_REGION then ~/.aws/config (handles the [profile NAME] quirk). Tokens: {icon}/{profile}/{region}. hide_default_profile strips the profile fragment when it equals default.
powerliners.gcp.contextActive gcloud configuration's project + account. Pure-fs probe of ~/.config/gcloud/active_config + configurations/config_<NAME>. Env overrides $CLOUDSDK_ACTIVE_CONFIG_NAME / $CLOUDSDK_CORE_PROJECT / $CLOUDSDK_CORE_ACCOUNT win.
powerliners.fusevm.jit_cachefusevm Cranelift JIT cache stats — recursively walks $FUSEVM_JIT_CACHE / $XDG_CACHE_HOME/fusevm-jit / ~/.cache/fusevm-jit. Tokens: {icon}/{count}/{entries}/{harddisk}/{size} (du -sh-style block allocation)/{bytes} (raw disk bytes)/{logical_size}/{logical_bytes} (stat-style content sum).
powerliners.zshrs.rkyv_cacheSingle-file stat of the zshrs authoritative rkyv archive at $ZSHRS_RKYV_CACHE / $ZSHRS_HOME/scripts.rkyv / $XDG_DATA_HOME/zshrs/scripts.rkyv / ~/.zshrs/scripts.rkyv. Same {size} / {bytes} / {logical_*} token surface as fusevm.jit_cache.
powerliners.stryke.rkyv_cacheSingle-file stat of ~/.stryke/scripts.rkyv (stryke's bytecode store, Cranelift-JIT'd via the shared fusevm runtime).
powerliners.awkrs.rkyv_cacheSingle-file stat of ~/.awkrs/scripts.rkyv.
powerliners.zshrs.versionLatest installed zshrs version (parsed from <bin> --version). In-process TTL cache (default 300 s, configurable via ttl_secs) so the daemon doesn't fork on every prompt tick. Tokens: {icon}/{version}. Returns None when the binary isn't on PATH.
powerliners.stryke.versionSame as zshrs.version for the stryke binary — handles the This is stryke vX.Y.Z — ... prefix shape via the shared extract_version SemVer scanner.
powerliners.awkrs.versionSame shared helper, default binary awkrs.
powerliners.exec.execUser-defined exec adapter — spawn an arbitrary script, parse stdout, render with a user-supplied format. Resolves via bare "function": "exec" as well as the dotted path.

All segments live under src/extensions/<module>.rs and register through the daemon's ADAPTERS table in src/bin/shared/render_runtime.rs — see the audit section below for the live icon → segment cross-reference.

Cache-size segments — shared resolution chain & token surface

The four cache-size segments — powerliners.fusevm.jit_cache, powerliners.zshrs.rkyv_cache, powerliners.stryke.rkyv_cache, powerliners.awkrs.rkyv_cache — share a deliberately uniform contract so the same theme JSON works across all four. Each segment is a pure filesystem probe; no subprocess, no daemon RPC.

Resolution chain (4-level fallback, first hit wins). For <NAME> ∈ {ZSHRS, STRYKE, AWKRS} the rkyv segments resolve path as:

  1. $<NAME>_RKYV_CACHE — explicit override, used verbatim
  2. $<NAME>_HOME/scripts.rkyv
  3. $XDG_DATA_HOME/<name>/scripts.rkyv — used only when the file exists; otherwise falls through
  4. ~/.<name>/scripts.rkyv — final fallback

The fusevm.jit_cache sibling uses the directly-analogous 3-level chain ($FUSEVM_JIT_CACHE$XDG_CACHE_HOME/fusevm-jit~/.cache/fusevm-jit) but recursively walks a cache directory rather than stat-ing a single archive, and also exposes an {entries} token for the recursive file count. Symlinks count as a single entry and are never followed — avoids infinite loops across re-symlinked cache dirs. The precedence chains are unit-tested via a pure-functional default_path_with(get_env, path_exists) seam in src/extensions/{zshrs,stryke,awkrs}_rkyv.rs and a matching default_root_with(get_env) seam in src/extensions/fusevm_jit.rs — every precedence level pinned without mutating the process env.

Format tokens (identical across all four segments):

tokenmeaning
{icon}Segment glyph — substituted downstream by the render runtime, not by the segment module itself. The Nerd Font / Unicode / ASCII tier is selected by POWERLINERS_ICONS.
{size}On-disk allocation, human-formatted (B/K/M/G/T/P, 1024-base, precision tiers <10→2dp / <100→1dp / else→0dp). Matches du -sh.
{bytes}On-disk allocation as a raw integer (block count × 512 on Unix; stat .st_blocks).
{logical_size}File content size from stat .st_size, human-formatted.
{logical_bytes}File content size as a raw integer.

Missing-archive behavior. When the target file doesn't exist (the runtime never ran on this machine, or the archive hasn't been written yet), the segment returns None by default and renders no chunk. Setting "show_when_empty": true in the segment args makes it render with zeroed stats (0B/0) so the chunk stays present as a UI affordance.

Highlight-group chain. Every cache-size segment ships a 3-level highlight chain — <segment>_rkyv_cache<segment>information:regular. The trailing information:regular is a neutral fallback so the chunk renders in any colorscheme even when the segment-specific groups are absent (see fusevm_jit::jit_cache() for the rationale that motivated this convention).

Glyph rendering

Every glyph below is a real Nerd Font codepoint loaded into this page via the @font-face block in docs/hud-static.css. The font is the official SymbolsNerdFont subset (~270 KB) served from jsdelivr CDN, with a unicode-range directive gating the load to PUA ranges only (U+E000-F8FF + U+F0000-FFFFD):

@font-face {
  font-family: 'SymbolsNerdFont';
  font-display: swap;
  src: url('https://cdn.jsdelivr.net/gh/ryanoasis/nerd-fonts@master/patched-fonts/NerdFontsSymbolsOnly/SymbolsNerdFont-Regular.ttf') format('truetype');
  unicode-range: U+E000-F8FF, U+F0000-FFFFD;
}

Body text and code blocks continue to render in Share Tech Mono / Orbitron — only PUA codepoints resolve through the NF subset, so the font swap has no layout impact. The same chain operates at runtime via POWERLINERS_ICONS: unset/default → NF glyph, =unicode → text-presentation Unicode fallback (e.g. ✓ ✗ 🐳 ⎈ ⛁), =ascii → label-style ASCII (e.g. OK FAIL D: k8s: D:).

Icon audit

Every Nerd Font codepoint shipped by src/extensions/icons.rs is listed here with its canonical upstream identity (verified against ryanoasis/nerd-fonts master, NF 3.4.0 default load order, legacy i_material.sh excluded). Each glyph below renders live via the SymbolsNerdFont @font-face declaration in hud-static.css — if a shipped glyph fails to match its claimed identity, it would be immediately visible in this table.

Three-tier fallback chain: Nerd Font (default) → Unicode (POWERLINERS_ICONS=unicode, text-presentation only) → ASCII (POWERLINERS_ICONS=ascii, label-style for non-UTF8 terminals).

A. Icon getters (33)

fncodepointNF namesourceNFUnicodeASCIIused by segment
icons::disk()U+F0A0i_fa_hard_drivei_fa.shD:disk_io, disk_usage, disk_usage_percent
icons::cpu()U+F4BCi_oct_cpui_oct.shCPU:ad_cpu_load_percent, thermal
icons::gpu()U+F2DBi_fa_microchipi_fa.shGPU:gpu_usage_percent, gpu_vram, thermal
icons::memory()U+EFC5i_fa_memoryi_fa.shMEM:ad_mem_usage_percent, gpu_vram, mem_usage
icons::thermometer()U+F2C8i_fa_temperature_three_quartersi_fa.sh🌡T:— unused
icons::branch()U+E0A0i_pl_branchi_ple.shb:git_status
icons::github()U+F09Bi_fa_githubi_fa.sh🐙gh:ci_status, git_status
icons::tag()U+F02Bi_fa_tagi_fa.shtag:git_status
icons::docker()U+F0868i_md_dockeri_md.sh󰡨🐳D:containers
icons::kubernetes()U+F10FEi_md_kubernetesi_md.sh󱃾k8s:kubecontext
icons::process()U+F0493i_md_cogi_md.sh󰒓P:process_count
icons::aws()U+F0E0Fi_md_awsi_md.sh󰸏awsawscontext
icons::gcp()U+F11F6i_md_google_cloudi_md.sh󱇶gcpgcpcontext
icons::ci_ok()U+F49Ei_oct_check_circlei_oct.shOKci_status
icons::ci_fail()U+F52Fi_oct_x_circlei_oct.shFAILci_status
icons::ci_run()U+F46Ai_oct_synci_oct.sh🔄RUNci_status
icons::count()U+F0222i_md_file_multiplei_md.sh󰈢n=jit_cache
icons::harddisk()U+F02CAi_md_harddiski_md.sh󰋊d=jit_cache
icons::swap()U+F04E1i_md_swap_horizontali_md.sh󰓡SWAP:ad_mem_swap_percentage, mem_swap
icons::io()U+F0E79i_md_arrow_up_downi_md.sh󰹹IO:disk_io
icons::fusevm()U+F01FAi_md_enginei_md.sh󰇺jit:jit_cache
icons::zshrs()U+F07B7i_md_console_linei_md.sh󰞷zshrs:rkyv_cache
icons::stryke()U+E769i_dev_perli_dev.shλstryke:rkyv_cache
icons::awkrs()U+E741i_dev_awki_dev.sh$0awkrs:rkyv_cache
icons::weather_sunny()U+E30Di_weather_day_sunnyi_weather.shSUNweather
icons::weather_night()U+E32Bi_weather_night_cleari_weather.shNIGHTweather
icons::weather_rainy()U+E318i_weather_raini_weather.shRAINweather
icons::weather_cloudy()U+E312i_weather_cloudyi_weather.shCLOUDSweather
icons::weather_snowy()U+E31Ai_weather_snowi_weather.shSNOWweather
icons::weather_stormy()U+E31Di_weather_thunderstormi_weather.shSTORMweather
icons::weather_foggy()U+E313i_weather_fogi_weather.sh🌫FOGweather
icons::weather_windy()U+E31Fi_weather_strong_windi_weather.sh🌬WINDweather
icons::weather_unknown()U+E374i_weather_nai_weather.sh??weather

The nine weather_* getters are keyed by upstream condition name and consumed by the weather adapter (ad_weather in src/bin/shared/render_runtime.rs) — it pre-seeds day/sunny/night/rainy/cloudy/snowy/stormy/foggy/windy/unknown defaults from the active icon tier before applying any theme icons override. icons::thermometer() remains the only getter with no live consumer (— unused above).

B. Segment defaults (24)

Default format strings shipped by each powerliners.* / powerlinemem.* adapter when the theme JSON omits format. Live NF render uses the same glyphs as Table A.

segment nameicons usedlive NF render
powerliners.gpu.gpu_usage_percent 47%
powerliners.gpu.gpu_vram  8.2G/12G
powerliners.disk.disk_usage 142G/512G
powerliners.disk.disk_usage_percent 28%
powerliners.disk.disk_io 󰹹 󰹹 R 23M W 5M
powerliners.thermal.thermal 67°C 1240RPM
powerliners.vcs.git_status   main ⇡2
powerliners.docker.containers󰡨󰡨 4/7
powerliners.k8s.kubecontext󱃾󱃾 prod:default
powerliners.proc.process_count󰒓󰒓 348
powerliners.github.ci_status   5/5
powerliners.aws.context󰸏󰸏 prod@us-east-1
powerliners.gcp.context󱇶󱇶 my-proj:me@e.com
powerliners.fusevm.jit_cache󰇺 󰈢 󰋊󰇺 󰈢 207 󰋊 828K
powerliners.zshrs.rkyv_cache󰞷󰞷 4.00K
powerliners.stryke.rkyv_cache 34.9M
powerliners.awkrs.rkyv_cache 8.00K
powerliners.zshrs.version󰞷󰞷 0.11.26
powerliners.stryke.version 0.16.8
powerliners.awkrs.version 0.4.13
powerlinemem.mem_usage.mem_usage 12G/32G
powerlinemem.mem_usage.mem_usage_percent 47%
powerlinemem.mem_usage.mem_swap󰓡󰓡 1.2G/4G
powerlinemem.mem_usage.mem_swap_percent󰓡󰓡 30%

Anti-fakery rationale: a future maintainer who swaps a glyph (intentionally or by typo) without updating both the code AND this audit table will produce visibly wrong rendering on this page — the symptom is observable, not hidden behind a Rust unit test. The used-by-segment column on Table A is computed from the real adapter code in src/bin/shared/render_runtime.rs at build time (each fn ad_X body is scanned for icons::Y() calls and cross-referenced against the ADAPTERS dispatch table); a getter listed as — unused is dead code awaiting either a consumer or removal. The i_material.sh legacy MDI set is intentionally excluded from verification since NF 3.4.0 does not load it by default; codepoints that collide between i_oct.sh and legacy i_material.sh (e.g. U+F52F) resolve via the Octicons subset in any standard Nerd Font install.