# RFC: Secure Semantic Data Injection (SDI) via OSC 7777 Escape Sequences

**Author:** Jessica Mulein  
**Revised by:** Claude (security review)  
**Status:** Proposal / Draft Standard  
**Date:** May 2026

---

## 1. Abstract

Modern terminal workflows routinely interact with, generate, and process multi-field, highly structured ephemeral data—such as dynamic test credentials, cloud session authentication elements, and complex infrastructure connection contexts.

However, the protocol layer interfacing terminal emulator tasks with the host operating system remains restricted to flat text streaming or unsecured, single-value system clipboards. This document proposes an open, cross-platform standard for Secure Semantic Data Injection (SDI) using a new operating system command sequence (OSC 7777), enabling isolated, cryptographically validated pipeline structures to communicate typed JSON states natively between shells and background companion applications without risking clipboard exposure or text-forgery attacks.

---

## 2. The Problem & The Vulnerability Vector

### 2.1 Limitations of the System Clipboard

Passing rich structural schemas (such as a username, password, and seed phrase simultaneously) via the native OS clipboard forces an unideal developer compromise: either concatenating strings into fragile formats, manually copying individual fields iteratively, or leaking sensitive credentials to local background clipboard manager histories.

### 2.2 Terminal Line Hijacking & Rogue Code Execution

Relying on standard unauthenticated Operating System Commands (OSC) to communicate with native desktop applications poses a severe security hazard. If a terminal environment blindly parses and acts upon plaintext escape sequences embedded within standard output, any untrusted asset—such as a malicious repository file evaluated via `cat`, a deceptive git commit log, or an unvetted server Message of the Day (MOTD)—can forge sequences to inject structural data or trigger unintended desktop-agent side-effects.

---

## 3. Architecture Specification

The Secure SDI standard introduces a distinct decoupled separation between the Transport Vector (the PTY text pipeline) and the Authentication Control Layer (an out-of-band IPC handshake).

### 3.1 Out-of-Band Cryptographic Registration

Before any data injection takes place, an interactive shell session registers itself with a localized, user-restricted background Desktop Agent Daemon running on the host system.

1. **Local Channel:** The Agent hosts a restricted Unix domain socket accessible exclusively by the local user account (`chmod 600`, `stat.st_uid == getuid()`). The socket path **must** include a per-boot or per-agent random component (e.g. `/run/user/<uid>/sdi-agent-<16-random-hex>.sock`) to prevent socket-path squatting by a malicious process that starts before the agent. The socket path is communicated to child shells via a protected environment variable (`SDI_AGENT_SOCK`) set only at agent startup.

2. **Ephemeral Exchange:** The shell instance connects to the socket during its initialization phase and performs an X25519 ECDH key agreement followed by HKDF-SHA256 derivation to produce a unique 32-byte session key ($K_{session}$) alongside a transient Session-ID.

3. This key resides strictly within the active memory space of that specific shell process and the Desktop Agent.

4. **Session Expiry:** Sessions have a maximum lifetime of 8 hours regardless of activity. The agent **must** refuse to decrypt OSC 7777 sequences for expired sessions and **must** log the attempt. Shells that outlive their session must re-register.

5. **Squatting Defense:** On startup the agent **must** verify that no file exists at the chosen socket path before binding. If a socket file already exists at that path, the agent **must** abort with a fatal error rather than overwriting it.

### 3.2 Registration Wire Protocol

The registration handshake uses a compact binary framing over the Unix domain socket — **no JSON, no length prefix**.

**Shell → Agent** (48 bytes, sent atomically):

| Offset | Length | Content |
| --- | --- | --- |
| 0 | 16 | `session_id` — 16 cryptographically random bytes generated by the shell |
| 16 | 32 | `shell_pub` — the shell's ephemeral X25519 public key (raw 32-byte little-endian) |

**Agent → Shell** (32 bytes):

