Zig Forwarder Refactor Plan
Goals
- Per-process Zig forwarder replaces Node
fwdpath; minimal latency + RSS. - Unix/macOS only; no daemon.
- Node web server stays optional; consumes same session files + IPC.
- No app-specific (Claude) activity detection; generic active/idle only.
Non-Goals
- Windows support.
- New UI features.
- Server-managed PTY rewrite in this phase.
Current State (vt / fwd)
vtwrapper →vibetunnel-fwd(zig) → PTY + stdout + ipc.sock + session.json.- Writes
~/.vibetunnel/control/<id>/session.json+stdout(asciinema v2). - IPC via
ipc.sockwith framed protocol (stdin/resize/kill/update-title). - Server derives session activity from recent input/output timestamps (UI active/idle).
Target Architecture (per-process)
Compatibility Contracts
- Control dir layout:
~/.vibetunnel/control/<id>/session.json,stdout(asciinema v2),stdin(FIFO),ipc.sock.
- Session JSON schema:
web/src/shared/types.tsSessionInfo. - IPC framing:
web/src/server/pty/socket-protocol.ts- 1 byte type + 4 byte BE length + payload.
STDIN_DATAraw bytes (utf8),CONTROL_CMDJSON.
- CLI flags:
--session-id,--title-mode,--verbosity,--update-title,--log-file,--. - Env:
VIBETUNNEL_SESSION_ID,VIBETUNNEL_TITLE_MODE,VIBETUNNEL_LOG_LEVEL.
Claude Activity Detection Removal (explicit)
Done:- Removed
claude-turnevent path + app-specific status updates. - Removed config toggle + UI setting + push notification type.
- Generic active/idle derived from input/output activity (no Claude-specific status).
Refactor Phases
Phase 0: Repo Layout + Scaffolding
- New Zig package:
native/vt-fwd/(orzig/vt-fwd/). - Copy minimal Ghostty POSIX PTY code:
src/pty.zig(POSIX branch only).- Keep MIT header + attribution.
- Build outputs:
web/bin/vibetunnel-fwd(npm).mac/VibeTunnel/Resources/vibetunnel-fwd(app bundle).
Phase 1: Core PTY + Process Spawn
- PTY open:
openpty,setsid,TIOCSCTTY,ioctlfor size. - Spawn command with PTY slave as stdio.
- Capture master fd for read/write.
- Env: set
TERM,VIBETUNNEL_SESSION_ID. - Graceful shutdown on SIGTERM/SIGINT.
Phase 2: Session Files + Asciinema v2
- Create control dir + FIFO (
stdin) likeSessionManager. - Write
session.jsoninitial:starting, pid, cwd, command, title mode. - Asciinema v2 writer:
- Header line JSON.
- Output events:
[time,"o","..."]. - Input events:
[time,"i","..."](for stdin echo). - Resize events:
[time,"r","COLSxROWS"]. - Exit event:
["exit", code, sessionId].
- Update
session.jsonon exit:status,exitCode,pid. lastClearOffset: start0; optional later pruning detection.
Phase 3: IPC Socket Server
- Create
ipc.sockin session dir; enforce macOS path length limit. - Accept multiple clients; parse frames.
- Handle:
STDIN_DATA: write to PTY, write input event.CONTROL_CMD:resize: PTY resize + asciinema resize event.kill: send signal to child.reset-size: set to current terminal size if TTY.update-title: updatesession.jsonname; local title update.
- Backpressure: queue writes; avoid partial frame parsing errors.
Phase 4: Title Modes (no activity)
none: pass through, no title updates.filter: strip OSC title sequences from output before writing.static: set title on start + onupdate-title.- Keep
vt titlesemantics via--update-titleclient path.
Phase 5: Integration + Switch
- Update
web/bin/vtto prefervibetunnel-fwdif present. vibetunnel fwdpath: execvibetunnel-fwddirectly (no Node fallback).- Packaging:
web/scripts/build-npm.js: include Zig binary.mac/scripts/build-web-frontend.sh: copy Zig binary into app bundle.
Phase 6: Cleanup (post-forwarder)
- Ensure STATUS_UPDATE is ignored end-to-end.
Test Plan
- Zig unit tests: frame parser, asciinema writer, PTY resize.
- Integration:
vibetunnel-fwd echo hi→ session.json + stdout + exit event.- IPC stdin write → output appears.
- Resize command → asciinema resize event.
- Web server:
- stream watcher reads Zig stdout file.
vt titleupdates session name.
- Verified no
claude-turntests remain.
Rollout Plan
- Zig forwarder always-on; Node forwarder removed.
- Metrics: start latency, RSS, failures in
vtintegration tests.
Risks + Mitigations
- PTY edge cases (macOS TTY size): guard + fallback cols/rows.
- Encoding issues: strict UTF-8; preserve escape sequences.
- Socket path length: short session IDs.
- Crash cleanup: handle signals; remove
ipc.sock; update status.
Open Questions
- Final binary name + path?
- Pruning detection parity now or later?