Skip to content

feat: Linux desktop app with FUSE mount#220

Merged
FSM1 merged 17 commits into
mainfrom
feat/phase-11.3-linux-desktop
Feb 28, 2026
Merged

feat: Linux desktop app with FUSE mount#220
FSM1 merged 17 commits into
mainfrom
feat/phase-11.3-linux-desktop

Conversation

@FSM1

@FSM1 FSM1 commented Feb 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add Linux platform support to CipherBox desktop (FUSE mount via libfuse3, system tray, keyring via Secret Service API, xdg-open integration)
  • Linux cfg gates for mount options (DefaultPermissions), unmount (fusermount3 fallback chain), tray (green icon for dark mode), registry ("Linux Desktop"), platform special file filtering (.Trash-*, .gvfs, .xdg-volume-info)
  • Tauri bundle config for .deb (libfuse3-3, fuse3 deps) and AppImage packaging
  • CI: cargo-check-linux and build-desktop-linux jobs on ubuntu-22.04
  • Fix vendored fuser channel.rs for Linux /dev/fuse (use read() instead of recv(MSG_PEEK))
  • Fix file overwrite truncation race condition (Linux kernel sends open() before setattr(size=0) for O_TRUNC)

UAT Results

18/18 tests passed against staging API on Linux:

  • File ops: create, read, overwrite, delete, rename
  • Dir ops: create, nested, rename, delete
  • Large files: 1MB + 10MB round-trip (MD5 verified)
  • Batch: 20 files created
  • Platform filter: .Trash-*, .gvfs, .xdg-volume-info rejected
  • Tray: green icon visible in dark mode, Open launches file manager
  • Background sync: detects remote changes
  • Unmount: clean via Quit

Bugs Fixed During UAT

  1. DefaultPermissionDefaultPermissions (plural)
  2. Vendored fuser recv(MSG_PEEK) fails on /dev/fuse — cfg-gated to read() on Linux
  3. AutoUnmount requires /etc/fuse.conf modification — removed from Linux options
  4. File overwrite truncation race — write_generation counter + FOPEN_DIRECT_IO

Test plan

  • CI cargo-check-linux job passes (first run)
  • CI build-desktop-linux job passes (produces .deb + AppImage)
  • Existing macOS and Windows CI jobs unaffected

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Linux desktop support with FUSE integration and system tray functionality.
    • Introduced Linux packaging via .deb and AppImage formats.
    • Implemented Linux credential storage and authentication integration.
  • Bug Fixes

    • Fixed file write race conditions and inode consistency issues.
    • Corrected platform-specific file filtering and icon visibility on Linux.
    • Improved unmount behavior and cleanup procedures on Linux.
  • Chores

    • Added Linux-specific CI/build pipelines to GitHub Actions.
    • Updated project planning and roadmap to mark Linux desktop phase as complete.

FSM1 and others added 15 commits February 28, 2026 06:30
Phase 11.3: Linux Desktop
- Implementation decisions documented
- Phase boundary established
- Move PKG_CONFIG_PATH from global [env] to macOS-only target sections
  so Linux builds use system pkg-config to find fuse3.pc from libfuse3-dev
- Add linux-native-sync-persistent feature to keyring crate for
  Secret Service API (D-Bus) and keyutils backend on Linux

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: f5ed94b91d4f
…d special files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 2da25e26c658
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 17bbc8ad4d04
- Add linux.deb.depends: libfuse3-3, fuse3, libwebkit2gtk-4.1-0, libayatana-appindicator3-1
- Add linux.appimage.bundleMediaFramework: false
- WinFsp resource glob left as-is (placeholder MSI tracked in git, harmless on Linux)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: b542a6051fe9
- cargo-check-linux: ubuntu-22.04, system deps, fuse feature check
- build-desktop-linux: full Tauri build producing .deb and AppImage
- Both jobs install libwebkit2gtk-4.1-dev, libfuse3-dev, and all Tauri deps
- Cache key prefix: linux-cargo- (distinct from macos/windows)
- Matches existing macOS/Windows job patterns exactly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 10e7a38cc434
Tasks completed: 2/2
- Linux bundle config in tauri.conf.json
- CI workflow with Linux cargo-check and build jobs

SUMMARY: .planning/phases/11.3-linux-desktop/11.3-02-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 18f9f17781f4
…ng feature

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: d198ece01424
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: bccd15e4d631
Phase completion reverted — code was never compiled or tested locally.
Added Plan 03: build, run, and full UAT against staging API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 54357797e2c3
- Fix vendored fuser channel.rs: use read() on Linux /dev/fuse instead of
  recv(MSG_PEEK) which returns ENOTSOCK on device files (not sockets)
- Keep recv(MSG_PEEK) loop-read for macOS FUSE-T socket transport
- Remove AutoUnmount from Linux mount options to avoid requiring
  user_allow_other in /etc/fuse.conf
- Fix DefaultPermission -> DefaultPermissions typo

Desktop app now compiles, authenticates via dev-key, and mounts FUSE
successfully on Linux.

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

Three interconnected bugs discovered during Linux UAT:

1. Vendored fuser channel.rs used recv(MSG_PEEK) on /dev/fuse, which returns
   ENOTSOCK on Linux device files. Fixed with cfg(target_os) to use simple
   read() on Linux and keep recv() loop-read for macOS FUSE-T sockets.

2. File overwrite (echo "new" > file) produced stale content because:
   - Linux kernel calls open() BEFORE setattr(size=0) for O_TRUNC
   - open() pre-populated temp file with old content before truncation
   - setattr(size=0) with fh=None couldn't find the temp file to truncate
   Fixed: setattr now scans open_files by ino when fh=None.

3. Stale upload race: old upload completing after truncate would overwrite
   the cleared CID with the old CID. Fixed with write_generation counter
   on InodeData — uploads carry generation at spawn time, drain skips
   mismatched generations.

Additional fixes:
- Remove AutoUnmount from Linux mount options (requires user_allow_other)
- Add FOPEN_DIRECT_IO to bypass kernel page cache (prevents stale reads)
- Fix DefaultPermission -> DefaultPermissions typo

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