| Offset | Length | Content |
| --- | --- | --- |
| 0 | 32 | `agent_pub` — the agent's ephemeral X25519 public key (raw 32-byte little-endian) |

After the exchange both sides independently derive the session key:

$$K_{session} = \text{HKDF-SHA256}(\text{IKM} = X25519(\text{priv}, \text{peer\_pub}),\ \text{salt} = \text{session\_id},\ \text{info} = \text{"sdi-session-key"},\ L = 32)$$

The socket connection is then closed. $K_{session}$ is never transmitted.

**Session-ID encoding:** The raw 16-byte `session_id` is encoded as 32 lowercase hex characters in the OSC sequence (e.g. `2a3f...`).

**Rate limiting:** The agent **must** enforce a rate limit of no more than 10 failed authentication attempts per minute per connecting PID. Exceeding this threshold causes the agent to close the connection and log a warning. This mitigates local brute-force enumeration of session IDs.

### 3.3 The Secure OSC 7777 Sequence Syntax

When a utility or process inside the shell wants to broadcast structured semantic data, it wraps the payload inside an encrypted OSC 7777 macro structure:

```
\e]7777;<session-id-hex>;<base64-counter>;<type>;<base64-context>;<base64-nonce>;<base64-ciphertext>;<base64-auth-tag>\a
```

| Field | Encoding | Description |
| --- | --- | --- |
| `session-id-hex` | 32-char lowercase hex | Maps the sequence to a registered session key |
| `base64-counter` | standard Base64 | 8-byte big-endian unsigned monotonic sequence counter (see §3.5) |
| `type` | plaintext ASCII | Payload schema identifier (e.g. `ephemeral-auth`) |
| `base64-context` | standard Base64 | Routing context (e.g. API URL); Base64 to avoid semicolon collisions |
| `base64-nonce` | standard Base64 | 12-byte AES-GCM initialization vector |
| `base64-ciphertext` | standard Base64 | AES-256-GCM encrypted JSON payload |
| `base64-auth-tag` | standard Base64 | 16-byte GCM authentication tag |

`\a` (BEL, 0x07) is the sequence terminator.

> **Note on `type` field confidentiality:** The `type` field is transmitted in plaintext and will be visible to any observer of the PTY stream (e.g. terminal recordings, log captures). Implementations that consider the payload schema identifier sensitive **should** use a generic `type` value (e.g. `sdi-payload`) and encode the true type inside the encrypted JSON body. See §4 for schema conventions.

### 3.4 Additional Authenticated Data (AAD)

The `counter`, `type`, and `context` values are bound into the AES-256-GCM authentication tag as Additional Authenticated Data. The AAD **must** be constructed using length-prefixed encoding to prevent boundary confusion attacks:

$$\text{AAD} = \text{LE32}(\text{len}(\mathit{counter\_bytes})) \mathbin\| \mathit{counter\_bytes} \mathbin\| \text{LE32}(\text{len}(\mathit{type\_bytes})) \mathbin\| \mathit{type\_bytes} \mathbin\| \text{LE32}(\text{len}(\mathit{context\_bytes})) \mathbin\| \mathit{context\_bytes}$$

where `LE32(n)` is a 4-byte little-endian encoding of `n`, `counter_bytes` is the raw 8-byte big-endian counter, `type_bytes` is the UTF-8 encoding of the type string, and `context_bytes` is the raw decoded bytes of the Base64 context field.

The agent **must** supply all three values during decryption. If any field is absent its length prefix is `LE32(0)` and it contributes zero payload bytes (the length prefix itself is still included).

This construction ensures that a captured ciphertext cannot be replayed under a different type, context, or counter value even if the session key were somehow extracted.

### 3.5 Replay Protection via Monotonic Counter

Each shell session maintains a per-session monotonic counter, initialized to `0` at registration and incremented by 1 for every OSC 7777 sequence emitted. The current counter value is encoded as an 8-byte big-endian unsigned integer, base64-encoded, and included in the sequence (see §3.3).

