---
created: 2026-06-23
updated: 2026-06-23T00:51
tags:
  - openclaw
  - link
  - recovery
  - runbook
---
# Link Recovery Runbook

**Goal:** If the live Link instance dies, rebuild it cleanly from this vault folder with zero loss.

This file is plain Markdown so **Obsidian Sync always carries it** (dotfolders like `.openclaw` are NOT synced; that's why everything needed is inlined here).

## What Link is

- **Name/role:** Link — Brandon's Personal AI Chief of Staff (see `SOUL.md`, `IDENTITY.md`).
- **Profile:** `link` → live home `~/.openclaw-link` (config + runtime; NOT synced).
- **Workspace:** `~/.openclaw-link/workspace` is a **symlink → this vault folder**
  (`~/Documents/Mind Palace/Utility Bins/AI/Link`). So identity/soul/memory/agents/skills
  live here and sync via Obsidian Sync.
- **Gateway:** port **18790**, launchd service `ai.openclaw.link` (RunAtLoad + KeepAlive).
- **Model:** `dario/claude-opus-4-8` (Claude Opus 4.8) via local **dario** proxy at
  `http://127.0.0.1:3456`. 1M context, 128K max output. No beta header needed.
- **Signal:** account **+12135792424**, attaches to shared signal-cli HTTP daemon at
  `127.0.0.1:8080` (launchd `org.asamk.signal-cli`, autoStart:false — Link does NOT own it).
- **Manager:** sibling OpenClaw on port 18789 (profile default) set Link up.
- Hermes uses the OTHER Signal account +15056054516 (daemon :8081). OUT OF SCOPE.

## Rebuild steps

1. Restore config:
   ```bash
   mkdir -p ~/.openclaw-link
   # paste the "Link config" block below into:
   ~/.openclaw-link/openclaw.json
   ```
2. Re-link workspace to this vault folder:
   ```bash
   rm -rf ~/.openclaw-link/workspace
   ln -s "/Users/brandon/Documents/Mind Palace/Utility Bins/AI/Link" ~/.openclaw-link/workspace
   ```
3. Restore launchd services (paste blocks below):
   ```bash
   # ~/Library/LaunchAgents/ai.openclaw.link.plist
   # ~/Library/LaunchAgents/org.asamk.signal-cli.plist   (if signal daemon missing)
   launchctl load ~/Library/LaunchAgents/org.asamk.signal-cli.plist
   launchctl load ~/Library/LaunchAgents/ai.openclaw.link.plist
   ```
4. Ensure dario proxy is running on :3456 (`dario status` / `dario proxy`).
5. Verify:
   ```bash
   OPENCLAW_GATEWAY_PORT=18790 openclaw --profile link channels status
   OPENCLAW_GATEWAY_PORT=18790 openclaw --profile link agent --agent main -m "who are you?"
   ```

## Management wrapper (manager → Link)

Save as `~/.openclaw/workspace/bin/link`, `chmod +x`:
```bash
#!/bin/zsh
# Manager → Link bridge. Talk to the Link OpenClaw instance cleanly,
# bypassing the manager shell's OPENCLAW_GATEWAY_PORT=18789 contamination.
#
# Usage:
#   link "your message"          → one-shot agent turn to Link's main agent
#   link --raw <subcommand...>   → run any `openclaw --profile link ...` subcommand
#
export OPENCLAW_GATEWAY_PORT=18790
if [[ "$1" == "--raw" ]]; then
  shift
  exec openclaw --profile link "$@"
fi
exec openclaw --profile link agent --agent main -m "$*"
```

## Link config (`~/.openclaw-link/openclaw.json`)

> Token values are the live ones in the actual file; treat as sensitive.
```json
{
  "agents": {
    "defaults": {
      "workspace": "/Users/brandon/.openclaw-link/workspace",
      "model": {
        "primary": "dario/claude-opus-4-8"
      },
      "models": {
        "dario/claude-opus-4-8": {}
      }
    }
  },
  "gateway": {
    "mode": "local",
    "auth": {
      "mode": "token",
      "token": "7b8339fd0d2b86bf89dd3f633f756860316ca9bdddde3117"
    },
    "port": 18790,
    "bind": "loopback",
    "tailscale": {
      "mode": "off",
      "resetOnExit": false
    },
    "remote": {
      "url": "ws://127.0.0.1:18790",
      "token": "7b8339fd0d2b86bf89dd3f633f756860316ca9bdddde3117"
    }
  },
  "session": {
    "dmScope": "per-channel-peer"
  },
  "tools": {
    "profile": "coding"
  },
  "models": {
    "mode": "merge",
    "providers": {
      "dario": {
        "baseUrl": "http://127.0.0.1:3456",
        "api": "anthropic-messages",
        "apiKey": "dario-lan-54f04da29c68a316",
        "models": [
          {
            "id": "claude-opus-4-8",
            "name": "claude-opus-4-8 (Custom Provider)",
            "contextWindow": 1000000,
            "maxTokens": 128000,
            "input": [
              "text",
              "image"
            ],
            "cost": {
              "input": 0,
              "output": 0,
              "cacheRead": 0,
              "cacheWrite": 0
            },
            "reasoning": false
          }
        ]
      }
    }
  },
  "skills": {
    "install": {
      "nodeManager": "npm"
    }
  },
  "wizard": {
    "lastRunAt": "2026-06-23T06:25:16.972Z",
    "lastRunVersion": "2026.6.9",
    "lastRunCommand": "onboard",
    "lastRunMode": "local"
  },
  "meta": {
    "lastTouchedVersion": "2026.6.9",
    "lastTouchedAt": "2026-06-23T06:34:47.012Z"
  },
  "plugins": {
    "entries": {
      "signal": {
        "enabled": true
      }
    }
  },
  "channels": {
    "signal": {
      "enabled": true,
      "cliPath": "signal-cli",
      "account": "+12135792424",
      "httpHost": "127.0.0.1",
      "httpPort": 8080,
      "autoStart": false
    }
  }
}
```

## launchd: `ai.openclaw.link.plist`
```xml
<?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>ai.openclaw.link</string>
	<key>Comment</key>
	<string>OpenClaw "Link" instance (profile=link, port 18790, dario-backed)</string>
	<key>ProgramArguments</key>
	<array>
		<string>/opt/homebrew/bin/openclaw</string>
		<string>--profile</string>
		<string>link</string>
		<string>gateway</string>
		<string>--port</string>
		<string>18790</string>
	</array>
	<key>EnvironmentVariables</key>
	<dict>
		<key>HOME</key>
		<string>/Users/brandon</string>
		<key>PATH</key>
		<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
	</dict>
	<key>RunAtLoad</key>
	<true/>
	<key>KeepAlive</key>
	<true/>
	<key>StandardOutPath</key>
	<string>/Users/brandon/.openclaw-link/logs/gateway.out.log</string>
	<key>StandardErrorPath</key>
	<string>/Users/brandon/.openclaw-link/logs/gateway.err.log</string>
	<key>ThrottleInterval</key>
	<integer>10</integer>
</dict>
</plist>
```

## launchd: `org.asamk.signal-cli.plist` (shared Signal daemon for +12135792424)
```xml
<?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>org.asamk.signal-cli</string>
	<key>Comment</key>
	<string>signal-cli HTTP daemon for OpenClaw</string>
	<key>ProgramArguments</key>
	<array>
		<string>/opt/homebrew/bin/signal-cli</string>
		<string>-a</string>
		<string>+12135792424</string>
		<string>daemon</string>
		<string>--http</string>
		<string>--no-receive-stdout</string>
	</array>
	<key>EnvironmentVariables</key>
	<dict>
		<key>HOME</key>
		<string>/Users/brandon</string>
		<key>PATH</key>
		<string>/opt/homebrew/opt/openjdk@25/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
		<key>JAVA_HOME</key>
		<string>/opt/homebrew/opt/openjdk@25/libexec/openjdk.jdk/Contents/Home</string>
	</dict>
	<key>RunAtLoad</key>
	<true/>
	<key>KeepAlive</key>
	<true/>
	<key>StandardOutPath</key>
	<string>/Users/brandon/.openclaw/logs/signal-cli.log</string>
	<key>StandardErrorPath</key>
	<string>/Users/brandon/.openclaw/logs/signal-cli.err.log</string>
	<key>ThrottleInterval</key>
	<integer>10</integer>
</dict>
</plist>
```

## Nightly local-runtime backup

A launchd job backs up Link's **local runtime** every night at **03:15**:
- Script: `~/.openclaw/scripts/link-nightly-backup.sh`
- launchd: `~/Library/LaunchAgents/ai.openclaw.link-backup.plist` (label `ai.openclaw.link-backup`)
- Output: `~/.openclaw/backups/link/link-<timestamp>.tar.gz` (keeps last 14)
- Captures config, agents runtime + sessions, cron — keeps `workspace` AS a symlink
  (does NOT deref the ~9.8GB vault; vault is covered by Obsidian Sync + this runbook).
- Also drops copies of both launchd plists in the backup dir.
- Log: `~/.openclaw/backups/link/backup.log`

Restore a runtime snapshot:
```bash
launchctl unload ~/Library/LaunchAgents/ai.openclaw.link.plist
tar -xzf ~/.openclaw/backups/link/link-<timestamp>.tar.gz -C "$HOME"
# re-create workspace symlink if needed (see Rebuild steps), then:
launchctl load ~/Library/LaunchAgents/ai.openclaw.link.plist
```

## Isolated install (Link has its OWN openclaw — separate from manager)

Link runs from a DEDICATED openclaw install so updates/reinstalls never touch the
manager's global binary (and vice versa). This is the whole point: the manager
(global `/opt/homebrew`) stays rock-solid and runs risky ops on Link.

- **Link's install:** `~/.openclaw-link/runtime/` (npm prefix)
  - binary: `~/.openclaw-link/runtime/bin/openclaw`
  - root:   `~/.openclaw-link/runtime/lib/node_modules/openclaw`
- **Manager's install:** global `/opt/homebrew/lib/node_modules/openclaw` (DO NOT
  let Link's updates target this).
- Link's launchd plist `program` points at the isolated binary.
- The `link` wrapper (`~/.openclaw/workspace/bin/link`) uses the isolated binary.
- Node is shared (Homebrew node) — that's fine; only the openclaw package is isolated.

### ⚠️ Updating Link — DO NOT use `openclaw update`
`openclaw update` auto-detects a "managed gateway service root" and points back at
the GLOBAL install even when run from Link's binary. That's the trap that breaks
things. Update Link via npm --prefix instead:
```bash
~/.openclaw/scripts/link-update.sh            # latest
~/.openclaw/scripts/link-update.sh 2026.7.0   # specific version
```
That script: backs up first, `npm install -g --prefix ~/.openclaw-link/runtime
openclaw@<spec>`, restarts Link, then verifies manager version is UNCHANGED + Link
health (Signal + test turn). To rebuild the isolated install from scratch:
```bash
npm install -g --prefix ~/.openclaw-link/runtime openclaw@<version>
```
