Skill: webvictim/teleport-tbot-bootstrap (version 1.0.0)

--- name: teleport-tbot-bootstrap description: Bootstrap a persistent Teleport Machine ID (tbot) setup on macOS using LaunchAgent and tbot configure identity. Trigger when asked to set up, automate, or validate local Teleport bot identity refresh, including proxy/token/join-method inputs, LaunchAgent persistence, and first-run verification with tsh. Complements the teleport-tsh-ssh skill for day-to-day SSH/command/scp usage with the refreshed identity. --- # teleport-tbot-bootstrap Set up a local, persistent Machine ID bot on macOS for OpenClaw/agent SSH access. Pair this with `teleport-tsh-ssh` for operational host access once identity refresh is in place. ## Compatibility Tested against Teleport/tbot **18.7.0**. ## Inputs to collect - Teleport proxy address (for example `teleport.example.com:443`) - Bot onboarding secret (token and/or registration secret depending on cluster setup) - Bot role(s) / bot name context from Teleport setup - Optional output directory (default: `~/.openclaw/workspace/tbot`) ## LaunchAgent behavior (macOS) Use LaunchAgent for user-session persistence. - Starts automatically at user login. - Starts immediately when loaded with `launchctl bootstrap gui/<uid> ...`. - Restarts automatically when `KeepAlive` is true. - Does **not** require root when installed under `~/Library/LaunchAgents`. Use LaunchDaemon only when system-wide root context is explicitly required. ## Workflow 1. Ensure prerequisites: `tbot`, `tsh`, writable output dir. 2. Create output + state dirs (default `~/.openclaw/workspace/tbot` and `~/.openclaw/workspace/tbot/state`). 3. Generate config via `tbot configure identity` (do not hand-write config): - destination should point to output dir (`file://.../tbot`) - storage should point to state dir (`file://.../tbot/state`) - set proxy and join method (`bound_keypair` preferred) - write config file to `~/.openclaw/workspace/tbot/tbot.yaml` 4. Create LaunchAgent plist to run `tbot start -c <config>` with `RunAtLoad` + `KeepAlive`. 5. Load/start LaunchAgent. 6. Verify identity output exists and is fresh (`.../tbot/identity`). 7. Verify access path with `tsh -i <identity> --proxy=<proxy> ls`. 8. Report status and next steps. ## Bound keypair guidance Prefer `bound_keypair` join method for recoverability after interruptions (sleep/reboot). Use high recovery limits for resilient rejoin flows when appropriate. Use a fresh bot/state directory for first-time setup. Reusing state from a different bot/token can cause key lookup mismatches. Use Teleport-side preregistration first (Bot + role + join config). Keep access label-scoped (for example `openclaw-allowed: "true"`) so access is opt-in per node. See: - `references/teleport-prereq-examples.yaml` - https://goteleport.com/docs/reference/cli/tbot/ ## Security notes - Never commit onboarding tokens or registration secrets to git. - Treat `tbot.yaml`, bot state, and identity outputs as sensitive material. - Prefer secure secret delivery (interactive input, secret manager, env injection) over plaintext chat logs. ## Known limitations (v1.0.0) - Focuses on SSH identity output workflows (not Teleport app/db/kubernetes outputs). - Uses LaunchAgent user context; does not provide full LaunchDaemon/root automation. ## Commands (reference) - Generate config: - `tbot configure identity --output ~/.openclaw/workspace/tbot/tbot.yaml ...` - Start once (foreground test): - `tbot start -c ~/.openclaw/workspace/tbot/tbot.yaml` - LaunchAgent load: - `launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.openclaw.tbot.plist` - LaunchAgent restart: - `launchctl kickstart -k gui/$(id -u)/com.openclaw.tbot` ## Clawhub listing copy ### Clawhub short description Bootstrap a persistent Teleport Machine ID (`tbot`) identity on macOS using LaunchAgent and `tbot configure identity`. ### Companion skill Use with `teleport-tsh-ssh` for day-to-day SSH/command/scp operations using the refreshed identity. ### Clawhub long description Set up a local, persistent Machine ID bot for automation hosts. Generate config using `tbot configure identity`, install a user LaunchAgent (`com.openclaw.tbot`), and validate identity output with a `tsh` smoke test. Includes LaunchAgent persistence (no root), bound keypair onboarding support, Teleport prereq examples (Role/Bot/Token), label-scoped node access patterns, registration-secret retrieval guidance, and first-run fresh-state guidance. ## Resources - Setup script: `scripts/bootstrap_tbot_launchagent.sh` - Teleport YAML examples: `references/teleport-prereq-examples.yaml` - LaunchAgent template notes: `references/launchagent-notes.md` ## Uninstall / cleanup - `launchctl bootout gui/$(id -u)/com.openclaw.tbot` - `rm -f ~/Library/LaunchAgents/com.openclaw.tbot.plist` - Remove bot files if desired: `rm -rf ~/.openclaw/workspace/tbot`

path: references/teleport-prereq-examples.yaml
content: # Teleport-side examples for Machine ID setup (illustrative)
# Adjust names/labels/policies to your environment.

# Example bot role granting scoped node access + auto host-user creation.
kind: role
version: v8
metadata:
  name: openclaw-bot-role
spec:
  options:
    create_host_user_mode: insecure-drop
    create_host_user_default_shell: bash
  allow:
    logins: ["claw"]
    node_labels:
      openclaw-allowed: "true"
---
# Example Bot resource mapped to role(s).
kind: bot
version: v1
metadata:
  name: openclaw-bot
spec:
  roles: ["openclaw-bot-role"]