The agent **must**:

- Track the highest counter value seen for each active session.
- Reject any sequence whose counter value is less than or equal to the last accepted counter for that session.
- Accept counter values up to `last_accepted + 1000` to tolerate out-of-order delivery in pipelines, but reject values beyond that window.
- Log all replayed or out-of-window counter values as security events.

This prevents replay of any captured OSC 7777 sequence, even within an active session.

### 3.6 Injection Interface

The reference shell (`bsh`) exposes a builtin `bsh-inject` that implements the full encrypt-and-emit pipeline:

```
bsh-inject --type <type> --context <url>
```

The JSON payload is read from **stdin**. The builtin performs lazy session initialisation (if not already registered), increments the session counter, encrypts with $K_{session}$, and writes the OSC 7777 sequence **directly to `/dev/tty`** rather than stdout. This ensures the sequence reaches the terminal emulator regardless of how the caller has redirected stdout, and prevents the ciphertext from being accidentally written to files, pipes, or logs.

If stdout emission is explicitly required (e.g. for testing or piping to a custom terminal), the flag `--emit-stdout` may be passed to override this behavior. Callers using `--emit-stdout` are responsible for ensuring the sequence reaches a terminal emulator and not a log sink.

**Agent failure behavior:** If the agent is unavailable or the session has not been registered, `bsh-inject` **must** fail closed — it prints an error to stderr and exits non-zero. It **must not** fall back to emitting plaintext or unencrypted OSC sequences.

---

## 4. Standardized Payload Schemas

To maintain universal compatibility across browser extensions, form fillers, and desktop application management panels, payloads must adhere to predictable, strongly-typed semantic JSON specifications.

### 4.1 ephemeral-auth

Targeted at seeding short-lived accounts, hotseat multiplayer testing matrices, and web environment login flows.

```json
{
  "type": "ephemeral-auth",
  "context": "http://localhost:3005",
  "ttl": 300,
  "issued_at": 1748000000,
  "data": {
    "username": "player1",
    "password": "TemporarySecurePassword123!",
    "email": "player1@localhost.localdomain",
    "additional_fields": {
      "mnemonic": "fury appear bargain good coin load tattoo object convince render soft inside..."
    }
  }
}
```

### 4.2 db-connection

Targeted at dynamically focusing or pre-configuring native desktop graphic database viewers straight from an active terminal workspace context.

```json
{
  "type": "db-connection",
  "context": "development-cluster-alpha",
  "ttl": 60,
  "issued_at": 1748000000,
  "data": {
    "engine": "postgresql",
    "host": "127.0.0.1",
    "port": 5432,
    "user": "db_admin",
    "pass": "ephemeral_token_string"
  }
}
```

**Schema notes:**

- The `issued_at` field (Unix timestamp, seconds) is **required** in all payload schemas. Agents **should** reject payloads whose `issued_at` is more than `ttl` seconds in the past, or more than 60 seconds in the future, as an additional defense-in-depth layer against replayed payloads that bypass counter validation.
- The `ttl` field specifies the maximum lifetime of the decrypted state in the agent's memory. Agents **must** purge decrypted state after `ttl` seconds regardless of other conditions.

---

## 5. Security & Threat Mitigation Profiling

- **Rogue Injection Defense:** If an external malicious source outputs an unauthorized OSC 7777 block to stdout, the Desktop Agent intercepts the sequence, attempts validation via the mapped session key, and fails immediately at the AES-GCM Authentication Tag verification phase. The packet is dropped instantly with zero user impact.

- **Replay Defense:** The monotonic counter (§3.5) and `issued_at` timestamp (§4) together ensure that a captured OSC 7777 sequence cannot be re-submitted to the agent, even within an active session. Counter replay attempts are logged as security events.

