OSC 3008: Hierarchical Context Signalling

A terminal connects a user with programs. Control of the program side of terminals is typically passed around to various different components while the user is active: a shell might pass control to a process it invokes. If that process is run0 then primary control is passed to the privileged session of the target user. If systemd-nspawn is invoked to start a container, primary control is passed to that container, and so on.

A terminal emulator might be interested to know which component is currently in primary control of the program side of a terminal. OSC 3008 is a mechanism to inform it about such contexts. Each component taking over control can inform the terminal emulators that a new context begins now, and then use the terminal or pass control down to further apps, which can introduce contexts. Each context may carry various descriptive metadata fields.

Status

This OSC sequence has been invented by the systemd project and is generated by systemd. Currently, no terminal application is known that consumes these sequences.

Use Cases

Terminal emulators can use hierarchical context information:

  1. To introduce markers/bookmarks in the output that the user can jump between.

  2. To visually identify output from different contexts. For example the background of the associated output can be tinted in a reddish tone when privileges are acquired, and similar.

  3. Meta information on specific output can be shown in a tooltip or similar

  4. Programs (and all subcontexts) can be killed via a right-click menu on the output they generate.

  5. Similar, a right-click menu might offer an item to offer opening a new interactive shell in the same working directory that was current on the selected context.

  6. Failed commands or aborted sessions can be marked requesting user attention.

Context Types

There are various types of contexts defined by this specification:

  1. boot → a booted system initiates this context early at boot. (systemd’s PID 1 generates this on /dev/console.)

  2. container → a container manager initialized an interactive connection to a container. (systemd-nspawn generates this when interactively invoking a container. machinectl login, machinectl shell do this too.)

  3. vm → a VM manager initialized a terminal connection to a VM. (systemd-vmspawn generates this when interactively invoking a VM, as one example.)

  4. elevate → when the user interactively acquired higher privileges. (run0 initiates a context of this type whenever the user invokes it to acquire root privileges.)

  5. chpriv → similar, but when the user acquired different privileges, not necessarily higher ones. (run0 initiates a context of this type whenever the user invokes it to acquire non-root privileges of another user.)

  6. subcontext → similar, but the source and target privileges where identical. (run0 initiates a context of this type whenever the user invokes it to acquire privileges of the user itself.)

  7. remote → a user invoked a tool such as ssh to connect to a remote system.

  8. shell → an interactive terminal shell initiates this context

  9. command → a shell interactively invokes a new program.

  10. app → an interactive program may initiate this context.

  11. service → the service manager invokes an interactive service on the terminal

  12. session → a login session of the user is initialized.

Semantics

Contexts in the sense of OSC 3008 are hierarchical, and describe a tree structure: whenever a new context is opened it becomes the new active context, and the previously active context becomes its parent (if there is one). Only one context is currently active, but previously opened contexts remain valid in the background. Any other data written or read should be considered associated with the currently active context.

Each context carries an identifier, chosen by the component opening the context. The identifier can chosen freely, but must not be longer than 64 characters. The characters may be in the 32…126 byte range. Identifiers should be universally unique, for example randomly generated. A freshly generated UUID would work well for this, but this could also be something like the Linux boot ID combined with the 64bit inode number of Linux pidfds, or something hashed from it.

Fundamentally, there are two OSC 3008 commands defined:

  1. OSC “3008;start=” … (the start sequence) → this initiates, updates or indicates a return to a context. It carries a context identifier, and typically some metadata. This may be sent to first initiate a context. If sent again for the same context ID that was initiated already this indicates an update of the existing context. In this case, any previously set metadata fields for the context are flushed out, reset to their defaults, and then reinitialized from the newly supplied data. Also, in this case any subcontexts of the contexts are implicitly terminated.

  2. OSC “3008;end=” … (the end sequence) → this terminates a context. It carries a context identifier to close, initiated before with OSC “3008;start=”. It may also carry additional metadata.

General Syntax

This builds on ECMA-48, and reuses the OSC and ST concepts introduced there.

For sequences following this specification it is recommended to encode OSC as 0x1B 0x5D, and ST as 0x1B 0x5C.

ECMA-48 only allows characters from the range 0x20…0x7e (i.e. 32…126) inside OSC sequences. However, most terminal emulators nowadays allow the ASCII byte range > 0x7f in the OSC sequences they process, and so does this specification. Control characters (< 0x20 and 0x7f) are not allowed. The semicolon character (“;”) – which is used as field separator by this specification – shall be replaced by “\x3b” and the backslash character (“\”) shall be replaced by “\x5c”. All textual fields must be encoded in UTF-8, and then escaped with these two replacements.

