Project config

Three optional files in a workspace’s .serin folder tell Serin what detection can’t. Commit them with the project. Values of environment variables never go in them.

.serin/run.json

How the workspace runs. Targets listed here come first and replace detected ones with the same id.

{
  "default": "api",
  "targets": [
    { "id": "api", "command": "uv run api", "cwd": "backend", "port": 9000 }
  ]
}
FieldMeaning
defaultThe target Run starts. The one you ran last wins over it.
detectfalse uses only the targets listed here. Default true.
targets[].commandRequired. The shell command that starts it.
targets[].idStable name for the target.
targets[].nameWhat the Run chip shows. Defaults to the id.
targets[].cwdFolder to run in, inside the workspace. Default .
targets[].portThe port it would like. Serin may pick another.
targets[].installA command to run first, like installing packages.
targets[].webfalse for a worker with nothing to preview. Default true.
targets[].envExtra environment variables, name to value.
targets[].frameworkA label, like fastapi.

Every target gets PORT and HOST (127.0.0.1). Use them in your command, as the FastAPI starter does:

{
  "default": "api",
  "detect": false,
  "targets": [{
    "id": "api",
    "install": "(test -d .venv || python3 -m venv .venv) && .venv/bin/pip install -q -r requirements.txt",
    "command": ".venv/bin/uvicorn main:app --reload --host 127.0.0.1 --port $PORT",
    "port": 8000
  }]
}

.serin/serin.json

What the project needs: how to publish it, which environment variables it expects, and its ports.

{
  "publish": { "dir": "dist", "command": "npm run build", "spa": true, "site": "my-site" },
  "env": {
    "DATABASE_URL": "Postgres connection string",
    "SENTRY_DSN": { "description": "Error reporting", "optional": true }
  },
  "ports": [3000]
}

publish

FieldMeaning
dirThe folder whose files become the site, inside the workspace. Default: found from the framework.
commandThe build. Default: the build script in package.json. false publishes dir as it is.
cwdWhere the build runs and package.json is read. Default .
spatrue answers unknown paths with /index.html. Default: on when the build has exactly one HTML page.
siteThe site name to publish to when none is given.

More on how builds are found in Publish.

env and ports

env names the variables the project needs, each with a description. It can also be a list of names. "optional": true means the project runs without it. The app lists any that have no value yet under Needed by This Project. ports lists the ports the project uses.

.serin/agent.json

What proves an agent’s edit didn’t break anything.

{ "verify": ["npx tsc --noEmit"] }

Each verify command runs in the workspace. An agent’s edit, write_file and approved propose_edit apply only when they all pass. Check Project in the Problems panel runs the same list, and reads file:line:col errors from their output.

Without the file, Serin runs tsc --noEmit when there’s a tsconfig.json, plus Vitest or Jest on tests related to the changed files. Check Project also runs pyright for Python when it’s installed.

Starterverify
Next.js, Vite + Reactnpx tsc --noEmit
Astro blognpx astro build
Express APInode --test
FastAPI.venv/bin/python -m py_compile main.py
Websitenode --check script.js

Environment variables

Set them in the app under Environment Variables. The values stay on the server, outside the workspace, so they never show up in the file list, git status, a checkpoint or a copy of the workspace.

  • New terminals and run targets get them. Restart a running target to pick up a change.
  • Import copies the values from a .env file in the workspace. Nothing reads .env files on its own. The app warns when Git doesn’t ignore one.
  • Names use letters, digits and _, and don’t start with a digit.
  • Hide from Agents keeps a variable out of terminals an agent opens for itself. An agent you start in your own terminal still sees it, like any program there.