---
# Example token for Bot onboarding (bound keypair preferred + high recovery limit).
kind: token
version: v2
metadata:
  name: openclaw-bot-token
spec:
  roles: ["Bot"]
  bot_name: openclaw-bot
  join_method: bound_keypair
  bound_keypair:
    recovery:
      mode: standard
      limit: 999999

# Retrieve registration secret (first join only):
# tctl get token/openclaw-bot-token --format json \
#   | jq -r '.[].status.bound_keypair.registration_secret'
#
# Then pass to tbot start/configure as:
#   --registration-secret <value>


path: references/launchagent-notes.md
content: # LaunchAgent notes for tbot

## Why LaunchAgent

- Runs in user context (no root required).
- Good fit for OpenClaw host setups where agent runs as a user account.
- Starts at login and can be started immediately with `launchctl bootstrap`.

## Common operations

```bash
# Load/start
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.openclaw.tbot.plist

# Restart
launchctl kickstart -k gui/$(id -u)/com.openclaw.tbot

# Stop/unload
launchctl bootout gui/$(id -u)/com.openclaw.tbot

# Inspect status
launchctl print gui/$(id -u)/com.openclaw.tbot
```

## Caveats

- LaunchAgent requires a logged-in user session.
- For boot-before-login behavior, use LaunchDaemon (root-owned), which requires sudo/root setup.


path: scripts/bootstrap_tbot_launchagent.sh
content: #!/usr/bin/env bash
set -euo pipefail

# Bootstrap persistent tbot identity refresh via macOS LaunchAgent.
# Usage:
#   bootstrap_tbot_launchagent.sh --proxy teleport.example.com:443 --token <token> [--join-method bound_keypair] [--bot-name openclaw-bot] [--out-dir ~/.openclaw/workspace/tbot]

JOIN_METHOD="bound_keypair"
OUT_DIR="${HOME}/.openclaw/workspace/tbot"
BOT_NAME="openclaw-bot"
PROXY=""
TOKEN=""
REGISTRATION_SECRET=""

while [[ $# -gt 0 ]]; do
  case "$1" in
    --proxy) PROXY="$2"; shift 2;;
    --token) TOKEN="$2"; shift 2;;
    --registration-secret) REGISTRATION_SECRET="$2"; shift 2;;
    --join-method) JOIN_METHOD="$2"; shift 2;;
    --bot-name) BOT_NAME="$2"; shift 2;;
    --out-dir) OUT_DIR="$2"; shift 2;;
    *) echo "Unknown arg: $1" >&2; exit 1;;
  esac
done

command -v tbot >/dev/null || { echo "tbot not found" >&2; exit 1; }
command -v tsh >/dev/null || { echo "tsh not found" >&2; exit 1; }

if [[ -z "$PROXY" ]]; then
  echo "--proxy is required" >&2
  exit 1
fi

if [[ -z "$TOKEN" && -z "$REGISTRATION_SECRET" ]]; then
  echo "Provide either --token or --registration-secret" >&2
  exit 1
fi

STATE_DIR="${OUT_DIR}/state"
CONF_PATH="${OUT_DIR}/tbot.yaml"
IDENTITY_PATH="${OUT_DIR}/identity"
PLIST_PATH="${HOME}/Library/LaunchAgents/com.openclaw.tbot.plist"
LABEL="com.openclaw.tbot"

mkdir -p "$OUT_DIR" "$STATE_DIR" "${HOME}/Library/LaunchAgents"

CFG=(
  tbot configure identity
  --output "$CONF_PATH"
  --destination "file://${OUT_DIR}"
  --storage "file://${STATE_DIR}"
  --proxy-server "$PROXY"
  --join-method "$JOIN_METHOD"
)

if [[ -n "$TOKEN" ]]; then
  CFG+=(--token "$TOKEN")
fi
if [[ -n "$REGISTRATION_SECRET" ]]; then
  CFG+=(--registration-secret "$REGISTRATION_SECRET")
fi

"${CFG[@]}"

cat > "$PLIST_PATH" <<PLIST
<?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>${LABEL}</string>
  <key>ProgramArguments</key>
  <array>
    <string>$(command -v tbot)</string>
    <string>start</string>
    <string>-c</string>
    <string>${CONF_PATH}</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
  <key>StandardOutPath</key><string>${OUT_DIR}/tbot.stdout.log</string>
  <key>StandardErrorPath</key><string>${OUT_DIR}/tbot.stderr.log</string>
  <key>WorkingDirectory</key><string>${OUT_DIR}</string>
</dict>
</plist>
PLIST

launchctl bootout "gui/$(id -u)/${LABEL}" >/dev/null 2>&1 || true
launchctl bootstrap "gui/$(id -u)" "$PLIST_PATH"
launchctl kickstart -k "gui/$(id -u)/${LABEL}"

sleep 2

if [[ ! -s "$IDENTITY_PATH" ]]; then
  echo "Identity file not created yet at: $IDENTITY_PATH" >&2
  exit 2
fi

# quick smoke test (can fail if RBAC has no node access; still useful signal)
set +e
tsh -i "$IDENTITY_PATH" --proxy="$PROXY" ls >/tmp/tbot-smoke.out 2>/tmp/tbot-smoke.err
RC=$?
set -e

echo "Configured: $CONF_PATH"
echo "LaunchAgent: $PLIST_PATH"
echo "Identity: $IDENTITY_PATH"
echo "Smoke test exit: $RC"
if [[ $RC -ne 0 ]]; then
  echo "Smoke stderr:"
  sed -n '1,60p' /tmp/tbot-smoke.err
fi


Security scan verdicts — ClawScan: clean | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)