Skip to content

Merge upstream PR #828 into fork#1

Merged
da-beda merged 40 commits into
mainfrom
pr-828-merge
Mar 18, 2026
Merged

Merge upstream PR #828 into fork#1
da-beda merged 40 commits into
mainfrom
pr-828-merge

Conversation

@da-beda
Copy link
Copy Markdown
Owner

@da-beda da-beda commented Mar 18, 2026

This merges the locally resolved upstream PR manaflow-ai#828 branch into the fork.\n\nSource commit: ea1e719\n\nNotes:\n- Resolved the parent repo merge conflicts in docs/ghostty-fork.md and the ghostty submodule pointer.\n- Kept the ghostty submodule at 9b0febb591623d801b51f77566c138ce631ad940 to preserve the Linux embedded-host support required by the linux port.

shuhei0866 and others added 30 commits March 4, 2026 12:13
Add linux/ directory for the Rust-based Linux port of cmux.
Cargo workspace with 4 member crates:
- ghostty-sys (FFI bindings to libghostty)
- ghostty-gtk (safe GTK4 wrapper)
- cmux (main application)
- cmux-cli (CLI client)

Addresses manaflow-ai#330 — Linux support request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hand-written repr(C) translation of ghostty.h:
- All opaque types (app, config, surface, inspector)
- Platform enum with GHOSTTY_PLATFORM_LINUX addition
- 190+ key codes (W3C UIEvents spec)
- Input structs (key, mouse, scroll, modifiers)
- 60+ action types with tagged union
- Runtime callback function pointers (wakeup, action, clipboard, close)
- Full published API extern declarations (behind link-ghostty feature)
- build.rs: invokes `zig build -Dapp-runtime=embedded` + links libghostty.a

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Safe Rust abstraction over ghostty-sys FFI:
- GhosttyApp: app lifecycle (init, tick, focus, color scheme)
- GhosttyGlSurface: GObject subclass of GtkGLArea
  - realize/render/resize signal handlers
  - EventControllerKey for keyboard input
  - GestureClick + EventControllerMotion for mouse
  - EventControllerScroll for scroll events
  - GtkIMMulticontext for IME/compose support
  - Focus enter/leave tracking
- GhosttyCallbackHandler trait with double-indirection fat pointer
  pattern for passing dyn Trait through C void* userdata
- RuntimeCallbacks: 6 C trampolines (wakeup, action, read/confirm
  clipboard, write clipboard, close surface)
- GDK keyval → ghostty key mapping (raw u32 constants)
- Evdev hardware keycode → ghostty key table
- GDK modifier → ghostty modifier conversion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Core data model compatible with macOS cmux:
- Panel: terminal/browser with UUID, title, directory, git branch,
  listening ports, status items
- LayoutNode: recursive enum (Pane | Split) for binary split tree
  with insert, remove, collapse, and find operations
- Workspace: panel collection + layout tree + metadata (status,
  progress, logs, custom title, current directory)
- TabManager: ordered workspace list with selection tracking,
  navigation (next/prev/last), find-by-panel-id
- SplitOrientation: Horizontal | Vertical
- 12 unit tests covering layout manipulation, workspace CRUD,
  and tab manager navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GTK4 + libadwaita native UI implementation:
- window.rs: AdwApplicationWindow with AdwNavigationSplitView
  (sidebar + content), header bar with split/new-workspace buttons,
  keyboard shortcuts (Ctrl+Shift+T/W/D/E)
- sidebar.rs: GtkListBox workspace list with index labels, titles,
  unread notification badges, git branch indicators, click-to-select
- split_view.rs: recursive build_layout() converting LayoutNode tree
  into nested GtkPaned widgets, GtkStack for multi-panel panes
- terminal_panel.rs: GhosttyGlSurface wrapper for terminal panels,
  placeholder for browser panels (Phase 4)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Async socket server and protocol layer compatible with macOS cmux:
- server.rs: tokio UnixListener on /tmp/cmux.sock, per-client
  async tasks, line-delimited JSON, automatic socket cleanup
- v2.rs: v2 JSON protocol dispatch with 16 methods:
  - system.ping, system.capabilities
  - workspace.list/new/select/next/previous/last/close
  - workspace.set_status/report_git_branch/set_progress/append_log
  - pane.new, surface.send_input, notification.create
- auth.rs: SO_PEERCRED peer UID/PID extraction, SocketControlMode
  (open/cmux_only), same-UID authentication
- SharedState (Arc<Mutex<TabManager>>) for cross-thread access
  between GTK main thread and tokio socket tasks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Session persistence with macOS-compatible JSON format:
