Mac bridge: ssh to the Mac, omac, clip, screenshot hotkey

ssh mac works from this box. The key had been offered and refused for a day
because it had never reached the Mac — an earlier paste ran only part of its
command. Fixed by pulling the key over the Mac -> Unraid link that already
worked, so it never passed through the clipboard.

Two new commands in shell/bin, installed to /root/.local/bin at boot:

- omac translates an Unraid path to its Mac counterpart through the shared
  Syncthing folders, then opens it, prints it, emits a file:// URL or an OSC 8
  hyperlink, or verifies it has synced. It refuses rather than guesses outside
  a shared folder. Literal paths are tried before symlink-resolved ones, after
  readlink -m was found to resolve skills/memory out of the shared tree.
- clip puts text into the Mac's pasteboard over OSC 52, walking up the process
  tree to find a pty and wrapping for tmux passthrough.

Also records the Hammerspoon screenshot hotkey on the Mac side, how hc ls from
the Mac addresses terminals on both machines, and that screencapture cannot
work over ssh because macOS withholds Screen Recording from a non-GUI session.

Verified end to end: a PNG made here, copied to the Mac, pushed back with
hc put unraid/t13, named by shot and read in-session byte-intact.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
jazzymcandClaude Opus 5 committed 2026-11-08 07:39:12 +00:00
1 parent 0fbb5b4cfc
commit ccffbd1252
3 files changed
+110 -15

No files matched your search

