Clipboard support is deliberately optional. Install @gespenst/clipboard when a terminal should
accept browser paste events or expose the Kitty clipboard protocol to applications running through
the PTY.
pnpm add @gespenst/core @gespenst/clipboard
import { ClipboardAddon } from '@gespenst/clipboard';
const clipboard = new ClipboardAddon({
maxBytes: 32 * 1024 * 1024,
snapshotTtlMs: 30_000,
confirmUnsafePaste: ({ text }) => {
return window.confirm(`Paste this text into the terminal?\n\n${text}`);
},
onError(error) {
console.error(error.code, error.message);
},
});
terminal.loadAddon(clipboard);
await clipboard.ready;
The addon listens for native paste events on the terminal. When a terminal application has
enabled private mode 5522, Gespenst sends the available MIME types and holds the data in a
short-lived snapshot. The application can then request representations such as image/png or
text/html over OSC 5522. Ghostty parses and encodes the protocol; Gespenst supplies browser bytes
and policy.
When mode 5522 is not enabled, the first text representation uses the ordinary paste path and
honors bracketed-paste mode. Binary-only clipboard data produces an empty result rather than being
converted or discarded into the PTY.
Programmatic browser clipboard reads must remain inside a user activation. Do not insert an
unrelated timeout or network request before calling pasteFromClipboard():
pasteButton.addEventListener('click', async () => {
try {
const result = await clipboard.pasteFromClipboard();
if (result.status === 'unsafe') {
showStatus('Paste cancelled because it could execute commands');
}
} catch (error) {
showStatus('Clipboard permission was not granted');
}
});
navigator.clipboard.read() supplies all available MIME representations where the browser supports
it. The addon falls back to readText() on browsers with text-only clipboard access. HTTPS or
localhost is normally required.
| Action | Default behavior |
|---|---|
| User presses paste inside the terminal | Read and paste through the addon |
Trusted UI calls pasteFromClipboard() |
Read during the caller's user activation |
| Remote application requests clipboard data without a paste event | Deny |
| Remote application writes through OSC 52 or OSC 5522 | Deny; emit the existing audit event |
| Multiline text without bracketed paste | Return unsafe |
| Text containing a bracketed-paste terminator | Return unsafe |
Unsafe text stays rejected unless confirmUnsafePaste exists and resolves to true. Show the exact
text and require an explicit decision in that callback. Do not approve based only on PTY or host
identity; a compromised process connected to a trusted shell can still emit terminal sequences.
The default 32 MiB transaction limit prevents a clipboard item from creating an unbounded worker message or WASM allocation. The default 30-second snapshot lifetime limits how long a Kitty paste password can retrieve data. Each data request consumes the current snapshot, and a subsequent paste replaces any older one.
Uint8Array/ArrayBuffer across the browser API, worker boundary, and Ghostty ABI.