- snapshot.rs: full type hierarchy (AppSessionSnapshot → Window →
  TabManager → Workspace → Panel) with recursive layout serialization,
  bidirectional conversion helpers (snapshot ↔ model types)
- store.rs: XDG_DATA_HOME (~/.local/share/cmux/) storage,
  save/load session JSON, create_snapshot from live state

Notification system:
- notifications.rs: NotificationStore with add, unread count per
  workspace, mark-read, clear operations, desktop notification
  placeholder via gio::Notification (Phase 3)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Application entry point:
- main.rs: tracing-subscriber logging init, ghostty stub init,
  AdwApplication launch with activate callback
- app.rs: AppState (Rc<RefCell>, GTK main thread) and SharedState
  (Arc<Mutex>, thread-safe), application setup with background
  tokio socket server thread

CLI client (cmux-cli):
- clap-derive subcommand hierarchy:
  - workspace list/new/select/next/previous/last/close/status/git/progress/log
  - surface send-text
  - pane new
  - notify (desktop notification)
- Unix socket client connecting to /tmp/cmux.sock
- Human-readable and JSON output formatting
- v2 JSON protocol request/response handling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change socket file permissions from 0o700 (directory) to 0o600 (file)
- Add is_authorized() that checks peer credentials against SocketControlMode
- Reject unauthorized connections in the accept loop instead of
  silently allowing all authenticated peers
- Read CMUX_SOCKET_MODE env var at server startup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address Codex and Cubic review comments:

1. Unify TabManager state (P1): Remove separate AppState::tab_manager
   and SharedState::tab_manager instances. AppState now holds a
   reference to SharedState, so UI callbacks and socket handlers
   operate on the same Arc<Mutex<TabManager>>. Add AppState::tab_manager()
   convenience method for UI code.

2. Return error for unimplemented surface.send_input (P1): Instead of
   returning false success, return "not_implemented" error so clients
   don't silently assume terminal input was forwarded.

3. Clarify CmuxOnly auth comment: Document that CmuxOnly currently
   only enforces same-UID (equivalent to LocalUser) and that
   descendant-PID check via /proc will be added in Phase 2+.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes across 14 files addressing automated review comments:

Security & correctness:
- Socket server: add MAX_REQUEST_LEN (1MB) limit to prevent DoS via
  oversized requests; disconnect clients exceeding the limit
- Socket server: move startup from connect_activate to connect_startup
  to prevent spawning duplicate servers on re-activation
- Mutex: recover from poisoned mutex instead of panicking; add
  SharedState::lock_tab_manager() with into_inner() recovery
- Capabilities: remove unimplemented methods (surface.send_input,
  notification.create) from advertised capabilities list
- Notifications: don't log notification content (potential info leak)

Data model:
- Unify TabManager: already unified in previous commit, but also
  update convenience method to use shared recovery path
- move_workspace: fix selected_index remapping for all affected
  positions (not just the moved workspace)
- Workspace split: fall back to root split when focused panel is
  not found in layout tree
- Sidebar: guard against row.index() returning -1

Robustness:
- Session store: atomic write via tmp file + rename
- Session store: fix tilde literal in fallback path (use dirs::home_dir)
- Split view: clamp divider_position to [0.0, 1.0]
- Test: wrap bare matches!() in assert!() so it actually validates

Metadata:
- Track Cargo.lock (binary crate best practice for reproducible builds)
- Fix repository URL in Cargo.toml (cmux-linux → cmux)
- build.rs: emit per-file rerun-if-changed for ghostty source

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- snapshot.rs: add camelCase serde renames to all structs for macOS JSON compat
- v2.rs: add workspace.create alias, use workspace_id param name, selected field
- server.rs: replace read_line with bounded fill_buf/consume to prevent OOM
- surface.rs: handle NUL bytes in CString::new gracefully instead of panicking
- cmux-cli: fix --wrap flag (was always true), add socket timeout, fix selected field

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sidebar selection now rebuilds the content area, and workspace
add/close operations (buttons + keyboard shortcuts) refresh the
sidebar list so it stays in sync with TabManager state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- sidebar.rs: fix potential deadlock by dropping TabManager lock before
  calling select_row (which triggers row-selected handler that re-locks)
- snapshot.rs: clamp divider_position to 0.0-1.0 and handle non-finite
  values when restoring session layouts
