box/templates/grok/user-data.yaml

81 lines
3.5 KiB
YAML
Raw Normal View History

#cloud-config
users:
- name: grok
shell: /bin/bash
sudo: "ALL=(ALL) NOPASSWD:ALL"
lock_passwd: true
write_files:
- path: /home/grok/.grok/AGENTS.md
owner: "grok:grok"
permissions: '0644'
defer: true
content: |
# You are running inside a box (template: grok)
A box is a trust-less, network-isolated, ephemeral VM created by the
`box` CLI. Keep this context in mind:
- **Creds-free by default.** The box starts with no xAI and no git
credentials. If you need to authenticate, the operator runs
`grok-build login` interactively (SuperGrok / X Premium+). For git, the
operator adds their own credentials (a PAT or `gh auth login`). Never
assume credentials are present; never ask for or store secrets on disk
beyond what the operator sets up.
- **Isolated.** The box reaches the public internet but nothing on the host
or local network. There is no inbound path.
- **Disposable.** Nothing here is backed up. State is discarded when the box
is removed; the operator persists work via git push and via `box snapshot`.
- **Bootstrap runbook.** If the repository you are working in contains a
`.box/` folder (older repos may use `.claudebox/`), read it as your setup runbook — how to install
dependencies, start services, template environment files, seed data, and
smoke-test — and follow it. It is documentation for you, not a script the
host runs.
package_update: true
packages:
- git
- gh
- curl
- ca-certificates
- gnupg
- ripgrep
- jq
- tmux
- age
- unzip
- build-essential
runcmd:
- curl -fsSL https://get.docker.com | sh
- usermod -aG docker grok
# Grok Build's official installer (verified upstream: x.ai/cli). It drops the
# binary under the invoking user's home, so run it AS grok, not root.
- sudo -u grok bash -lc 'curl -fsSL https://x.ai/cli/install.sh | bash'
# 'box exec <b> -- grok-build …' runs a NON-interactive shell that reads no
fix: run 17's four real findings — migrate retire, expose proxy, wait_box, grok PATH The first drill run where every failure was the RELEASE CODE, not the environment. 71 passed, 5 failed; all five traced to four bugs: 1. migrate-host --retire-legacy could NEVER succeed. Re-homing ADDS user.box=1 but never removed user.claudebox=1, and legacy_boxes() counted the old tag — so retire saw its own freshly-migrated box as un-migrated and refused forever ('legacy boxes still exist: legacybox'), leaving claudenet + claude-dev behind. Now: a verified re-home drops the legacy tag LAST (after the move is proven, so a failure anywhere above still leaves the box valid under one tag or the other), and legacy_boxes() ignores boxes already carrying user.box=1. 2. box expose died with a bare 'could not add the proxy device' — it swallowed incus's reason, exactly the sin this repo keeps punishing. Now it prints incus's error. And the mechanism is corrected: a VM's proxy needs NAT mode, which requires a static NIC address, so expose pins the box's current lease first (which also fixes the restart caveat — the exposure no longer points at a lease the box may lose). 3. wait_box's 2-minute window was too short: the legacy box was declared dead and then every migration check against it passed. 4 minutes. 4. The grok template hunted for a regular file named exactly 'grok-build' under /home/grok and found nothing — an installer's drop may be a SYMLINK, and its binary name is upstream's to choose. Now it tries the plausible names and paths, falls back to any executable grok*, links both names, and SAYS what it found — or dumps what the installer actually left when it finds nothing. The drill likewise dumps the on-disk evidence and the cloud-init log on a --version failure instead of discarding the box.
2026-07-14 19:56:17 +00:00
# rc files, so whatever the installer dropped must be symlinked onto the
# system PATH — the same fix the claude template needed (#15).
#
# The FIRST cut of this hunted only for a regular file named exactly
# 'grok-build' under /home/grok, and found nothing: the drill's grok box
# minted fine and then failed '--version'. Two lessons, both applied here:
# an installer's drop may be a SYMLINK (so -type f alone misses it), and its
# binary name is upstream's to choose (so try the plausible names, and any
# 'grok*' executable, before giving up). Then SAY what was found — a silent
# miss is what cost the last run.
- |
fix: run 17's four real findings — migrate retire, expose proxy, wait_box, grok PATH The first drill run where every failure was the RELEASE CODE, not the environment. 71 passed, 5 failed; all five traced to four bugs: 1. migrate-host --retire-legacy could NEVER succeed. Re-homing ADDS user.box=1 but never removed user.claudebox=1, and legacy_boxes() counted the old tag — so retire saw its own freshly-migrated box as un-migrated and refused forever ('legacy boxes still exist: legacybox'), leaving claudenet + claude-dev behind. Now: a verified re-home drops the legacy tag LAST (after the move is proven, so a failure anywhere above still leaves the box valid under one tag or the other), and legacy_boxes() ignores boxes already carrying user.box=1. 2. box expose died with a bare 'could not add the proxy device' — it swallowed incus's reason, exactly the sin this repo keeps punishing. Now it prints incus's error. And the mechanism is corrected: a VM's proxy needs NAT mode, which requires a static NIC address, so expose pins the box's current lease first (which also fixes the restart caveat — the exposure no longer points at a lease the box may lose). 3. wait_box's 2-minute window was too short: the legacy box was declared dead and then every migration check against it passed. 4 minutes. 4. The grok template hunted for a regular file named exactly 'grok-build' under /home/grok and found nothing — an installer's drop may be a SYMLINK, and its binary name is upstream's to choose. Now it tries the plausible names and paths, falls back to any executable grok*, links both names, and SAYS what it found — or dumps what the installer actually left when it finds nothing. The drill likewise dumps the on-disk evidence and the cloud-init log on a --version failure instead of discarding the box.
2026-07-14 19:56:17 +00:00
bin=""
for cand in grok-build grok; do
for d in /home/grok/.local/bin /home/grok/.grok/bin /home/grok/bin /usr/local/bin /opt; do
[ -x "$d/$cand" ] && { bin="$d/$cand"; break 2; }
done
done
[ -n "$bin" ] || bin="$(find /home/grok /opt -maxdepth 5 \( -type f -o -type l \) -iname 'grok*' -perm -u+x 2>/dev/null | head -1)"
if [ -n "$bin" ]; then
ln -sf "$bin" /usr/local/bin/grok-build
ln -sf "$bin" /usr/local/bin/grok
echo "grok: linked $bin -> /usr/local/bin/{grok-build,grok}"
else
echo "grok: NO BINARY FOUND after x.ai/cli/install.sh — the box is up but the CLI is not on PATH." >&2
echo "grok: what the installer left under /home/grok:" >&2
find /home/grok -maxdepth 4 \( -type f -o -type l \) -perm -u+x 2>/dev/null | head -20 >&2
fi
- echo 'export PATH="$HOME/.local/bin:$PATH"' >> /home/grok/.bashrc