New docs/guide/ tree — canonical home for operator-facing procedural docs. Starlight frontmatter added to all files. 0.12 alignment fixes applied: - v0.11.0 → v0.12.0 throughout - PI_TUI_PROVIDER/MODEL → DEEPSEEK_API_KEY - Headless Codex login → Agent runtime setup (zot + RPC mode) - /login and auth.json references removed - pi → zot in provider-fallback spawn reference - colibri-provider-verify (was pi-provider-smoke) - Language cleanup: smoke test → verification, fake → test, can't self-fix → requires operator intervention, broken → unresponsive, Fix anything broken → Verify all checks pass Two-tree model: docs/wiki/ (decisions) + docs/guide/ (procedural). Single source of truth in colibri. clawdie-ai docs/public/ to be retired.
416 lines
13 KiB
Markdown
416 lines
13 KiB
Markdown
---
|
||
title: 'First Boot'
|
||
description: Edit one file, flash a USB, boot — get to a working assistant with minimal interaction.
|
||
---
|
||
|
||
> **Current ISO validation note:** the live ISO path is moving to a QML
|
||
> installer followed by post-install `/setup` in the Clawdie controlplane.
|
||
> Provider keys and Telegram are no longer install-time requirements. The
|
||
> `setup.txt` flow below documents the older non-interactive contract and will
|
||
> be rewritten after the current ISO live-install validation completes.
|
||
|
||
The first-boot model below is intentionally small. You edit a single
|
||
first-boot setup file (`setup.txt`), flash the USB, boot the
|
||
machine, and the
|
||
installer reads your file on first start. No interactive
|
||
out-of-the-box wizard, no autogenerated secrets you have to write
|
||
down later.
|
||
|
||
The first-boot setup file is versioned. If you do not set these
|
||
yourself, the installer fills safe defaults:
|
||
|
||
```text
|
||
SETUP_SCHEMA_VERSION=1
|
||
ISO_RELEASE=v0.10.0
|
||
ISO_GIT_COMMIT=
|
||
```
|
||
|
||
- `SETUP_SCHEMA_VERSION` tracks the file format the installer understands
|
||
- `ISO_RELEASE` records which Clawdie release the install media came from
|
||
- `ISO_GIT_COMMIT` is optional exact build provenance
|
||
|
||
## What you need
|
||
|
||
- The Clawdie ISO ([tags](https://codeberg.org/Clawdie/Clawdie-ISO/tags))
|
||
- A USB stick (16 GB+)
|
||
- A flashing tool (`dd`, Etcher, Rufus — your call)
|
||
- An [OpenRouter](https://openrouter.ai/keys) account (the
|
||
recommended bootstrap path; you can switch to direct provider
|
||
keys later)
|
||
- A Telegram account and a bot token from
|
||
[@BotFather](https://t.me/BotFather)
|
||
- Your Telegram numeric user ID from
|
||
[@userinfobot](https://t.me/userinfobot)
|
||
|
||
## Optional: inspect first
|
||
|
||
If you do not know your disk names, pool layout, PF-facing interface
|
||
names, or whether the machine already contains a Clawdie install, run
|
||
inspect first from the live environment:
|
||
|
||
```bash
|
||
./scripts/inspect-system.sh --output /path/to/writable/media
|
||
```
|
||
|
||
On a full Clawdie repo checkout, `npm run inspect -- --output ...` is
|
||
just a convenience wrapper around the same shell script.
|
||
|
||
To write the suggested install/storage values straight back into your
|
||
first-boot setup file:
|
||
|
||
```bash
|
||
./scripts/inspect-system.sh \
|
||
--output /path/to/writable/media \
|
||
--apply-setup /path/to/writable/media/setup.txt
|
||
```
|
||
|
||
That only fills blank or missing fields. It does not overwrite values
|
||
you already chose explicitly.
|
||
|
||
To populate the hardware contract file directly:
|
||
|
||
```bash
|
||
./scripts/inspect-system.sh \
|
||
--output /path/to/writable/media \
|
||
--apply-system-env /path/to/writable/media/system.env
|
||
```
|
||
|
||
That fills blank or missing hardware fields in `system.env` such as:
|
||
|
||
- `SYSTEM_SCHEMA_VERSION`
|
||
- `NETWORK_EXTERNAL_IF`
|
||
- `NETWORK_INTERNAL_IF`
|
||
- `TAILSCALE_IF`
|
||
- `ZFS_POOL`
|
||
- `ZFS_LAYOUT`
|
||
- `ZFS_DATA_DISKS`
|
||
- `ZFS_HOT_SPARES`
|
||
- `ZFS_DISKS`
|
||
- `ZFS_SPARE_DISKS`
|
||
- `ZFS_PREFIX`
|
||
- `GPU_DEVICE`
|
||
- `SND_DEVICE`
|
||
|
||
That writes:
|
||
|
||
- `system.txt` — a human summary
|
||
- `system.env` — hardware contract values the installer can use directly
|
||
- `inspect-facts.env` — richer machine-readable inspect metadata
|
||
- `suggested-setup.txt` — lines you can copy back into `setup.txt`
|
||
- raw artifacts such as `dmesg.txt`, `ifconfig.txt`, `zpool-status.txt`,
|
||
`zfs-list.txt`, and `pf-interfaces.txt`
|
||
|
||
The summary includes:
|
||
|
||
- detected disk device names
|
||
- observed ZFS pools, layouts, and datasets
|
||
- suggested `INSTALL_MODE`
|
||
- suggested `ZFS_LAYOUT`, `ZFS_DATA_DISKS`, and `ZFS_HOT_SPARES`
|
||
- detected interface names you can later reuse for PF/network setup
|
||
|
||
## The four lines
|
||
|
||
Open the first-boot setup file (`setup.txt`) in a text editor. The
|
||
required cognitive surface is **four lines**:
|
||
|
||
```text
|
||
OPENROUTER_API_KEY=sk-or-v1-...
|
||
TELEGRAM_BOT_TOKEN=123456:AA...
|
||
TELEGRAM_ADMIN_ID=12345678
|
||
ASSISTANT_NAME=Atlas
|
||
```
|
||
|
||
That is the minimum for a working install. Everything else in the
|
||
first-boot setup file is either prefilled with a sensible default or
|
||
optional. Leave any line you don't care about blank — the installer
|
||
will fall back.
|
||
|
||
### What each line does
|
||
|
||
| Line | What it controls |
|
||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `OPENROUTER_API_KEY` | The LLM provider used for chat and compaction. |
|
||
| `TELEGRAM_BOT_TOKEN` | The bot identity your assistant runs as. |
|
||
| `TELEGRAM_ADMIN_ID` | The one Telegram account that can talk to it as operator. |
|
||
| `ASSISTANT_NAME` | What your assistant calls itself in chat. It is display-only and does not rename shared DBs, datasets, or the service account. Blank → `Clawdie`. |
|
||
|
||
## Optional fields worth knowing about
|
||
|
||
You don't have to fill these in. They exist because you might want
|
||
them later or because some installs need them on day one.
|
||
|
||
### Install mode
|
||
|
||
```text
|
||
INSTALL_MODE=auto
|
||
```
|
||
|
||
- `auto` — detect an existing install and choose fresh vs upgrade
|
||
- `fresh` — fail if an existing install is detected
|
||
- `upgrade` — require an existing install and upgrade it in place
|
||
- `rescue` — require an existing install and attempt recovery/repair
|
||
|
||
`auto` is the default and the right starting point for most
|
||
operators. It also supports reinstall, upgrade, and rescue flows when paired
|
||
with `system.env` and existing ZFS metadata.
|
||
|
||
If you ran inspect first, `system.txt` will suggest the install mode
|
||
it thinks fits the machine right now.
|
||
|
||
### Profile
|
||
|
||
```text
|
||
PROFILE=balanced
|
||
```
|
||
|
||
`economy`, `balanced`, or `quality`. Maps to a coordinated bundle
|
||
of chat + fallback + compaction models. `balanced` is the prefilled
|
||
default. Switch only if you have a reason.
|
||
|
||
### Time zone and hostname
|
||
|
||
```text
|
||
TIMEZONE=Europe/Ljubljana
|
||
HOSTNAME=
|
||
```
|
||
|
||
`TIMEZONE` defaults to `UTC`. `HOSTNAME` defaults to `clawdie` unless you set it explicitly. It does not derive from `ASSISTANT_NAME`.
|
||
|
||
### Storage layout
|
||
|
||
```text
|
||
ZFS_POOL=zroot
|
||
ZFS_LAYOUT=single
|
||
ZFS_DATA_DISKS=1
|
||
ZFS_HOT_SPARES=0
|
||
ZFS_PREFIX=clawdie-runtime
|
||
```
|
||
|
||
These fields declare the intended storage shape.
|
||
|
||
- `ZFS_POOL` — pool name, usually `zroot`
|
||
- `ZFS_LAYOUT` — `single`, `mirror`, `raidz1`, or `raidz2`
|
||
- `ZFS_DATA_DISKS` — number of disks in the main data layout
|
||
- `ZFS_HOT_SPARES` — standby disks reserved as hot spares
|
||
- `ZFS_PREFIX` — dataset root for the install
|
||
|
||
Defaults:
|
||
|
||
- `single` is the default because it matches the simplest one-disk install
|
||
- `single` is **not** the recommended long-running layout
|
||
- `raidz1` is the recommended durable multi-disk shape once the
|
||
operator opts in
|
||
|
||
Examples:
|
||
|
||
```text
|
||
# Simple one-disk install
|
||
ZFS_LAYOUT=single
|
||
ZFS_DATA_DISKS=1
|
||
ZFS_HOT_SPARES=0
|
||
|
||
# Three-disk raidz1
|
||
ZFS_LAYOUT=raidz1
|
||
ZFS_DATA_DISKS=3
|
||
ZFS_HOT_SPARES=0
|
||
|
||
# Four disks total: 3 in raidz1, 1 hot spare
|
||
ZFS_LAYOUT=raidz1
|
||
ZFS_DATA_DISKS=3
|
||
ZFS_HOT_SPARES=1
|
||
```
|
||
|
||
The installer derives full dataset paths from these values. You do
|
||
not type raw dataset paths into the first-boot setup file.
|
||
|
||
These storage declarations are also part of the long-term upgrade
|
||
contract. The installer can later compare:
|
||
|
||
- `setup.txt` operator intent
|
||
- `system.env` hardware intent
|
||
- persisted ZFS dataset metadata
|
||
|
||
For advanced operators, that metadata lives as ZFS user properties on
|
||
the root dataset `<ZFS_POOL>/<ZFS_PREFIX>`, using keys such as:
|
||
|
||
- `org.clawdie:install-uuid`
|
||
- `org.clawdie:setup-schema`
|
||
- `org.clawdie:system-schema`
|
||
- `org.clawdie:iso-release`
|
||
- `org.clawdie:iso-commit`
|
||
- `org.clawdie:assistant-name`
|
||
- `org.clawdie:hostname`
|
||
- `org.clawdie:telegram-admin-hash`
|
||
- `org.clawdie:zfs-layout`
|
||
- `org.clawdie:zfs-data-disks`
|
||
- `org.clawdie:zfs-hot-spares`
|
||
|
||
This is deliberate: the upgrade fingerprint lives with the data,
|
||
survives reflash, and can be inspected with native `zfs get`.
|
||
|
||
If you ran inspect first, it will already have written suggested
|
||
storage lines based on:
|
||
|
||
- detected disk count
|
||
- observed ZFS pool topology
|
||
- existing pool/dataset state when present
|
||
|
||
### Dashboard credentials
|
||
|
||
```text
|
||
OPERATOR_EMAIL=you@example.com
|
||
OPERATOR_PASSWORD=...
|
||
```
|
||
|
||
Optional. If you set both, the dashboard is preconfigured. If you
|
||
leave them blank, the dashboard waits until you run
|
||
`npm run set-operator -- <email>` from the running system. Telegram
|
||
is your operator interface in the meantime.
|
||
|
||
> **Plaintext warning:** `OPERATOR_PASSWORD` lives in plaintext on
|
||
> the install media until you reformat it. The installer will warn
|
||
> you about this in the post-install summary. Reformat the USB
|
||
> before storing it or handing it to anyone else.
|
||
|
||
### Headless / unattended access
|
||
|
||
For installs you don't sit at the console for:
|
||
|
||
```text
|
||
SSH_AUTHORIZED_KEY=ssh-ed25519 AAAAC3... you@laptop
|
||
CLAWDIE_USER_PASSWORD=
|
||
```
|
||
|
||
`SSH_AUTHORIZED_KEY` is the recommended way. Public keys are not
|
||
secrets, so plaintext on the USB is fine. When set, the installer
|
||
enables key-only SSH for the service user and disables password
|
||
SSH.
|
||
|
||
`CLAWDIE_USER_PASSWORD` is a fallback for console login or sudo,
|
||
used only if no SSH key is provided. Same plaintext warning as
|
||
above. **There is no `ROOT_PASSWORD` field** — root login is
|
||
locked by design. Use `sudo` from the service user.
|
||
|
||
## Where The First-Boot Setup Lives
|
||
|
||
The flashed USB exposes a writable FAT32 config surface. Edit these files there:
|
||
|
||
- `setup.txt`
|
||
- `system.env`
|
||
|
||
The installer reads those files on first boot.
|
||
|
||
Why this path works:
|
||
|
||
- it is writable from Windows, macOS, Linux, and FreeBSD
|
||
- it supports long API keys without console typing
|
||
- it works with the inspect loop naturally
|
||
- it matches the ISO repo’s existing “editable config on removable media”
|
||
direction without requiring a second USB
|
||
|
||
Operator flow:
|
||
|
||
1. Flash the USB.
|
||
2. Reinsert it on your normal computer.
|
||
3. Open the writable config surface.
|
||
4. Edit `setup.txt`.
|
||
5. Optionally leave `system.env` blank and let inspect populate it.
|
||
6. Boot the target machine from that USB.
|
||
|
||
The installer treats:
|
||
|
||
- `setup.txt` as operator intent
|
||
- `system.env` as hardware intent
|
||
|
||
Both files are versioned and can be compared against persisted ZFS
|
||
metadata during later upgrade and rescue flows.
|
||
|
||
## Boot
|
||
|
||
1. Plug the USB into the target machine.
|
||
2. Power on. Boot from USB (BIOS/UEFI key varies by hardware).
|
||
3. The installer reads the first-boot setup and proceeds without prompting.
|
||
|
||
Expect first boot to take several minutes — the installer
|
||
provisions databases, jails, and copies skills. The post-install
|
||
summary tells you what's ready and surfaces any warnings (plaintext
|
||
passwords, unset optional fields).
|
||
|
||
## After first boot
|
||
|
||
### Talk to your assistant
|
||
|
||
Open Telegram, find the bot you registered, send `/start`. If your
|
||
`TELEGRAM_ADMIN_ID` matches, you're talking to your operator
|
||
channel.
|
||
|
||
### Set dashboard credentials (if you skipped them)
|
||
|
||
If you left `OPERATOR_EMAIL` / `OPERATOR_PASSWORD` blank in the
|
||
first-boot setup:
|
||
|
||
```bash
|
||
npm run set-operator -- you@example.com
|
||
```
|
||
|
||
You'll be prompted for a password (twice, no echo). The dashboard
|
||
becomes available afterwards. This is first-set only — to rotate a
|
||
password later, use the dashboard's change-password flow.
|
||
|
||
### Switch off OpenRouter
|
||
|
||
OpenRouter is the bootstrap path, not a permanent commitment. To
|
||
move chat to a direct provider:
|
||
|
||
- **Per chat:** `/model` in Telegram lets you swap provider/model
|
||
for a single chat.
|
||
- **System-wide:** set `DEEPSEEK_API_KEY` in `provider.env`.
|
||
The agent uses DeepSeek by default.
|
||
[Provider Fallback](../operate/provider-fallback/).
|
||
|
||
### Reformat the install media
|
||
|
||
If your first-boot setup contained any password fields
|
||
(`OPERATOR_PASSWORD`, `CLAWDIE_USER_PASSWORD`), the installer did
|
||
**not** delete the file (by design — operators have lost installs
|
||
to overzealous auto-wipes). Reformat the USB once you've confirmed
|
||
the install is healthy.
|
||
|
||
## Troubleshooting
|
||
|
||
Check the first-boot log and progress files first:
|
||
|
||
```bash
|
||
tail -100 /var/log/${AGENT_NAME}-firstboot.log
|
||
cat /var/log/${AGENT_NAME}-firstboot.progress
|
||
```
|
||
|
||
If the install stopped after a named setup step, resume from that step:
|
||
|
||
```bash
|
||
cd /home/${AGENT_NAME}/clawdie-ai
|
||
just install-from <step>
|
||
```
|
||
|
||
For a full post-install verification pass, use the
|
||
[Fresh install checklist](./fresh-install-checklist/).
|
||
|
||
## Reflashing later
|
||
|
||
Use `INSTALL_MODE` to choose how the installer treats an existing system:
|
||
|
||
- `auto` — detect the existing install and choose the safest path
|
||
- `fresh` — fail if an existing install is detected
|
||
- `upgrade` — require an existing install and upgrade it in place
|
||
- `rescue` — require an existing install and attempt recovery/repair
|
||
|
||
Keep backups before upgrade or rescue work, especially when changing storage
|
||
layout values.
|
||
|
||
## Related
|
||
|
||
- [ISO Install](./iso/) — image selection, USB writing, rebuild path.
|
||
- [Operator Commands](../operate/operator-commands/) — what's
|
||
available in Telegram once your assistant is up.
|
||
- [Provider Fallback](../operate/provider-fallback/) — how the
|
||
agent stays alive when a provider hits a usage cap.
|