- split_view.rs: show placeholder when no panel IDs resolve in a pane
- v2.rs: use usize::try_from instead of lossy u64-to-usize cast
- cmux-cli: add response length guard and EOF check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Review-loop converged in 3 rounds (13 fixes, 7 cross-validated):
- Bound log_entries (1000) and notifications (500) with drain eviction
- XDG_RUNTIME_DIR socket path with uid/mode validation
- Semaphore(64) connection limit, acquire before spawn
- spawn_blocking for Mutex in async context
- FFI callback null guards (handler_from_userdata returns Option)
- Safe PID i32→u32 conversion via try_from
- Prevent duplicate windows on re-activation
- CLI: bounded read_line via take(), matching socket path validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- XDG_RUNTIME_DIR: require exact 0700 mode per XDG spec (was 0022 mask)
- Socket server: 5-min idle timeout on fill_buf to free stalled clients
- surface.rs: document GObject auto-chaining for dispose (no parent_dispose needed)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…review artifacts

- Use block scopes consistently for MutexGuard in window.rs (Ctrl+Shift+W)
- Add did_split conditional rebuild pattern to all 4 split handlers
- Add UID to /tmp session fallback path for multi-user safety
- Move level/source truncation into workspace.rs append_log (defense-in-depth)
- Remove .claude/reviews/ artifacts per maintainer request

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ghostty submodule を新しい signed sync ベースに更新。
docs/ghostty-fork.md に Linux embedded guard セクション追加。
shuhei0866 and others added 10 commits March 13, 2026 10:56
display_quoted_path のサフィックス部分で $, バッククォート, \, " を
すべてエスケープし、生成される cd コマンドでシェル展開が起きないようにした。
$HOME のみ意図的に展開させる。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P1 修正:
- v2.rs: 不正な UUID/index パラメータをエラーとして拒否(silent fallback を防止)
- app.rs: close_panel の TOCTOU レース条件を単一ロックで解消

P2 修正:
- auth.rs: CmuxOnly モードで /proc 経由の descendant PID チェックを実装
- sidebar.rs: Path::strip_prefix でコンポーネント単位の HOME 判定に修正
- server.rs: ソケット削除前に FileType::is_socket() で種別を確認
- cmux-cli/main.rs: 不正レスポンスでも exit code 1 を返すように修正
- surface.rs: Display::translate_key で hardware keycode から unshifted codepoint を取得
- split_view.rs: mutex lock の poisoned 状態を unwrap せず安全に処理
- ghostty-sys/lib.rs: C bitflags を enum から type alias + const に変更(FFI 安全性)
- ghostty-gtk/app.rs: diagnostic message の null ポインタガードを追加
- snapshot.rs: macOS 互換の adjacently-tagged JSON 形式に変更
- capture-linux-port-demo.sh: Release metadata を workspace_three に正しくルーティング

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- server.rs: is_descendant 実装済みなのに残っていた stale warning を修正
- auth.rs: is_authorized に server_pid を引数化(毎回 process::id() を避ける)
- split_view.rs: poisoned mutex を silent ignore ではなく error ログに変更

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- v2.rs: replace all .lock().unwrap() with lock_or_recover helper
- v2.rs: truncate surface.send_input to 128KB
- v2.rs: scope lock in workspace.close to avoid holding across UI refresh
- server.rs: restrict umask before socket bind for 0o600 permissions
- window.rs: fix rebuild_content deadlock by cloning data before lock drop
- cmux-cli: change --send-desktop to --no-desktop flag with inverted logic
- README.md: fix socket fallback path to /tmp/cmux-$UID.sock

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- sidebar.rs: return "~" instead of "~/" when path is exactly $HOME
- server.rs: replace process-wide umask with set_permissions after bind
  to avoid racing other threads
- surface.rs: use event keyboard group in translate_key instead of
  hardcoded 0, fixing unshifted key detection for non-default layouts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- server.rs: revert to scoped umask (brief, side-effect-safe) with
  accurate comment explaining the tradeoff
- sidebar.rs: guard compact_path against HOME="/" and empty path
- cmux-cli: omit null index in workspace.close params
- window.rs: guard against row.index() returning -1
- Unify lock_or_recover: move to app.rs as pub fn, replace all
  production .lock().unwrap() in app.rs, window.rs, sidebar.rs,
  split_view.rs, session/store.rs (v2.rs already used it)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- FFI trampolines: wrap all 6 C callbacks with catch_unwind + panic logging
  to prevent UB from panics crossing the FFI boundary
- UUID validation: reject malformed surface/panel UUIDs in surface.send_input
  and notification.create (consistent error handling)
- Mutex safety: convert 2 remaining lock().unwrap() to lock_or_recover in
  window.rs shortcut handlers
- CLI: fix --wrap flag to accept --wrap false (action=Set, default_value_t=true)
- Demo script: add non-socket file guard and nc timeout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@da-beda da-beda merged commit cf7a2d7 into main Mar 18, 2026
2 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants