# First boot

From nothing to a node answering on your own machine, then the settings that decide what it is once it is more than a local experiment.

## Start it

The server needs one secret and one directory:

```bash
export AUTOMATON_VAULT_KEY="$(openssl rand -hex 32)"
automaton -data /var/lib/automaton
```

It listens on `127.0.0.1:8080` and serves everything on that one port: the API at the root, the MCP endpoint at `/mcp`, the sign-in callback at `/oauth/callback`, and inbound messages from connected apps at `/webhooks/{app}`, which sit outside the authenticated API because the app sending them holds no key.

The first start creates the databases under the data directory and applies their migrations. Every later start applies whatever migrations are new and leaves the rest alone, so there is no separate migration step to run and no window in which the schema and the binary disagree.

## The vault key

`AUTOMATON_VAULT_KEY` is thirty-two bytes, hex encoded. It seals every stored credential and every signing secret the deployment holds. There is no plaintext fallback and no derivation from a password: a missing or malformed key means the server refuses to start, which is the only acceptable behaviour from a process holding other people's credentials.

Three rules follow from that, and all three are worth writing into your runbook before the first real connection is made.

1. Back the key up somewhere that is not the data directory and not the same machine. A backup of the directory without the key restores nothing readable.
2. Hand it to the process the way you hand any secret to a process. An environment variable read from your secret manager at start is the shape this expects.
3. Understand what losing it costs. Every connected account becomes unreadable and every person has to connect again. The receipts survive, because a receipt records what happened rather than the credential it happened with.

## The settings that matter

| Setting | Default | What it decides |
|---|---|---|
| `-addr` | `127.0.0.1:8080` | Where it listens. Loopback until you mean otherwise. |
| `-data` | `data` | The directory holding every database. Back this up. |
| `-toolkits` | `catalog/toolkits` | The app definitions loaded at startup. |
| `-expansions` | `catalog/expansions` | The phrases that make search find the right action. |
| `-callback-url` | localhost | The address you registered with each provider for sign-in. |
| `-signup` | closed | Who may create an account: `open`, `invite`, or `closed`. |
| `-console-url` | localhost | Where the console is, for the links in the emails the node sends. |
| `-browser-origins` | empty | Which browser origins may call the API. |
| `-mail-driver` | log | How email is sent. `log` prints it instead of sending it. |

Two of those are the difference between a laptop experiment and something a second person can use. `-mail-driver` still set to `log` means sign-in links are printed to the node's own log rather than delivered, which is fine for one person and wrong for everybody else. `-signup` still at `closed` means nobody but you can create an account, which is the correct default and a deliberate change when you open it. [Hardening](../operating/hardening.md) covers both, along with the settings that face a network.

## Registering the sign-in applications

Connecting an app means atmon acting as an application registered with that provider, and the registrations behind the hosted service are ours. On your own node you register your own, once per app you intend to use, against your own callback address.

Until you do, connections to that app cannot be made. Everything else works, including the whole refusal path: a call with no connected account comes back as `not_connected` before anything is sent anywhere.

## Check it

Two probes answer without a key, and an orchestrator reads them rather than guessing from the port being open. `/healthz` asks whether the process is alive, and a failure there means restart it. `/readyz` asks whether it wants traffic, and a failure there means stop sending it any. A draining node answers the first and refuses the second, which is what makes a rolling restart end in completed calls rather than reset connections.

The same binary probes them, because the deployment image carries no shell and no `curl`:

```bash
automaton health -url http://127.0.0.1:8080
automaton health -url http://127.0.0.1:8080 -ready
```

## Then

- [Claiming a deployment](./claiming-a-deployment.md), to make the first account.
- [The console on your node](./the-console.md), to give people a browser interface.
- [Hardening](../operating/hardening.md), before it faces anything but your laptop.
