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

# Service rules

> Dev runs, dependencies, shared instances, and wiring

The exact rules for dev runs and dependencies, for looking things up. What a single command
does is in the [CLI reference](https://tesser.sh/docs/cli-reference.md), and timers and box sizes are
in [Configuration](https://tesser.sh/docs/config.md).

The [Services](https://tesser.sh/docs/services.md) and [Connect a frontend to a backend](https://tesser.sh/docs/wiring.md) pages explain the ideas.

## Dev runs

### When `tesser dev` restarts the server

`tesser dev` reuses the box for this worktree and service. On a box whose
server is already running, it only syncs, and prints when the server started
and that its inputs are unchanged. It starts the server again when:

* the server is not running, or its app crashed under a file watcher
* the manifest or the env changed
* a file `setup` or `dev` depends on changed:
  * the env files the manifest lists
  * any `package.json`, lockfile, or toolchain pin such as `.nvmrc`
  * a `Dockerfile` or compose file
  * any file the `setup` command names
  * any shell script the `dev` command names (a `.sh`, `.bash` or `.zsh`
    file, or one with no extension)
  * every file in the manifest's directory except `.md` files and the other
    services' manifests

A file a command names counts only when it is not gitignored, so a build
output such as `node_modules/.bin/...` never forces a restart. A source file
the `dev` command names, such as `src/server.ts` in
`bun --hot src/server.ts`, does not count either, because the server reloads
it.

### While `tesser dev` waits

* It prints the last line of the server's log every 30 seconds. When the
  health check keeps returning the same 5xx status, the line says for how
  long.
* When `setup` prints nothing new for 5 minutes, it prints a warning with the
  last line and keeps waiting. Raise `[health] timeout` only when that line
  still shows progress.

### A service name instead of a box id

`tesser status`, `tesser logs`, `tesser sync`, and `tesser wait` take a
service name for this worktree's box of that service.

* When the worktree has no box for it, `status`, `logs`, and `wait` use the
  service's shared instance, and `status` and `logs` say so on stderr.
* `sync` never falls back to the shared instance. It fails and suggests
  `tesser dev <service>`.
* For a script, `tesser status <service> --json | jq -r .boxId` prints the
  box id.

## Dependencies and shared instances

### A dependency with nothing behind it

* `dev`, `make <service>`, and `make --ensure-running` still start the box,
  and print one line per such port on stderr:

  ```text theme={"theme":"css-variables"}
  tesser: dep :5001 (api) has nothing behind it: tesser wire <box_id> api <target> or tesser make api --ensure-running <sha>
  ```

* When this worktree has a manifest for `api`, the line first names
  `tesser dev api` in this worktree, which backs the dependency even when it
  starts after this box (see
  [Your own box from the same worktree](#your-own-box-from-the-same-worktree)).

* When a `make api --ensure-running` has started a new shared instance but
  not switched over yet, that instance is **staged**, and the line names it:

  ```text theme={"theme":"css-variables"}
  tesser: dep :5001 (api) waits on api's instance staged at <short_sha> (<box_id>): it routes here once the --ensure-running promoting it finishes; if none is running, `tesser make api --ensure-running <sha>` finishes it (the human's call; ask first)
  ```

  The dependency reaches the new instance as soon as that command finishes,
  with no wire. If the command is no longer running, running it again with the
  same commit finishes the switch.

* `dev` prints these lines when it returns: after the service is serving, or
  just before the error when the run fails, so they come at the end of its
  output. With `--detach`, it prints them when the box starts. `tesser wait`,
  and `tesser sync --restart` without `--detach`, print them when they return.
  Each check reads the box's current wiring, so a port wired in the meantime
  is not reported.

* On the box, every connection to that port is reset. While the service runs,
  the box notes the reset in the dev log (`tesser logs`), at most once a
  minute per port. On the laptop, the request gets a 503 naming the
  `tesser wire` command that fixes it.

### Your own box from the same worktree

* A dependency with no wire and no shared instance reaches your own dev box
  of that service from the same worktree, if one exists. The worktree is the
  folder that `tesser dev` ran in.

* Only boxes you started with `tesser dev` from your own login count. A
  teammate's box, a box from another worktree, and a box started with a
  service-account or CI token never do.

* With several such boxes, the newest one wins. Removing it moves the
  dependency to the next one, or to nothing.

* A wire or a shared instance always takes precedence over this fallback.
  `tesser wire <box_id> api --shared` with no shared instance running says
  the dependency routes to the same-worktree box instead.

* When a shared instance takes precedence over your own box of that service
  from this worktree, `dev` says so and prints the wire that routes the
  dependency to your box:

  ```text theme={"theme":"css-variables"}
  tesser: dep :5001 (api) -> <box_id>, the org's shared instance, not your api box <box_id> from this worktree; tesser wire <box_id> api <box_id> routes it to yours
  ```

  If the box that depends on it started first, `dev api` prints the same
  advice when it returns:

  ```text theme={"theme":"css-variables"}
  tesser: <box_id> (web) routes api to <box_id>, the org's shared instance, not this box; tesser wire <box_id> api <box_id> routes it here
  ```

  Both lines appear only when both boxes are yours.

* `dev` prints the fallback on stderr when it returns:

  ```text theme={"theme":"css-variables"}
  tesser: dep :5001 (api) -> <box_id>, api's box from the same worktree (no wire, no shared instance)
  ```

* `tesser wire <box_id>` lists such a dependency as `api -> <box_id> (same worktree)`.

* If another box from the worktree was already running when this one began
  backing its dependency, `dev` prints:

  ```text theme={"theme":"css-variables"}
  tesser: <box_id> (web) started before <box_id> backed its dep; restart it so it reconnects: tesser sync <box_id> --restart
  ```

* Handing a box to another member clears its worktree, so it stops backing
  your other boxes.

### Waking dependencies

* `dev` starts only the requested services. It wakes the dependencies you own
  and the shared instances it depends on, and once its own server is ready it
  waits for them to serve, printing which ones it is waiting on.
* If a dependency is removed, its server exits, it is still asleep after 15
  seconds, or it does not come up in time, `dev` reports that it is not
  serving and finishes anyway. `tesser status <box_id>` shows where that box
  is.
* `dev` never creates missing dependencies. `make --ensure-running` creates
  shared instances.

### `make --ensure-running`

* It creates a box, checks out the commit, runs `setup` and `dev`, waits for
  the health check, and makes the box the org's shared instance.
* With the same commit again, it does nothing and prints the same box id,
  unless the shared instance is unhealthy, in which case it runs `setup` and
  `dev` again on the same box. With a new commit, it starts a new box, waits
  until it is healthy, switches over, and removes the old box.
* From a worktree that already has its own box of the service, when no shared
  instance runs that commit yet, that box becomes the shared instance and
  keeps its warm build. This applies only when the box is the size the
  manifest sets, is not kept, and has no `--sleep-after` of its own; otherwise
  a new box is created. The CLI resets the box to the commit, deletes files
  git neither tracks nor ignores (installs and build output stay), runs
  `setup` and `dev` again with the org's env values, waits for the health
  check, and switches over. The box then belongs to the org, and the
  worktree's next `tesser dev` of the service starts a new box.
* `--restart` runs `setup` and `dev` again on the same box even while the
  health check passes, for a server that is stuck but still answers it. On a
  staged or draining instance it fails and changes nothing, and on the current
  one it does not switch the org over again.
* A shared instance belongs to the org, so it cannot be synced to. Its env
  comes from `tesser env set` and the committed files in the manifest's
  [`env.load`](https://tesser.sh/docs/manifest#loading-env-files).

### During a switch

* Both boxes run `dev`: the new one from when `make` starts it, the old one,
  if awake, until it is removed 60 seconds after the switch.
* Both see `TESSER_SLOT=pinned`, so a queue keyed on `TESSER_SLOT` has workers
  on both commits, and the old commit can take jobs meant for the new one.
  Removal kills any job still running on the old box. Make jobs safe to retry
  and to run on either commit.
* If a job's producer and workers run on the same box, key the queue on
  `TESSER_BOX_ID` instead. Jobs still in the old box's queue when it is
  removed never run.

### `tesser service show`

Prints one service's repo and, for each shared instance, the full commit sha,
the box id, and a `tesser ssh` command that prints the manifest it runs. That
reads the file on the box, which can differ from the copy committed at that
sha. `--json` prints the same as JSON.

### `tesser wire`

* It refuses a target that is not a box in your org, has been removed, or runs
  a different service than the dependency names.

* After saving, it waits until this laptop's daemon routes the change. If the
  daemon has not loaded the wire after 70 seconds, the command fails, but the
  wire stays saved. `tesser daemon stop` then `tesser use` restarts the
  daemon.

* A wire changes where new connections go but restarts nothing. If the box's
  server ran while the dependency had nothing behind it, its client may have
  given up, and requests can hang even after the wire. Then `wire` prints:

  ```text theme={"theme":"css-variables"}
  tesser: <box_id> ran with api unbacked; restart it from its worktree so it reconnects: tesser sync <box_id> --restart
  ```

* A wire names a box and stays put. If that box is removed, the dependency
  fails loud instead of falling back to the shared instance. Re-wire it or
  return it to `--shared`.

* A wire connects boxes, whichever worktree or repository they came from.

* The panel marks a branch `custom wiring` when a laptop port reaches a
  different box than the box's own dependency on that port, and names any
  route that is failing. Laptop focus can override subtrees without changing
  wires.

* `tesser dev` reuses the box for a worktree and service, so each
  simultaneous frontend-plus-backend test needs its own frontend worktree. In
  a monorepo, run the frontend from the backend change's worktree.

* When both a dashboard and its backend depend on a third service, wire both
  to the copy meant for the test. Focusing the dashboard brings every
  reachable service to localhost, but does not fix inconsistent wiring on the
  boxes.

### `tesser exec --deps-of`

The workbench gets that service's dependency ports for as long as the command
runs. They resolve exactly as for that service's box in this worktree, wires
included. `--in <service>` on its own only changes the working directory to
the service's `root`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.