Skip to main content
A service is anything that listens on a port, such as a web app, an API, or a worker with a health endpoint. Tesser learns about your services from TOML files in .claude/skills/tesser/, one per service. The filename is the service name.

The smallest manifest

ports lists the ports the app listens on. setup prepares the box and runs before every dev, so it needs to be safe to run repeatedly, and dev starts the server. With this file in place, tesser dev web does everything else. The manifest reference describes every field. The ones most projects use are:
  • root = "apps/web", for a monorepo, so commands run inside the package directory.
  • [health] path = "/healthz", when a port being open is not enough to know the app is ready.
  • [deps], when this service calls another one. See Wiring services together.
  • [env], for env files and the names of required secrets, described below.

Starting, restarting, and stopping

tesser dev web creates or reuses the box for this worktree and service, syncs the worktree, runs setup, starts dev, waits until the port answers, and prints the box id. Running it again replaces the server, which is also how you recover from a crashed one. tesser dev <box_id> -- <cmd> runs a command you specify instead of the manifest’s dev. tesser sync <box_id> pushes your edits so the server’s hot reload can pick them up. tesser sync <box_id> --restart pushes the edits and then runs setup and dev again, which you need for changes the running server cannot take on its own, such as env changes, config changes, or a new dependency. tesser stop <box_id> stops the server and leaves the box running. tesser logs <box_id> -f follows the server’s output.

Env files

Sync follows .gitignore, so a file like .env.local is not copied to the box. When a dev server fails because a variable is missing, this is usually the reason. There are two ways to handle it. The first is to list the file in the manifest, after which every sync includes it:
The second is to push it once with tesser env push <box_id>, which sends every gitignored .env* file at the worktree root, or the specific files you name. After a push, run tesser sync <box_id> --restart so the server starts with the new values.

Secrets for shared instances

A pinned instance is the shared copy of a service that teammates’ boxes connect to (see Wiring services together). It has no laptop attached, so its env values come from the org instead:
Tesser stores the values and injects them when the instance starts. They never appear in the manifest, which is committed to the repository. Listing a variable in the manifest as env.required = ["DATABASE_URL"] makes a missing value a clear error at start time instead of a crash later.

Several services in one repository

Each service gets its own file. tesser dev api and tesser dev web then create two boxes from the same worktree, and if web.toml lists api as a dependency, the web box’s api port is connected to your api box automatically. When a repository has more than one manifest, dev and make require the service name; when it has exactly one, that service is the default. Service names are shared across the whole org, so api refers to the same service in every repository. If a second repository tries to register a name that the first already owns, the registration fails with an error.