Compare commits
18 Commits
bf0d48ec30
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 200de3d0b4 | |||
| 0377aa3a0b | |||
| c1558d2630 | |||
| c204d99dc6 | |||
| 2028508cb9 | |||
| 43877eb78f | |||
| dc29b7a087 | |||
| 3399829eb6 | |||
| d86ede0036 | |||
| 617fc789b1 | |||
| d622cf6aef | |||
| d7205a911c | |||
| af51e0cfa3 | |||
| 39f61f48ce | |||
| 507bec3f46 | |||
| c47a220993 | |||
| 6643ac003c | |||
| 0d733c77e7 |
@@ -0,0 +1,141 @@
|
||||
# Handoff: [TASK_TITLE - replace this]
|
||||
|
||||
## Session Metadata
|
||||
- Created: 2026-09-15 19:42:09
|
||||
- Project: /home/claude/bin/mac
|
||||
- Branch: master
|
||||
- Session duration: [estimate how long you worked]
|
||||
|
||||
### Recent Commits (for context)
|
||||
- 617fc78 docs: reconcile CLAUDE.md/DECISIONS with what shipped (sudo approach, rrsync, set -e lessons)
|
||||
- d622cf6 fix: chmod 600 the ssh Include file (ssh rejects group-writable config)
|
||||
- d7205a9 fix: pass pubkey to tank-side.sh via env, not ssh command arg
|
||||
- af51e0c fix: make SparkFun removal and launchd bootstrap non-fatal under set -e
|
||||
- 39f61f4 fix: unattended sudo via temporary NOPASSWD drop-in with triple cleanup
|
||||
|
||||
## Handoff Chain
|
||||
|
||||
- **Continues from**: None (fresh start)
|
||||
- **Supersedes**: None
|
||||
|
||||
> This is the first handoff for this task.
|
||||
|
||||
## Current State Summary
|
||||
|
||||
Bootstrapped `~/bin/mac` from scratch: scaffolded the project, SSH'd into Seth's new MacBook Pro (M1 Pro, 32GB, macOS 26.2 Tahoe), brainstormed + specced + planned a debloat/Linux-feel/Ableton-first setup, then implemented and RAN it end-to-end. `scripts/run.sh` applied cleanly (exit 0, idempotent on re-run), and the tank backup path is proven working with rrsync jailing + sanoid history. Remaining work is entirely Seth's GUI-only manual checklist and installing Ableton Live — no more code needed to ship the base setup.
|
||||
|
||||
## Codebase Understanding
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
Managed over SSH from steel141: `scripts/run.sh` rsyncs the repo to `~/mac` on the Mac, installs a TEMPORARY NOPASSWD sudoers drop-in (so Homebrew's long, child-spawning install has sudo), runs `scripts/setup.sh` there, removes the drop-in, pulls `defaults` backups to `.backup/mac/`. `setup.sh` is idempotent: each section prints `[skip]` or `[set]`. Config files (bashrc, kitty.conf, ssh_homelab) live in `config/` and are *included* by marker lines in the real dotfiles, so editing the repo edits the machine. Backup = launchd nightly rsync of `~/Music/Ableton`+`~/Documents` to `pve173:/tank/backups/mac`, key rrsync-jailed, sanoid `tank_media` template for history.
|
||||
|
||||
### Critical Files
|
||||
|
||||
| File | Purpose | Relevance |
|
||||
|------|---------|-----------|
|
||||
| `scripts/run.sh` | steel141 entry point (rsync+sudo drop-in+setup+cleanup) | run as `scripts/run.sh [--no-tank]`; needs `$HOMELAB_PASSWORD` |
|
||||
| `scripts/setup.sh` | on-Mac idempotent configurator | 2nd run = all `[skip]` |
|
||||
| `scripts/_install_sudoers.sh` | installs temp NOPASSWD drop-in (validated) | password piped to stdin |
|
||||
| `scripts/backup.sh` | nightly rsync to tank | `/opt/homebrew/bin/rsync`, rrsync remote |
|
||||
| `scripts/tank-side.sh` | pve173: dataset+sanoid+rrsync key | PUB via env, not arg |
|
||||
| `docs/manual-checklist.md` | GUI-only Tahoe steps for Seth | the remaining work |
|
||||
| `docs/reference/inventory-2026-09-15.md` | pre-change baseline | "was X stock?" |
|
||||
|
||||
### Key Patterns Discovered
|
||||
|
||||
- `set -euo pipefail` everywhere; optional/cosmetic steps MUST be `|| warn` or they abort the run (learned the hard way — SparkFun + launchctl bootstrap).
|
||||
- SSH runs on macOS are NON-login: `source ~/.bash_profile` before expecting GNU PATH.
|
||||
- ssh Include files must be `chmod 600` (rsync -a brings over group-writable 664, which ssh rejects).
|
||||
- Passing args through `ssh host 'cmd' -- "$X"` re-splits on spaces remotely; pass via env instead.
|
||||
- `defaults`/`pmset` backed up to `~/.mac-setup-backup/` before writes; pulled to `.backup/mac/`.
|
||||
|
||||
## Work Completed
|
||||
|
||||
### Tasks Finished
|
||||
|
||||
- [x] Project scaffolded, repo `git.sethpc.xyz/Seth/mac`, `ssh mac` alias
|
||||
- [x] Inventory captured (pre-change)
|
||||
- [x] Design spec + implementation plan written
|
||||
- [x] Brewfile, setup.sh, run.sh, backup.sh, tank-side.sh, checklist written
|
||||
- [x] `run.sh` applied on the Mac (exit 0), verified idempotent
|
||||
- [x] Linux feel verified: bash 5.3.20, GNU sed, `ip`, hostname `mac`, tools in /opt/homebrew
|
||||
- [x] DAW power profile, Dock=2 apps, defaults all applied
|
||||
- [x] Tank backup proven: rrsync restriction enforced, upload + `--delete` + sanoid snapshots working
|
||||
|
||||
### Files Modified
|
||||
|
||||
| File | Changes | Rationale |
|
||||
|------|---------|-----------|
|
||||
| [no modified files detected] | | |
|
||||
|
||||
### Decisions Made
|
||||
|
||||
| Decision | Options Considered | Rationale |
|
||||
|----------|-------------------|-----------|
|
||||
| Temp NOPASSWD sudoers over pty password injection | Seth chose it; pty inject raced & hung 14 min | see DECISIONS.md |
|
||||
| rrsync-jailed backup key | laptop key must not be unrestricted root on tank | enforced & proven |
|
||||
| bash5 / no Cmd-swap / Rectangle / rsync-not-TimeMachine | Linux feel + DAW safety | DECISIONS.md |
|
||||
|
||||
## Pending Work
|
||||
|
||||
### Immediate Next Steps
|
||||
|
||||
1. Hand Seth `docs/manual-checklist.md` — the GUI-only steps (iCloud Drive Desktop&Documents OFF, DAW Focus, app first-launch grants for Rectangle/kitty/Tailscale, Screen Time/Handoff off).
|
||||
2. Install Ableton Live 12 from ableton.com, then re-run `scripts/run.sh --no-tank` so the Dock picks up Live (setup.sh only adds it if `/Applications/Ableton Live 12*.app` exists).
|
||||
3. Once an audio interface is attached: set it in Live > Settings > Audio (48kHz/128 buffer), verify no sleep/notifications during a 15-min AC session with DAW Focus on. Also unresolved: Traktor S4 MK1 macOS support (NI dropped it years ago) — verify before assuming it works as a Live controller.
|
||||
|
||||
### Blockers/Open Questions
|
||||
|
||||
- [ ] None blocking. Open: S4 MK1 macOS controller support unverified; audio interface not yet chosen/attached.
|
||||
- [ ] FileVault is ON — after any reboot, `ssh mac` is dead until someone types the password at the lid.
|
||||
|
||||
### Deferred Items
|
||||
|
||||
- SparkFun PPP service left (macOS refuses removal of sole service on a port; harmless).
|
||||
- Tailscale not yet logged in (GUI). AeroSpace tiling deferred (Rectangle chosen).
|
||||
- Suite/third-party sample packs: in-app + SMB copy from Milkie, on the checklist.
|
||||
|
||||
## Context for Resuming Agent
|
||||
|
||||
### Important Context
|
||||
|
||||
The base setup is DONE and verified — do NOT re-run destructively or reconfigure. `scripts/run.sh` is idempotent and safe to re-run (it re-installs/removes the temp sudoers drop-in each time). Everything left is Seth's GUI checklist + Ableton install. If `ssh mac` fails, first suspect a reboot (FileVault locks SSH until password entry at the lid), not a config problem. Never leave `/etc/sudoers.d/mac-setup` behind — run.sh removes it three ways, but if a run is force-killed mid-way, verify with `ssh mac 'test -e /etc/sudoers.d/mac-setup && echo BAD || echo ok'`.
|
||||
|
||||
### Assumptions Made
|
||||
|
||||
- Seth is physically at the Mac (enabled Remote Login mid-session).
|
||||
- `$HOMELAB_PASSWORD` is the Mac's login/sudo password (verified — it worked).
|
||||
- The Mac stays on AC while producing (power profile only changes AC, not battery).
|
||||
|
||||
### Potential Gotchas
|
||||
|
||||
- `pkill -f 'ssh ... mac'` will kill your OWN session (matched my command line once — cost a retry).
|
||||
- SSH is non-login: GNU tools/PATH need `source ~/.bash_profile`.
|
||||
- A fast test passing does NOT mean it's reliable — the pty password inject passed a 20s test then hung 14 min live.
|
||||
|
||||
## Environment State
|
||||
|
||||
### Tools/Services Used
|
||||
|
||||
- `ssh mac` (192.168.0.94, key auth), `ssh pve173` (tank host). Homebrew at /opt/homebrew.
|
||||
- backup launchd: `~/Library/LaunchAgents/xyz.sethpc.mac-backup.plist` (03:30 nightly).
|
||||
|
||||
### Active Processes
|
||||
|
||||
- Backup launchd agent loaded (idle until 03:30). sanoid.timer active on pve173.
|
||||
- No leftover background jobs from this session (drop-in removed, self-destruct sleepers killed).
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- `HOMELAB_PASSWORD` (in claude's env on steel141; used by run.sh and cleanup).
|
||||
|
||||
## Related Resources
|
||||
|
||||
- `docs/plans/2026-09-15-mac-setup-design.md` (spec), `docs/plans/2026-09-15-mac-setup-plan.md` (plan)
|
||||
- `docs/manual-checklist.md` (remaining work), `docs/reference/inventory-2026-09-15.md` (baseline)
|
||||
- `DECISIONS.md` (why), `~/bin/ableton/` (Wine attempt archive, sibling project)
|
||||
|
||||
---
|
||||
|
||||
**Security Reminder**: Before finalizing, run `validate_handoff.py` to check for accidental secret exposure.
|
||||
@@ -0,0 +1,121 @@
|
||||
# Handoff: kitty titlebar fix (mac)
|
||||
|
||||
## Session Metadata
|
||||
- Created: 2026-09-15 20:03:54
|
||||
- Project: /home/claude/bin/mac
|
||||
- Branch: master
|
||||
- Session duration: ~10 min
|
||||
|
||||
### Recent Commits (for context)
|
||||
- 3399829 fix(kitty): keep macOS titlebar — it's the only drag handle; color it to match bg
|
||||
- d86ede0 docs: session handoff — mac base setup shipped & verified
|
||||
- 617fc78 docs: reconcile CLAUDE.md/DECISIONS with what shipped (sudo approach, rrsync, set -e lessons)
|
||||
- d622cf6 fix: chmod 600 the ssh Include file (ssh rejects group-writable config)
|
||||
- d7205a9 fix: pass pubkey to tank-side.sh via env, not ssh command arg
|
||||
|
||||
## Handoff Chain
|
||||
|
||||
- **Continues from**: [2026-09-15-194209-mac-setup-shipped.md](./2026-09-15-194209-mac-setup-shipped.md)
|
||||
- Previous title: kitty titlebar fix (mac)
|
||||
- **Supersedes**: [list any older handoffs this replaces, or "None"]
|
||||
|
||||
> Review the previous handoff for full context before filling this one.
|
||||
|
||||
## Current State Summary
|
||||
|
||||
Short follow-up to the base-setup session. Seth reported kitty on the MacBook had no title bar and no way to move the window. Root cause: `config/kitty.conf` carried `hide_window_decorations titlebar-only` (from the plan) plus a leftover `hide_window_decorations yes` inherited verbatim from `~/bin/kitty-web/config/kitty.conf` (where Xpra provides the frame). On macOS the title bar is the only drag handle; Rectangle snaps but doesn't free-drag. Fixed by deleting the `yes` line and replacing `titlebar-only` with `macos_titlebar_color background` so the bar stays but is painted #0a0a0a. Synced to the Mac, committed, pushed. Seth still needs to Cmd+Q/relaunch kitty (decoration changes don't hot-reload). Everything from the previous handoff's Pending Work is still pending and unchanged.
|
||||
|
||||
## Codebase Understanding
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
Unchanged from previous handoff. Relevant slice: `config/kitty.conf` in the repo is *included* by `~/.config/kitty/kitty.conf` on the Mac, and the repo is rsynced to `~/mac/` — so a one-file fix is `rsync -a config/kitty.conf mac:mac/config/kitty.conf` (no need for the full `run.sh`, which installs/removes the temp sudoers drop-in).
|
||||
|
||||
### Critical Files
|
||||
|
||||
| File | Purpose | Relevance |
|
||||
|------|---------|-----------|
|
||||
| `config/kitty.conf` | kitty config, included on the Mac | macOS section at the bottom; last-write-wins, so watch for Linux leftovers above it |
|
||||
| `config/.backup/kitty.conf.*` | pre-edit copy | per global backup rule |
|
||||
| `docs/manual-checklist.md` | Seth's GUI steps | still the remaining work |
|
||||
|
||||
### Key Patterns Discovered
|
||||
|
||||
- `config/kitty.conf` was copied from `~/bin/kitty-web/` — audit any other Linux/Xpra-specific lines before assuming they make sense on macOS.
|
||||
- `kill -USR1 $(pgrep -x kitty)` reloads kitty config remotely, but window-decoration options only apply on relaunch.
|
||||
|
||||
## Work Completed
|
||||
|
||||
### Tasks Finished
|
||||
|
||||
- [x] Diagnosed missing titlebar -> `hide_window_decorations`
|
||||
- [x] Fixed `config/kitty.conf`, rsynced to Mac, SIGUSR1 reload sent
|
||||
- [x] Committed + pushed (`fix(kitty): keep macOS titlebar ...`)
|
||||
|
||||
### Files Modified
|
||||
|
||||
| File | Changes | Rationale |
|
||||
|------|---------|-----------|
|
||||
| [no modified files detected] | | |
|
||||
|
||||
### Decisions Made
|
||||
|
||||
| Decision | Options Considered | Rationale |
|
||||
|----------|-------------------|-----------|
|
||||
| Keep titlebar, paint it `background` | (a) `hide_window_decorations no` stock bar; (b) keep hidden + global `NSWindowShouldDragOnGesture` (Ctrl+Cmd+drag anywhere); (c) chosen | (c) keeps drag + traffic lights with a flat look; (b) is a global `defaults` change on the DAW box — untested against Live's modifier-drag shortcuts, so deferred |
|
||||
|
||||
## Pending Work
|
||||
|
||||
### Immediate Next Steps
|
||||
|
||||
1. Seth: Cmd+Q and relaunch kitty on the Mac; confirm the bar is there and matches the bg.
|
||||
2. Resume previous handoff's list: hand Seth `docs/manual-checklist.md`, install Ableton Live 12, re-run `scripts/run.sh --no-tank` so the Dock picks up Live.
|
||||
3. Audio interface + 15-min AC session test with DAW Focus on (see previous handoff).
|
||||
|
||||
### Blockers/Open Questions
|
||||
|
||||
- [ ] None new. Carried: S4 MK1 macOS controller support unverified; FileVault ON kills `ssh mac` after reboot until password typed at lid.
|
||||
|
||||
### Deferred Items
|
||||
|
||||
- `defaults write -g NSWindowShouldDragOnGesture -bool true` (Linux Alt+drag equivalent) — only if Seth wants the bar gone again; global, so verify against Live first and add it to `setup.sh` with a defaults backup, not by hand.
|
||||
- Carried from previous: SparkFun PPP service, Tailscale GUI login, AeroSpace tiling.
|
||||
|
||||
## Context for Resuming Agent
|
||||
|
||||
### Important Context
|
||||
|
||||
This session changed ONE repo file and nothing else on the Mac. Read `2026-09-15-194209-mac-setup-shipped.md` for the real state of the machine — base setup is DONE and verified; do not reconfigure. If Seth reports kitty still has no bar, he hasn't relaunched it yet (reload via SIGUSR1 does not apply decoration changes). If `ssh mac` fails, suspect a FileVault reboot first.
|
||||
|
||||
### Assumptions Made
|
||||
|
||||
- kitty 0.48.2 on the Mac honours `macos_titlebar_color background` (option has existed for years; not visually verified from steel141).
|
||||
- Seth wants a movable window more than a bar-less look (he reported the missing bar as a problem, not the bar as noise).
|
||||
|
||||
### Potential Gotchas
|
||||
|
||||
- kitty config is last-write-wins: a Linux-section `hide_window_decorations yes` silently overrides nothing only because a later macOS line existed. Check for other duplicated keys before editing.
|
||||
- Don't run full `run.sh` for a config-only tweak; rsync the file.
|
||||
|
||||
## Environment State
|
||||
|
||||
### Tools/Services Used
|
||||
|
||||
- `ssh mac` (192.168.0.94, key auth). kitty 0.48.2 via Homebrew cask. `gitea push`.
|
||||
|
||||
### Active Processes
|
||||
|
||||
- kitty running on the Mac (needs relaunch). Backup launchd agent + sanoid unchanged from previous handoff.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- None used this session. (`HOMELAB_PASSWORD` still needed for `run.sh`.)
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Previous handoff: `.claude/handoffs/2026-09-15-194209-mac-setup-shipped.md` (authoritative for machine state)
|
||||
- `DECISIONS.md`, `docs/manual-checklist.md`, `docs/plans/2026-09-15-mac-setup-plan.md` (Task 1 step 3 is where `titlebar-only` came from)
|
||||
|
||||
---
|
||||
|
||||
**Security Reminder**: Before finalizing, run `validate_handoff.py` to check for accidental secret exposure.
|
||||
@@ -0,0 +1,135 @@
|
||||
# Handoff: ssh aliases + two-key split (mac)
|
||||
|
||||
## Session Metadata
|
||||
- Created: 2026-09-15 20:31:33
|
||||
- Project: /home/claude/bin/mac
|
||||
- Branch: master
|
||||
- Session duration: ~30 min
|
||||
|
||||
### Recent Commits (for context)
|
||||
- 2028508 feat(ssh): full homelab alias set on the Mac + separate interactive key
|
||||
- 43877eb docs(checklist): kitty needs the Local Network grant — else LAN ssh = No route to host
|
||||
- dc29b7a docs: session handoff — kitty titlebar fix
|
||||
- 3399829 fix(kitty): keep macOS titlebar — it's the only drag handle; color it to match bg
|
||||
- d86ede0 docs: session handoff — mac base setup shipped & verified
|
||||
|
||||
## Handoff Chain
|
||||
|
||||
- **Continues from**: [2026-09-15-200354-kitty-titlebar-fix.md](./2026-09-15-200354-kitty-titlebar-fix.md)
|
||||
- Previous title: kitty titlebar fix (mac)
|
||||
- **Supersedes**: None
|
||||
|
||||
> Review the previous handoff for full context before filling this one.
|
||||
|
||||
## Current State Summary
|
||||
|
||||
Seth asked for ssh aliases on the Mac. `config/ssh_homelab` already existed with 6 hosts, but the Mac's only key (`id_ed25519`) is rrsync-jailed on pve173, so `ssh pve173` would have hit the jail and nothing else was authorized. Shipped: the curated alias set from `~/bin/CLAUDE.md` (pve nodes, machines, key CTs, switch, tailscale hosts), a second key `id_ed25519_homelab` for interactive use (`Host *` default), `backup.sh` pinned to the jailed key, and `scripts/authorize-mac-key.sh` (runs on steel141, pushes the interactive pubkey via claude's aliases, wired into `run.sh`). Applied without a full `run.sh`: rsync + keygen over ssh + the authorize script. 12 aliases verified from the Mac as the right user@host; jail proven with `id`; `backup.sh` ran clean. Five hosts were offline and did not get the key. Committed + pushed (`2028508`).
|
||||
|
||||
## Codebase Understanding
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
- `~/.ssh/config` on the Mac is ONE line: `Include ~/mac/config/ssh_homelab`. The repo file is the whole config; `Host *` at its bottom is the default block (first-obtained-wins semantics).
|
||||
- Two-key design: `id_ed25519` (seth@mac) = backup only, jailed by `restrict,command="/usr/bin/rrsync /tank/backups/mac"` on pve173 line 19; `id_ed25519_homelab` = interactive, unrestricted everywhere. sshd matches the FIRST authorized_keys line per key, so a single key cannot serve both roles on pve173.
|
||||
- Proxmox nodes share `/etc/pve/priv/authorized_keys` (root's `~/.ssh/authorized_keys` is a symlink) — one append covers all four nodes; that's why the push printed `[set] pve112` then `[skip]` for the rest.
|
||||
|
||||
### Critical Files
|
||||
|
||||
| File | Purpose | Relevance |
|
||||
|------|---------|-----------|
|
||||
| `config/ssh_homelab` | all aliases + `Host *` defaults | edit here, then `rsync -a config/ssh_homelab mac:mac/config/`; must stay mode 600 (ssh refuses a group-writable Include) |
|
||||
| `scripts/authorize-mac-key.sh` | pushes the interactive pubkey to LAN hosts from steel141 | idempotent; re-run for hosts that were offline |
|
||||
| `scripts/backup.sh` | nightly rsync | `-o IdentitiesOnly=yes -i $HOME/.ssh/id_ed25519` pins the jailed key; do not drop it |
|
||||
| `scripts/setup.sh` | generates both keys | `[skip]` on rerun |
|
||||
| `DECISIONS.md` | two-key rationale, alias-set scope, `bedroom`=root | |
|
||||
|
||||
### Key Patterns Discovered
|
||||
|
||||
- Config-only tweaks: rsync the file, don't run `run.sh` (it installs/removes a temp sudoers drop-in).
|
||||
- Remote one-liners take data via env in the command string (`PUB='$PUB' H=$H bash -s` with the script on stdin) — same trick as `tank-side.sh`; avoids ssh re-splitting the key line.
|
||||
- `rrsync` whitelists `true` as a connectivity ping (`if command == 'true': sys.exit(0)`). A jail check must use `id`, not `true` — `true` returning 0 looked like a broken jail and cost a diagnosis loop.
|
||||
|
||||
## Work Completed
|
||||
|
||||
### Tasks Finished
|
||||
|
||||
- [x] `config/ssh_homelab`: full curated alias set, `Host *` -> `id_ed25519_homelab`, `StrictHostKeyChecking accept-new`
|
||||
- [x] `id_ed25519_homelab` generated on the Mac (setup.sh now does this; ran the same keygen by hand)
|
||||
- [x] `backup.sh` pins the jailed key; verified jail (`id` refused) and backup run (exit 0, `Documents` on tank)
|
||||
- [x] `scripts/authorize-mac-key.sh` written, run: key on all 4 pve nodes, vdj-rig, caddy, rtorrent, emby, mcsmanager, pihole, arr, steel141 (seth)
|
||||
- [x] 12 aliases verified from the Mac (`ssh <alias> 'whoami@hostname'`)
|
||||
- [x] DECISIONS.md + CLAUDE.md updated; committed + pushed
|
||||
|
||||
### Files Modified
|
||||
|
||||
| File | Changes | Rationale |
|
||||
|------|---------|-----------|
|
||||
| [no modified files detected] | | |
|
||||
|
||||
### Decisions Made
|
||||
|
||||
| Decision | Options Considered | Rationale |
|
||||
|----------|-------------------|-----------|
|
||||
| Second key rather than re-keying the backup | (a) rename existing key to `_backup` + regenerate default; (b) add `id_ed25519_homelab` | (b): no migration, no change to `run.sh`'s tank-side push, server side untouched |
|
||||
| `bedroom` alias user `seth` -> `root` | keep `seth` (previous session, undocumented) | matches steel141's alias + CLAUDE.md table; avoids a sudo special-case in the push. One-word revert if Seth wants `seth` |
|
||||
| Alias scope = CLAUDE.md curated table, not claude's full `~/.ssh/config` | mirror everything | nixos/thinkpad-nano/mortdec-auth/vast are stale or claude-only |
|
||||
| `StrictHostKeyChecking accept-new` (not `no`) | `no` like steel141 | TOFU still detects later key changes; same zero-prompt first connect |
|
||||
|
||||
## Pending Work
|
||||
|
||||
### Immediate Next Steps
|
||||
|
||||
1. When bedroom / thinkcentre / seth-pi / pinail / openclaw2 are powered on: `scripts/authorize-mac-key.sh` from steel141 (idempotent, prints `[set]`/`[skip]`/`[FAIL]` per host).
|
||||
2. Confirm with Seth that `bedroom` as root is what he wants from the Mac.
|
||||
3. Carried from previous handoffs: Seth relaunches kitty; `docs/manual-checklist.md`; install Ableton Live 12 then `scripts/run.sh --no-tank`; audio interface + AC session test.
|
||||
|
||||
### Blockers/Open Questions
|
||||
|
||||
- [ ] yoga / bebop aliases are dead until Tailscale is logged in on the Mac (manual-checklist item); their authorized_keys were not touched.
|
||||
- [ ] `switch` alias is password-only by design (TP-Link); needs `sshpass` on the Mac if Seth wants it non-interactive (not installed).
|
||||
|
||||
### Deferred Items
|
||||
|
||||
- Gitea ssh access from the Mac (`git@git.sethpc.xyz`) — not asked; would need the homelab pubkey added in Gitea, not authorized_keys.
|
||||
- Keys for rosy / matt-strix — other people's boxes, not Seth's call to make from here; aliases intentionally omitted.
|
||||
- Carried: SparkFun PPP service, Tailscale GUI login, AeroSpace, `NSWindowShouldDragOnGesture`.
|
||||
|
||||
## Context for Resuming Agent
|
||||
|
||||
### Important Context
|
||||
|
||||
Do NOT collapse the two keys back into one. If `ssh pve173` from the Mac ever lands in `rrsync error: ... does not run rsync`, the homelab key is missing from pve173's authorized_keys — run `authorize-mac-key.sh`, don't touch the jailed line. If `backup.sh` ever gets an unjailed root shell (test: `ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@192.168.0.173 id` must be REFUSED), the `-i` pin in backup.sh was lost. `true` is NOT a valid jail test — rrsync allows it. This session's machine changes: one new key on the Mac, `config/ssh_homelab` + `scripts/backup.sh` rsynced, pubkey appended on the hosts listed above, seth@steel141 authorized_keys appended via sudo.
|
||||
|
||||
### Assumptions Made
|
||||
|
||||
- Seth wants root on pve nodes/CTs and his own user on his machines (mirrors how claude's aliases are documented).
|
||||
- The five offline hosts are just powered down, not decommissioned (bedroom/thinkcentre/seth-pi/pinail/openclaw2 all still in CLAUDE.md).
|
||||
|
||||
### Potential Gotchas
|
||||
|
||||
- `rsync -a` carries file mode: `config/ssh_homelab` must be 600 locally or the Mac's ssh silently ignores the Include (`Bad owner or permissions`).
|
||||
- `authorize-mac-key.sh` uses claude's steel141 aliases, which have the SAME names as the Mac's — easy to confuse which side you're on. It only runs from steel141.
|
||||
- `sudo -u seth bash -c` sets HOME to /home/seth on steel141 (verified) — `~/.ssh` in the shared one-liner resolves correctly.
|
||||
|
||||
## Environment State
|
||||
|
||||
### Tools/Services Used
|
||||
|
||||
- `ssh mac` (192.168.0.94, key auth). OpenSSH 10.0p2 on the Mac. `gitea push`. `sudo -n` on steel141 (passwordless, used for seth's authorized_keys).
|
||||
|
||||
### Active Processes
|
||||
|
||||
- Nothing new. Backup launchd agent unchanged (next run 03:30 uses the pinned key — first unattended run with the new `-e` string; check `/tank/backups/mac` next day).
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- None used. (`HOMELAB_PASSWORD` still required for `run.sh`; `authorize-mac-key.sh` needs none.)
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Previous handoff: `.claude/handoffs/2026-09-15-200354-kitty-titlebar-fix.md` (kitty relaunch still pending) and `2026-09-15-194209-mac-setup-shipped.md` (machine state).
|
||||
- `DECISIONS.md` (two-key entry), `~/bin/CLAUDE.md` ssh alias table (source of the alias set), `scripts/tank-side.sh` (how the jail line is written).
|
||||
|
||||
---
|
||||
|
||||
**Security Reminder**: Before finalizing, run `validate_handoff.py` to check for accidental secret exposure.
|
||||
@@ -0,0 +1,137 @@
|
||||
# Handoff: tank automount on the Mac (autofs + SMB)
|
||||
|
||||
## Session Metadata
|
||||
- Created: 2026-09-15 22:24:55
|
||||
- Project: /home/claude/bin/mac
|
||||
- Branch: master
|
||||
- Session duration: ~35 min
|
||||
|
||||
### Recent Commits (for context)
|
||||
- c1558d2 feat(tank): autofs SMB mount of tank at /Volumes/tank
|
||||
- c204d99 docs: session handoff — ssh aliases + two-key split
|
||||
- 2028508 feat(ssh): full homelab alias set on the Mac + separate interactive key
|
||||
- 43877eb docs(checklist): kitty needs the Local Network grant — else LAN ssh = No route to host
|
||||
- dc29b7a docs: session handoff — kitty titlebar fix
|
||||
|
||||
## Handoff Chain
|
||||
|
||||
- **Continues from**: [2026-09-15-203133-mac-ssh-aliases.md](./2026-09-15-203133-mac-ssh-aliases.md)
|
||||
- Previous title: ssh aliases + two-key split (mac)
|
||||
- **Supersedes**: None
|
||||
|
||||
> Review the previous handoff for full context before filling this one.
|
||||
|
||||
## Current State Summary
|
||||
|
||||
Seth asked to "mount tank on the mac as a drive". Shipped `scripts/tank-automount.sh`: an autofs direct map (`/- auto_smb -nosuid` in `/etc/auto_master`) + root-owned 0600 `/etc/auto_smb` mounting `//Administrator@192.168.0.173/tank` (soft) at `/Volumes/tank`. Mounts on access, unmounts when idle — chosen over a Finder Login-Item mount because the laptop roams. Verified: mount works, writes land as root on tank, two consecutive all-`[skip]` reruns, wired into `run.sh`. The literal "as a drive" is NOT achievable this way: macOS marks all automounted fs `nobrowse` (confirmed in `man auto_master`), so it shows as a folder and needs a Finder Favorite (added to `docs/manual-checklist.md`). A Finder window on `/Volumes/tank` was opened on Seth's screen. Not yet verified across a reboot (FileVault means Seth must be at the lid). Committed + pushed.
|
||||
|
||||
## Codebase Understanding
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
- `run.sh` now has 3 on-Mac stages: `setup.sh` (temp NOPASSWD sudoers), `tank-automount.sh` (own `sudo -S -v` from the stdin password — needs no temp sudoers), then tank-side + `authorize-mac-key.sh` from steel141.
|
||||
- tank export facts (pve173): NFS `/tank *(rw,no_root_squash,insecure)`; Samba shares `[tank]` and `[Z]` (same path, `force user = root`, users `Administrator`/`octoprint`). steel141's seth uses `//173/Z` as Administrator; vdj-rig uses NFS ro with a systemd automount — the roaming pattern this copies.
|
||||
- The Mac's `seth` is uid 501, which is why NFS was rejected: writes would be an unknown uid and only 777 dirs are writable.
|
||||
|
||||
### Critical Files
|
||||
|
||||
| File | Purpose | Relevance |
|
||||
|------|---------|-----------|
|
||||
| `scripts/tank-automount.sh` | the whole feature; password on stdin | idempotent; see header comment for the three macOS gotchas |
|
||||
| `/etc/auto_smb` (on the Mac) | contains the SMB password, root 0600 | never copy into the repo or `.mac-setup-backup` |
|
||||
| `/etc/auto_master` (on the Mac) | direct-map line appended | pre-change copy in `~/.mac-setup-backup/auto_master-<ts>` on the Mac |
|
||||
| `docs/manual-checklist.md` | new "tank" section + fixed Gitea key + samples step | Seth's next GUI actions |
|
||||
| `DECISIONS.md` | autofs-vs-Finder-mount rationale; Finder mount in Deferred/Rejected | |
|
||||
|
||||
### Key Patterns Discovered
|
||||
|
||||
- macOS `automount -vc` does NOT create direct-map trigger dirs ("mountpoint unavailable", `getattrlist:fail:2`). `mkdir -p` first.
|
||||
- No-tty sudo ticket (`sudo -S -v` over ssh) is per parent pid: `sudo` in a pipeline or as a direct child works; `$(sudo cat ...)` silently fails. Cost a false "not idempotent".
|
||||
- `nobrowse` in a map's options and `nobrowse` on the resulting mount are different things (man page says so); there is no map option that makes an automount Finder-browsable.
|
||||
- Secrets to on-Mac scripts go via stdin (`printf | ssh mac 'bash script'`), never argv/env in the ssh command string; URL-encoding the password also went through python3 stdin, not argv.
|
||||
|
||||
## Work Completed
|
||||
|
||||
### Tasks Finished
|
||||
|
||||
- [x] `scripts/tank-automount.sh` written, applied, verified (mount, root write, idempotent reruns)
|
||||
- [x] `run.sh` runs it after `setup.sh`
|
||||
- [x] `docs/manual-checklist.md`: tank Favorite + reboot check; Gitea key -> `id_ed25519_homelab`; samples step now uses `/Volumes/tank`
|
||||
- [x] DECISIONS.md + CLAUDE.md updated; committed + pushed
|
||||
- [x] ~~Finder window opened on `/Volumes/tank` for Seth~~ — `open` over ssh did nothing visible; Seth given the Cmd+Shift+G path instead
|
||||
|
||||
### Files Modified
|
||||
|
||||
| File | Changes | Rationale |
|
||||
|------|---------|-----------|
|
||||
| [no modified files detected] | | |
|
||||
|
||||
### Decisions Made
|
||||
|
||||
| Decision | Options Considered | Rationale |
|
||||
|----------|-------------------|-----------|
|
||||
| autofs (roaming-safe, folder) over Finder Login-Item mount (drive icon, hard mount) | both; a custom LaunchAgent doing `mount_smbfs` was the third (rung-7) option | laptop leaves the LAN; hard mounts beachball Finder and throw login dialogs. Icon can be revisited |
|
||||
| SMB as Administrator over NFS | NFS is passwordless | uid 501 on the Mac; SMB `force user = root` matches how seth writes tank from steel141 |
|
||||
| `/Volumes/tank` as the path | `~/tank`, `/tank` via synthetic.conf | reads as a drive path; persistence across reboot is the open question — fallback is synthetic.conf `/tank` (needs a reboot) |
|
||||
| Password in `/etc/auto_smb` root 0600 | keychain (not honoured by automountd) | same class as steel141's `~/.smbcredentials`; never leaves the Mac |
|
||||
|
||||
## Pending Work
|
||||
|
||||
### Immediate Next Steps
|
||||
|
||||
1. After Seth's next reboot: `ssh mac 'mount | grep auto_smb'` must show the `/Volumes/tank` trigger. If `/Volumes/tank` vanished (boot-time /Volumes cleanup), switch the map to `/tank` via `/etc/synthetic.conf` (`tank` bare line) — needs another reboot.
|
||||
2. Seth: Cmd+Shift+G -> `/Volumes/tank`, drag `tank` from the window title into Favorites (checklist). He was told this at 22:40; not yet confirmed done. Ask whether the folder-not-drive result is acceptable or he wants the Locations icon (then: Finder Connect-to-Server + Login Item, documented as rejected-for-now in DECISIONS).
|
||||
3. Carried: `authorize-mac-key.sh` rerun for bedroom/thinkcentre/seth-pi/pinail/openclaw2; `bedroom`=root confirmation; kitty relaunch; Ableton install then `run.sh --no-tank`; audio interface test.
|
||||
|
||||
### Blockers/Open Questions
|
||||
|
||||
- [ ] Reboot persistence of the `/Volumes/tank` trigger dir — unverified (FileVault: needs Seth at the lid).
|
||||
- [ ] Idle-unmount timing not measured (macOS default `AUTOMOUNT_TIMEOUT` in `/etc/autofs.conf`, 3600 s); fine unless Seth notices.
|
||||
|
||||
### Deferred Items
|
||||
|
||||
- Real drive icon under Locations (Finder mount + Login Item) — rejected for now, see DECISIONS.
|
||||
- LaunchAgent-based `mount_smbfs` reconnect daemon — would give icon + roaming, but it's custom code for a problem autofs solves well enough.
|
||||
- Carried: SparkFun PPP, Tailscale login, AeroSpace, `NSWindowShouldDragOnGesture`, Gitea key upload.
|
||||
|
||||
## Context for Resuming Agent
|
||||
|
||||
### Important Context
|
||||
|
||||
`/etc/auto_smb` on the Mac holds the homelab password — never `cat` it into a transcript, never back it up into the repo or `~/.mac-setup-backup`. If tank "isn't mounting": `ls /Volumes/tank` is what triggers it (autofs is lazy); check `mount | grep tank` for the autofs trigger line first — if the trigger is missing, `automount -vc` failed (usually the dir is gone); if the trigger is there but smbfs isn't, it's network/creds (`log show --last 2m --predicate 'process == "automountd"'`). Re-running `printf '%s\n' "$HOMELAB_PASSWORD" | ssh mac 'bash ~/mac/scripts/tank-automount.sh'` is always safe. This session changed on the Mac: `/etc/auto_master` (+1 line), `/etc/auto_smb` (new), `/Volumes/tank` (dir), and opened one Finder window; nothing else.
|
||||
|
||||
### Assumptions Made
|
||||
|
||||
- Seth's Mac sudo password == `$HOMELAB_PASSWORD` (true: run.sh's cleanup relies on it) and the Samba `Administrator` password == the same (verified by the mount succeeding).
|
||||
- Seth values roaming-safety over the drive icon (inferred from "portable" use; not confirmed — item 2 in next steps).
|
||||
|
||||
### Potential Gotchas
|
||||
|
||||
- `/Volumes` has the `UF_HIDDEN` flag on every Mac — Finder never shows it when browsing Macintosh HD, and `/System/Volumes` (APFS Data/Preboot/VM) is unrelated. Seth hit exactly this ("I don't even see a /Volumes folder"). Path in: Cmd+Shift+G -> `/Volumes/tank`; in kitty `ls /Volumes/tank`.
|
||||
- `open /Volumes/tank` over ssh returned success but no Finder window appeared for Seth. Don't use `open` from ssh as a "show it to Seth" signal; tell him the path instead.
|
||||
- The Ableton Live 12 Trial Installer DMG was mounted at `/Volumes/Ableton Live 12 Trial Installer` during this session — Seth is mid-install; don't unmount/eject volumes wholesale.
|
||||
- `mount | grep tank` shows TWO lines (autofs trigger + smbfs) when mounted; only the trigger when idle. Both are normal.
|
||||
- `sed -i ""` (BSD form) is what works on the Mac even with GNU sed on PATH via gnubin — check `which sed` before scripting sed there.
|
||||
|
||||
## Environment State
|
||||
|
||||
### Tools/Services Used
|
||||
|
||||
- `ssh mac`; `/usr/sbin/automount`, `/usr/libexec/automountd`; `/usr/bin/python3` (Xcode CLT) used for URL-encoding; `gitea push`.
|
||||
|
||||
### Active Processes
|
||||
|
||||
- tank currently mounted at `/Volumes/tank` on the Mac (will idle-unmount). Finder window open on it. Backup launchd agent unchanged.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- `HOMELAB_PASSWORD` — piped on stdin to `tank-automount.sh` (and by `run.sh`); never in argv.
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Previous handoff: `.claude/handoffs/2026-09-15-203133-mac-ssh-aliases.md` (two-key ssh design, offline hosts list).
|
||||
- `man auto_master` on the Mac (nobrowse semantics); `/etc/autofs.conf` (timeouts); `~/bin/CLAUDE.md` Storage section (tank export facts, use .173 not .200).
|
||||
|
||||
---
|
||||
|
||||
**Security Reminder**: Before finalizing, run `validate_handoff.py` to check for accidental secret exposure.
|
||||
@@ -21,16 +21,21 @@ laptop" shape).
|
||||
|
||||
## Current State
|
||||
|
||||
- **Phase:** ideation -> design approved-ish; spec at `docs/plans/2026-09-15-mac-setup-design.md`
|
||||
- **Phase:** shipping — `scripts/run.sh` applied & verified 2026-09-15. Manual checklist (`docs/manual-checklist.md`) pending Seth; Ableton not yet installed.
|
||||
- **Repo:** `git.sethpc.xyz/Seth/mac`
|
||||
- **Deploy target:** the MacBook, `ssh mac` (192.168.0.94, user seth, en0 Wi-Fi)
|
||||
- **Hardware:** MacBookPro18,1 (16" 2021), M1 Pro, 32 GB, 512 GB. **macOS 26.2 Tahoe.**
|
||||
- Remote Login enabled + claude key installed 2026-09-15. `ssh mac` works from steel141.
|
||||
- **FileVault is on** — no SSH after a reboot until the password is typed at the lid.
|
||||
- Stock state is already clean: no Homebrew/CLT, only Safari in /Applications,
|
||||
Siri off, Apple Intelligence opted out. Full snapshot:
|
||||
`docs/reference/inventory-2026-09-15.md` — read when checking "was X stock?".
|
||||
- No changes made yet.
|
||||
- Applied: Homebrew + Brewfile (GNU userland, kitty, rectangle, tailscale-app),
|
||||
bash5 login shell, hostname `mac`, Linux-feel + debloat `defaults`, Dock=2 apps,
|
||||
DAW power profile (AC sleep 0 / powernap 0), nightly rsync backup agent.
|
||||
Pre-change inventory: `docs/reference/inventory-2026-09-15.md`.
|
||||
- Backup: `~/Music/Ableton` + `~/Documents` -> `pve173:/tank/backups/mac` nightly
|
||||
03:30 via launchd; key is rrsync-jailed; sanoid keeps history (tank_media template).
|
||||
- ssh aliases (`config/ssh_homelab`, included from `~/.ssh/config`) mirror the `~/bin/CLAUDE.md` set. Two keys: `id_ed25519` = backup (rrsync-jailed on pve173), `id_ed25519_homelab` = interactive. `scripts/authorize-mac-key.sh` (from steel141) pushes the interactive key; re-run it when an offline host comes back.
|
||||
- tank: autofs SMB at `/Volumes/tank` (`scripts/tank-automount.sh`, password on stdin). Mounts on access, `nobrowse` by macOS design → a Finder Favorite, not a Locations drive. Not yet verified across a reboot.
|
||||
- SparkFun PPP service left in place (macOS won't remove the sole service on a port; harmless).
|
||||
|
||||
## Conventions
|
||||
|
||||
@@ -40,3 +45,5 @@ laptop" shape).
|
||||
- Before any `launchctl disable`/`bootout`: record current `launchctl print` state to `.backup/`
|
||||
- Homebrew `Brewfile` at repo root is the package manifest — `brew bundle` is the install
|
||||
- macOS gotchas go in this file's Conventions; per-decision reasoning goes in `DECISIONS.md`
|
||||
- **Apply everything:** `scripts/run.sh` from steel141. It installs a TEMPORARY `/etc/sudoers.d/mac-setup` (NOPASSWD) via `_install_sudoers.sh` for the run and removes it on every exit path (+40-min on-Mac self-destruct backstop). Needs `$HOMELAB_PASSWORD`. Second run is all `[skip]`.
|
||||
- GUI-only steps live in `docs/manual-checklist.md` — read when something "didn't apply" (it's probably on that list).
|
||||
|
||||
@@ -16,10 +16,19 @@ Format: `YYYY-MM-DD: <decision> — <why>`
|
||||
- 2026-09-15: Spotlight indexing stays on, Siri/web suggestions off — Finder search is useful; Live's browser has its own index.
|
||||
- 2026-09-15: iCloud account stays, iCloud Drive Desktop&Documents sync OFF — cloud sync mid-save is a DAW project killer.
|
||||
|
||||
- 2026-09-15: **Unattended sudo = temporary `/etc/sudoers.d/mac-setup` (NOPASSWD), not pty password injection.** Piping the password into `ssh -tt` raced and hung 14 min on the real run (passed a fast test). The drop-in is validated before activation, removed on every run.sh exit path, and self-destructs on the Mac after 40 min. See run.sh / _install_sudoers.sh.
|
||||
- 2026-09-15: Mac's backup key on pve173 is **rrsync-restricted** (`command="/usr/bin/rrsync /tank/backups/mac"`) — a travelling laptop's key must never be unrestricted root on the tank host. Proven: an interactive command over that key is refused.
|
||||
- 2026-09-15: **Optional/cosmetic steps must be non-fatal under `set -e`.** The SparkFun removal (macOS refuses) and the GUI-domain `launchctl bootstrap` (flaky over SSH) both aborted the run until guarded with `|| warn`. Rule for this repo: anything that can legitimately fail without breaking the goal gets a best-effort guard.
|
||||
- 2026-09-15: **Two ssh keys on the Mac: `id_ed25519` (backup, rrsync-jailed) + `id_ed25519_homelab` (interactive, `Host *` default).** sshd honours the first matching authorized_keys line per key, so one key can't be both jailed and a shell on pve173. `backup.sh` pins the jailed key with `-i` + `IdentitiesOnly`; verify with `id`, not `true` (rrsync whitelists `true` as a ping). Key push = `scripts/authorize-mac-key.sh` from steel141 (uses claude's aliases; pve nodes share `/etc/pve/priv/authorized_keys`, so one write covers all four).
|
||||
- 2026-09-15: Mac ssh aliases mirror the curated set in `~/bin/CLAUDE.md` (not claude's full `~/.ssh/config`) — stale/undocumented entries (nixos, thinkpad-nano, mortdec-auth, vast) left out. `bedroom` = root, matching steel141's alias (was `seth`, undocumented).
|
||||
- 2026-09-15: **tank on the Mac = autofs + SMB (`Administrator`, = root on tank) at `/Volumes/tank`, via `scripts/tank-automount.sh`** — mounts on access, unmounts idle, `soft`: a laptop that leaves the LAN gets no hung Finder and no login-time "server unavailable" dialogs (same idea as vdj-rig's `x-systemd.automount`). SMB over NFS because the Mac's seth is uid 501: NFS would write as an unknown uid and only into the 777 dirs; SMB `force user = root` matches steel141's `/mnt/Z`. Cost: the password sits in root-owned `/etc/auto_smb` (0600) — same class as steel141's `~/.smbcredentials`. `automount` does not create direct-map trigger dirs, so the script `mkdir -p`s it. sudo inside `$(...)` loses the no-tty ticket — keep sudo in pipelines/direct calls.
|
||||
- 2026-09-15: `defaults displaysleep` on AC = 30 min, not 0 — display sleep doesn't stop Live's audio; only system `sleep` does.
|
||||
|
||||
## Deferred / Rejected
|
||||
<!-- Decisions NOT to do something are just as valuable -- prevents re-proposing rejected ideas -->
|
||||
- 2026-09-15: Cmd<->Ctrl global swap REJECTED — Live 12 and every Mac tutorial are Cmd-native; kitty already handles Ctrl+Shift+C/V. Karabiner per-app remaps are the fallback, not a global swap.
|
||||
- 2026-09-15: yabai REJECTED — useful features need partial SIP disable. SIP/Gatekeeper/FileVault all stay on.
|
||||
- 2026-09-15: Time Machine REJECTED — over SMB it's fragile and whole-disk; rsync + tank-side ZFS snapshot covers the actual need.
|
||||
- 2026-09-15: Removing `/System/Applications` apps REJECTED — sealed system volume; hide from Dock instead.
|
||||
- 2026-09-15: Finder "Connect to Server" + Login Item for tank REJECTED (for now) — it's the only way to get a real drive icon under Locations, but it hard-mounts: off-LAN logins throw a server dialog and a dropped network beachballs Finder for ~60 s. autofs mounts are always `nobrowse` (`man auto_master`), so the trade is drive-icon vs roaming-safe; roaming-safe won. Revisit if Seth wants the icon.
|
||||
- 2026-09-15: Samples/projects on the SMB share REJECTED — local disk only; SMB is a copy source. External SSD is the capacity upgrade path.
|
||||
|
||||
+1
-2
@@ -29,7 +29,6 @@ tab_title_max_length 20
|
||||
|
||||
# Window layout - no wasted space
|
||||
window_padding_width 2
|
||||
hide_window_decorations yes
|
||||
placement_strategy top-left
|
||||
|
||||
# Scrollback
|
||||
@@ -77,5 +76,5 @@ sync_to_monitor no
|
||||
|
||||
# --- macOS ---
|
||||
macos_option_as_alt yes
|
||||
hide_window_decorations titlebar-only
|
||||
macos_titlebar_color background # keep the titlebar (only drag handle on macOS), just paint it to match
|
||||
macos_quit_when_last_window_closed yes
|
||||
|
||||
+60
-3
@@ -1,5 +1,10 @@
|
||||
# Included from ~/.ssh/config. Key ~/.ssh/id_ed25519 is generated by setup.sh;
|
||||
# only pve173 is authorized so far (rrsync-restricted, for backup.sh).
|
||||
# Included from ~/.ssh/config (setup.sh adds the Include line). Two keys, both made by setup.sh:
|
||||
# ~/.ssh/id_ed25519_homelab interactive — authorized on the hosts below by scripts/authorize-mac-key.sh
|
||||
# ~/.ssh/id_ed25519 backup only — rrsync-jailed on pve173; backup.sh pins it with -i
|
||||
# They must stay separate: sshd honours the FIRST matching authorized_keys line per key, so one key
|
||||
# can't be both jailed (nightly rsync) and unrestricted (a shell) on pve173.
|
||||
|
||||
# --- Proxmox nodes ---
|
||||
Host pve173
|
||||
HostName 192.168.0.173
|
||||
User root
|
||||
@@ -12,12 +17,64 @@ Host pve197
|
||||
Host pve241
|
||||
HostName 192.168.0.241
|
||||
User root
|
||||
|
||||
# --- Machines ---
|
||||
Host steel141
|
||||
HostName 192.168.0.141
|
||||
User seth
|
||||
Host bedroom
|
||||
HostName 192.168.0.235
|
||||
User root
|
||||
Host vdj-rig
|
||||
HostName 192.168.0.143
|
||||
User seth
|
||||
Host thinkcentre
|
||||
HostName 192.168.0.211
|
||||
User seth
|
||||
Host seth-pi
|
||||
HostName 192.168.0.102
|
||||
User seth
|
||||
Host pinail
|
||||
HostName 192.168.0.203
|
||||
User pi
|
||||
# TP-Link switch: password only (no pubkey support) — $HOMELAB_PASSWORD
|
||||
Host switch
|
||||
HostName 192.168.0.250
|
||||
User admin
|
||||
PubkeyAuthentication no
|
||||
|
||||
# --- Key CTs ---
|
||||
Host caddy
|
||||
HostName 192.168.0.185
|
||||
User root
|
||||
Host openclaw2
|
||||
HostName 192.168.0.175
|
||||
User root
|
||||
Host rtorrent
|
||||
HostName 192.168.0.213
|
||||
User root
|
||||
Host emby
|
||||
HostName 192.168.0.215
|
||||
User root
|
||||
Host mcsmanager
|
||||
HostName 192.168.0.244
|
||||
User root
|
||||
Host pihole
|
||||
HostName 192.168.0.153
|
||||
User root
|
||||
Host arr
|
||||
HostName 192.168.0.158
|
||||
User root
|
||||
|
||||
# --- Tailscale-only (dead until Tailscale is logged in on the Mac — docs/manual-checklist.md) ---
|
||||
Host yoga
|
||||
HostName 100.113.203.99
|
||||
User seth
|
||||
Host bebop
|
||||
HostName 100.78.2.106
|
||||
User seth
|
||||
|
||||
Host *
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
IdentityFile ~/.ssh/id_ed25519_homelab
|
||||
StrictHostKeyChecking accept-new
|
||||
ServerAliveInterval 30
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0"><dict>
|
||||
<key>Label</key><string>xyz.sethpc.mac-backup</string>
|
||||
<key>ProgramArguments</key><array><string>/Users/seth/mac/scripts/backup.sh</string></array>
|
||||
<key>StartCalendarInterval</key><dict><key>Hour</key><integer>3</integer><key>Minute</key><integer>30</integer></dict>
|
||||
<key>StandardOutPath</key><string>/Users/seth/Library/Logs/mac-backup.log</string>
|
||||
<key>StandardErrorPath</key><string>/Users/seth/Library/Logs/mac-backup.log</string>
|
||||
</dict></plist>
|
||||
@@ -0,0 +1,37 @@
|
||||
# Manual checklist — GUI-only steps on macOS 26 Tahoe
|
||||
|
||||
Do these once, in order, after `scripts/run.sh` has completed. Tick and date.
|
||||
|
||||
## Apply the shell-level changes
|
||||
- [ ] Log out / log in once (key repeat, press-and-hold, scroll direction only fully apply to a fresh session).
|
||||
|
||||
## Cloud + privacy
|
||||
- [ ] System Settings > [your name] > iCloud > Drive (or "Saved to iCloud" > Drive): **Desktop & Documents Folders = OFF**. Keep account signed in.
|
||||
- [ ] System Settings > General > AirDrop & Handoff: Handoff OFF, AirDrop = Contacts Only.
|
||||
- [ ] System Settings > Screen Time: App & Website Activity OFF.
|
||||
- [ ] System Settings > Spotlight: untick Siri Suggestions and any web/store result types; "Help Apple Improve Search" OFF.
|
||||
- [ ] System Settings > Notifications: Allow notifications when mirroring/sharing OFF (keeps popups off a projector).
|
||||
- [ ] System Settings > Network: delete the stray **SparkFun Pro Micro** service if present (setup.sh can't — macOS refuses to remove the sole service on a PPP port). Harmless if left.
|
||||
|
||||
## DAW Focus
|
||||
- [ ] System Settings > Focus > "+" > Custom > name **DAW**, icon of your choice. Allowed notifications: none. "Share across devices" OFF. Toggle it from the menu-bar moon before a session.
|
||||
|
||||
## Apps needing a first-launch grant
|
||||
- [ ] Launch **Rectangle** once -> grant Accessibility when prompted. Set your snap keys.
|
||||
- [ ] Launch **kitty** once. Gatekeeper prompt -> Open. **Local Network prompt -> Allow** (or System Settings > Privacy & Security > Local Network > kitty ON). Symptom if missed: `ssh 192.168.0.x` says `No route to host` in kitty while Terminal works — macOS 15+ returns EHOSTUNREACH to LAN IPs for apps without the grant.
|
||||
- [ ] Launch **Tailscale** -> log in to the tailnet. Confirm `tailscale status` in kitty and that `ssh mac` from bebop resolves via MagicDNS later.
|
||||
- [ ] Gitea: `cat ~/.ssh/id_ed25519_homelab.pub` (the interactive key; `id_ed25519` is the rrsync-jailed backup key) -> https://git.sethpc.xyz/user/settings/keys -> Add Key. Then `git clone git@git.sethpc.xyz:Seth/mac.git` works from the Mac if you ever want to edit there.
|
||||
|
||||
## Ableton
|
||||
- [ ] ableton.com > account > download Live 12 (Suite/trial) -> install to /Applications -> authorize.
|
||||
- [ ] Re-run `scripts/run.sh --no-tank` from steel141 so the Dock picks up Live (the script only adds it if installed).
|
||||
- [ ] Live > Browser > Packs: download the Suite packs you want (they're part of the license — do not torrent them).
|
||||
- [ ] Third-party packs: copy from `/Volumes/tank/Downloads/Software/Milkie/` (tank automounts on access — see "tank" below) to `~/Music/Ableton/Samples/` (local disk — never run samples off SMB).
|
||||
- [ ] Live > Settings > Audio: once an interface is attached, pick it, 48 kHz, buffer 128 (raise if crackle). Test with the DAW Focus on and the lid open on AC for 15 min: no sleep, no notification.
|
||||
|
||||
## tank
|
||||
- [ ] Finder > **Cmd+Shift+G** > `/Volumes/tank` (this triggers the mount; `/Volumes` is a hidden folder — you won't find it by browsing Macintosh HD, and `/System/Volumes` is something else), then drag the `tank` folder from the path bar / window title into the sidebar **Favorites**. It will NOT appear under Locations as a drive: macOS marks every automounted share `nobrowse` (documented in `man auto_master`) — the Favorite is the one-click entry, and the share mounts on first click and unmounts when idle, so leaving the LAN never hangs Finder or throws login dialogs.
|
||||
- [ ] After the first reboot: `mount | grep auto_smb` in kitty should show the `/Volumes/tank` trigger. If it's gone, `/Volumes` got cleaned at boot — tell Claude; the fix is a `synthetic.conf` mountpoint instead.
|
||||
|
||||
## Backup
|
||||
- [ ] `ssh mac ~/mac/scripts/backup.sh` by hand once -> confirm `/tank/backups/mac/Ableton` appears on pve173 and `~/Library/Logs/mac-backup.log` exists.
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
# Installs a TEMPORARY /etc/sudoers.d/mac-setup (NOPASSWD for seth) so an unattended setup run
|
||||
# has sudo for Homebrew's long, child-spawning install. Reads the sudo password from stdin.
|
||||
# Called by run.sh, which removes the drop-in afterward; a 40-min self-destruct is the backstop.
|
||||
set -euo pipefail
|
||||
read -r SUDO_PW
|
||||
tmp=$(mktemp)
|
||||
printf 'seth ALL=(ALL) NOPASSWD: ALL\n' > "$tmp"
|
||||
sudo -S -p '' -v <<<"$SUDO_PW" # prime once from the piped password
|
||||
sudo visudo -cf "$tmp" # validate OFF to the side (bad sudoers breaks sudo)
|
||||
sudo install -m 440 -o root -g wheel "$tmp" /etc/sudoers.d/mac-setup
|
||||
rm -f "$tmp"
|
||||
nohup sudo bash -c 'sleep 2400; rm -f /etc/sudoers.d/mac-setup' >/dev/null 2>&1 & # survives ssh drop
|
||||
echo "[set] temp NOPASSWD drop-in installed (+40m self-destruct)"
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
# From steel141: put the Mac's interactive pubkey on every LAN host in config/ssh_homelab, via
|
||||
# claude's own aliases (same names, same target users). Idempotent; called by run.sh, safe alone.
|
||||
# steel141 itself is the one exception: seth's authorized_keys needs sudo from the claude account.
|
||||
set -euo pipefail
|
||||
PUB=$(ssh -o BatchMode=yes mac cat .ssh/id_ed25519_homelab.pub)
|
||||
[[ $PUB == ssh-ed25519\ AAAA* ]] || { echo "no ~/.ssh/id_ed25519_homelab on the Mac — run setup.sh first"; exit 1; }
|
||||
ADD='umask 077; mkdir -p ~/.ssh; if grep -qF "$PUB" ~/.ssh/authorized_keys 2>/dev/null; then echo "[skip] $H"; else echo "$PUB" >> ~/.ssh/authorized_keys; echo "[set] $H"; fi'
|
||||
for H in pve112 pve173 pve197 pve241 bedroom vdj-rig thinkcentre seth-pi pinail caddy openclaw2 rtorrent emby mcsmanager pihole arr; do
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=5 "$H" "PUB='$PUB' H=$H bash -s" <<<"$ADD" || echo "[FAIL] $H"
|
||||
done
|
||||
sudo -n -u seth env PUB="$PUB" H=steel141 bash -c "$ADD"
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/opt/homebrew/bin/bash
|
||||
# Nightly by launchd (config/xyz.sethpc.mac-backup.plist). Mirrors the DAW-relevant
|
||||
# dirs to tank. History comes from sanoid snapshots of tank/backups/mac on pve173,
|
||||
# which is what makes `--delete` safe.
|
||||
set -euo pipefail
|
||||
src=()
|
||||
for d in "$HOME/Music/Ableton" "$HOME/Documents"; do [[ -d $d ]] && src+=("$d"); done
|
||||
[[ ${#src[@]} -gt 0 ]] || { echo "nothing to back up yet"; exit 0; }
|
||||
# Remote path is relative to the rrsync root (/tank/backups/mac) set in root's authorized_keys on pve173.
|
||||
# -i pins the jailed key; the Host * default (id_ed25519_homelab) has a root shell on pve173 and must NOT be offered first.
|
||||
# Check the jail holds: ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@192.168.0.173 id -> "rrsync error: ... does not run rsync" (NOT `true`: rrsync whitelists it as a ping).
|
||||
exec /opt/homebrew/bin/rsync -a --delete -e "ssh -o BatchMode=yes -o IdentitiesOnly=yes -i $HOME/.ssh/id_ed25519" "${src[@]}" root@192.168.0.173:/
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
# From steel141: sync repo to the Mac, run setup there UNATTENDED, pull backups back, then tank side.
|
||||
# Usage: scripts/run.sh [--no-tank]
|
||||
#
|
||||
# Homebrew's install needs sudo for 10+ min across child processes. We grant a TEMPORARY
|
||||
# /etc/sudoers.d/mac-setup (NOPASSWD) for the run and guarantee its removal three ways:
|
||||
# 1. explicit removal after setup, 2. an EXIT trap (covers failures / a dropped ssh from here),
|
||||
# 3. a 40-min self-destruct on the Mac itself (covers steel141 losing the network).
|
||||
# $HOMELAB_PASSWORD is used once (piped, never in argv) to install the drop-in.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
[[ -n ${HOMELAB_PASSWORD:-} ]] || { echo "HOMELAB_PASSWORD must be set"; exit 1; }
|
||||
|
||||
rsync -a --delete --exclude .git --exclude .backup --exclude Brewfile.lock.json ./ mac:mac/
|
||||
|
||||
cleanup(){ printf '%s\n' "$HOMELAB_PASSWORD" | ssh -o BatchMode=yes mac "sudo -S -p '' rm -f /etc/sudoers.d/mac-setup 2>/dev/null; sudo -K 2>/dev/null" || true; }
|
||||
trap cleanup EXIT
|
||||
|
||||
# Install temp NOPASSWD drop-in (installer is a repo file, rsynced above; password piped to its stdin).
|
||||
printf '%s\n' "$HOMELAB_PASSWORD" | ssh -o BatchMode=yes mac 'bash ~/mac/scripts/_install_sudoers.sh'
|
||||
|
||||
# sudo is now passwordless on the Mac; run setup unattended (Homebrew + everything).
|
||||
ssh -o BatchMode=yes mac "HOSTNAME_WANT=mac ~/mac/scripts/setup.sh"
|
||||
|
||||
# tank autofs mount (reads the password on stdin: SMB credential + its own sudo -S).
|
||||
printf '%s\n' "$HOMELAB_PASSWORD" | ssh -o BatchMode=yes mac 'bash ~/mac/scripts/tank-automount.sh'
|
||||
|
||||
# Explicit removal + confirm; then disarm the trap so it doesn't double-run.
|
||||
cleanup; trap - EXIT
|
||||
ssh -o BatchMode=yes mac 'test ! -e /etc/sudoers.d/mac-setup && echo "[ok] temp sudoers removed" || echo "[WARN] temp sudoers STILL PRESENT"'
|
||||
|
||||
mkdir -p .backup/mac && rsync -a mac:.mac-setup-backup/ .backup/mac/
|
||||
scripts/authorize-mac-key.sh
|
||||
[[ ${1:-} == --no-tank ]] && exit 0
|
||||
PUB=$(ssh mac cat .ssh/id_ed25519.pub)
|
||||
ssh pve173 "PUB='$PUB' bash -s" < scripts/tank-side.sh
|
||||
echo "tank side done; test: ssh mac ~/mac/scripts/backup.sh"
|
||||
Executable
+156
@@ -0,0 +1,156 @@
|
||||
#!/bin/bash
|
||||
# setup.sh — run ON the Mac as seth, via scripts/run.sh from steel141.
|
||||
# Idempotent: every section prints [skip] when already in the desired state.
|
||||
set -euo pipefail
|
||||
|
||||
REPO="$HOME/mac"
|
||||
BK="$HOME/.mac-setup-backup"; TS=$(date +%s); mkdir -p "$BK"
|
||||
CHANGED=0
|
||||
|
||||
log(){ printf '\033[1;33m[%s]\033[0m %s\n' "$1" "$2"; }
|
||||
|
||||
# ---------- preflight ----------
|
||||
[[ $(uname -m) == arm64 && $(sw_vers -productVersion) == 26.* ]] || { echo "not the Mac this was written for"; exit 1; }
|
||||
[[ ${HOSTNAME_WANT:-} ]] || { echo "HOSTNAME_WANT=<name> required"; exit 1; }
|
||||
sudo -n true 2>/dev/null || { echo "passwordless sudo unavailable — run via scripts/run.sh (it installs a temp drop-in)"; exit 1; }
|
||||
|
||||
# ---------- helpers ----------
|
||||
backup_domain(){ # once per domain per run
|
||||
local d=$1; local f="$BK/${d//\//_}-$TS.plist"
|
||||
[[ -e $f || -e $f.absent ]] && return 0
|
||||
defaults export "$d" "$f" 2>/dev/null || : > "$f.absent"
|
||||
}
|
||||
setd(){ # setd <domain> <key> <bool|int|string> <value>
|
||||
local d=$1 k=$2 t=$3 v=$4 cur want=$4
|
||||
[[ $t == bool ]] && { [[ $v == true ]] && want=1 || want=0; }
|
||||
cur=$(defaults read "$d" "$k" 2>/dev/null || echo __unset__)
|
||||
if [[ $cur == "$want" ]]; then log skip "$d $k=$v"; return 0; fi
|
||||
backup_domain "$d"; defaults write "$d" "$k" "-$t" "$v"; log set "$d $k=$v"; CHANGED=1
|
||||
}
|
||||
install_marker(){ # install_marker <file> <line> — ensure a line exists in file (create if absent)
|
||||
local f=$1 line=$2
|
||||
if [[ -f $f ]] && grep -qxF "$line" "$f"; then log skip "$f has include"; return 0; fi
|
||||
[[ -f $f ]] && cp "$f" "$BK/$(basename "$f")-$TS"
|
||||
mkdir -p "$(dirname "$f")"; printf '%s\n' "$line" >> "$f"; log set "$f += $line"; CHANGED=1
|
||||
}
|
||||
|
||||
# ---------- brew ----------
|
||||
if [[ -x /opt/homebrew/bin/brew ]]; then log skip "homebrew installed"; else
|
||||
log set "installing homebrew (installs Xcode CLT headless first; 5-15 min)"
|
||||
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
CHANGED=1
|
||||
fi
|
||||
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||
if brew bundle check --file="$REPO/Brewfile" >/dev/null 2>&1; then log skip "brew bundle satisfied"; else
|
||||
log set "brew bundle"; brew bundle --file="$REPO/Brewfile"; CHANGED=1
|
||||
fi
|
||||
|
||||
# ---------- shell ----------
|
||||
grep -qx /opt/homebrew/bin/bash /etc/shells || { echo /opt/homebrew/bin/bash | sudo tee -a /etc/shells >/dev/null; log set "/etc/shells += homebrew bash"; }
|
||||
if [[ $(dscl . -read "/Users/$USER" UserShell | awk '{print $2}') == /opt/homebrew/bin/bash ]]; then log skip "login shell bash5"; else
|
||||
sudo chsh -s /opt/homebrew/bin/bash "$USER"; log set "login shell -> homebrew bash"; CHANGED=1
|
||||
fi
|
||||
install_marker "$HOME/.bash_profile" '[ -f ~/mac/config/bashrc ] && . ~/mac/config/bashrc # mac-setup'
|
||||
install_marker "$HOME/.config/kitty/kitty.conf" "include $HOME/mac/config/kitty.conf"
|
||||
|
||||
# ---------- hostname ----------
|
||||
for k in ComputerName LocalHostName HostName; do
|
||||
if [[ $(scutil --get $k 2>/dev/null || true) == "$HOSTNAME_WANT" ]]; then log skip "$k=$HOSTNAME_WANT"; else
|
||||
sudo scutil --set $k "$HOSTNAME_WANT"; log set "$k=$HOSTNAME_WANT"; CHANGED=1
|
||||
fi
|
||||
done
|
||||
|
||||
# ---------- ssh (homelab) ----------
|
||||
mkdir -p "$HOME/.ssh"; chmod 700 "$HOME/.ssh"
|
||||
if [[ -f $HOME/.ssh/id_ed25519 ]]; then log skip "ssh key exists"; else
|
||||
ssh-keygen -t ed25519 -N '' -C "seth@$HOSTNAME_WANT" -f "$HOME/.ssh/id_ed25519" >/dev/null; log set "generated ~/.ssh/id_ed25519"; CHANGED=1
|
||||
fi
|
||||
if [[ -f $HOME/.ssh/id_ed25519_homelab ]]; then log skip "ssh homelab key exists"; else # interactive key; see config/ssh_homelab header
|
||||
ssh-keygen -t ed25519 -N '' -C "seth@$HOSTNAME_WANT-homelab" -f "$HOME/.ssh/id_ed25519_homelab" >/dev/null; log set "generated ~/.ssh/id_ed25519_homelab"; CHANGED=1
|
||||
fi
|
||||
if [[ -f $HOME/.ssh/config ]] && grep -q '^Include ~/mac/config/ssh_homelab' "$HOME/.ssh/config"; then log skip "ssh config include"; else
|
||||
[[ -f $HOME/.ssh/config ]] && cp "$HOME/.ssh/config" "$BK/ssh_config-$TS"
|
||||
{ echo 'Include ~/mac/config/ssh_homelab'; [[ -f $HOME/.ssh/config ]] && cat "$HOME/.ssh/config"; } > "$HOME/.ssh/config.new"
|
||||
mv "$HOME/.ssh/config.new" "$HOME/.ssh/config"; chmod 600 "$HOME/.ssh/config"; log set "ssh config include"; CHANGED=1
|
||||
fi
|
||||
chmod 600 "$REPO/config/ssh_homelab" 2>/dev/null || true # ssh refuses a group/world-writable Include target
|
||||
ssh-keygen -F 192.168.0.173 >/dev/null || { ssh-keyscan -t ed25519 192.168.0.173 >> "$HOME/.ssh/known_hosts" 2>/dev/null; log set "known_hosts += pve173"; }
|
||||
|
||||
# ---------- defaults: Linux feel ----------
|
||||
G=NSGlobalDomain
|
||||
setd $G KeyRepeat int 2
|
||||
setd $G InitialKeyRepeat int 15
|
||||
setd $G ApplePressAndHoldEnabled bool false # hold-key repeats instead of accent popup
|
||||
setd $G com.apple.swipescrolldirection bool false # "natural" scrolling off
|
||||
setd $G NSAutomaticWindowAnimationsEnabled bool false
|
||||
setd $G AppleShowAllExtensions bool true
|
||||
F=com.apple.finder
|
||||
setd $F ShowPathbar bool true
|
||||
setd $F ShowStatusBar bool true
|
||||
setd $F _FXShowPosixPathInTitle bool true
|
||||
setd $F FXPreferredViewStyle string Nlsv # list view
|
||||
setd $F NewWindowTarget string PfHm # new windows open at ~
|
||||
setd $F NewWindowTargetPath string "file://$HOME/"
|
||||
setd com.apple.desktopservices DSDontWriteNetworkStores bool true
|
||||
setd com.apple.desktopservices DSDontWriteUSBStores bool true
|
||||
|
||||
# ---------- defaults: debloat / Dock ----------
|
||||
D=com.apple.dock
|
||||
setd $D autohide bool true
|
||||
setd $D autohide-delay int 0
|
||||
setd $D show-recents bool false
|
||||
setd $D tilesize int 48
|
||||
for c in tl tr bl br; do setd $D wvous-$c-corner int 1; done # 1 = no action (stock br = Quick Note)
|
||||
# Dock apps: only what exists. Finder + Trash are implicit.
|
||||
dock_want=()
|
||||
for app in /Applications/kitty.app "/Applications/Ableton Live 12"*.app "/System/Applications/System Settings.app"; do
|
||||
[[ -d $app ]] && dock_want+=("file://${app// /%20}/")
|
||||
done
|
||||
dock_cur=$(defaults read $D persistent-apps 2>/dev/null | grep -oE '_CFURLString" = "[^"]+' | sed 's/.*= "//' | tr '\n' ' ' || true)
|
||||
if [[ "$dock_cur" == "${dock_want[*]} " ]]; then log skip "dock apps"; else
|
||||
backup_domain $D
|
||||
defaults write $D persistent-apps -array
|
||||
for u in "${dock_want[@]}"; do
|
||||
defaults write $D persistent-apps -array-add "<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>$u</string><key>_CFURLStringType</key><integer>15</integer></dict></dict><key>tile-type</key><string>file-tile</string></dict>"
|
||||
done
|
||||
log set "dock apps = ${dock_want[*]}"; CHANGED=1
|
||||
fi
|
||||
|
||||
# ---------- stray network service (best-effort; never fatal) ----------
|
||||
# macOS refuses -removenetworkservice for the sole service on a PPP port ("parameters were not
|
||||
# valid"), so this may not remove it. It's a harmless disconnected entry — warn and move on.
|
||||
if networksetup -listallnetworkservices | grep -qx 'SparkFun Pro Micro'; then
|
||||
if sudo networksetup -removenetworkservice 'SparkFun Pro Micro' 2>/dev/null; then
|
||||
log set "removed SparkFun Pro Micro PPP service"; CHANGED=1
|
||||
else
|
||||
log warn "could not remove SparkFun PPP service (macOS refuses; harmless — remove via Settings > Network if desired)"
|
||||
fi
|
||||
else log skip "no SparkFun service"; fi
|
||||
|
||||
# ---------- power (DAW) — AC profile only ----------
|
||||
pm_cur=$(pmset -g custom | awk '/AC Power/{f=1;next} /Battery Power/{f=0} f && $1 ~ /^(sleep|displaysleep|disksleep|powernap)$/{printf "%s=%s ", $1, $2}')
|
||||
if [[ $pm_cur == *"sleep=0 "* && $pm_cur == *"displaysleep=30 "* && $pm_cur == *"disksleep=0 "* && $pm_cur == *"powernap=0 "* ]]; then log skip "pmset AC profile"; else
|
||||
pmset -g custom > "$BK/pmset-$TS.txt"
|
||||
sudo pmset -c sleep 0 displaysleep 30 disksleep 0 powernap 0; log set "pmset -c sleep 0 displaysleep 30 disksleep 0 powernap 0"; CHANGED=1
|
||||
fi
|
||||
setd com.ableton.live NSAppSleepDisabled bool true # App Nap off for Live (domain exists before install; harmless)
|
||||
|
||||
# ---------- backup agent ----------
|
||||
PL="$HOME/Library/LaunchAgents/xyz.sethpc.mac-backup.plist"
|
||||
if [[ -f $PL ]] && cmp -s "$REPO/config/xyz.sethpc.mac-backup.plist" "$PL"; then log skip "backup launchd agent"; else
|
||||
mkdir -p "$HOME/Library/LaunchAgents"; cp "$REPO/config/xyz.sethpc.mac-backup.plist" "$PL"
|
||||
launchctl bootout "gui/$(id -u)/xyz.sethpc.mac-backup" 2>/dev/null || true
|
||||
# bootstrap into the GUI domain is flaky over SSH (no bound Aqua session); the plist in
|
||||
# ~/Library/LaunchAgents auto-loads at next login regardless, so never let this abort setup.
|
||||
if launchctl bootstrap "gui/$(id -u)" "$PL" 2>/dev/null; then log set "backup launchd agent loaded (03:30 nightly)"
|
||||
else log warn "backup agent copied; bootstrap-over-SSH failed (normal) — loads at next login"; fi
|
||||
CHANGED=1
|
||||
fi
|
||||
|
||||
# ---------- apply ----------
|
||||
[[ $CHANGED == 1 ]] && { killall Dock Finder SystemUIServer 2>/dev/null || true; log set "restarted Dock/Finder"; }
|
||||
|
||||
# ---------- done ----------
|
||||
echo; log pubkey "$(cat "$HOME/.ssh/id_ed25519.pub")"
|
||||
[[ $CHANGED == 1 ]] && log note "some changes (key repeat, scroll direction) apply fully after logout/login"
|
||||
exit 0
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/opt/homebrew/bin/bash
|
||||
# tank-automount.sh — run ON the Mac as seth. From steel141:
|
||||
# printf '%s\n' "$HOMELAB_PASSWORD" | ssh mac 'bash ~/mac/scripts/tank-automount.sh'
|
||||
# tank at /Volumes/tank via autofs + SMB, as Administrator (= root on tank, same as steel141's
|
||||
# /mnt/Z). autofs mounts on first access and unmounts when idle, so a laptop that leaves the LAN
|
||||
# gets no login-time "server not found" dialogs and no hung Finder. Password lives ONLY in
|
||||
# root-owned /etc/auto_smb (0600); it is also seth's sudo password here, so stdin serves both.
|
||||
# Known limit: automounted fs are always `nobrowse` -> not a drive in Finder's Locations sidebar.
|
||||
# It's a folder: drag /Volumes/tank to Finder Favorites once (docs/manual-checklist.md).
|
||||
set -euo pipefail
|
||||
IFS= read -r PW; [[ -n $PW ]] || { echo "password expected on stdin"; exit 1; }
|
||||
BK="$HOME/.mac-setup-backup"; TS=$(date +%s); mkdir -p "$BK"
|
||||
log(){ printf '\033[1;33m[%s]\033[0m %s\n' "$1" "$2"; }
|
||||
printf '%s\n' "$PW" | sudo -S -p '' -v # one ticket for the run; -K at the end
|
||||
trap 'sudo -K' EXIT
|
||||
|
||||
if grep -qE '^/-[[:space:]]+auto_smb' /etc/auto_master; then log skip "auto_master direct map"; else
|
||||
sudo cp /etc/auto_master "$BK/auto_master-$TS"
|
||||
printf '/-\t\t\tauto_smb\t-nosuid\n' | sudo tee -a /etc/auto_master >/dev/null; log set "auto_master += auto_smb"
|
||||
fi
|
||||
|
||||
# password via stdin (never argv) -> %-encoded for the URL
|
||||
ENC=$(printf '%s' "$PW" | python3 -c 'import sys,urllib.parse;print(urllib.parse.quote(sys.stdin.read(),safe=""))')
|
||||
MAP="/Volumes/tank -fstype=smbfs,soft ://Administrator:${ENC}@192.168.0.173/tank"
|
||||
# pipeline, not $(sudo cat): the no-tty sudo ticket is per parent pid and a subshell breaks it
|
||||
if printf '%s\n' "$MAP" | sudo cmp -s - /etc/auto_smb 2>/dev/null; then log skip "/etc/auto_smb"; else
|
||||
printf '%s\n' "$MAP" | sudo tee /etc/auto_smb >/dev/null # no backup: it's ours and holds the password
|
||||
sudo chmod 600 /etc/auto_smb; sudo chown root:wheel /etc/auto_smb; log set "/etc/auto_smb"
|
||||
fi
|
||||
|
||||
sudo mkdir -p /Volumes/tank # automount won't create direct-map triggers ("mountpoint unavailable"); a plain dir here persists
|
||||
sudo automount -vc >/dev/null
|
||||
if ls /Volumes/tank >/dev/null 2>&1 && mount | grep -q ' /Volumes/tank (smbfs'; then log ok "tank mounted: $(mount | grep ' /Volumes/tank (smbfs' | cut -d' ' -f1)"
|
||||
else log warn "trigger present but mount failed — off the LAN, or bad SMB credentials? (check: log show --last 2m --predicate 'process == \"automountd\"')"; fi
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
# Run on pve173: ssh pve173 "PUB='<pubkey line>' bash -s" < scripts/tank-side.sh
|
||||
# PUB comes via env (not a command arg) so ssh's remote-side re-splitting can't truncate the
|
||||
# space-separated key line. $1 kept as a fallback for a local run.
|
||||
set -euo pipefail
|
||||
PUB=${PUB:-${1:-}}
|
||||
[[ $PUB == ssh-ed25519\ AAAA* ]] || { echo "not a full pubkey line: '$PUB'"; exit 1; }
|
||||
DS=tank/backups/mac; TS=$(date +%s)
|
||||
if zfs list "$DS" >/dev/null 2>&1; then echo "[skip] dataset $DS"; else zfs create "$DS"; echo "[set] created $DS"; fi
|
||||
if grep -q "^\[$DS\]" /etc/sanoid/sanoid.conf; then echo "[skip] sanoid stanza"; else
|
||||
mkdir -p /etc/sanoid/.backup; cp /etc/sanoid/sanoid.conf "/etc/sanoid/.backup/sanoid.conf-$TS"
|
||||
printf '\n[%s]\n\tuse_template = tank_media\n' "$DS" >> /etc/sanoid/sanoid.conf; echo "[set] sanoid stanza"
|
||||
fi
|
||||
KEYLINE="restrict,command=\"/usr/bin/rrsync /$DS\" $PUB"
|
||||
if grep -qF "$PUB" /root/.ssh/authorized_keys; then echo "[skip] key present"; else
|
||||
cp /root/.ssh/authorized_keys "/root/.ssh/authorized_keys.bak-$TS"
|
||||
echo "$KEYLINE" >> /root/.ssh/authorized_keys; echo "[set] rrsync-restricted key added"
|
||||
fi
|
||||
Reference in New Issue
Block a user