The start sequence begins with OSC, followed by the string 3008;start=, followed by the context ID. This is then followed by any number of metadata fields, including none. Metadata fields begin with a semicolon (;) followed by in a string identifying the type of field, followed by an equal sign (=), and the field value. The sequence ends in ST.

The end sequence begins with OSC, followed by the string 3008;end=, followed by the context ID, and a series of metadata fields in the same syntax as for the start sequence. The sequence ends in ST.

Metadata Fields

The following fields are currently defined for the start sequence:

Field Context Types Description
type= all Declares the context type, one of the types described above
user= all UNIX user name the process issuing the sequence runs as
hostname= all UNIX host name of the system the process issuing the sequence runs on
machineid= all The machine ID (i.e. /etc/machine-id) of the system the process issuing the sequence runs on
bootid= all The boot ID (i.e. /proc/sys/kernel/random/boot_id) of the system the process issuing the sequence runs on
pid= all The numeric PID of the process issuing the sequence, in decimal notation
pidfdid= all The 64bit inode number of the pidfd of the process issuing the sequence, in decimal notation
comm= all The process name (i.e. /proc/$PID/comm, PR_GET_NAME) of the process issuing the sequence
cwd= shell, command The current working directory
cmdline= command The full command line of the invoked command
vm= vm The name of the VM being invoked
container= container The name of the container being invoked
targetuser= elevate, chpriv, vm, container, remote, session Target UNIX user name
targethost= remote Target UNIX, DNS host name, or IP address
sessionid= session New allocated session ID

The following fields are currently defined for the end sequence:

Field Context Types Description
exit= command One of success, failure, crash, interrupt, indicating how the program terminated
status= command The command’s numeric exit status, i.e. the 0…255 value a program returns
signal= command The termination signal of the command, if it died abnormally. A symbolic signal name. (SIGKILL, …)

All fields are optional, including the context type. However, it is generally recommended to always include the first 7 fields listed above, to make it easy to pinpoint the origin of a context in a race-free fashion, without any ambiguities.

The order of the metadata fields is undefined, they may appear in any order (including that type= is specified at the very end or in the middle!). Note that start= and end= are not considered metadata fields but part of the start sequence, and hence must always appear right after OSC.

Processing, Limits, Security

All context information provided like this should be considered auxiliary and – to some degree – redundant information. Hence, it would be wise for a terminal to enforce limits on various resources, dropping additional data once these limits are hit. Most importantly, a maximum stacking depth should probably enforced: any attempts to initiate further contexts should be ignored once the stack limit is hit (i.e. the earlier contexts should be kept, the later contexts be discarded, not the opposite). Overly long fields should be discarded (or potentially truncated, depending on the field type). This specification does not recommend any specific stack or string limits for now.

The usual terminal reset sequences should not affect the stack of contexts (this is a safety feature: a program down the stack should not be able to affect the stack further up, possibly hiding relevant information). A temporary TTY hangup (vhangup()) should result in a full reset of the stack.

All provided data should be processed in a lenient, graceful fashion: if a sequence contains invalid fields, those fields should be ignored, but the rest of the fields should still be used. In particular, unknown fields should be ignored.

The fields provided in these sequences should not contain sensitive information. Context IDs should not be considered confidential, but it is strongly recommended to generate them in a fashion that guarantees their sufficient uniqueness and avoids accidental or intended clashes with other contents.

Examples

  1. A new container foobar has been invoked by user lennart on host zeta: OSC "3008;start=bed86fab93af4328bbed0a1224af6d40;type=container;user=lennart;hostname=zeta;machineid=3deb5353d3ba43d08201c136a47ead7b;bootid=d4a3d0fdf2e24fdea6d971ce73f4fbf2;pid=1062862;pidfdid=1063162;comm=systemd-nspawn;container=foobar" ST

  2. This context ends: OSC "3008;end=bed86fab93af4328bbed0a1224af6d40" ST

Syntax in ABNF

OSC          = %x1B %x5D
ST           = %x1B %x5C

DECIMAL      = "0"-"9"
HEX          = "0"-"9" / "A"-"F" / "a-f"
ID128        = 32*36(HEX / "-")
UINT64       = 1*20DECIMAL
ESCSEMICOLON = "\x3b"
ESCBACKSLASH = "\x5c"
SAFE         = %x20-3a / %x3c-5b / %x5d-7e / ESCSEMICOLON / ESCBACKSLASH

