# docs/10-security.md · lucenx/signaltty

[View on GitCafe](https://git.cafe/lucenx/signaltty/blob/refs%2Fheads%2Forch%2Ft031-quickstart/docs/10-security.md)

Repository: [lucenx/signaltty](https://git.cafe/lucenx/signaltty)

Visibility: public

Requested revision: refs/heads/orch/t031-quickstart

Commit: 6ce4af99f702ed3f8c2584105425ba0f7d646994

Blob: 2a8af13bf235ac96a1f7f7151e6dfdfb3eb4ba0e

Size: 3370 bytes

[Immutable source](https://git.cafe/lucenx/signaltty/blob/6ce4af99f702ed3f8c2584105425ba0f7d646994/docs/10-security.md?format=markdown)

```
# 10 — Security Model

The server is security-sensitive: it spawns processes, writes to PTYs,
and accepts automation commands. Threat model: malicious or buggy
**local** content (terminal output, hook payloads, plugin scripts,
notification text) and other local users; no remote attacker in scope
for the MVP (no network listener).

## Boundaries & requirements

- **Local-only**: Unix socket only, under `$XDG_RUNTIME_DIR/signaltty/`
  (dir 0700, socket 0600, user-owned). No TCP listener by default; any
  future remote bridge must be explicit, authenticated, and documented.
- **Peer identity**: `SO_PEERCRED` check — refuse connections whose
  uid ≠ server uid, even if filesystem perms were loosened.
- **Input validation**: every IPC param validated (id format, size
  clamps, argv non-empty + executable lookup, signal allowlist,
  base64 bounds, 16 MiB line cap). Malformed input → error, never panic.
- **Kill scope**: `pane.signal` defaults to the child pid only;
  process-group kill requires explicit `group: true` and only targets
  the pane's own pgid. No arbitrary-pid signalling.
- **Spawn scope**: `pane.spawn` runs as the server user with cwd
  constrained to directories the user can access; env overrides pass
  an allowlist + blocklist (`LD_PRELOAD`, `LD_LIBRARY_PATH` stripped
  unless explicitly permitted in server config).
- **Automation boundaries**: `pane.input`/`pane.read` are the
  orchestration primitives — available to any local client of the
  user's own socket. Document that any process running as the user can
  drive panes; this matches tmux semantics. Future macaroons/scopes
  (read-only automation tokens) are a Phase 4 hardening item, not MVP.
- **Notification sanitization**: title/body stripped of C0/C1 controls
  (except `\n\t`), OSC/CSI sequences neutralized, length-capped
  (title 200, body 2000 chars) before storage, IPC fan-out, or desktop
  delivery. Desktop actions carry only opaque ids — clicking never
  executes embedded commands.
- **Hook payloads**: treated as untrusted JSON; size-capped (256 KiB),
  parsed strictly, unknown fields ignored; hook shims run with user
  privileges. Direct supported agent launches configure their integration
  automatically (ADR-0013); manual install remains available. The installer
  preserves foreign keys/commands, refuses invalid configs and unrelated plugin
  collisions, and serializes atomic updates with an advisory lock. Provider trust
  and explicit disabled-hook settings are preserved.
- **Terminal output**: the server's OSC scanner only *reads*; extracted
  strings go through the same sanitizer. VTE/GUI rendering of untrusted
  bytes is the widget's job (VTE already handles this).
- **Persistence**: `state_dir` 0700; snapshot contains no secrets by
  construction (env allowlist); scrollback tails may contain
  credentials the user pasted — file perms 0600, documented, with a
  `redact` option to disable tail persistence per workspace.
- **Executable plugins (Phase 4)**: run with full user privileges;
  install is explicit and the trust requirement is documented at
  install time. No sandboxing in MVP; investigated later.

## Non-goals (MVP)

Multi-user servers, network exposure, privilege separation between
server/workers, secret redaction inside scrollback content, audit log
(signing). Revisit if a network listener is ever added.

```