+33
View File
@@ -275,6 +275,39 @@ failure. Named directories shorten the share paths (`~projects`, `~ampeco`,
`~appdata`, `~vault`, `~flash`), and `cdpath` makes `cd backend` work from `~appdata`, `~vault`, `~flash`), and `cdpath` makes `cd backend` work from
anywhere, matching the `tmux-work` bookmarks. anywhere, matching the `tmux-work` bookmarks.
### Commands in `shell/bin/`
`install.sh` installs everything in `/boot/config/shell/bin/` to
`/root/.local/bin` at mode 755. These are real executables, not shell
functions, because a non-interactive shell — an agent's tool call, `ssh box
cmd`, cron — never sources `.bashrc` but does inherit `PATH`.
| Command | Purpose |
|---------|---------|
| `shot` | Name the files `hc put` dropped into this Herdr terminal |
| `omac` | Act on an Unraid file as its Mac counterpart: open, path, `file://` URL, OSC 8 link, or verify it has synced |
| `clip` | Put text into the Mac's pasteboard from a session here, over OSC 52 |
`omac` translates through the shared Syncthing folders. The Unraid paths in
Syncthing's `config.xml` are *container* paths and must be resolved through the
`syncthing` container's mounts (`/data1` → `/mnt/user/projects`):
| Folder id | Unraid | Mac |
|-----------|--------|-----|
| `jmmgo-qzpqp` (obsidian) | `/mnt/user/projects/obsidian` | `/Users/kaloyandanchev/Obsidian` |
| `homelab` | `/mnt/user/projects/homelab` | `/Users/kaloyandanchev/Homelab` |
| `ampeco` | `/mnt/user/projects/ampeco` | `/Users/kaloyandanchev/Ampeco` |
| `personal` | `/mnt/user/projects/personal` | `/Users/kaloyandanchev/Personal` |
| `claude-skills` | `/mnt/user/appdata/claude-code/.claude/skills` | `/Users/kaloyandanchev/.claude/skills` |
| `cooperator` | `/mnt/user/appdata/cooperator` | `/Users/kaloyandanchev/.cooperator` |
`/mnt/user/projects/infrastructure` is **not** synced, so this repo has no Mac
counterpart and cannot be click-opened. `omac` reports that rather than
returning a path that does not exist.
Full detail, including the Hammerspoon screenshot hotkey on the Mac, is in
`14-HERDR-REMOTE-MACHINES.md`.
### Persistence ### Persistence
`/` is a RAM rootfs, so every dotfile in `/root` is rebuilt at boot and anything `/` is a RAM rootfs, so every dotfile in `/root` is rebuilt at boot and anything
+66 -15
View File
@@ -208,23 +208,74 @@ inherit `PATH`.
With no drops it prints the `hc put` command that would create one. With no drops it prints the `hc put` command that would create one.
### Reaching the Mac from here (not yet set up) ### Reaching the Mac from here
`hc machine ls` on this core returns `{"machines":[]}` — the relationship is Set up 2026-11-08. `ssh mac` works from this box, by key over Tailscale.
one-directional from the Mac today. Registering the Mac here would add
`hc file read|write|edit --on mac` ("an agent's hands there") and
`hc exec --on mac`, which is how a session on this box gets at Mac files
without a mount. Two caveats: `hc file read` is **text only**, so Mac images
must travel by `hc put`; and the Mac is a laptop, so register it by its tailnet
name (`100.68.118.59`, `kaloyans-macbook-air`) and expect it to be unreachable
when asleep.
Verified 2026-11-07: the Mac answers over Tailscale in 16 ms (direct, via | Property | Value |
`192.168.20.101`) and port 22 is open, so macOS Remote Login is already on. It |----------|-------|
does **not** advertise Tailscale SSH (`sshHostKeys: false`; macOS cannot act as | Alias | `mac`, `macbook` in `/boot/config/ssh/root/config` |
a Tailscale SSH server), so unlike this box there is no keyless path — it needs | Address | `100.68.118.59` — the tailnet IP, not a LAN one, because it is a laptop |
this host's `unraid@xtrm` key in the Mac's `authorized_keys`. A commented `Host | User | `kaloyandanchev` (uid 501, in `com.apple.access_ssh`) |
mac` entry is staged in `/boot/config/ssh/root/config`. | Key | `/root/.ssh/id_ed25519`, `SHA256:DT7YBFfdI9+Ccb4Dfet+Q/xXkhqfmqeQi6ecRpSFQSY` |
| Auth | key only — macOS cannot be a Tailscale SSH server, so there is no keyless path |
The key had been offered and refused for a day. The cause was mundane: it had
never reached the Mac, because an earlier paste ran only part of its command.
The fix avoided the clipboard entirely by pulling the key over the link that
already worked in the other direction:
```bash
# on the Mac
K=$(ssh unraid 'cat /root/.ssh/id_ed25519.pub')
grep -qF "$K" ~/.ssh/authorized_keys || echo "$K" >> ~/.ssh/authorized_keys
```
`hc machine ls` on **this** core still returns `{"machines":[]}` and was left
that way: ssh covers what registering would have bought, and `hc file read` is
text-only so Mac images would still have to travel by `hc put`.
**What ssh to the Mac cannot do:** `screencapture` fails with `could not create
image from rect`. macOS withholds Screen Recording permission from a non-GUI
session. Screen capture therefore belongs to Hammerspoon, in the GUI session.
### Addressing terminals across both machines
`hc ls --json` **from the Mac** lists every live terminal on both machines in
one array — local ones as `t7`, this box's as `unraid/t13` — with `status`,
`agent`, `cwd` and a `where` label. The envelope is
`{"hidden_finished":N,"machines":[...],"terminals":[...]}`.
That is what makes a single Mac-side hotkey able to target any session
anywhere, without knowing in advance which machine is in front of the user.
### The screenshot hotkey (Mac side)
Appended to `~/.hammerspoon/init.lua` on 2026-11-08 (160 → 266 lines, backup
`init.lua.bak-20261108-093027`).
| Key | Action |
|-----|--------|
| `Cmd+Shift+U` | Clipboard image, or capture a region if there is none; pick a terminal; push and prompt |
| `Cmd+Ctrl+U` | The same, straight to the terminal chosen last time |
It runs `hc put --json <target> <file>`, parses the returned path, then
`hc send <target> "Look at the screenshot at <path>"` so the agent is told
without anything being typed.
Implementation notes that matter if it is ever edited:
- `hs.pasteboard.readImage()` reads the pasteboard natively — no `pngpaste`, no
Homebrew, which keeps the "no second environment" constraint.
- Every `hc` call goes through `hs.task`, never `hs.execute`: a put to this box
travels over ssh, and blocking Hammerspoon's main thread would freeze every
other hotkey.
- Bindings avoid the `Cmd+Shift` letters the read-aloud hotkeys already use
(Z, X, D, A, R).
Verified end to end on 2026-11-08: a 24×24 PNG made on this box, copied to the
Mac, pushed back with `hc put unraid/t13`, named by `shot` and read in-session
byte-intact.
## Notes ## Notes
+11
View File
@@ -2,6 +2,17 @@
**Purpose:** Major infrastructure events only. Minor changes are in git commit messages. **Purpose:** Major infrastructure events only. Minor changes are in git commit messages.
---
## 2026-11-08
### Mac ↔ Unraid bridge: screenshots in, clipboard and click-to-open out
- **[FIXED]** `ssh mac` from this box now works. The key had been offered and refused for a day; the cause was that it had never reached the Mac, because an earlier paste ran only part of its command. Fixed by pulling the key over the Mac → Unraid link that already worked, so it never passed through the clipboard. Auth is key-only: macOS cannot act as a Tailscale SSH server, unlike this box.
- **[HOST]** New `omac` (`/boot/config/shell/bin/omac` → `/root/.local/bin/omac`) translates an Unraid path to its Mac counterpart through the shared Syncthing folders and then opens it, prints it, emits a `file://` URL or an OSC 8 hyperlink, or verifies the file has actually synced. It refuses rather than guesses for anything outside a shared folder — `/mnt/user/projects/infrastructure` is not synced. A bug found in testing: `readlink -m` alone resolved `skills/memory` out of the shared tree via a symlink, so the literal path is now tried before the resolved one.
- **[HOST]** New `clip` (`shell/bin/clip` → `/root/.local/bin/clip`) puts text into the Mac's pasteboard by writing an OSC 52 escape to this session's pty — no ssh, no mount, no sync. Walks up the process tree to find a pty, since a tool call or pipeline has none of its own, and wraps for tmux passthrough when `$TMUX` is set. OSC 52 has no reply, so a `clipboard-write = deny` refusal cannot be detected from here.
- **[HOST]** Screenshot hotkey appended to the Mac's `~/.hammerspoon/init.lua` (160 → 266 lines, backup kept): `Cmd+Shift+U` picks a terminal and pushes the clipboard image, `Cmd+Ctrl+U` repeats to the last one. The picker lists every live terminal on **both** machines, because `hc ls` from the Mac addresses local terminals as `t7` and this box's as `unraid/t13`. Verified end to end — a PNG made here, sent to the Mac, pushed back with `hc put`, named by `shot` and read in-session byte-intact.
- **[HOST]** Documented that `screencapture` cannot work over ssh (`could not create image from rect`): macOS withholds Screen Recording permission from a non-GUI session. Screen capture belongs to Hammerspoon, in the GUI session.
- **[NOTE]** `hc` is 0.1.18 on the Mac and 0.1.17 here.
--- ---
## 2026-11-07 ## 2026-11-07