> ## 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.

# Wiring services together

> How a frontend reaches its backend, how a backend reaches its worker, and how everything else reaches a shared copy of main

Most apps are more than one service, and each service expects to find the
others at a fixed `localhost` port. Tesser keeps that arrangement on the box.
A dependency is a loopback port declared in the manifest, and Tesser connects
that port to the right instance of the other service, wherever it is
actually running.

```toml theme={"theme":"css-variables"}
# .claude/skills/tesser/web.toml
ports = [3000]

[run]
setup = "pnpm install"
dev   = "pnpm dev"

[deps]
5001 = "api"      # localhost:5001 on the web box reaches api
5432 = "db"       # any TCP service, so a database works too
```

The app keeps its existing `API_URL=http://localhost:5001` and nothing in it
needs to change.

## Which instance a dependency reaches

A dependency names a service, not a box. Which box it connects to is decided
separately for each of your boxes, in this order:

1. If the same worktree has its own box for that service (because you ran
   `tesser dev api` from it), your web box's `api` port connects to that box.
   Tesser wires sibling boxes from one worktree to each other automatically.
2. Otherwise it connects to the org's shared instance of `api`, which is the
   pinned copy of `main` described below.
3. If neither exists, `dev` still starts the server and prints a warning that
   the dependency has nothing behind it.

Shared instances never connect back into anyone's dev box, so work in
progress on one person's branch cannot affect a teammate.

## The shared copy of main

A pinned instance runs one service at one specific commit, with no laptop
involved. It is what everyone's boxes use for the services they are not
currently working on.

```sh theme={"theme":"css-variables"}
tesser make api --ensure-running $(git rev-parse origin/main)
```

This creates a box, checks out that commit on it, runs `setup` and `dev`,
waits for the health check to pass, and makes the box the org's default for
`api`. Running the command again with the same commit does nothing and prints
the same box id. Running it with a new commit starts a new box, waits until
the new box is healthy, switches the org's default over, and then removes the
old box. To roll back, run it again with the previous commit.

Usually CI runs this for each service on every push to main. The box belongs
to the org rather than to a person, so it cannot be synced to, and its env
values come from `tesser env set` rather than from a file (see
[Services](/docs/services#secrets-for-shared-instances)).

When you start a service whose dependencies are pinned, Tesser wakes or
starts those dependencies first.

## Pointing a dependency somewhere else

```sh theme={"theme":"css-variables"}
tesser override <box_id> api <other_box_id>
tesser override <box_id> api --clear
```

The first command points one box's `api` dependency at a box you choose, for
example a teammate's box or an older build. `--clear` returns it to whatever
Tesser would choose on its own. The dependency switcher in the panel does the
same thing with a click.

## Tests that need the dependencies

A workbench has no dependencies of its own, because it does not serve
anything. For an integration test that expects `localhost:5432` to be a
database, borrow a service's dependencies for the run:

```sh theme={"theme":"css-variables"}
tesser exec --deps-of api -- pnpm test:integration
```

The workbench gets `api`'s dependency ports for as long as the command runs
and releases them afterwards. `--in api` on its own only changes the working
directory to `api`'s `root`.

## Names

Service names are shared across the org. `api` means the same service whether
the manifest lives in this repository or another one, which is what lets a
dependency declared in one repository reach a service declared in another.
Registering a name that another repository already owns is an error.