- **AAD Boundary Confusion Defense:** Length-prefixed AAD encoding (§3.4) prevents any two distinct `(type, context)` pairs from producing identical AAD bytes, closing the boundary confusion attack surface present in naive concatenation.

- **Socket Squatting Defense:** Randomized socket paths and pre-bind existence checks (§3.1) prevent a malicious process from pre-occupying the socket path before the agent starts.

- **Process Ring-Fencing:** Because session encryption keys are isolated per terminal window or tab instance, background commands running concurrently in other workspaces cannot intercept, manipulate, or extract state data crossing adjacent active streams.

- **Memory-Bound Persistence (Zero Leaks):** Decrypted states are stored exclusively within the volatile memory space of the background companion daemon. Payloads automatically drop when their specified Time-To-Live metric expires or when the terminal session registers a clean exit sequence, avoiding long-term storage footprints.

- **Fail-Closed Injection:** `bsh-inject` writes directly to `/dev/tty` and refuses to operate if the agent is unavailable, preventing accidental ciphertext persistence or silent fallback to unprotected channels.

- **Type Field Visibility:** Implementors are advised that the `type` field is visible in plaintext on the PTY stream. Sensitive schema classification should be encoded within the encrypted payload body (§3.3).

---

## 6. Implementation Ecosystem & Future Directions

The reference implementation of this protocol is actively deployed and validated inside the BSH shell engine ecosystem (`bsh`), paired with a Swift-based macOS Desktop Agent (`SDIAgent`) that runs as a menu-bar background process, hosts the Unix domain socket, and routes decrypted payloads to registered application handlers.

By standardizing structured semantic pipelines, we pave the way for downstream integrations—such as secure browser plug-ins that auto-populate active web testing fields based on localized terminal history context, and unified system tray utilities that bridge CLI development speed with the accessibility of modern desktop interface tooling.

### 6.1 Browser Extension Integration — Security Considerations

Browser extension integrations **must** be treated as a distinct, high-risk trust boundary. Browser extensions operate under a significantly weaker security model than native daemons: content scripts, extension messaging APIs, and browser-controlled sandbox policies introduce attack surfaces not present in the native IPC layer defined in this RFC. Any downstream integration that routes decrypted payloads to a browser extension **must** define its own security protocol, including:

- A separate authenticated channel between the Desktop Agent and the extension (e.g. native messaging with explicit host manifest allowlisting).
- Strict scoping of which payload types are permitted to flow to the browser layer.
- An explicit threat model addressing extension compromise, malicious content scripts, and cross-origin message interception.

Treating browser extension delivery as a transparent extension of the native SDI pipeline is explicitly out of scope for this RFC and **must not** be assumed by implementors.

---

## Appendix A: Summary of Security Changes from Initial Draft

| Issue | Resolution |
| --- | --- |
| No replay protection | Monotonic counter added to OSC sequence and AAD (§3.3, §3.5) |
| AAD boundary confusion via naive concatenation | Length-prefixed AAD encoding required (§3.4) |
| Predictable socket path enables squatting | Randomized socket path + pre-bind existence check required (§3.1) |
| `bsh-inject` writes to stdout (risks log/pipe leakage) | Writes to `/dev/tty` by default; `--emit-stdout` is opt-in (§3.6) |
| No session expiry | 8-hour maximum session lifetime defined (§3.1) |
| No rate limiting on socket | 10 failed attempts/min/PID limit required (§3.2) |
| `type` field confidentiality not addressed | Advisory note added; sensitive type should be inside encrypted body (§3.3) |
| Browser extension surface unaddressed | Explicit high-risk boundary callout with required separate protocol (§6.1) |
| No agent failure handling | Fail-closed behavior defined; no silent plaintext fallback (§3.6) |
| `issued_at` absent from schemas | Required in all payload schemas; agent should validate against `ttl` (§4) |
| Wire protocol says "TCP connection" for a Unix socket | Corrected to "socket connection" throughout |
