> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boxd.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Jobs

> How a script becomes a background job: states, retries, waiting on a connection, sleep and wake, forks, and what the console shows.

`run <file>.run.ts` runs a script. What happens next depends on what the script did while its top level ran.

## One-shot or long-runner

* **One-shot**: the script never called `.on(...)` or `every(...)`. It runs like a command. Output streams to your terminal, it returns with the script's own exit code, and it leaves nothing behind whether it succeeded or failed. Nothing is registered and nothing retries.
* **Long-runner**: the script registered a trigger or a schedule. Its top-level output streams to your terminal until it settles, then it is moved to the **background** as a job and your shell returns:

```
a3f01: running in the background - `run logs a3f01` to follow
```

The detection is automatic. There is no flag.

## Identity: one file, one job

A job is identified by its file's absolute path, and its 5-character id is derived from it. Running the same file again **replaces** the running job with the new version, under the same id. That is the deploy flow: edit, `run` the file, done. Subscriptions and schedules don't churn on a replace.

```bash theme={"theme":"github-dark"}
run jobs                 # every registered job and its state
run logs a3f01           # its log
run logs a3f01 -f        # …followed live
run stop a3f01           # stop it; triggers unmount, the row stays
run restart a3f01        # stop + re-register from the file (resets the retry counter)
run remove a3f01         # stop and forget it everywhere (alias: rm)
```

```text theme={"theme":"github-dark"}
$ run jobs
ID      STATE        SCHEDULE           STARTED   PATH
a3f01   background   1d                 3h ago    /home/boxd/my-automation/daily-digest.run.ts

`run logs <ID> [-f]` follows one · `run stop|restart|remove <ID>` manages it
```

Jobs survive reboots. Everything in the `background` or `retrying` state is started again when the machine boots. A job's log is one file per job, rewritten each time the job (re)starts, and `run logs -f` notices and re-prints from the top.

## What a job runs with

A job is started by the machine, and it inherits more than a bare environment:

|                      |                                                                                                                                                                   |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Working directory    | the script's own directory, so `bun add` next to it and relative paths both work                                                                                  |
| `HOME`               | `/home/boxd`                                                                                                                                                      |
| Env vars and secrets | everything from [`boxd env set`](/guides/env-secrets) that applies to this machine, re-read at every start, so a new value lands on the next run without a reboot |
| Machine identity     | `BOXD_VM_NAME`, `BOXD_VM_ID`, `BOXD_VM_HOST`                                                                                                                      |
| Your laptop          | the device the job was started from, so [`boxd.local`](/guides/automations/scripts#boxdlocal-your-laptop) keeps working in the background                         |

Jobs currently run as `root`, so files they create are owned by `root` even under `/home/boxd`. Use `sudo chown boxd:boxd` if you need to edit them from a shell afterwards.

## States

| State        | Meaning                                                                  |
| ------------ | ------------------------------------------------------------------------ |
| `running`    | The top level is still executing                                         |
| `background` | Registered a trigger or schedule, running and waiting for work           |
| `blocked`    | Waiting on a connection that isn't there yet, never started              |
| `retrying`   | Crashed, will be started again shortly (attempt *n* of 5)                |
| `failed`     | Crashed five times, parked until you `run restart` it or re-run the file |
| `exited`     | Finished, or stopped with `run stop`                                     |

## Waiting on a connection

Before starting a script, boxd works out which connections it needs: every integration it imports, at the scope it uses, across all local imports. Anything missing is reported up front, and the job waits instead of failing at its first call:

```
a3f01: waiting on linear (personal), slack (scope: ops) - it will start on its own once connected
```

Connect the named integration (the console's Integrations page, or `run auth linear`) and the job starts by itself. The message always names the **scope**. A personal Linear does not satisfy `linear.scoped("ops")`. The console shows the same job as **Waiting to connect**.

## Crashes and retries

A **long-running** job that exits with an error is restarted with exponential backoff (2s, 4s, 8s, 16s) up to five attempts, then parked as `failed` with the log kept for inspection. A one-shot is never retried and just reports its exit code. If the job crashes while you're still watching the first run, your terminal comes back at the first retry rather than waiting out the ladder:

```
a3f01: crashed - retrying in the background (`run logs a3f01 -f` to watch, `run jobs` for its state)
```

When the ladder is exhausted:

```
a3f01: failed - `run logs a3f01` for the full log
```

A `failed` job is **not** restarted on boot or after a fork. That's deliberate, so a crash loop can't hide by resetting itself. Fix the script and re-run the file, or `run restart <id>`. Errors thrown *inside* a trigger or schedule handler are logged and don't count as crashes.

Long-runners should be **restart-tolerant**. boxd restarts them on fork, snapshot restore, ownership change, and whenever the team's set of integrations changes. Nothing in flight is preserved across a restart, so put anything that must survive in [`object(...)`](/guides/automations/scripts#durable-state).

## Sleep and wake

A machine with automations still auto-suspends and hibernates like any other. An idle job makes no network calls of its own, so it never keeps the machine awake.

* **Schedules**: the platform holds the next time any `every(...)` on the machine is due and wakes a hibernated machine shortly before it. A cron slot that a late wake slipped past fires once on wake.
* **Events**: a trigger firing wakes the machine and delivers the event. Events that arrive while the machine is waking are held for up to **15 minutes**. Anything older than that is dropped rather than delivered arbitrarily late.

## Forks and snapshots

A forked machine, or one restored from a snapshot, inherits every job of its source and starts them immediately (except `failed` ones). The console write-up and diagram come along, since they're keyed by the script's content. Forking is the way to run the same automation many times over.

## The console

**Automations** in the sidebar lists every long-running job across your machines. The page is read-only. The files on the machine are the source of truth.

Each card shows:

* a generated **name** (editable, click the pencil on the detail view) and one-line **summary**.
* **Triggers** in plain English (*Every morning at 9am*, *Linear issue created*) and **Uses**, the integrations it calls, with their logos.
* a status pill: **On** (or **Off**), **Waiting to connect**, **Retrying (2/5)**, **Failed**, **Stopped**.
* the last run time. A waiting card says exactly what it's waiting on, and a failed card shows the last error and the `run logs <id>` command to inspect it.

A banner at the top counts automations that **need attention**. Filters: **All**, **Active**, **Needs attention**. Search matches the script's name and file path.

Open a card for the detail view: the machine and file, **How it flows** (a pan-and-zoom diagram of triggers, schedules, integrations and the machine, with labelled arrows and a description on hover over each node) and a write-up in four sections, **Overview**, **Behaviour**, **Integrations** and **Notes**. The same cards appear on each machine's **Automations** tab, and its Overview shows compact tiles.

### Generated documentation

The name, summary, trigger wording, diagram and write-up are generated automatically a few minutes after an automation first runs, from the script and its local imports. Before a script's source leaves the machine, anything that looks like a credential (API-key prefixes, tokens, long random strings) is redacted, and the write-up treats those as opaque secrets. When you change the script, the documentation is regenerated with the previous version as reference, so names and diagram layout stay stable rather than reshuffling on every edit. Very large scripts are summarized from a capped portion of the source and the write-up says so.

Only long-running jobs are documented. A one-shot script leaves nothing behind.
