https://github.com/ripplethor/macfusegui
macFUSEGui is a lightweight macOS menu bar app for mounting and managing SSHFS remotes with macFUSE, with native Apple Silicon and Intel builds
https://github.com/ripplethor/macfusegui
apple-silicon developer-tools filesystem fuse fuse-filesystem fuse-tools macfuse macfuse-gui macfusegui macos macos-app menu-bar-app mount network-filesystem remote remote-mount sshfs sshfs-gui sshfs-mounting
Last synced: 15 days ago
JSON representation
macFUSEGui is a lightweight macOS menu bar app for mounting and managing SSHFS remotes with macFUSE, with native Apple Silicon and Intel builds
- Host: GitHub
- URL: https://github.com/ripplethor/macfusegui
- Owner: ripplethor
- License: gpl-3.0
- Created: 2026-02-17T16:35:45.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-03-08T16:12:52.000Z (19 days ago)
- Last Synced: 2026-03-08T20:46:26.128Z (18 days ago)
- Topics: apple-silicon, developer-tools, filesystem, fuse, fuse-filesystem, fuse-tools, macfuse, macfuse-gui, macfusegui, macos, macos-app, menu-bar-app, mount, network-filesystem, remote, remote-mount, sshfs, sshfs-gui, sshfs-mounting
- Language: Swift
- Homepage: http://www.macfusegui.app/
- Size: 18.9 MB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# macfuseGui
`macfuseGui` is a macOS menu bar agent app for mounting remote directories with `sshfs` + macFUSE.
- Website: https://www.macfusegui.app/
- macOS 13+
- Apple Silicon first (`arm64`), Intel supported (`ARCH_OVERRIDE=x86_64`)
- Release flow defaults to dual-arch artifacts (`ARCH_OVERRIDE=both`)
- `NSStatusItem` menu bar UX, no Dock icon (`LSUIElement=true`)
- SwiftUI settings + AppKit menu bar integration
## Key Features
- Multiple remotes with per-remote connect/disconnect.
- Startup duplicate-instance guard (singleton lock + running-app check) to avoid duplicate menu bar icons.
- Secure persistence:
- Non-sensitive config in JSON.
- Passwords in macOS Keychain.
- Test Connection in add/edit (real temporary mount + unmount validation).
- Per-remote startup toggle: `Auto-connect on app launch`.
- Recovery handling for sleep/wake and network restoration.
- Busy unmount diagnostics (shows blocking processes via `lsof`).
- Open-in-editor plugin system:
- Built-in editor plugins (`VS Code`, `VSCodium`, `Cursor`, `Zed`)
- Preferred editor + fallback across active plugins
- Real-time enable/disable in Settings
- External plugin manifests from disk
- Finder-style remote browser:
- Sidebar (Favorites / Recents / Roots)
- Breadcrumb navigation
- Directories-only table view
- Sticky cache with reconnect-state banners (no silent blank state)
- Diagnostics snapshot + `Copy Diagnostics` menu action.
## Dependency Install
```bash
brew install --cask macfuse
brew install gromgit/fuse/sshfs-mac
```
## Install via Homebrew (App)
```bash
brew tap ripplethor/macfusegui https://github.com/ripplethor/macfuseGUI
brew install --cask ripplethor/macfusegui/macfusegui
```
Expected `sshfs` search order:
1. `/opt/homebrew/bin/sshfs`
2. `/usr/local/bin/sshfs`
3. `sshfs` from `$PATH`
## Open-In-Editor Plugins
The menu popover provides:
- Primary one-click action: `Open in `
- Explicit picker action: `Open In…`
Default behavior:
- Built-ins shipped: `vscode`, `vscodium`, `cursor`, `zed`
- Only `vscode` starts active by default
- Preferred editor auto-rehomes to the next active plugin if needed
- If all attempts fail, app falls back to Finder and records diagnostics
- Each built-in plugin manifest is editor-specific (no cross-editor mixed attempts inside one plugin)
- Built-in manifests live in codebase under:
- `macfuseGui/Resources/EditorPlugins/vscode/plugin.json`
- `macfuseGui/Resources/EditorPlugins/vscodium/plugin.json`
- `macfuseGui/Resources/EditorPlugins/cursor/plugin.json`
- `macfuseGui/Resources/EditorPlugins/zed/plugin.json`
Settings behavior:
- Toggle plugins on/off in real time (no restart)
- Select preferred editor from active plugins
- Reload plugin manifests manually with `Reload Plugins`
- Open dedicated `Editor Plugins…` window from Settings
- Reveal plugin directory in Finder
- Create a new plugin manifest from template (`New Plugin JSON`)
- Edit manifest JSON inline for any selected plugin (`Inline JSON Editor`)
- Remove external plugins directly from the plugin catalog (`Trash`)
External manifests:
- Directory: `~/Library/Application Support/macfuseGui/editor-plugins`
- File type: `*.json` (one plugin per file)
- The app auto-creates this folder on first load with:
- `README.md` usage guide
- `examples/custom-editor.json.template`
- `builtin-reference/*.json` (reference definitions for shipped editors; not loaded as external plugins)
- Security rules:
- only `/usr/bin/open` and `/usr/bin/env` executables
- launch attempt must include `{folderPath}` placeholder
- command arrays only; no shell interpolation
Example manifest:
```json
{
"id": "windsurf",
"displayName": "Windsurf",
"priority": 50,
"defaultEnabled": false,
"launchAttempts": [
{
"label": "open app Windsurf",
"executable": "/usr/bin/open",
"arguments": ["-a", "Windsurf", "{folderPath}"],
"timeoutSeconds": 3
}
]
}
```
## Build / Run / Clean
From repo root:
```bash
./scripts/build.sh
./scripts/run.sh
./scripts/clean.sh
```
`build.sh` includes a pre-step:
- `ARCH_OVERRIDE= ./scripts/build_libssh2.sh`
Supported `ARCH_OVERRIDE` values:
- `arm64`
- `x86_64`
- `both`
- `universal`
Third-party output roots are arch-specific:
- `build/third_party/openssl-arm64`, `build/third_party/openssl-x86_64`, `build/third_party/openssl-universal`
- `build/third_party/libssh2-arm64`, `build/third_party/libssh2-x86_64`, `build/third_party/libssh2-universal`
App output paths:
- Single-arch (`arm64`, `x86_64`, `universal`): `build/macfuseGui.app`
- Dual-arch (`both`): `build/macfuseGui-arm64.app` and `build/macfuseGui-x86_64.app`
DerivedData roots:
- `build/DerivedData-arm64`
- `build/DerivedData-x86_64`
- `build/DerivedData-universal`
## Make Targets
```bash
make build
make run
make clean
```
## Common Build Overrides
```bash
# Intel build
ARCH_OVERRIDE=x86_64 ./scripts/build.sh
# Build separate arm64 + x86_64 apps
ARCH_OVERRIDE=both ./scripts/build.sh
# Universal app build
ARCH_OVERRIDE=universal ./scripts/build.sh
# Release build
CONFIGURATION=Release ./scripts/build.sh
# Allow signing if needed
CODE_SIGNING_ALLOWED=YES ./scripts/build.sh
```
## Release
```bash
# Default release mode is dual-arch (both)
./scripts/release.sh
# Verify dual-arch release actions without publishing
ARCH_OVERRIDE=both ./scripts/release.sh --dry-run
# Force a single-arch release if needed
ARCH_OVERRIDE=arm64 ./scripts/release.sh
ARCH_OVERRIDE=x86_64 ./scripts/release.sh
```
## Xcode CLI Fallback
```bash
ARCH_OVERRIDE=arm64 ./scripts/build.sh
xcodebuild -project macfuseGui.xcodeproj -scheme macfuseGui -configuration Debug -derivedDataPath build/DerivedData build
```
## Run Tests
```bash
ARCH_OVERRIDE=arm64 ./scripts/build.sh
xcodebuild -project macfuseGui.xcodeproj -scheme macfuseGui -configuration Debug -derivedDataPath build/DerivedData -destination 'platform=macOS,arch=arm64' test CODE_SIGNING_ALLOWED=NO
```
## Reliability Gate
```bash
ARCH_OVERRIDE=arm64 ./scripts/build.sh
scripts/audit_mount_calls.py && xcodebuild -project macfuseGui.xcodeproj -scheme macfuseGui -configuration Debug -derivedDataPath build/DerivedData -destination 'platform=macOS,arch=arm64' test CODE_SIGNING_ALLOWED=NO
```
## VS Code
Included:
- `.vscode/tasks.json`
- `.vscode/launch.json`
- `.vscode/settings.json`
Tasks:
- `build` -> `scripts/build.sh`
- `run` -> build + launch app
- `clean` -> `scripts/clean.sh`
- `sourcekit-reset-cache` -> `scripts/reset_sourcekit_cache.sh`
Debug launch:
- Program: `build/macfuseGui.app/Contents/MacOS/macfuseGui`
- Pre-launch task: `build`
## Documentation
- **[New Contributor Guide](CONTRIBUTING.md)**: Start here if you want to modify the app.
- **[Architecture & Jargon Buster](ARCHITECTURE.md)**: Visual diagrams and plain-English explanations.
- **[Safe Change Rules (AGENTS.md)](AGENTS.md)**: Critical rules for AI agents and developers.
- **[Commit & Push Workflow (COMMIT_WORKFLOW.md)](COMMIT_WORKFLOW.md)**: Commit format, changelog prefix rules, and release notes guidance.
## Security Notes
- All external command execution uses `Process` with argument arrays.
- No shell interpolation for user input.
- Password mode uses temporary `SSH_ASKPASS` helper (`0700`) and ephemeral env vars.
- Passwords are never stored in JSON or logs.
- `KeychainService.readPassword` trims leading/trailing whitespace on read — prevents silent auth failures from clipboard-pasted trailing newlines without altering the stored credential.
- IPv6 host addresses are automatically bracketed (`[::1]`) in sshfs arguments; bare IPv6 input is also rejected at the validation layer.
- Diagnostics redact sensitive content.
## Browser Subsystem Notes
Browser internals are session-based (`openSession` / `listDirectories` / `health` / `closeSession`) with per-session health and sticky-cache behavior.
Current transport implementation uses native libssh2 SFTP through a C bridge (`LibSSH2Bridge.c/.h`) behind an internal transport abstraction.
Recovery contract:
- Keepalive runs every 12s only while the browser session is idle.
- Keepalive failures do not hard-close sessions; they schedule list-based recovery.
- Recovery backoff: `0.2s`, `0.8s`, `2s`, `5s`.
- Empty listings are confirmation-checked before being treated as truly empty.
- Browser keeps last-good entries visible during transient failures.
## Troubleshooting
### Missing dependencies
```bash
brew install --cask macfuse
brew install gromgit/fuse/sshfs-mac
```
### xcodebuild first-launch issues
```bash
sudo xcodebuild -runFirstLaunch
```
### VS Code SourceKit stale errors
```bash
./scripts/reset_sourcekit_cache.sh
```
Then in VS Code:
1. Reload window
2. Restart Swift LSP
3. Re-index project
### Mount/unmount issues
- Use `Copy Diagnostics` from the app menu.
- For busy unmount, diagnostics now include blocking process hints.
### Duplicate menu icon or stale second app process
```bash
pkill -x macfuseGui
open -a /Applications/macfuseGui.app
```
If this still happens, check for multiple running processes:
```bash
pgrep -lf macfuseGui
```
### Browser diagnostics interpretation
- `state=healthy` with `isConfirmedEmpty=true`: path is reachable and truly has no subfolders.
- `state=reconnecting` or `state=degraded` with `fromCache=true`: browser is showing last-good cached data while retrying.
- `lastSuccessAt` and `lastLatencyMs` in `Browser Sessions` show the last confirmed successful list.
- Repeated `keepalive failed` followed by `recovery attempt` means auto-recovery is active; use `Retry now` in the browser UI to force an immediate list call.
### Mount concurrency proof logs (actor funnel check)
Use this during wake/reconnect testing:
```bash
log stream --style compact --predicate 'process == "macfuseGui"' \
| rg 'Operation start remoteID=|Operation end remoteID=|mount call op=|actor enter op=|probe start op=sshfs-connect|probe end op=sshfs-connect'
```
Interpretation:
- Healthy parallelism: different remotes show overlapping `probe start/end op=sshfs-connect` windows.
- Funnel warning: one remote repeatedly waits with high `queueDelayMs` before `actor enter`.
## License
This project is licensed under the GNU General Public License v3.0.
If you distribute a modified version, you must also provide the source under GPLv3.
See [`LICENSE`](./LICENSE).