From ac9a5f89afcbac6d6caa64d8eac6669c8c0a498c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Dec 2025 09:24:14 +0100 Subject: [PATCH] specs: import OSC 3008 spec from systemd This stuff is really generic, and fits the purpose of the UAPI group, hence let's migrate this here. This is mostly a 1:1 copy of the spec from systemd, but I enclosed the final part that carries a list of OSC sequences currently known in markdown comments, because it's not really part of the spec, but still kinda useful for spec writers. (Maybe one day we should turn that table into a "registry" style spec of its own?) --- README.md | 3 + specs/osc_context.md | 346 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 349 insertions(+) create mode 100644 specs/osc_context.md diff --git a/README.md b/README.md index aa82c32..d41b54a 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,9 @@ The following specifications have been accepted by the UAPI group: * [UAPI.14 Virtual Machine Generation ID](specs/vmgenid.md): Describes the mechanism for detecting virtual machine rollback events. ([canonical online location](https://uapi-group.org/specifications/specs/vmgenid/)) +* [UAPI.15 OSC 3008: Hierarchical Context Signalling](specs/osc_context.md): + Defines a mechanism for terminal emulators to follow the context hierarchy of what's on screen. + ([canonical online location](https://uapi-group.org/specifications/specs/osc_context/)) ## Work in Progress diff --git a/specs/osc_context.md b/specs/osc_context.md new file mode 100644 index 0000000..c414a2f --- /dev/null +++ b/specs/osc_context.md @@ -0,0 +1,346 @@ +--- +title: "UAPI.15 OSC 3008: Hierarchical Context Signalling" +category: Concepts +layout: default +version: 1.0 +SPDX-License-Identifier: CC-BY-4.0 +weight: 15 +aliases: +- /UAPI.15 +- /15 +--- + +# UAPI.15 OSC 3008: Hierarchical Context Signalling + +| Version | Changes | +|---------|---------| +| 1.0 | Initial release | + +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 systemd's `run0` then primary control is passed to the privileged session of +the target user. If `systemd-nspawn` is then 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. + +## 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. + +7. A "breadcrumb" style display can be implementing shown the nesting of contexts to the user. + +## 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.) + +5. `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.) + +6. `remote` → a user invoked a tool such as `ssh` to connect to a remote + system. + +7. `shell` → an interactive terminal shell initiates this context + +8. `command` → a shell interactively invokes a new program. + +9. `app` → an interactive program may initiate this context. + +10. `service` → the service manager invokes an interactive service on the terminal + +11. `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 + +```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 +``` + +