Skip to main content
ansicode

APC — Application Program Command (`ESC _ … ESC \\` / `\x9F … \x9C`)

The string introducer ECMA-48 reserves for application-to-application messages. Modern use: Kitty graphics protocol, VS Code shell integration, Windows Terminal API patches. Sibling to DCS / OSC / PM / SOS.

Byte forms

Every common string-literal form so you can paste-and-search either direction.

\\x1b[\x1b_<body>\x1b\\ (7-bit) \x9f<body>\x9c (8-bit C1)
\\033[\033_<body>\033\\ (7-bit) \237<body>\234 (8-bit C1)
\\e[\e_<body>\e\\
ESC [ESC _ <body> ESC \\ or 0x9F <body> 0x9C
hex1b 5f <body> 1b 5c / 9f <body> 9c

Description

APC (*Application Program Command*) is the ECMA-48 string introducer reserved for application-to-application messages — bytes the terminal is supposed to pass through transparently to a peer application listening on the same TTY, without interpreting the body itself. The 7-bit form is \x1b_<body>\x1b\\ (ESC _ + opaque bytes + ST); the 8-bit C1 form is the single byte \x9F introducer + body + single-byte \x9C terminator. Sibling to DCS (\x1bP, device control), OSC (\x1b], OS command), PM (\x1b^, privacy message), SOS (\x1bX, start of string) — five 'string-shaped' sequence families framed by the same ST.

Why APC matters in 2026. ECMA-48 originally designed APC for tunneling between an application running on a remote host and a co-resident application on the same terminal (early thin-client / mainframe-frontend setups). That use case is mostly dead, but the byte shape has been co-opted as a 'safe escape hatch' for vendor-specific protocols that don't want to step on OSC's user-facing semantics:

- Kitty's graphics protocol is the headline modern user — Kitty + Konsole + WezTerm + Ghostty + Wayst accept image data via \x1b_G<key1>=<val1>,<key2>=<val2>,...;<base64-pixels>\x1b\\. The G first-byte after _ is Kitty's family tag; subsequent emulators that adopted Kitty graphics keep it. (Despite the slug name dcs-kitty-graphics, the actual byte sequence is APC, not DCS — the slug predates this clarification.) - VS Code shell integration uses APC with a 633; prefix as a private channel for shell-integration markers that VS Code's terminal doesn't want to advertise via OSC 133. - Windows Terminal is experimenting with \x1b_<JSON>\x1b\\ for terminal-API JSON messages (preview builds, not stable). - ConPTY pass-through — Windows ConPTY filters most OSC sequences for security, but historically passed APC through more permissively, so some Windows-side tools route through APC to reach the host terminal unaltered.

Parser rules. Treat the body as opaque bytes until ST is seen at packet boundary. Specifically: ECMA-48 says any 'escape sequence other than ESC \\' inside the body must be ignored by the terminal but preserved when passed through; a strict parser reads bytes into a buffer, scanning for \x1b followed immediately by \\ (the only valid 7-bit ST) or for the single byte \x9C. Naive 'split on ESC' parsers will mis-frame any body that legitimately contains ESC bytes (Kitty graphics in particular embeds binary data that statistically hits 0x1B).

Security stance. Because APC is meant to be passed through opaquely, terminals that *do* implement APC dispatch must whitelist the protocols they recognise (Kitty graphics tag G, VS Code 633;, etc.) and silently drop unknown APC bodies — emitting a stranger's APC body to the user would let one application impersonate another. Terminals that *don't* implement any APC protocols (Alacritty, gnome-terminal, macOS Terminal, Linux console) should silently swallow + discard, which they do; treat APC as 'will reach a peer that knows the tag, or vanish completely' — never as a guaranteed-visible channel.

Distinction from DCS / OSC / PM / SOS. - DCS (\x1bP) — device control (data targeted at the *terminal* itself: DECRQSS, DECDLD, DECUDK, Sixel, terminfo cap query). - OSC (\x1b]) — OS command (data targeted at the terminal-as-OS-shim: window title, palette, clipboard, hyperlink, prompt markers — user-facing semantics). - APC (\x1b_) — application command (data targeted at a *peer application*, not the terminal — the terminal is just a courier). - PM (\x1b^) — privacy message (similar courier semantics to APC, less adopted in practice; some legacy thin-client gear used it). - SOS (\x1bX) — start of string (the most generic introducer, no semantic; almost universally unused in 2026).

Common parsing bug — terminals that implement OSC and DCS but forget APC: they see \x1b_G... , don't recognise the introducer, and emit _G... to the visible screen. Look for stray _G runs on broken Kitty-graphics renders to diagnose this.

Coverage — APC dispatch (i.e. routing the body to a registered handler) is niche. Kitty + WezTerm + Konsole + Ghostty = partial-to-full (each routes APC G to its graphics handler; other APC bodies discarded). xterm = partial (recognises APC framing, discards body by default — apcDispatch X resource off). mlterm + iTerm2 + Windows Terminal = partial (APC framing recognised, vendor-tagged bodies handled, other bodies discarded). Alacritty + gnome-terminal + macOS Terminal + Linux console + cmd / ConPTY = no-op-but-correct (APC framing recognised, body silently swallowed — no visible artifact). Modern emulators all do at least the framing correctly — buggy ones that emit the body literally are limited to very old builds.

Spec citation: ECMA-48 §8.3.2 (APC) / xterm-ctlseqs (APC) / Kitty graphics protocol

Parameters

Introducer (7-bit)ESC + underscore (\x1b 0x5f). The C0-safe form; works under UTF-8.
Introducer (8-bit)Single byte 0x9F. UTF-8 unsafe (continuation byte). Avoid except in known-Latin-1 channels.
TerminatorESC + backslash (\x1b\\, 7-bit ST) or single byte 0x9C (8-bit ST). Either is valid; match the introducer width.
BodyOpaque bytes — terminal must not interpret. Vendor protocols use a first-byte family tag (Kitty 'G', VS Code '633;', ...).

Examples

bash
# Probe whether the terminal accepts a Kitty-graphics APC frame.\n# Empty image (1x1 transparent PNG, base64-tiny) — if rendered, terminal supports Kitty graphics.\nprintf '\\033_Gf=32,s=1,v=1,a=T;AAAAAA==\\033\\\\'\n# Emit a custom APC tag — most terminals will silently swallow.\nprintf '\\033_myapp;hello\\033\\\\'\necho 'still here'   # no visible artifact on any modern terminal
python
import sys\n# Send a Kitty-graphics-style APC frame.\ndef apc(body):\n    sys.stdout.write('\\x1b_' + body + '\\x1b\\\\')\n    sys.stdout.flush()\napc('Gf=32,s=1,v=1,a=T;AAAAAA==')   # Kitty 'G' tag + base64 payload\napc('myapp;state=ready')             # private tag — silently swallowed on non-vendor terminals
go
// Robust APC parser: read until ST at packet boundary, never split on raw ESC.\nfunc readAPC(r *bufio.Reader) ([]byte, error) {\n    var buf bytes.Buffer\n    for {\n        b, err := r.ReadByte()\n        if err != nil { return nil, err }\n        if b == 0x9c { return buf.Bytes(), nil }      // 8-bit ST\n        if b == 0x1b {\n            n, _ := r.ReadByte()\n            if n == '\\\\' { return buf.Bytes(), nil }    // 7-bit ST\n            buf.WriteByte(b); buf.WriteByte(n); continue\n        }\n        buf.WriteByte(b)\n    }\n}
javascript
// Emit APC + listen for response APC from a peer.\nfunction sendAPC(body) {\n  process.stdout.write('\\x1b_' + body + '\\x1b\\\\');\n}\n// Parse incoming APC (collect bytes until ST, never split on raw ESC).\nlet apcBuf = '';\nprocess.stdin.on('data', chunk => {\n  const m = /\\x1b_([^\\x1b]*(?:\\x1b[^\\\\][^\\x1b]*)*)\\x1b\\\\/.exec(apcBuf + chunk.toString('binary'));\n  if (m) console.error('APC body:', m[1]);\n});
c
/* Emit APC with custom vendor tag. */\n#include <stdio.h>\nvoid send_apc(const char* tag, const char* body) {\n    printf(\"\\x1b_%s;%s\\x1b\\\\\", tag, body);\n    fflush(stdout);\n}\n/* Usage: send_apc(\"myapp\", \"hello\");  // silently swallowed on non-vendor terms */

Used in

Real-world tools that emit this sequence — anchors the bytes to commands you've already used.

  • Kitty graphics protocol (`kitty +kitten icat`)Kitty's image renderer emits APC frames of the form `\x1b_G<key>=<val>,...;<base64-pixels>\x1b\\` — the `G` tag after `_` is Kitty's family identifier. Konsole, WezTerm, Ghostty, and Wayst accept the same protocol unchanged. `kitten icat ~/image.png` is the canonical user-facing emitter
  • VS Code shell integration (`OSC 633;` channel)VS Code's terminal sends APC `633;` frames (e.g. `\x1b_633;A\x1b\\` for prompt-start, `\x1b_633;B\x1b\\` for command-start) to mark prompt boundaries without polluting OSC 133 — distinct from the OSC-based public shell-integration spec. Visible by `$VSCODE_INJECTION` shell hooks emitting these wrappers
  • tmux passthrough mode (`set -g allow-passthrough on`)tmux ≥ 3.3 with `allow-passthrough on` forwards APC frames from inner panes to the outer terminal — used by `kitten icat` inside tmux to deliver Kitty graphics to the host terminal. Without the option set, tmux strips APC at the pane boundary (default-deny posture)
  • WezTerm `wezterm.action.SendString` raw APC handlersWezTerm's Lua config can register custom APC handlers via `wezterm.on('window-config-reloaded', ...)` paired with `set_user_var` — terminal-side scripts emit `\x1b_<custom-tag>;<payload>\x1b\\` and WezTerm routes the body to the registered Lua callback. Used for editor-aware status-bar integrations and project-context overlays
  • mosh client-server diff protocolmosh's state-synchronization-protocol (SSP) uses APC framing as the wire envelope for terminal-state diffs between mosh-client and mosh-server — the SSP frame `\x1b_<base64-protobuf>\x1b\\` survives intermediate terminals transparently, exploiting APC's pass-through semantics for tunneling without DCS / OSC entanglement

Frequently asked

Short answers to the questions developers actually search for this sequence.

What is an APC sequence — \x1b_...\x1b\\ — and which real-world tools use it?
APC (Application Program Command, C1 control 0x9F introduced as 7-bit \x1b_) is a generic transparent envelope for application-specific binary payloads, terminated by ST \x1b\\. Unlike CSI / OSC / DCS, the payload bytes are NOT interpreted by the terminal — they're passed through to whatever protocol the application defined inside. Modern real-world users: (a) Kitty graphics protocol wraps base64-encoded PNG / RGBA chunks inside \x1b_G<params>;<base64>\x1b\\ for inline image rendering, (b) VS Code shell integration uses the OSC 633 channel (not APC, but architecturally similar — \x1b]633;...\x07) for prompt-end / command-finished markers, (c) WezTerm user vars uses APC \x1b_<key>=<value>\x1b\\ for shell-set state visible to the terminal config, (d) iTerm2 inline image uses OSC 1337 (not APC). APC's strength is its blanket pass-through semantics — well-behaved terminals just ignore unknown APC payloads instead of garbling display.
Why does my APC sequence end up echoed as literal text on some terminals?
Two causes. (1) Missing ST terminator — APC enters string-parsing mode at \x1b_ and only exits on \x1b\\; if you forget the ST, the parser keeps eating bytes including subsequent program output until eventually mis-syncing. (2) Terminal doesn't recognise APC at all and falls through to literal display — this is the case for older xterm patch levels (< 332), macOS Terminal (always literal), GNU screen ≤ 4.x (literal unless you enable bind ^_a apc), and tmux without allow-passthrough on (drops APC entirely or echoes literally depending on version). Defensive pattern: probe XTVERSION first to know what you're talking to, fall back to plain output if the terminal is in the literal-echo set, and ALWAYS pair \x1b_ with its matching \x1b\\ ST — never use BEL (\x07) as a substitute terminator (BEL terminates OSC, not APC).

Terminal support

xterm
partial
Linux console (fbcon)
no
macOS Terminal.app
no
iTerm2
partial
Windows Terminal
partial
cmd.exe / ConPTY
no
kitty
yes
alacritty
no
WezTerm
yes
Ghostty
yes
GNOME Terminal
no
Konsole
yes
tmux
yes
GNU screen
yes

Related sequences

In the family cookbook

ESC cookbook · 6. APC — the modern application-to-application envelope `\x1b_ … \x1b\\`