- Create tray-icon-linux.png and tray-icon-linux@2x.png with green (#00D084) icon
- Use Linux-specific icon via cfg(not(any(macos, windows))) in tray/mod.rs
- Set icon_as_template to macOS-only via cfg!(target_os = "macos")
- Black template icon was invisible on dark Linux desktop panels

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 4447935e6dd7
Tasks completed: 7/7
- Build desktop app on Linux with cargo build --features fuse
- Dev-key auth against staging, FUSE mount verified
- File operations UAT (create, read, overwrite, delete, rename)
- Directory operations UAT (create, nest, rename, delete)
- Large file round-trip (1MB, 10MB MD5 match) and batch (20 files)
- Platform special files filtered (.Trash-*, .gvfs, .xdg-volume-info)
- Tray, sync, and unmount verified (human-verified)

UAT: 18/18 tests passed
Bugs fixed: 4 + 1 UX fix

SUMMARY: .planning/phases/11.3-linux-desktop/11.3-03-SUMMARY.md
UAT: .planning/phases/11.3-linux-desktop/11.3-UAT.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: a9df03e4a06d
18/18 UAT tests passed. 4 bugs fixed during testing.
PLAT-01 requirement marked Complete.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 5de19067603d
@coderabbitai

coderabbitai Bot commented Feb 28, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@FSM1 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 51 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between ed1be62 and 9c8d396.

📒 Files selected for processing (1)
  • apps/desktop/src-tauri/vendor/fuser/src/channel.rs

Walkthrough

This pull request completes Phase 11.3 - Linux Desktop by implementing Linux support for the CipherBox desktop application. Changes include platform-specific FUSE code gating, Linux CI/CD workflows, tauri.conf.json packaging configuration, write-generation tracking for upload staleness detection, and comprehensive planning documentation. Phase is marked complete across planning artifacts.

Changes

Cohort / File(s) Summary
Planning & Phase Completion
.planning/REQUIREMENTS.md, .planning/ROADMAP.md, .planning/STATE.md
Updated phase status from pending to complete, marked Phase 11.3 as 3/3 done with completion date 2026-02-28, adjusted performance metrics and phase focus.
Phase 11.3 Documentation
.planning/phases/11.3-linux-desktop/11.3-* (CONTEXT, RESEARCH, PLAN, SUMMARY, UAT, VERIFICATION)
Added comprehensive planning, research, implementation plans (3 plans), UAT results, and verification documents detailing Linux FUSE integration, packaging, CI setup, and test outcomes (18/18 passing).
CI/CD Workflow
.github/workflows/ci.yml
Added two new Ubuntu 22.04 jobs: cargo-check-linux (system deps, Rust toolchain, cargo check with fuse feature) and build-desktop-linux (full build with tauri, producing .deb and AppImage artifacts).
Build & Dependency Configuration
apps/desktop/src-tauri/Cargo.toml, apps/desktop/src-tauri/.cargo/config.toml, apps/desktop/src-tauri/tauri.conf.json
Added linux-native-sync-persistent keyring feature, PKG_CONFIG_PATH environment scoping, and Linux bundle config (.deb dependencies and AppImage settings).
FUSE Core Implementation
apps/desktop/src-tauri/src/fuse/mod.rs, apps/desktop/src-tauri/src/fuse/inode.rs, apps/desktop/src-tauri/src/fuse/operations.rs
Introduced write_generation field to track stale uploads, added platform-specific mount options (Linux vs macOS), implemented Linux-specific unmount via fusermount3, added FOPEN_DIRECT_IO for direct I/O, and Linux-specific inode initialization.
Windows FUSE Compatibility
apps/desktop/src-tauri/src/fuse/windows/operations.rs
Added write_generation field initialization for Windows inode compatibility.
System Integration
apps/desktop/src-tauri/src/registry/mod.rs, apps/desktop/src-tauri/src/tray/mod.rs
Implemented platform-aware device model strings (Linux Desktop, Windows Desktop, macOS Desktop), Linux tray icon asset (tray-icon-linux@2x.png), xdg-open for Linux tray open action, and macOS-only icon templating.
Vendor FUSE Library
apps/desktop/src-tauri/vendor/fuser/src/channel.rs
Refactored receive() to use platform-specific logic: Linux uses single read() call, non-Linux (macOS) retains existing header-peek multi-read logic.

Sequence Diagram

sequenceDiagram
    actor User
    participant App as CipherBox App
    participant FUSE as FUSE Kernel
    participant BackgroundSync as Background Sync
    participant UploadQueue as Upload Queue

    User->>App: Open file for writing (O_TRUNC)
    App->>FUSE: write_generation = N (initialize)
    App->>FUSE: Create temp file, mount at CipherBox home
    
    User->>FUSE: Write data to file
    FUSE->>App: onWrite (with FOPEN_DIRECT_IO bypass)
    App->>App: Update inode, increment write_generation to N+1
    App->>App: Clear file CID (stale)
    
    App->>UploadQueue: Queue upload with write_generation = N+1
    
    par Background Operations
        BackgroundSync->>UploadQueue: Poll for uploads
        UploadQueue->>App: Retrieve upload (write_generation = N+1)
        App->>App: Check: current write_generation == N+1?
        App->>App: Yes → Update remote CID, publish IPNS
        App->>BackgroundSync: Upload complete
    and User Continues
        User->>FUSE: Write more data before upload finishes
        FUSE->>App: onWrite
        App->>App: Increment write_generation to N+2
    end
    
    Note over App,BackgroundSync: Stale upload detected: N+1 ≠ N+2
    App->>App: Discard N+1 upload, mark as stale
    App->>UploadQueue: Queue new upload with write_generation = N+2
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Linux desktop app with FUSE mount' clearly and concisely summarizes the main change: adding Linux platform support to the CipherBox desktop application with FUSE filesystem integration.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/phase-11.3-linux-desktop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…mpat

[target.<triple>.env] is not supported in Rust 1.88 (pinned in
rust-toolchain.toml). Global [env] is safe because the custom
pkg-config/ only contains fuse.pc (macOS FUSE-T), not fuse3.pc,
so Linux builds fall through to system pkg-config for libfuse3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 9eb28f8f5a66

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
apps/desktop/src-tauri/.cargo/config.toml (1)

1-5: Clarify PKG_CONFIG_PATH behavior with force = true.

The comment explains the intent well, but force = true will override any user-set PKG_CONFIG_PATH. On Linux, this works because fuse3.pc isn't in the local pkg-config/ directory, so pkg-config will search system paths as a fallback.

However, if a Linux developer has a custom PKG_CONFIG_PATH for other dependencies, this could cause issues. Consider whether force = false would work (allowing user paths to take precedence while still adding this as a fallback).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src-tauri/.cargo/config.toml` around lines 1 - 5, The
PKG_CONFIG_PATH entry in the [env] table currently uses force = true which will
overwrite any user-set PKG_CONFIG_PATH; change it to force = false (or remove
the force key) so the repo-provided "pkg-config" path is appended/used as a
fallback rather than overriding developer environments; update the
PKG_CONFIG_PATH declaration (the PKG_CONFIG_PATH key in the [env] block in
config.toml) and adjust the comment to mention it acts as a fallback when not
present in the user path.
.github/workflows/ci.yml (1)

340-352: Deduplicate Linux dependency install steps to reduce CI drift.

The apt package block is duplicated in two jobs. Extracting it into a shared anchor/composite action will prevent divergence over time.

♻️ Example with YAML anchor
+ x-linux-deps: &linux_deps |
+   sudo apt-get update
+   sudo apt-get install -y \
+     libwebkit2gtk-4.1-dev \
+     libayatana-appindicator3-dev \
+     librsvg2-dev \
+     libssl-dev \
+     libxdo-dev \
+     libfuse3-dev \
+     pkg-config \
+     build-essential
...
       - name: Install system dependencies
-        run: |
-          sudo apt-get update
-          sudo apt-get install -y \
-            libwebkit2gtk-4.1-dev \
-            libayatana-appindicator3-dev \
-            librsvg2-dev \
-            libssl-dev \
-            libxdo-dev \
-            libfuse3-dev \
-            pkg-config \
-            build-essential
+        run: *linux_deps
...
       - name: Install system dependencies
-        run: |
-          sudo apt-get update
-          sudo apt-get install -y \
-            libwebkit2gtk-4.1-dev \
-            libayatana-appindicator3-dev \
-            librsvg2-dev \
-            libssl-dev \
-            libxdo-dev \
-            libfuse3-dev \
-            pkg-config \
-            build-essential
+        run: *linux_deps

Also applies to: 481-493

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 340 - 352, The "Install system
dependencies" apt-get install block is duplicated; extract that package list
into a single reusable definition (either a YAML anchor or a composite action)
and replace both occurrences with a reference to it to avoid drift; specifically
refactor the step named "Install system dependencies" that installs
libwebkit2gtk-4.1-dev, libayatana-appindicator3-dev, librsvg2-dev, libssl-dev,
libxdo-dev, libfuse3-dev, pkg-config and build-essential (appearing around the
shown block and again at lines 481-493) into one shared anchor or action and
update both job steps to use that anchor/action.
apps/desktop/src-tauri/src/fuse/mod.rs (1)

1199-1199: Consider handling potential non-UTF-8 paths gracefully.

The unwrap() on mount_path.to_str() could panic if the home directory contains non-UTF-8 characters. While this is rare and consistent with the macOS implementation, consider using to_string_lossy() or returning an error for robustness.

♻️ Optional: Handle non-UTF-8 paths more gracefully
-    let mount_str = mount_path.to_str().unwrap();
+    let mount_str = mount_path.to_str()
+        .ok_or_else(|| format!("Mount path contains invalid UTF-8: {:?}", mount_path))?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src-tauri/src/fuse/mod.rs` at line 1199, The code calls
mount_path.to_str().unwrap() which can panic on non-UTF-8 paths; replace the
unwrap with a safe conversion (e.g., use mount_path.to_string_lossy() and assign
into mount_str) or propagate a Result/error from the enclosing function instead
of unwrapping so non-UTF-8 home dirs are handled gracefully; update any
downstream uses of mount_str accordingly (look for the mount_path and mount_str
variables where this conversion occurs).
🤖 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/desktop/src-tauri/vendor/fuser/src/channel.rs`:
- Around line 66-115: The header framing is broken: stop breaking out after the
first MSG_PEEK and instead loop calling libc::recv(fd, ..., MSG_PEEK) until
header_read == 4 (or return EOF/error) so header_buf contains all 4 bytes before
parsing; parse expected = u32::from_ne_bytes(header_buf) and then set to_read =
expected (not min(buffer.len())) and ensure you either resize/validate buffer to
expected or fail if buffer is too small, then loop-read exactly to_read bytes
using libc::read into buffer until total == to_read (handling errors/EOF as
before); update the header_buf/header_read, expected, to_read and total logic
accordingly.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 340-352: The "Install system dependencies" apt-get install block
is duplicated; extract that package list into a single reusable definition
(either a YAML anchor or a composite action) and replace both occurrences with a
reference to it to avoid drift; specifically refactor the step named "Install
system dependencies" that installs libwebkit2gtk-4.1-dev,
libayatana-appindicator3-dev, librsvg2-dev, libssl-dev, libxdo-dev,
libfuse3-dev, pkg-config and build-essential (appearing around the shown block
and again at lines 481-493) into one shared anchor or action and update both job
steps to use that anchor/action.

In `@apps/desktop/src-tauri/.cargo/config.toml`:
- Around line 1-5: The PKG_CONFIG_PATH entry in the [env] table currently uses
force = true which will overwrite any user-set PKG_CONFIG_PATH; change it to
force = false (or remove the force key) so the repo-provided "pkg-config" path
is appended/used as a fallback rather than overriding developer environments;
update the PKG_CONFIG_PATH declaration (the PKG_CONFIG_PATH key in the [env]
block in config.toml) and adjust the comment to mention it acts as a fallback
when not present in the user path.

In `@apps/desktop/src-tauri/src/fuse/mod.rs`:
- Line 1199: The code calls mount_path.to_str().unwrap() which can panic on
non-UTF-8 paths; replace the unwrap with a safe conversion (e.g., use
mount_path.to_string_lossy() and assign into mount_str) or propagate a
Result/error from the enclosing function instead of unwrapping so non-UTF-8 home
dirs are handled gracefully; update any downstream uses of mount_str accordingly
(look for the mount_path and mount_str variables where this conversion occurs).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 453f0f7 and ed1be62.

⛔ Files ignored due to path filters (3)
  • apps/desktop/src-tauri/Cargo.lock is excluded by !**/*.lock
  • apps/desktop/src-tauri/icons/tray-icon-linux.png is excluded by !**/*.png
  • apps/desktop/src-tauri/icons/tray-icon-linux@2x.png is excluded by !**/*.png
📒 Files selected for processing (24)
  • .github/workflows/ci.yml
  • .planning/REQUIREMENTS.md
  • .planning/ROADMAP.md
  • .planning/STATE.md
  • .planning/phases/11.3-linux-desktop/11.3-01-PLAN.md
  • .planning/phases/11.3-linux-desktop/11.3-01-SUMMARY.md
  • .planning/phases/11.3-linux-desktop/11.3-02-PLAN.md
  • .planning/phases/11.3-linux-desktop/11.3-02-SUMMARY.md
  • .planning/phases/11.3-linux-desktop/11.3-03-PLAN.md
  • .planning/phases/11.3-linux-desktop/11.3-03-SUMMARY.md
  • .planning/phases/11.3-linux-desktop/11.3-CONTEXT.md
  • .planning/phases/11.3-linux-desktop/11.3-RESEARCH.md
  • .planning/phases/11.3-linux-desktop/11.3-UAT.md
  • .planning/phases/11.3-linux-desktop/11.3-VERIFICATION.md
  • apps/desktop/src-tauri/.cargo/config.toml
  • apps/desktop/src-tauri/Cargo.toml
  • apps/desktop/src-tauri/src/fuse/inode.rs
  • apps/desktop/src-tauri/src/fuse/mod.rs
  • apps/desktop/src-tauri/src/fuse/operations.rs
  • apps/desktop/src-tauri/src/fuse/windows/operations.rs
  • apps/desktop/src-tauri/src/registry/mod.rs
  • apps/desktop/src-tauri/src/tray/mod.rs
  • apps/desktop/src-tauri/tauri.conf.json
  • apps/desktop/src-tauri/vendor/fuser/src/channel.rs

Comment thread apps/desktop/src-tauri/vendor/fuser/src/channel.rs
- Loop MSG_PEEK until all 4 header bytes are received (previously
  broke after first recv, risking incomplete header parse)
- Error instead of silently truncating if FUSE message exceeds
  buffer size (prevents socket desync on next read)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 98b7b28a889b
@FSM1 FSM1 merged commit 0f7cf95 into main Feb 28, 2026
16 checks passed
@FSM1 FSM1 deleted the feat/phase-11.3-linux-desktop branch March 4, 2026 01:16
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.

1 participant