docs: refresh for Scarf v2.5.1 — profiles, density, keychain sync, pill, remote project add, release gate

Alan Wizemann
2026-04-27 16:49:58 +02:00
parent 7b80f7fa19
commit 07283d17bc
8 changed files with 75 additions and 22 deletions
+24 -1
@@ -27,6 +27,29 @@ Streams tokens, thoughts, and tool calls live via the [ACP subprocess](ACP-Subpr
**Voice mode controls:** PTT (push-to-talk), TTS playback, STT transcription preferences live in **Settings → Voice**. The chat toolbar exposes the basic toggles. **Voice mode controls:** PTT (push-to-talk), TTS playback, STT transcription preferences live in **Settings → Voice**. The chat toolbar exposes the basic toggles.
## Chat density preferences _(v2.5.1+, Mac)_
**Settings → Display → Chat density** has three Scarf-local controls that change how the chat is rendered. They're independent of the Hermes config flags one section below (`Show Reasoning`, `Show Cost`, `Compact`) — those gate what Hermes EMITS, these gate how Scarf RENDERS what was emitted.
- **Tool calls** — `Full card` (today's expandable card per call), `Compact chip` (one-line tappable chip per call — kind icon + function name + status dot — opening the right-pane inspector with the same details), `Hidden` (per-call rows skipped; the always-visible group summary pill stays and becomes tappable so the inspector is still one click away).
- **Reasoning** — `Disclosure box` (today's yellow box), `Inline (italic)` (italic faded caption text inline above the reply with a small brain prefix — same data, far less vertical space), `Hidden` (reasoning text not rendered; per-message token count stays visible in the bubble's metadata footer).
- **Chat font size** — 85% to 130% slider (5% step) applied at the chat root via `.environment(\.dynamicTypeSize, ...)` so the message list, input bar, session info bar, and inspector pane scale together.
Defaults match today's UI exactly so existing users see no change until they opt in. Per-turn stopwatch, per-message tokens, finish reason, and timestamp stay in the bubble metadata footer in every density mode; SessionInfoBar's input/output/reasoning tokens, USD cost, model, project, and git branch are unaffected. See issues [#47](https://github.com/awizemann/scarf/issues/47) and [#48](https://github.com/awizemann/scarf/issues/48) for the original asks and the full preservation audit.
## Streaming performance _(v2.5.1)_
Pre-2.5.1 long chats progressively bogged down because every streamed ACP token rebuilt the full message-group array AND every `MessageGroupView` / `RichMessageBubble` re-evaluated its body. v2.5.1 caps per-chunk work at O(1) for settled groups via `Equatable` + `.equatable()` short-circuits, plus a trailing-group patch helper that replaces the per-chunk full rebuild. ScarfGo's chat (different rendering path — `LazyVStack` directly over `controller.vm.messages`) gained an iOS-equivalent `MessageBubble: Equatable`. Issue [#46](https://github.com/awizemann/scarf/issues/46).
## iOS keyboard dismissal _(v2.5.1+)_
Pre-fix the chat composer's `TextField` had no keyboard dismissal at all — the keyboard would rise and stick, hiding the system tab bar (which iOS auto-hides while a keyboard is up) and trapping users in the Chat tab. v2.5.1 adds two redundant dismissal paths:
- `.scrollDismissesKeyboard(.interactively)` on the message list — drag the messages downward to collapse the keyboard with the gesture.
- A `keyboard.chevron.compact.down` button in the keyboard accessory toolbar above the system keyboard.
Either dismisses the keyboard, the system tab bar reappears, and the user can switch tabs again. Issue [#51](https://github.com/awizemann/scarf/issues/51).
## Terminal mode ## Terminal mode
The full `hermes chat` CLI rendered in an embedded SwiftTerm terminal: The full `hermes chat` CLI rendered in an embedded SwiftTerm terminal:
@@ -66,4 +89,4 @@ Each Mac window is bound to one server, so chat in window A talks to local Herme
- [Settings — Voice tab](Gateway-Cron-Health-Logs) for TTS/STT configuration (Settings is documented there). - [Settings — Voice tab](Gateway-Cron-Health-Logs) for TTS/STT configuration (Settings is documented there).
--- ---
_Last updated: 2026-04-25 — Scarf v2.5.0 (3-pane Mac redesign + v0.11 chat parity + project-scoped slash commands)_ _Last updated: 2026-04-27 — Scarf v2.5.1 (chat density preferences + streaming O(n)-per-token fix + iOS keyboard dismissal)_
+4 -3
@@ -24,7 +24,8 @@ Canonical layout of `~/.hermes/`. Scarf reads these paths through [`HermesPathSe
| `~/.hermes/skills/` | Installed skills (with v0.11 SKILL.md frontmatter) | read + write (install/update/uninstall) | | `~/.hermes/skills/` | Installed skills (with v0.11 SKILL.md frontmatter) | read + write (install/update/uninstall) |
| `~/.hermes/plugins/` | Installed plugins (cloned from Git URLs) | read + write | | `~/.hermes/plugins/` | Installed plugins (cloned from Git URLs) | read + write |
| `~/.hermes/personalities/` | Personalities + their `SOUL.md` | read + write | | `~/.hermes/personalities/` | Personalities + their `SOUL.md` | read + write |
| `~/.hermes/profiles/` | Isolated Hermes instances | read + write | | `~/.hermes/profiles/<name>/` | Isolated Hermes instances (Hermes v0.11+) — each profile carries its own `state.db`, `sessions/`, `config.yaml`, `.env`, `memories/`, `cron/`, etc. | read + write |
| `~/.hermes/active_profile` | Single-line text file holding the active profile name (or absent / empty for default). _v2.5.1+:_ Scarf reads this via [`HermesProfileResolver`](Core-Services) and routes every derived path under it, so `hermes profile use coder` followed by a Scarf relaunch correctly reads the new profile's data. | read-only |
| `~/.hermes/mcp-tokens/*.json` | Per-server MCP OAuth tokens | read (detect) + delete (clear) | | `~/.hermes/mcp-tokens/*.json` | Per-server MCP OAuth tokens | read (detect) + delete (clear) |
## Scarf-owned paths under `~/.hermes/scarf/` ## Scarf-owned paths under `~/.hermes/scarf/`
@@ -64,7 +65,7 @@ ScarfGo can't write to `~/Library/Caches/scarf/...` — it lives in its own iOS
| SQLite snapshots | iOS Caches dir → `<sandbox>/Library/Caches/scarf/snapshots/<server-id>/state.db` | | SQLite snapshots | iOS Caches dir → `<sandbox>/Library/Caches/scarf/snapshots/<server-id>/state.db` |
| Skill snapshots ("What's New" pill) | `UserDefaults` (the iOS sandbox doesn't have a clean Application Support equivalent for tiny per-server JSON) | | Skill snapshots ("What's New" pill) | `UserDefaults` (the iOS sandbox doesn't have a clean Application Support equivalent for tiny per-server JSON) |
| Server registry (multi-server) | `UserDefaults` key `com.scarf.ios.servers.v2` (auto-migrated from legacy `ScarfGo.servers.v1`) | | Server registry (multi-server) | `UserDefaults` key `com.scarf.ios.servers.v2` (auto-migrated from legacy `ScarfGo.servers.v1`) |
| SSH private keys | iOS Keychain — service `com.scarf.ssh-key`, account `server-key:<UUID>`, accessibility `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`. Never iCloud-synced. | | SSH private keys | iOS Keychain — service `com.scarf.ssh-key`, account `server-key:<UUID>`. Default accessibility `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` + `kSecAttrSynchronizable=false` (device-local). _v2.5.1+:_ opt-in System → Security toggle flips writes to `kSecAttrAccessibleAfterFirstUnlock` + `kSecAttrSynchronizable=true` so the key syncs to iCloud Keychain across the user's signed-in Apple devices. Off by default. |
| Template config secrets | iOS Keychain — service `com.scarf.template.<slug>`, account `<fieldKey>:<project-path-hash>` | | Template config secrets | iOS Keychain — service `com.scarf.template.<slug>`, account `<fieldKey>:<project-path-hash>` |
## Log line format ## Log line format
@@ -72,4 +73,4 @@ ScarfGo can't write to `~/Library/Caches/scarf/...` — it lives in its own iOS
Hermes log lines may carry an optional `[session_id]` tag between the level and the logger name. `HermesLogService.parseLine` treats the session tag as an optional capture group, so older untagged lines still parse correctly. Hermes log lines may carry an optional `[session_id]` tag between the level and the logger name. `HermesLogService.parseLine` treats the session tag as an optional capture group, so older untagged lines still parse correctly.
--- ---
_Last updated: 2026-04-25 — Scarf v2.5.0 (Scarf-owned paths section, slash-commands/template lock-files, iOS sandbox + Keychain layout)_ _Last updated: 2026-04-27 — Scarf v2.5.1 (active_profile awareness via HermesProfileResolver; iOS Keychain optional iCloud sync)_
+5 -3
@@ -37,9 +37,11 @@ Scarf reads Hermes's SQLite database directly and parses CLI output from `hermes
## v0.11 CLIs not yet adopted ## v0.11 CLIs not yet adopted
Hermes v0.11 added a handful of new top-level CLIs Scarf doesn't yet drive directly: `hermes plugins`, `hermes profile`, `hermes webhook`, `hermes insights`, `hermes logs`, `hermes dashboard`, `hermes completion`. Scarf still reads the underlying files (`~/.hermes/plugins/`, `~/.hermes/profiles/`, etc.) directly today, which keeps working. Switching to the canonical CLIs is forward-compatible work for v2.6+. Hermes v0.11 added a handful of new top-level CLIs Scarf doesn't yet drive directly: `hermes plugins`, `hermes webhook`, `hermes insights`, `hermes logs`, `hermes dashboard`, `hermes completion`. Scarf still reads the underlying files (`~/.hermes/plugins/`, `~/.hermes/webhooks/`, etc.) directly today, which keeps working. Switching to the canonical CLIs is forward-compatible work for v2.6+.
`hermes memory reset` is the one v0.11 CLI v2.5 *does* drive — surfaced as the **Reset memory…** toolbar action on Memory views (Mac + iOS) with a destructive-confirmation dialog. **`hermes memory reset`** is surfaced as the **Reset memory…** toolbar action on Memory views (Mac + iOS) with a destructive-confirmation dialog.
**`hermes profile`** is half-adopted as of v2.5.1. Scarf doesn't yet drive `hermes profile create / use / delete` from in-app UI (still file-based, still local for now), BUT Scarf v2.5.1+ **respects the active profile** by reading `~/.hermes/active_profile` at path-resolution time via [`HermesProfileResolver`](Core-Services). So `hermes profile use coder` on the host followed by a Scarf relaunch correctly reads `~/.hermes/profiles/coder/state.db`, `config.yaml`, sessions, memory, cron, etc. See [Projects & Profiles](Projects-and-Profiles) for the full profile model. In-app profile switching (writing `active_profile` from a UI control) is a separate v2.6+ feature — handling running ACP processes through a profile switch is more involved than just flipping a file.
## What breaks across major Hermes versions ## What breaks across major Hermes versions
@@ -61,4 +63,4 @@ If a new Hermes release breaks something in Scarf, please file an issue includin
- The relevant log snippet from `~/.hermes/logs/errors.log` (filter sensitive content first). - The relevant log snippet from `~/.hermes/logs/errors.log` (filter sensitive content first).
--- ---
_Last updated: 2026-04-25 — Scarf v2.5.0 + Hermes v0.11.0_ _Last updated: 2026-04-27 — Scarf v2.5.1 + Hermes v0.11.0 (active_profile awareness)_
+12 -4
@@ -30,16 +30,24 @@ The full schema is documented in [`scarf/docs/DASHBOARD_SCHEMA.md`](https://gith
## Profiles ## Profiles
A profile is an isolated Hermes installation — separate config, sessions, memory, skills, the lot. Useful for keeping work / personal context separate, or for testing a config change without disturbing your main instance. A profile is an isolated Hermes installation — separate config, sessions, memory, skills, the lot. Useful for keeping work / personal context separate, or for testing a config change without disturbing your main instance. Hermes ships profiles as of **v0.11.0**.
**How profile storage actually works** _(v0.11+, as Scarf reads it in v2.5.1+):_
- The "default" profile is `~/.hermes/` itself — backward compatible, zero migration.
- Named profiles live under `~/.hermes/profiles/<name>/`, each a fully independent `HERMES_HOME`. Each profile carries its own `state.db`, `sessions/`, `config.yaml`, `.env`, `memories/`, `cron/`, `skills/`, `gateway_state.json`, etc.
- The active profile is recorded in `~/.hermes/active_profile` — a single-line text file containing the profile name, or absent / empty when default is active. `hermes profile use <name>` writes that file; the Hermes CLI then sets `HERMES_HOME` accordingly per invocation.
- **Scarf v2.5.1+ reads `active_profile`** via [`HermesProfileResolver`](Core-Services) and routes every derived path through it — `state.db`, `sessions/`, `config.yaml`, `memories/`, `cron/jobs.json`, `auth.json`, plugins, gateway state, logs, all of it. So switching profiles on the host with `hermes profile use coder` and relaunching Scarf correctly reads the new profile's data. The chat session info bar surfaces a small profile chip when not on default so you can tell at a glance which profile Scarf is reading from.
- Pre-2.5.1 Scarf hardcoded `~/.hermes` and ignored `active_profile`, which silently read the wrong DB after a profile switch (issue [#50](https://github.com/awizemann/scarf/issues/50)). If you're on 2.5.0 or older, upgrade.
**Operations** (all wrap `hermes profile ...` via `context.runHermes`): **Operations** (all wrap `hermes profile ...` via `context.runHermes`):
- **Switch** — make a profile active. Scarf shows a "restart Scarf to fully apply" reminder because the active profile path is read at launch. - **Switch** — make a profile active. Scarf shows a "restart Scarf to fully apply" reminder; the resolver re-reads `active_profile` on launch and on each `HermesPathSet` construction (5s cache).
- **Create / rename / delete** — straightforward. - **Create / rename / delete** — straightforward.
- **Export** — zips the profile directory; useful for backup or moving to a new machine. - **Export** — zips the profile directory; useful for backup or moving to a new machine.
- **Import** — unzip into a new profile slot. - **Import** — unzip into a new profile slot.
Profiles live under `~/.hermes/profiles/`. The currently active profile is whatever Hermes points its `~/.hermes/` symlink (or equivalent) at — Scarf reflects that. Remote SSH contexts don't yet auto-resolve `active_profile``HermesPathSet.defaultRemoteHome` stays at the configured remote home. If you're using profiles on a remote, set the **Hermes data directory** field in Manage Servers to point at `~/.hermes/profiles/<name>` for that server context. Issue [#53](https://github.com/awizemann/scarf/issues/53)'s degraded-pill diagnostics will tell you when this is the cause of an empty dashboard.
## Related pages ## Related pages
@@ -50,4 +58,4 @@ Profiles live under `~/.hermes/profiles/`. The currently active profile is whate
- [Settings](Gateway-Cron-Health-Logs) — exposes "Backup & Restore" buttons (`hermes backup` / `hermes import`) at the profile level. - [Settings](Gateway-Cron-Health-Logs) — exposes "Backup & Restore" buttons (`hermes backup` / `hermes import`) at the profile level.
--- ---
_Last updated: 2026-04-25 — Scarf v2.5.0 (per-project Sessions / Site / Slash Commands tabs + AGENTS.md context handoff)_ _Last updated: 2026-04-27 — Scarf v2.5.1 (Hermes v0.11 profile awareness via HermesProfileResolver + active_profile chip in chat info bar)_
+3 -1
@@ -13,6 +13,8 @@ Mac releases are produced by a single local script: [`scripts/release.sh`](https
A full release bumps the version, archives Universal (arm64 + x86_64) + ARM64-only variants, signs with Developer ID, notarizes via `xcrun notarytool`, staples, EdDSA-signs the appcast entry with Sparkle's key, pushes the appcast to `gh-pages`, and creates a GitHub release with both zips attached. A full release bumps the version, archives Universal (arm64 + x86_64) + ARM64-only variants, signs with Developer ID, notarizes via `xcrun notarytool`, staples, EdDSA-signs the appcast entry with Sparkle's key, pushes the appcast to `gh-pages`, and creates a GitHub release with both zips attached.
**Post-package verification gate** _(v2.5.1+)._ After every variant's final `ditto`, the script extracts the packaged zip into a temp dir and runs `codesign --verify --strict --deep --verbose=4` + `spctl --assess --type execute --verbose` on the extracted bundle. Either failure aborts the release. This catches any regression in the shipped artifact (stapler edge cases, post-staple modifications, framework-seal drift) before users see "Scarf.app is damaged" reports — issue [#49](https://github.com/awizemann/scarf/issues/49).
A draft release stops after the GitHub release is uploaded, so the current version stays "latest" until explicitly promoted. A draft release stops after the GitHub release is uploaded, so the current version stays "latest" until explicitly promoted.
## Release notes ## Release notes
@@ -58,4 +60,4 @@ ScarfGo ships through:
The iOS `MARKETING_VERSION` should match the Mac `MARKETING_VERSION` for the same release; the iOS `CURRENT_PROJECT_VERSION` (build number) increments independently per Apple's monotonic-build-number rule. There's no automation for iOS bumping yet — manual edit in the Xcode target before archiving. The iOS `MARKETING_VERSION` should match the Mac `MARKETING_VERSION` for the same release; the iOS `CURRENT_PROJECT_VERSION` (build number) increments independently per Apple's monotonic-build-number rule. There's no automation for iOS bumping yet — manual edit in the Xcode target before archiving.
--- ---
_Last updated: 2026-04-25 — Scarf v2.5.0 (added iOS release flow + cross-link to TestFlight + App Store metadata files)_ _Last updated: 2026-04-27 — Scarf v2.5.1 (post-package verification gate)_
+7 -6
@@ -15,7 +15,7 @@ ScarfGo never asks for your account password. It also never holds an Apple-side
1. Server details (hostname, user, port, optional nickname) 1. Server details (hostname, user, port, optional nickname)
2. Choose: generate a new SSH key, or import one you already have 2. Choose: generate a new SSH key, or import one you already have
3. Generate (or paste) the keypair — the private half lives in the iOS Keychain, never iCloud-synced 3. Generate (or paste) the keypair — the private half lives in the iOS Keychain (device-local by default; opt-in iCloud Keychain sync from System → Security as of v2.5.1)
4. Show the public key — copy and paste it into `~/.ssh/authorized_keys` on the host 4. Show the public key — copy and paste it into `~/.ssh/authorized_keys` on the host
5. Test connection — ScarfGo SSHes in, looks for the `hermes` binary, saves on success 5. Test connection — ScarfGo SSHes in, looks for the `hermes` binary, saves on success
@@ -50,9 +50,10 @@ Either way, the private half is stored in the iOS Keychain with these attributes
- **Service:** `com.scarf.ssh-key` - **Service:** `com.scarf.ssh-key`
- **Account:** `server-key:<UUID>` (one entry per configured server) - **Account:** `server-key:<UUID>` (one entry per configured server)
- **Accessibility:** `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` - **Accessibility (default):** `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` + `kSecAttrSynchronizable=false`. Key unreachable while the device is locked, doesn't leave the device, survives passcode changes. Pre-v2.5.1 this was the only mode.
- **Accessibility (opt-in, v2.5.1+):** `kSecAttrAccessibleAfterFirstUnlock` + `kSecAttrSynchronizable=true`. Toggle from System → Security → "Sync SSH key with iCloud Keychain". The Keychain entry now syncs across signed-in Apple devices, end-to-end encrypted by iCloud Keychain (with Advanced Data Protection enabled, the encryption keys never leave your devices). Adding a second device no longer requires generating a fresh key — install ScarfGo, sign in to the same Apple ID with iCloud Keychain enabled, the same key shows up.
That last attribute means the key is unreachable while the device is locked, doesn't sync to iCloud (so adding the same key to a second device requires re-onboarding), and survives device passcode changes. If you ever delete the app, iOS purges the Keychain group along with it. If you ever delete the app, iOS purges its Keychain group; for synced items, iCloud Keychain mirrors the deletion across devices.
### 4. Show the public key — paste into authorized_keys ### 4. Show the public key — paste into authorized_keys
@@ -140,10 +141,10 @@ Cancelling Face ID or the device passcode prompt no longer drops you back into o
## Privacy and key handling — quick recap ## Privacy and key handling — quick recap
- **No iCloud sync.** Keys are explicitly marked `ThisDeviceOnly` and excluded from iCloud Keychain. - **iCloud sync is opt-in (v2.5.1+).** Default is device-local — keys are marked `ThisDeviceOnly` and excluded from iCloud Keychain unless you enable the System → Security toggle. With it on, the key syncs end-to-end encrypted via iCloud Keychain (Advanced Data Protection makes the encryption keys client-side only).
- **No cloud accounts.** Scarf has no developer-controlled server. Your iPhone connects directly to your Hermes host over SSH. - **No cloud accounts.** Scarf has no developer-controlled server. Your iPhone connects directly to your Hermes host over SSH.
- **No analytics.** ScarfGo doesn't transmit any data to any third party. - **No analytics.** ScarfGo doesn't transmit any data to any third party.
- **One key per device.** Adding a second iPhone means a second `authorized_keys` line. Same convention you'd follow for a new Mac. - **One key per device — unless you opt into sync.** Default behavior: adding a second device means a second `authorized_keys` line. With iCloud Keychain sync enabled, the same key appears on every signed-in Apple device with iCloud Keychain on, so a single `authorized_keys` line covers all of them.
Full policy: [Privacy Policy](Privacy-Policy). Full policy: [Privacy Policy](Privacy-Policy).
@@ -156,4 +157,4 @@ Full policy: [Privacy Policy](Privacy-Policy).
- [Support](Support) — bug reports, feature requests, security disclosures. - [Support](Support) — bug reports, feature requests, security disclosures.
--- ---
_Last updated: 2026-04-25 — Scarf v2.5.0 (audit pass: corrected PATH-prefix candidate count + `~/.hermes/bin` claim)_ _Last updated: 2026-04-27 — Scarf v2.5.1 (opt-in iCloud Keychain sync for SSH keys)_
+2 -2
@@ -26,7 +26,7 @@ ScarfGo is in **public TestFlight**. Apple-provided test environment, free to jo
Onboarding details: Onboarding details:
- ScarfGo generates a fresh Ed25519 keypair on first run. The private half lives in the iOS Keychain (`kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` — never iCloud-synced). - ScarfGo generates a fresh Ed25519 keypair on first run. The private half lives in the iOS Keychain. **Default:** device-local (`kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`, not iCloud-synced). **v2.5.1+:** an opt-in toggle in System tab → Security flips it to sync via iCloud Keychain (`kSecAttrAccessibleAfterFirstUnlock` + `kSecAttrSynchronizable=true`) so iPhone + iPad + Mac see the same key without onboarding each device. End-to-end encrypted by iCloud Keychain; with Advanced Data Protection enabled, the encryption keys never leave your devices.
- The public-key snippet is shown for you to copy and append to `~/.ssh/authorized_keys` on your Hermes host. Plain `ssh-copy-id` doesn't work from iPhone, so the manual paste is the safest path. - The public-key snippet is shown for you to copy and append to `~/.ssh/authorized_keys` on your Hermes host. Plain `ssh-copy-id` doesn't work from iPhone, so the manual paste is the safest path.
- A one-tap "Test connection" verifies SSH + the `hermes` binary's path before saving. - A one-tap "Test connection" verifies SSH + the `hermes` binary's path before saving.
@@ -90,7 +90,7 @@ See [Platform Differences](Platform-Differences) for a full Mac-vs-iOS feature m
A: No. Hermes is a Python agent that needs Python plus a model provider's CLI plus a writable filesystem. iOS doesn't make any of that practical. ScarfGo is a thin client. A: No. Hermes is a Python agent that needs Python plus a model provider's CLI plus a writable filesystem. iOS doesn't make any of that practical. ScarfGo is a thin client.
**Q: Will my SSH key sync to my other devices?** **Q: Will my SSH key sync to my other devices?**
A: No. The Keychain entry is `ThisDeviceOnly` — explicitly excluded from iCloud Keychain sync. Adding a second device means a second key + a second `authorized_keys` line. A: Optional as of v2.5.1. Default is **off** — the Keychain entry is `ThisDeviceOnly` and adding a second device means a second key + a second `authorized_keys` line. Toggle **System → Security → Sync SSH key with iCloud Keychain** to flip the entry to a synced item. iCloud Keychain encrypts the bundle end-to-end; with Advanced Data Protection on, the keys are encrypted client-side with material that never leaves your devices. Off is still the right choice if you want a hard guarantee that the key is bound to one device — issue [#52](https://github.com/awizemann/scarf/issues/52).
**Q: Can I use ScarfGo with a Hermes host that's not on my LAN?** **Q: Can I use ScarfGo with a Hermes host that's not on my LAN?**
A: Yes — anywhere reachable over SSH. Tailscale, port forwarding, a VPS, anything. The Hermes host doesn't know it's being driven by an iPhone vs a Mac. A: Yes — anywhere reachable over SSH. Tailscale, port forwarding, a VPS, anything. The Hermes host doesn't know it's being driven by an iPhone vs a Mac.
+18 -2
@@ -39,7 +39,23 @@ The remote host must have:
If the connection pill is green but the Dashboard shows "Stopped", "unknown", or empty values, the SSH user can't read the Hermes state files. If the connection pill is green but the Dashboard shows "Stopped", "unknown", or empty values, the SSH user can't read the Hermes state files.
**Manage Servers → 🩺 Run Diagnostics** (or click the yellow "Can't read Hermes state" pill in the toolbar) runs **fourteen** checks in one SSH session: connectivity, `sqlite3` presence, read access to `config.yaml` and `state.db`, the effective non-login `$PATH`, etc. Each failure explains itself with a remediation hint. **Copy Full Report** dumps the whole output for bug reports. **The pill itself diagnoses common cases inline** _(v2.5.1+)._ Clicking the yellow "Can't read Hermes state" pill opens a popover with:
- The specific reason (`config.yaml is missing`, `permission denied on config.yaml`, `~/.hermes` doesn't exist, `Hermes profile <name> is active`, etc.)
- An actionable hint paragraph (`run hermes setup on the remote`, `chmod a+r ~/.hermes/config.yaml`, etc.)
- A Run Diagnostics button (opens the heavy 14-check sheet) and a Retry button
For the "profile is active" case the popover includes a copy-paste `hermes profile use default` command. See [Projects & Profiles](Projects-and-Profiles) for the full Hermes v0.11 profile model.
**Manage Servers → 🩺 Run Diagnostics** runs **fourteen** checks in one SSH session: connectivity, `sqlite3` presence, read access to `config.yaml` and `state.db`, the effective non-login `$PATH`, etc. Each failure explains itself with a remediation hint. **Copy Full Report** dumps the whole output for bug reports.
**Pill probe and diagnostics now use the same plumbing** _(v2.5.1+)._ Both go through the shared [`SSHScriptRunner`](Core-Services) (raw `/usr/bin/ssh ... -- /bin/sh -s`, script piped via stdin) instead of the prior split where the pill went through `runProcess`'s argument quoting and the diagnostics view used a local workaround. They no longer disagree about what the remote sees — issue [#44](https://github.com/awizemann/scarf/issues/44).
## Adding a project on a remote server _(v2.5.1+)_
The Add Project sheet is now context-aware. On a local server it works as before — click **Browse...** to pick a directory with `NSOpenPanel`. On a remote server the Browse button is hidden (a Mac-local Finder dialog can't see the remote filesystem) and replaced with a **Verify** button that runs `transport.stat(path)` over SSH and renders a green ✓ if the path exists and is a directory, or a yellow ⚠ if it's missing / a file / unreadable. Edit the path field and the verification resets to idle so you don't see a stale ✓ for a path you've since changed.
A full SFTP-backed remote directory picker is on the roadmap (issue [#54](https://github.com/awizemann/scarf/issues/54)). Until then, type the absolute remote path (or paste from a remote shell), Verify, then Add.
## Switching the active window ## Switching the active window
@@ -56,4 +72,4 @@ See [Keyboard Shortcuts](Keyboard-Shortcuts).
- [Hermes Paths](Hermes-Paths) for what each remote file is. - [Hermes Paths](Hermes-Paths) for what each remote file is.
--- ---
_Last updated: 2026-04-25 — Scarf v2.5.0 (added ScarfGo cross-references)_ _Last updated: 2026-04-27 — Scarf v2.5.1 (granular degraded-pill popover + unified pill/diagnostics transport + context-aware Add Project)_