feat(ipc): add Unix domain socket for inter-process communication#10
Conversation
- Create ipc.rs module with async socket server - Implement commands: open, ping, show - Socket path: ~/.arandu/arandu.sock with 0600 permissions - Update CLI script to use socket with fallback to open - Add tokio features: net, io-util - Graceful degradation if socket setup fails
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
📝 WalkthroughWalkthroughAdds Unix-domain socket IPC at Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as CLI Process
participant Socket as Unix Socket\n(~/.arandu/arandu.sock)
participant Server as IPC Server Task
participant App as Tauri App
participant Window as Main Window
CLI->>Socket: connect()
CLI->>Server: send JSON {"command":"open","path":"/abs/path"}\n
Server->>Server: parse JSON, canonicalize path
Server->>App: emit "open-file" event (abs path)
App->>Window: show / unminimize / focus
App-->>Server: event handled
Server->>Socket: send JSON {"success":true}\n
CLI->>CLI: exit(0)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/tauri/src-tauri/src/cli_installer.rs`:
- Around line 8-17: The IPC block around SOCKET currently assumes nc always
succeeds, skips sending when no args are passed, and embeds raw paths into JSON;
change it so you first detect socket existence, then if $# -eq 0 send a single
'{"command":"open"}' over the socket, otherwise iterate over for f in "$@" and
build ABS as now but escape backslashes and double quotes in "$ABS" (e.g.
replace \ with \\ and " with \") before embedding into the JSON payload, write
the payload to nc and check nc's exit status for each send; if any nc call fails
(non-zero) or the socket is unreachable, stop using the socket and fall back to
the non-IPC code path (i.e. remove the unconditional exit 0 and only exit when
all IPC sends succeeded). Ensure you update references in this block: SOCKET,
the for f in "$@" loop, ABS, the JSON echo/pipe to nc and the exit 0 behavior.
In `@apps/tauri/src-tauri/src/ipc.rs`:
- Around line 24-53: The current setup() sets SocketState before attempting
UnixListener::bind, which can cause cleanup to remove another instance's socket
if bind fails; change the flow so you call socket_path() and
cleanup_stale_socket(&sock_path) as before, then attempt
UnixListener::bind(&sock_path) first, and only after bind returns Ok(listener)
acquire the state mutex (state.0.lock()) and set *guard =
Some(sock_path.clone()) before spawning the async task that calls
socket_listener_loop; on Err(e) do not set SocketState and handle/log/propagate
the error instead.
In `@apps/tauri/src-tauri/src/lib.rs`:
- Line 376: The code unconditionally registers
ipc::SocketState(Mutex::new(None)) with .manage(...) but ipc::SocketState is
only available on Unix builds; wrap the manage call in a cfg guard so it only
compiles on Unix (e.g., use #[cfg(unix)] or cfg_if! around the .manage(...)
invocation) or provide a non-Unix stub type and conditional module export;
target the .manage call that references ipc::SocketState and the ipc module to
make the registration conditional.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/tauri/src-tauri/Cargo.tomlapps/tauri/src-tauri/src/cli_installer.rsapps/tauri/src-tauri/src/ipc.rsapps/tauri/src-tauri/src/lib.rs
…cfg guard - CLI script: handle empty args (send "show"), escape paths for JSON, check nc exit status and fall back on failure - ipc.rs: set SocketState only after successful UnixListener::bind - lib.rs: wrap ipc::SocketState manage call with #[cfg(unix)]
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/tauri/src-tauri/src/ipc.rs`:
- Around line 63-71: The ~/.arandu directory creation in socket_path does not
enforce restrictive permissions; after creating the directory with
std::fs::create_dir_all(&arandu_dir) update the code to explicitly set
permissions to 0o700 (owner rwx only) to prevent cross‑user access. Use
std::fs::set_permissions(&arandu_dir, std::fs::Permissions::from_mode(0o700))
(with std::os::unix::fs::PermissionsExt imported) and propagate/map any error
into the same Result error path so failures to apply the 0700 permissions are
handled and logged similarly to the existing create_dir_all error. Ensure this
change is applied in the socket_path function surrounding the arandu_dir
variable.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/tauri/src-tauri/src/cli_installer.rsapps/tauri/src-tauri/src/ipc.rsapps/tauri/src-tauri/src/lib.rs
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/tauri/src-tauri/src/ipc.rs (1)
94-110: Add a small backoff on accept errors to avoid tight loops.If the listener starts erroring (e.g., after shutdown), the loop can spin and log rapidly; a short delay prevents that. If Tokio’s
timefeature isn’t enabled yet, you’ll need to add it.♻️ Proposed backoff on accept errors
-use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; +use tokio::time::sleep; +use std::time::Duration;Err(e) => { eprintln!("Accept error: {}", e); + sleep(Duration::from_millis(200)).await; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/tauri/src-tauri/src/ipc.rs` around lines 94 - 110, The accept error handling in socket_listener_loop currently immediately continues on Err(e), which can cause a tight loop; modify the Err branch in socket_listener_loop to await a short sleep (e.g., tokio::time::sleep(Duration::from_millis(100)).await) after logging the error to provide a small backoff, and ensure the tokio "time" feature is enabled in Cargo.toml if not already; keep using the same listener.accept().await flow and spawn behavior for Ok((stream, _addr)).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/tauri/src-tauri/src/ipc.rs`:
- Around line 94-110: The accept error handling in socket_listener_loop
currently immediately continues on Err(e), which can cause a tight loop; modify
the Err branch in socket_listener_loop to await a short sleep (e.g.,
tokio::time::sleep(Duration::from_millis(100)).await) after logging the error to
provide a small backoff, and ensure the tokio "time" feature is enabled in
Cargo.toml if not already; keep using the same listener.accept().await flow and
spawn behavior for Ok((stream, _addr)).
Summary
~/.arandu/arandu.sockfor fast IPC communicationopen(opens markdown files),ping(health check),show(brings window to focus)open --argswhen app isn't runningTest plan
~/.arandu/arandu.sockecho '{"command":"ping"}' | nc -U ~/.arandu/arandu.sockecho '{"command":"open","path":"/path/to/file.md"}' | nc -U ~/.arandu/arandu.sockecho '{"command":"show"}' | nc -U ~/.arandu/arandu.sockarandu path/to/file.md(should use socket if app is running)arandu file.md(should start app using traditional method)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Chores