CTXID        = 1*64SAFE
TYPEENUM     = "service" / "session" / "shell" / "command" / "vm" / "container" / "elevate" / "chpriv"  / "subcontext" / "remote" / "boot" / "app"

TYPE         = "type=" TYPEENUM
USER         = "user=" 1*255SAFE
HOSTNAME     = "hostname=" 1*255SAFE
MACHINEID    = "machineid=" 1D128
BOOTID       = "bootid=" ID128
PID          = "pid=" UINT64
PIDFDID      = "pidfdid=" UINT64
COMM         = "comm=" 1*255SAFE
CWD          = "cwd=" 1*255SAFE
CMDLINE      = "cmdline=" *255SAFE
VM           = "vm=" 1*255SAFE
CONTAINER    = "container=" 1*255SAFE
TARGETUSER   = "targetuser=" 1*255SAFE
TARGETHOST   = "targethost=" 1*255SAFE
SESSIONID    = "sessionid=" 1*255SAFE

STARTFIELD   = TYPE / USER / HOSTNAME / MACHINEID / BOOTID / PID / PIDFDID / COMM / CWD / CMDLINE / VM / CONTAINER / TARGETUSER / TARGETHOST / SESSIONID
STARTSEQ     = OSC "3008;start=" CTXID *(";" STARTFIELD) ST

EXITENUM     = "success" / "failure" / "crash" / "interrupt"
SIGNALENUM   = "SIGBUS" / "SIGTRAP" / "SIGABRT" / "SIGSEGV" / …

EXIT         = "exit=" EXITENUM
STATUS       = "status=" UINT64
SIGNAL       = "signal=" SIGNALENUM

ENDFIELD     = EXIT / STATUS / SIGNAL
ENDSEQ       = OSC "3008;end=" CTXID *(";" ENDFIELD) ST

Known OSC Prefixes

Here’s a list of OSC prefixes used by the various sequences currently in public use in various terminal emulators. It’s not going to be complete, but I tried to do some reasonably thorough research to avoid conflicts with the new OSC sequence defined above.

OSC Prefix Purpose
OSC "0;…" Icon name + window title
OSC "1;…" Icon name
OSC "2;…" Window title
OSC "3;…" X11 property
OSC "4;…" Palette
OSC "5;…" Special palette
OSC "6;…" Disable special color
OSC "7;…" Report cwd
OSC "8;…" Hyperlink
OSC "9;…" Progress bar (conemu) [conflict: also growl notifications]
OSC "10;…" Change colors
OSC "11;…"
OSC "12;…"
OSC "13;…"
OSC "14;…"
OSC "15;…"
OSC "16;…"
OSC "17;…"
OSC "18;…"
OSC "19;…"
OSC "21;…" Query colors (kitty)
OSC "22;…" Cursor shape
OSC "46;…" Log file
OSC "50;…" Set font
OSC "51;…" Emacs shell
OSC "52;…" Manipulate selection data (aka clipboard)
OSC "60;…" Query allowed
OSC "61;…" Query disallowed
OSC "99;…" Notifications (kitty)
OSC "104;…" Reset color
OSC "105;…" Enable/disable special color
OSC "110;…" Reset colors
OSC "111;…"
OSC "112;…"
OSC "113;…"
OSC "114;…"
OSC "115;…"
OSC "116;…"
OSC "117;…"
OSC "118;…"
OSC "119;…"
OSC "133;…" Prompt/command begin/command end (finalterm/iterm2)
OSC "440;…" Audio (mintty)
OSC "633;…" vscode action (Windows Terminal)
OSC "666;…" “termprop” (vte)
OSC "701;…" Locale (mintty)
OSC "777;…" Notification (rxvt)
OSC "3008;…" This specification
OSC "7704;…" ANSI colors (mintty)
OSC "7750;…" Emoji style (mintty)
OSC "7770;…" Font size (mintty)
OSC "7771;…" Glyph coverage (mintty)
OSC "7721:…" Copy window title (mintty)
OSC "7777;…" Window size (mintty)
OSC "9001;…" Action (Windows Terminal)
OSC "1337;…" iterm2 multiplex seeuqnece
OSC "5522;…" Clipboard (kitty)
OSC "30001;…" Push color onto stack (kitty)
OSC "30101;…" Pop color from stack (kitty)
OSC "77119;…" Wide chars (mintty)