← back to Nas Setup
3-2-1: dual-destination dw_unified pull (Henry + NAS), runtime-probed rsync flags
ace1e09f64a412e3265530a2d64fbb19dcc44863 · 2026-06-26 12:49:37 -0700 · Steve
Files touched
M README.mdM launchd/com.steve.nas-dwdump-mirror.plistM scripts/pull-dw-dump.sh
Diff
commit ace1e09f64a412e3265530a2d64fbb19dcc44863
Author: Steve <steve@designerwallcoverings.com>
Date: Fri Jun 26 12:49:37 2026 -0700
3-2-1: dual-destination dw_unified pull (Henry + NAS), runtime-probed rsync flags
---
README.md | 56 +++++++---
launchd/com.steve.nas-dwdump-mirror.plist | 11 +-
scripts/pull-dw-dump.sh | 173 +++++++++++++++++++++---------
3 files changed, 174 insertions(+), 66 deletions(-)
diff --git a/README.md b/README.md
index a5fb703..b0ed887 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,31 @@
Wires a new NAS (2TB + 4TB drives, JBOD = two separate shares) into the existing fleet:
backup hardening for `dw_unified`, cold archive + Time Machine, and a monitored offload
-target. **Nothing here is live yet** — activate in order once the NAS is racked.
+target.
+
+## 3-2-1 backup topology for `dw_unified` (DTD verdict, 2026-06-26)
+
+The on-prem `dw_unified` backup is **3-2-1**:
+
+| Copy | Where | Status |
+|------|-------|--------|
+| Cloud | Kamatera `/root/backups/db/dw_unified_*.dump` (nightly 03:00) | exists |
+| On-prem #1 | **Henry** local drive `/Volumes/Henry/dw-backups/dw_unified` | **LIVE today** |
+| On-prem #2 | **NAS 2TB share** `/Volumes/DW-Backups/dw_unified` | auto-activates on mount |
+
+`scripts/pull-dw-dump.sh` is **dual-destination**: one read-only pull from Kamatera fans
+out to BOTH Henry and the NAS. Each leg is **independent** — if one volume is
+unmounted/unwritable it's skipped with a logged WARNING but the other still completes, and
+the job only FAILs/alerts if *neither* destination got a verified copy. So the Henry leg
+runs today; the NAS leg starts the moment `/Volumes/DW-Backups` is mounted, no code change.
+
+> **Per-copy freshness monitoring is part of the verdict** — a second on-prem copy that
+> isn't itself monitored fails silently (the exact Jun 3–15 2026 failure mode). Both
+> panelists flagged this. See `patches/disk-space-canary-add-nas.md` (space) and the
+> proposed `dw-backup-canary` freshness diff drafted to the yolo approval queue (gated).
+
+The NAS pieces below (cold archive, Time Machine, the 4TB share) are **not live yet** —
+activate in order once the NAS is racked.
## Layout decided
- **4TB share → `DW-Archive`** — cold assets: design masters / 150-DPI TIFs, ComfyUI &
@@ -37,15 +61,19 @@ System Settings → General → Time Machine → Add Backup Disk → pick `DW-Ar
Do this on Mac2 (and Mac1 / the Ultra later).
### 4. Activate the dw_unified dump mirror (the high-value piece)
+The mirror is **already dual-destination and validated** — the Henry leg ran live
+2026-06-26 (first on-prem second copy created). The NAS leg auto-activates on mount;
+no script change is needed. Remaining steps:
```sh
-# a. set the real share path in the plist if it differs from /Volumes/DW-Backups
-# (edit launchd/com.steve.nas-dwdump-mirror.plist → NAS_BACKUP_DIR)
+# a. confirm the NAS path in the plist if it differs from /Volumes/DW-Backups
+# (edit launchd/com.steve.nas-dwdump-mirror.plist → NAS_BACKUP_DIR / HENRY_BACKUP_DIR)
-# b. dry-run the pull once by hand (read-only against Kamatera):
-NAS_BACKUP_DIR=/Volumes/DW-Backups/dw_unified bash scripts/pull-dw-dump.sh
-cat data/latest.json # expect verdict:"PASS"
+# b. run the pull once by hand (read-only against Kamatera; Henry leg writes locally):
+bash scripts/pull-dw-dump.sh
+cat data/latest.json # expect verdict:"PASS" (or "PARTIAL" while the NAS is still unmounted)
-# c. install the nightly 03:45 job (after Kamatera's 03:00 dump):
+# c. install the nightly 03:45 job (after Kamatera's 03:00 dump) — GATED, Steve installs:
+# a new recurring autonomous scheduler is Steve's call (drafted to the yolo approval queue).
cp launchd/com.steve.nas-dwdump-mirror.plist ~/Library/LaunchAgents/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.steve.nas-dwdump-mirror.plist
launchctl list | grep nas-dwdump-mirror # confirm registered
@@ -69,8 +97,12 @@ the local library outgrows 1TB, add a **Thunderbolt external SSD on the Ultra**
speed) — not the NAS.
## Pieces in this repo
-- `scripts/pull-dw-dump.sh` — nightly read-only pull of newest Kamatera dump → NAS, with
- size-floor + `pg_restore --list` integrity check; no-ops if the share isn't mounted;
- retention OFF by default (never autonomous deletion).
-- `launchd/com.steve.nas-dwdump-mirror.plist` — the 03:45 scheduler (staged, not loaded).
-- `patches/disk-space-canary-add-nas.md` — ready-to-apply canary edit.
+- `scripts/pull-dw-dump.sh` — nightly read-only pull of the newest Kamatera dump →
+ **BOTH Henry and the NAS 2TB share** (3-2-1), each destination independent, with a
+ per-destination size-floor + `pg_restore --list` integrity check; skips any unmounted/
+ unwritable destination with a logged WARNING and FAILs only if neither got a verified
+ copy; retention OFF by default (never autonomous deletion). rsync flags are probed at
+ runtime so it works on macOS openrsync and Homebrew/Linux rsync 3.x.
+- `launchd/com.steve.nas-dwdump-mirror.plist` — the 03:45 scheduler (staged, not loaded;
+ install is gated — Steve's call). Env block now sets both HENRY_BACKUP_DIR + NAS_BACKUP_DIR.
+- `patches/disk-space-canary-add-nas.md` — ready-to-apply canary edit (space monitoring).
diff --git a/launchd/com.steve.nas-dwdump-mirror.plist b/launchd/com.steve.nas-dwdump-mirror.plist
index 61c39a8..0ea7d50 100644
--- a/launchd/com.steve.nas-dwdump-mirror.plist
+++ b/launchd/com.steve.nas-dwdump-mirror.plist
@@ -1,10 +1,13 @@
<?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">
<!--
- com.steve.nas-dwdump-mirror — pull the nightly Kamatera dw_unified dump to the NAS.
+ com.steve.nas-dwdump-mirror — pull the nightly Kamatera dw_unified dump to BOTH
+ on-prem destinations (3-2-1 topology, DTD verdict 2026-06-26): Henry local drive
+ + NAS 2TB share. Each leg is independent — the Henry leg works today; the NAS leg
+ auto-activates once /Volumes/DW-Backups is mounted.
Runs 03:45 (after the 03:00 Kamatera db-backup.sh has finished writing).
- DO NOT load until the NAS share is mounted; activation steps are in README.md.
- Adjust NAS_BACKUP_DIR below to your actual 2TB share path before loading.
+ Safe to load NOW: the Henry leg runs nightly, the NAS leg no-ops until mounted.
+ Adjust the *_BACKUP_DIR env vars below if your paths differ.
-->
<plist version="1.0">
<dict>
@@ -17,6 +20,8 @@
</array>
<key>EnvironmentVariables</key>
<dict>
+ <key>HENRY_BACKUP_DIR</key>
+ <string>/Volumes/Henry/dw-backups/dw_unified</string>
<key>NAS_BACKUP_DIR</key>
<string>/Volumes/DW-Backups/dw_unified</string>
<key>PATH</key>
diff --git a/scripts/pull-dw-dump.sh b/scripts/pull-dw-dump.sh
index e8e3f3f..80248c6 100755
--- a/scripts/pull-dw-dump.sh
+++ b/scripts/pull-dw-dump.sh
@@ -1,26 +1,34 @@
#!/bin/bash
-# pull-dw-dump.sh — pull the newest Kamatera dw_unified pg_dump down to the NAS 2TB
-# backup share, giving you a SECOND, on-prem copy of the DB. This is the fix for the
+# pull-dw-dump.sh — pull the newest Kamatera dw_unified pg_dump down to TWO on-prem
+# destinations (3-2-1 topology, DTD verdict 2026-06-26):
+# * Henry local drive (default /Volumes/Henry/dw-backups/dw_unified)
+# * NAS 2TB share (default /Volumes/DW-Backups/dw_unified — auto-activates on mount)
+# giving you TWO on-prem copies + the cloud copy = 3-2-1. This is the fix for the
# 12-day silent backup death (Jun 3–15 2026): one cloud copy is a single point of failure.
#
# Safe-by-design:
# * READ-ONLY against Kamatera (ssh + rsync pull only — never writes to the server).
-# * Exits cleanly (code 0, logged) if the NAS share isn't mounted — safe to schedule
-# before the NAS arrives; it just no-ops until /Volumes/<share> exists.
+# * Each destination is INDEPENDENT: if one is unmounted/unwritable it's skipped with a
+# logged WARNING, but the other still completes. So the Henry leg works TODAY and the
+# NAS leg auto-activates the moment the share is mounted — no code change needed.
# * Retention defaults to KEEP-ALL (RETENTION_KEEP=0). Pruning old copies is OFF unless
# you opt in, honoring Steve's standing rule: NEVER autonomous deletion. When enabled
# it logs every file it removes.
-# * Verifies the pulled dump with `pg_restore --list` (structure-valid) + a size floor.
+# * Verifies each pulled dump with `pg_restore --list` (structure-valid) + a size floor.
+# * FAILs (exit 1 + alert) only if NEITHER destination got a verified copy. A partial
+# success (one leg up, one leg skipped/unmounted) is exit 0 with a logged WARNING.
#
# Env knobs (all optional):
-# NAS_BACKUP_DIR target dir on the 2TB share (default /Volumes/DW-Backups/dw_unified)
-# PGDUMP_HOST ssh target (default root@45.61.58.125)
-# PGDUMP_GLOB remote dump glob (default /root/backups/db/dw_unified_*.dump)
-# PGDUMP_FLOOR_MB reject dumps smaller than this (default 100)
+# HENRY_BACKUP_DIR target dir on the Henry drive (default /Volumes/Henry/dw-backups/dw_unified)
+# NAS_BACKUP_DIR target dir on the 2TB share (default /Volumes/DW-Backups/dw_unified)
+# PGDUMP_HOST ssh target (default root@45.61.58.125)
+# PGDUMP_GLOB remote dump glob (default /root/backups/db/dw_unified_*.dump)
+# PGDUMP_FLOOR_MB reject dumps smaller than this (default 100)
# RETENTION_KEEP keep N newest local copies, 0 = keep all (default 0)
-# PULL_TO alert email (default steve@designerwallcoverings.com)
+# PULL_TO alert email (default steve@designerwallcoverings.com)
set -uo pipefail
+HENRY_BACKUP_DIR="${HENRY_BACKUP_DIR:-/Volumes/Henry/dw-backups/dw_unified}"
NAS_BACKUP_DIR="${NAS_BACKUP_DIR:-/Volumes/DW-Backups/dw_unified}"
HOST="${PGDUMP_HOST:-root@45.61.58.125}"
GLOB="${PGDUMP_GLOB:-/root/backups/db/dw_unified_*.dump}"
@@ -34,62 +42,125 @@ LATEST="$SELF_DIR/../data/latest.json"
FLOOR_BYTES=$(( FLOOR_MB * 1024 * 1024 ))
log(){ echo "[$(date -Iseconds)] $1" | tee -a "$LOG"; }
+# Resumable-transfer flags vary by rsync build. macOS ships openrsync (2.6.9-compatible)
+# which lacks --append-verify; modern (Homebrew/Linux) rsync 3.x has it. Probe once and
+# pick the strongest resumable flag set the local rsync actually supports.
+RSYNC_FLAGS="-a"
+if rsync --help 2>&1 | grep -q -- '--append-verify'; then
+ RSYNC_FLAGS="-a --partial --append-verify" # rsync 3.x: resumable + re-checksum on resume
+elif rsync --help 2>&1 | grep -q -- '--partial'; then
+ RSYNC_FLAGS="-a --partial" # partial-resume without verify
+fi
+
alert(){ # CNCP card + George email on FAIL. args: <reason>
local reason="$1"; local CNCP="${CNCP_URL:-http://localhost:3333}"
- local note="[NAS DUMP MIRROR $(date +%F)] dw_unified pull-down FAILED: ${reason}. The on-prem second copy is the safety net for the cloud pg_dump — investigate."
+ local note="[DW DUMP MIRROR $(date +%F)] dw_unified on-prem pull FAILED: ${reason}. The on-prem copies (Henry + NAS) are the 3-2-1 safety net for the cloud pg_dump — investigate."
curl -sS --max-time 10 "$CNCP/api/parking-lot" -H 'Content-Type: application/json' \
- -d "$(jq -n --arg u "nas://dw_unified-mirror" --arg note "$note" '{url:$u,note:$note}')" >/dev/null 2>&1 || true
+ -d "$(jq -n --arg u "onprem://dw_unified-mirror" --arg note "$note" '{url:$u,note:$note}')" >/dev/null 2>&1 || true
if [ -f "$HOME/.claude/skills/_shared/george-send.sh" ]; then
. "$HOME/.claude/skills/_shared/george-send.sh"
- local body="<div style=\"font-family:-apple-system,sans-serif;color:#222\"><h3 style=\"color:#b23b3b\">⚠ NAS dw_unified mirror FAILED</h3><div>${reason}</div><div style=\"color:#666;font-size:13px\">This job pulls the nightly Kamatera dump to the NAS 2TB share so there's a local restore source. Without it you're back to one cloud copy.</div></div>"
- george_send info "${PULL_TO:-steve@designerwallcoverings.com}" "⚠ NAS dw_unified mirror FAIL — $(date +%F)" "$body" >/dev/null 2>&1 || true
+ local body="<div style=\"font-family:-apple-system,sans-serif;color:#222\"><h3 style=\"color:#b23b3b\">⚠ dw_unified on-prem mirror FAILED</h3><div>${reason}</div><div style=\"color:#666;font-size:13px\">This job pulls the nightly Kamatera dump to BOTH Henry and the NAS 2TB share so there's a local restore source (3-2-1). Without it you're back to one cloud copy.</div></div>"
+ george_send info "${PULL_TO:-steve@designerwallcoverings.com}" "⚠ dw_unified on-prem mirror FAIL — $(date +%F)" "$body" >/dev/null 2>&1 || true
fi
}
-# ── 0. NAS share mounted? If not, no-op cleanly (safe to schedule pre-arrival) ──
-NAS_ROOT="$(echo "$NAS_BACKUP_DIR" | awk -F/ '{print "/"$2"/"$3}')" # e.g. /Volumes/DW-Backups
-if ! mount | grep -q " on ${NAS_ROOT} "; then
- log "NAS share not mounted ($NAS_ROOT) — skipping (no-op until the NAS is online)"
- exit 0
-fi
-mkdir -p "$NAS_BACKUP_DIR" || { log "cannot create $NAS_BACKUP_DIR on the share"; alert "target dir not writable: $NAS_BACKUP_DIR"; exit 1; }
-
-# ── 1. newest remote dump (read-only) ──
+# ── 0. newest remote dump (read-only) — resolved ONCE, reused per destination ──
REMOTE=$(ssh -o ConnectTimeout=15 -o BatchMode=yes "$HOST" "ls -t $GLOB 2>/dev/null | head -1")
if [ -z "$REMOTE" ]; then log "FAIL: no remote dump found ($GLOB)"; alert "no remote dump matching $GLOB"; exit 1; fi
BASE="$(basename "$REMOTE")"
-DEST="$NAS_BACKUP_DIR/$BASE"
log "newest remote dump: $REMOTE"
-# ── 2. rsync it down (resumable, skips if already complete & same size) ──
-if rsync -a --partial --append-verify -e "ssh -o ConnectTimeout=15 -o BatchMode=yes" "$HOST:$REMOTE" "$DEST" 2>>"$LOG"; then
- log "rsync ok → $DEST"
-else
- log "FAIL: rsync of $BASE failed"; alert "rsync failed for $BASE"; exit 1
-fi
+# Track per-destination outcome for the final verdict + latest.json.
+OK_COUNT=0
+SKIP_COUNT=0
+declare -a DEST_JSON=()
-# ── 3. integrity: size floor + pg_restore --list structure check ──
-SIZE=$(stat -f %z "$DEST" 2>/dev/null || echo 0); SIZE_MB=$(( SIZE / 1024 / 1024 ))
-if [ "$SIZE" -lt "$FLOOR_BYTES" ]; then
- log "FAIL: pulled dump ${SIZE_MB}MB < floor ${FLOOR_MB}MB (truncated?)"; alert "pulled dump ${SIZE_MB}MB < ${FLOOR_MB}MB"; exit 1
-fi
-TOC=$(pg_restore --list "$DEST" 2>>"$LOG" | grep -c ';' || echo 0)
-if [ "$TOC" -lt 100 ]; then
- log "FAIL: pg_restore --list shows only $TOC entries — dump not structurally valid"; alert "pg_restore --list invalid ($TOC entries)"; exit 1
+# pull_one <label> <dest_dir>
+# Returns 0 on a verified pull, 1 on a hard failure of THIS destination.
+# Skips (return 2) if the destination's volume root isn't mounted/writable.
+pull_one(){
+ local LABEL="$1" DIR="$2"
+ local ROOT; ROOT="$(echo "$DIR" | awk -F/ '{print "/"$2"/"$3}')" # e.g. /Volumes/Henry
+
+ # mounted?
+ if ! mount | grep -q " on ${ROOT} "; then
+ log "[$LABEL] volume not mounted ($ROOT) — skipping this destination (will activate on mount)"
+ return 2
+ fi
+ if ! mkdir -p "$DIR" 2>>"$LOG"; then
+ log "[$LABEL] WARN: cannot create $DIR — skipping this destination"
+ return 2
+ fi
+ if [ ! -w "$DIR" ]; then
+ log "[$LABEL] WARN: $DIR not writable — skipping this destination"
+ return 2
+ fi
+
+ local DEST="$DIR/$BASE"
+
+ # rsync down (resumable; skips if already complete & same size). RSYNC_FLAGS is probed
+ # above so this works on both macOS openrsync and Homebrew/Linux rsync 3.x.
+ if rsync $RSYNC_FLAGS -e "ssh -o ConnectTimeout=15 -o BatchMode=yes" "$HOST:$REMOTE" "$DEST" 2>>"$LOG"; then
+ log "[$LABEL] rsync ok → $DEST"
+ else
+ log "[$LABEL] FAIL: rsync of $BASE failed"; return 1
+ fi
+
+ # integrity: size floor + pg_restore --list structure check
+ local SIZE SIZE_MB TOC
+ SIZE=$(stat -f %z "$DEST" 2>/dev/null || echo 0); SIZE_MB=$(( SIZE / 1024 / 1024 ))
+ if [ "$SIZE" -lt "$FLOOR_BYTES" ]; then
+ log "[$LABEL] FAIL: pulled dump ${SIZE_MB}MB < floor ${FLOOR_MB}MB (truncated?)"; return 1
+ fi
+ TOC=$(pg_restore --list "$DEST" 2>>"$LOG" | grep -c ';' || echo 0)
+ if [ "$TOC" -lt 100 ]; then
+ log "[$LABEL] FAIL: pg_restore --list shows only $TOC entries — dump not structurally valid"; return 1
+ fi
+ log "[$LABEL] PASS: $BASE — ${SIZE_MB}MB, $TOC TOC entries → $DEST"
+
+ # retention (OFF by default; logs every deletion when enabled)
+ if [ "$RETENTION_KEEP" -gt 0 ]; then
+ local f; local -a OLD=()
+ while IFS= read -r f; do OLD+=("$f"); done < <(ls -t "$DIR"/dw_unified_*.dump 2>/dev/null | tail -n +"$((RETENTION_KEEP+1))")
+ for f in "${OLD[@]}"; do log "[$LABEL] retention: removing $(basename "$f")"; rm -f "$f"; done
+ [ "${#OLD[@]}" -gt 0 ] && log "[$LABEL] retention: pruned ${#OLD[@]} old copy(ies), kept newest $RETENTION_KEEP"
+ else
+ local COUNT; COUNT=$(ls "$DIR"/dw_unified_*.dump 2>/dev/null | wc -l | tr -d ' ')
+ log "[$LABEL] retention OFF (keep-all) — $COUNT local copies"
+ fi
+
+ DEST_JSON+=("$(jq -n --arg label "$LABEL" --arg dir "$DIR" --argjson size_mb "$SIZE_MB" --argjson toc "$TOC" \
+ '{label:$label,dir:$dir,size_mb:$size_mb,toc_entries:$toc,verdict:"PASS"}')")
+ return 0
+}
+
+# ── 1. Henry leg (primary on-prem second medium; works TODAY) ──
+pull_one "Henry" "$HENRY_BACKUP_DIR"; rc=$?
+case $rc in 0) OK_COUNT=$((OK_COUNT+1));; 2) SKIP_COUNT=$((SKIP_COUNT+1));; esac
+
+# ── 2. NAS leg (auto-activates once /Volumes/DW-Backups is mounted) ──
+pull_one "NAS" "$NAS_BACKUP_DIR"; rc=$?
+case $rc in 0) OK_COUNT=$((OK_COUNT+1));; 2) SKIP_COUNT=$((SKIP_COUNT+1));; esac
+
+# ── 3. verdict: FAIL only if NEITHER destination got a verified copy ──
+DESTS_NDJSON=$(printf '%s\n' "${DEST_JSON[@]:-}")
+DESTS_ARR=$(printf '%s' "$DESTS_NDJSON" | jq -s 'map(select(. != null))' 2>/dev/null || echo '[]')
+
+if [ "$OK_COUNT" -eq 0 ]; then
+ log "FAIL: no destination got a verified copy of $BASE (ok=$OK_COUNT skip=$SKIP_COUNT)"
+ alert "no on-prem destination received a verified copy of $BASE"
+ jq -n --arg base "$BASE" --argjson dests "$DESTS_ARR" \
+ '{pulled_at:(now|todate),dump:$base,destinations:$dests,verdict:"FAIL"}' > "$LATEST"
+ exit 1
fi
-log "PASS: $BASE — ${SIZE_MB}MB, $TOC TOC entries"
-
-# ── 4. retention (OFF by default; logs every deletion when enabled) ──
-if [ "$RETENTION_KEEP" -gt 0 ]; then
- mapfile -t OLD < <(ls -t "$NAS_BACKUP_DIR"/dw_unified_*.dump 2>/dev/null | tail -n +"$((RETENTION_KEEP+1))")
- for f in "${OLD[@]}"; do log "retention: removing $(basename "$f")"; rm -f "$f"; done
- [ "${#OLD[@]}" -gt 0 ] && log "retention: pruned ${#OLD[@]} old copy(ies), kept newest $RETENTION_KEEP"
-else
- COUNT=$(ls "$NAS_BACKUP_DIR"/dw_unified_*.dump 2>/dev/null | wc -l | tr -d ' ')
- log "retention OFF (keep-all) — $COUNT local copies on the share"
+
+VERDICT="PASS"
+if [ "$SKIP_COUNT" -gt 0 ]; then
+ VERDICT="PARTIAL"
+ log "WARN: $OK_COUNT destination(s) verified, $SKIP_COUNT skipped (unmounted/unwritable) — still have an on-prem copy"
fi
-jq -n --arg base "$BASE" --argjson size_mb "$SIZE_MB" --argjson toc "$TOC" --arg dir "$NAS_BACKUP_DIR" \
- '{pulled_at:(now|todate),dump:$base,size_mb:$size_mb,toc_entries:$toc,dir:$dir,verdict:"PASS"}' > "$LATEST"
-log "✓ mirror complete"
+jq -n --arg base "$BASE" --argjson dests "$DESTS_ARR" --arg verdict "$VERDICT" \
+ '{pulled_at:(now|todate),dump:$base,destinations:$dests,ok:($dests|length),verdict:$verdict}' > "$LATEST"
+log "✓ mirror complete ($VERDICT) — $OK_COUNT verified, $SKIP_COUNT skipped"
exit 0
← eb6346c NAS setup package: dw_unified dump mirror + canary patch + r
·
back to Nas Setup
·
chore: macstudio3 migration — reconcile from mac2 + repoint 404391e →