{"id":44979932,"url":"https://github.com/ripplethor/macfusegui","last_synced_at":"2026-04-18T02:18:47.011Z","repository":{"id":339047350,"uuid":"1160185841","full_name":"ripplethor/macFUSEGui","owner":"ripplethor","description":"macFUSEGui is a lightweight macOS menu bar app for mounting and managing SSHFS remotes with macFUSE, with native Apple Silicon and Intel builds","archived":false,"fork":false,"pushed_at":"2026-03-08T16:12:52.000Z","size":19823,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-08T20:46:26.128Z","etag":null,"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"],"latest_commit_sha":null,"homepage":"http://www.macfusegui.app/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ripplethor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-17T16:35:45.000Z","updated_at":"2026-03-08T16:12:56.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ripplethor/macFUSEGui","commit_stats":null,"previous_names":["ripplethor/macfusegui"],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/ripplethor/macFUSEGui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripplethor%2FmacFUSEGui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripplethor%2FmacFUSEGui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripplethor%2FmacFUSEGui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripplethor%2FmacFUSEGui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ripplethor","download_url":"https://codeload.github.com/ripplethor/macFUSEGui/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripplethor%2FmacFUSEGui/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30406402,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-11T22:36:59.286Z","status":"ssl_error","status_checked_at":"2026-03-11T22:36:57.544Z","response_time":84,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["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"],"created_at":"2026-02-18T18:04:13.437Z","updated_at":"2026-03-11T23:05:33.886Z","avatar_url":"https://github.com/ripplethor.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# macfuseGui\n\n`macfuseGui` is a macOS menu bar agent app for mounting remote directories with `sshfs` + macFUSE.\n\n- Website: https://www.macfusegui.app/\n- macOS 13+\n- Apple Silicon first (`arm64`), Intel supported (`ARCH_OVERRIDE=x86_64`)\n- Release flow defaults to dual-arch artifacts (`ARCH_OVERRIDE=both`)\n- `NSStatusItem` menu bar UX, no Dock icon (`LSUIElement=true`)\n- SwiftUI settings + AppKit menu bar integration\n\n## Key Features\n\n- Multiple remotes with per-remote connect/disconnect.\n- Startup duplicate-instance guard (singleton lock + running-app check) to avoid duplicate menu bar icons.\n- Secure persistence:\n  - Non-sensitive config in JSON.\n  - Passwords in macOS Keychain.\n- Test Connection in add/edit (real temporary mount + unmount validation).\n- Per-remote startup toggle: `Auto-connect on app launch`.\n- Recovery handling for sleep/wake and network restoration.\n- Busy unmount diagnostics (shows blocking processes via `lsof`).\n- Open-in-editor plugin system:\n  - Built-in editor plugins (`VS Code`, `VSCodium`, `Cursor`, `Zed`)\n  - Preferred editor + fallback across active plugins\n  - Real-time enable/disable in Settings\n  - External plugin manifests from disk\n- Finder-style remote browser:\n  - Sidebar (Favorites / Recents / Roots)\n  - Breadcrumb navigation\n  - Directories-only table view\n  - Sticky cache with reconnect-state banners (no silent blank state)\n- Diagnostics snapshot + `Copy Diagnostics` menu action.\n\n## Dependency Install\n\n```bash\nbrew install --cask macfuse\nbrew install gromgit/fuse/sshfs-mac\n```\n\n## Install via Homebrew (App)\n\n```bash\nbrew tap ripplethor/macfusegui https://github.com/ripplethor/macfuseGUI\nbrew install --cask ripplethor/macfusegui/macfusegui\n```\n\nExpected `sshfs` search order:\n1. `/opt/homebrew/bin/sshfs`\n2. `/usr/local/bin/sshfs`\n3. `sshfs` from `$PATH`\n\n## Open-In-Editor Plugins\n\nThe menu popover provides:\n- Primary one-click action: `Open in \u003cPreferred Editor\u003e`\n- Explicit picker action: `Open In…`\n\nDefault behavior:\n- Built-ins shipped: `vscode`, `vscodium`, `cursor`, `zed`\n- Only `vscode` starts active by default\n- Preferred editor auto-rehomes to the next active plugin if needed\n- If all attempts fail, app falls back to Finder and records diagnostics\n- Each built-in plugin manifest is editor-specific (no cross-editor mixed attempts inside one plugin)\n- Built-in manifests live in codebase under:\n  - `macfuseGui/Resources/EditorPlugins/vscode/plugin.json`\n  - `macfuseGui/Resources/EditorPlugins/vscodium/plugin.json`\n  - `macfuseGui/Resources/EditorPlugins/cursor/plugin.json`\n  - `macfuseGui/Resources/EditorPlugins/zed/plugin.json`\n\nSettings behavior:\n- Toggle plugins on/off in real time (no restart)\n- Select preferred editor from active plugins\n- Reload plugin manifests manually with `Reload Plugins`\n- Open dedicated `Editor Plugins…` window from Settings\n- Reveal plugin directory in Finder\n- Create a new plugin manifest from template (`New Plugin JSON`)\n- Edit manifest JSON inline for any selected plugin (`Inline JSON Editor`)\n- Remove external plugins directly from the plugin catalog (`Trash`)\n\nExternal manifests:\n- Directory: `~/Library/Application Support/macfuseGui/editor-plugins`\n- File type: `*.json` (one plugin per file)\n- The app auto-creates this folder on first load with:\n  - `README.md` usage guide\n  - `examples/custom-editor.json.template`\n  - `builtin-reference/*.json` (reference definitions for shipped editors; not loaded as external plugins)\n- Security rules:\n  - only `/usr/bin/open` and `/usr/bin/env` executables\n  - launch attempt must include `{folderPath}` placeholder\n  - command arrays only; no shell interpolation\n\nExample manifest:\n\n```json\n{\n  \"id\": \"windsurf\",\n  \"displayName\": \"Windsurf\",\n  \"priority\": 50,\n  \"defaultEnabled\": false,\n  \"launchAttempts\": [\n    {\n      \"label\": \"open app Windsurf\",\n      \"executable\": \"/usr/bin/open\",\n      \"arguments\": [\"-a\", \"Windsurf\", \"{folderPath}\"],\n      \"timeoutSeconds\": 3\n    }\n  ]\n}\n```\n\n## Build / Run / Clean\n\nFrom repo root:\n\n```bash\n./scripts/build.sh\n./scripts/run.sh\n./scripts/clean.sh\n```\n\n`build.sh` includes a pre-step:\n- `ARCH_OVERRIDE=\u003carch\u003e ./scripts/build_libssh2.sh`\n\nSupported `ARCH_OVERRIDE` values:\n- `arm64`\n- `x86_64`\n- `both`\n- `universal`\n\nThird-party output roots are arch-specific:\n- `build/third_party/openssl-arm64`, `build/third_party/openssl-x86_64`, `build/third_party/openssl-universal`\n- `build/third_party/libssh2-arm64`, `build/third_party/libssh2-x86_64`, `build/third_party/libssh2-universal`\n\nApp output paths:\n- Single-arch (`arm64`, `x86_64`, `universal`): `build/macfuseGui.app`\n- Dual-arch (`both`): `build/macfuseGui-arm64.app` and `build/macfuseGui-x86_64.app`\n\nDerivedData roots:\n- `build/DerivedData-arm64`\n- `build/DerivedData-x86_64`\n- `build/DerivedData-universal`\n\n## Make Targets\n\n```bash\nmake build\nmake run\nmake clean\n```\n\n## Common Build Overrides\n\n```bash\n# Intel build\nARCH_OVERRIDE=x86_64 ./scripts/build.sh\n\n# Build separate arm64 + x86_64 apps\nARCH_OVERRIDE=both ./scripts/build.sh\n\n# Universal app build\nARCH_OVERRIDE=universal ./scripts/build.sh\n\n# Release build\nCONFIGURATION=Release ./scripts/build.sh\n\n# Allow signing if needed\nCODE_SIGNING_ALLOWED=YES ./scripts/build.sh\n```\n\n## Release\n\n```bash\n# Default release mode is dual-arch (both)\n./scripts/release.sh\n\n# Verify dual-arch release actions without publishing\nARCH_OVERRIDE=both ./scripts/release.sh --dry-run\n\n# Force a single-arch release if needed\nARCH_OVERRIDE=arm64 ./scripts/release.sh\nARCH_OVERRIDE=x86_64 ./scripts/release.sh\n```\n\n## Xcode CLI Fallback\n\n```bash\nARCH_OVERRIDE=arm64 ./scripts/build.sh\nxcodebuild -project macfuseGui.xcodeproj -scheme macfuseGui -configuration Debug -derivedDataPath build/DerivedData build\n```\n\n## Run Tests\n\n```bash\nARCH_OVERRIDE=arm64 ./scripts/build.sh\nxcodebuild -project macfuseGui.xcodeproj -scheme macfuseGui -configuration Debug -derivedDataPath build/DerivedData -destination 'platform=macOS,arch=arm64' test CODE_SIGNING_ALLOWED=NO\n```\n\n## Reliability Gate\n\n```bash\nARCH_OVERRIDE=arm64 ./scripts/build.sh\nscripts/audit_mount_calls.py \u0026\u0026 xcodebuild -project macfuseGui.xcodeproj -scheme macfuseGui -configuration Debug -derivedDataPath build/DerivedData -destination 'platform=macOS,arch=arm64' test CODE_SIGNING_ALLOWED=NO\n```\n\n## VS Code\n\nIncluded:\n- `.vscode/tasks.json`\n- `.vscode/launch.json`\n- `.vscode/settings.json`\n\nTasks:\n- `build` -\u003e `scripts/build.sh`\n- `run` -\u003e build + launch app\n- `clean` -\u003e `scripts/clean.sh`\n- `sourcekit-reset-cache` -\u003e `scripts/reset_sourcekit_cache.sh`\n\nDebug launch:\n- Program: `build/macfuseGui.app/Contents/MacOS/macfuseGui`\n- Pre-launch task: `build`\n\n## Documentation\n\n- **[New Contributor Guide](CONTRIBUTING.md)**: Start here if you want to modify the app.\n- **[Architecture \u0026 Jargon Buster](ARCHITECTURE.md)**: Visual diagrams and plain-English explanations.\n- **[Safe Change Rules (AGENTS.md)](AGENTS.md)**: Critical rules for AI agents and developers.\n- **[Commit \u0026 Push Workflow (COMMIT_WORKFLOW.md)](COMMIT_WORKFLOW.md)**: Commit format, changelog prefix rules, and release notes guidance.\n\n## Security Notes\n\n- All external command execution uses `Process` with argument arrays.\n- No shell interpolation for user input.\n- Password mode uses temporary `SSH_ASKPASS` helper (`0700`) and ephemeral env vars.\n- Passwords are never stored in JSON or logs.\n- `KeychainService.readPassword` trims leading/trailing whitespace on read — prevents silent auth failures from clipboard-pasted trailing newlines without altering the stored credential.\n- IPv6 host addresses are automatically bracketed (`[::1]`) in sshfs arguments; bare IPv6 input is also rejected at the validation layer.\n- Diagnostics redact sensitive content.\n\n## Browser Subsystem Notes\n\nBrowser internals are session-based (`openSession` / `listDirectories` / `health` / `closeSession`) with per-session health and sticky-cache behavior.\n\nCurrent transport implementation uses native libssh2 SFTP through a C bridge (`LibSSH2Bridge.c/.h`) behind an internal transport abstraction.\n\nRecovery contract:\n- Keepalive runs every 12s only while the browser session is idle.\n- Keepalive failures do not hard-close sessions; they schedule list-based recovery.\n- Recovery backoff: `0.2s`, `0.8s`, `2s`, `5s`.\n- Empty listings are confirmation-checked before being treated as truly empty.\n- Browser keeps last-good entries visible during transient failures.\n\n## Troubleshooting\n\n### Missing dependencies\n\n```bash\nbrew install --cask macfuse\nbrew install gromgit/fuse/sshfs-mac\n```\n\n### xcodebuild first-launch issues\n\n```bash\nsudo xcodebuild -runFirstLaunch\n```\n\n### VS Code SourceKit stale errors\n\n```bash\n./scripts/reset_sourcekit_cache.sh\n```\n\nThen in VS Code:\n1. Reload window\n2. Restart Swift LSP\n3. Re-index project\n\n### Mount/unmount issues\n\n- Use `Copy Diagnostics` from the app menu.\n- For busy unmount, diagnostics now include blocking process hints.\n\n### Duplicate menu icon or stale second app process\n\n```bash\npkill -x macfuseGui\nopen -a /Applications/macfuseGui.app\n```\n\nIf this still happens, check for multiple running processes:\n\n```bash\npgrep -lf macfuseGui\n```\n\n### Browser diagnostics interpretation\n\n- `state=healthy` with `isConfirmedEmpty=true`: path is reachable and truly has no subfolders.\n- `state=reconnecting` or `state=degraded` with `fromCache=true`: browser is showing last-good cached data while retrying.\n- `lastSuccessAt` and `lastLatencyMs` in `Browser Sessions` show the last confirmed successful list.\n- 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.\n\n### Mount concurrency proof logs (actor funnel check)\n\nUse this during wake/reconnect testing:\n\n```bash\nlog stream --style compact --predicate 'process == \"macfuseGui\"' \\\n  | rg 'Operation start remoteID=|Operation end remoteID=|mount call op=|actor enter op=|probe start op=sshfs-connect|probe end op=sshfs-connect'\n```\n\nInterpretation:\n- Healthy parallelism: different remotes show overlapping `probe start/end op=sshfs-connect` windows.\n- Funnel warning: one remote repeatedly waits with high `queueDelayMs` before `actor enter`.\n\n## License\n\nThis project is licensed under the GNU General Public License v3.0.\n\nIf you distribute a modified version, you must also provide the source under GPLv3.\n\nSee [`LICENSE`](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fripplethor%2Fmacfusegui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fripplethor%2Fmacfusegui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fripplethor%2Fmacfusegui/lists"}