Every day

Projects

Adding a project, what the agent does with it, and how it reaches a URL.

A project is a git repository or a folder on the server. The agent keeps a registry of them, and the app shows what the registry says. Nothing is inferred.

Adding one

“New project”, on the dashboard or in the sidebar. Give a git URL or point at a folder that already exists under the projects directory; the agent reads the source before declaring anything — the package manager it locks, the script that starts it, the port it asks for if that port is free — and the form proposes the rest: a name taken from the address, a port no declared project holds, the start command. Everything stays editable; the server decides what it accepts and says so. Once created, the agent:

  1. clones the repository into your projects directory, or adopts the folder;
  2. detects the package manager and installs dependencies;
  3. reads whatever start command the project declares;
  4. starts it in a tmux session named after the project;
  5. reports the port it listens on, the URL, and the log stream.

If a step needs something the machine does not have — a runtime you did not install, a database the project expects — it says so and names the module that provides it.

Where it lives

Projects sit under the directory you chose during core.system, one folder per project, owned by dev. The registry records the absolute path, so a project moved by hand is a project the agent can still find.

Running and stopping

Starting and stopping go through tmux. That is deliberate: a project keeps running when you close the app, when your laptop sleeps, and when your connection drops. Reopen the app and it reattaches to the same session with the same scrollback.

Logs

The app streams the session output as the agent reads it. The scrollback is the tmux scrollback; nothing is buffered on the platform, and nothing is stored anywhere but the machine.

Environment files

If tool.1password is installed, a project that carries an .env.local template gets its file generated from your vault when it starts. Without it, you manage environment files yourself, in the folder, like anywhere else.

Variables from Pupitre

Every project receives what Pupitre knows about it in PUPITRE_* variables: its port, its local and public addresses, those of its other processes, the server’s domain. A script reads them instead of hardcoding them, and when you publish a route or change the domain in the app, the project follows without a code change.

VariableValue
PUPITRE1, everywhere on the server
PUPITRE_PROJECTS_DIRthe projects root
PUPITRE_DOMAINthe published domain, absent without exposure
PUPITRE_PROJECT, PUPITRE_PROJECT_DIR, PUPITRE_PROJECT_URLthe project’s name, folder and address
PUPITRE_PROCESS_<ID>_PORT, PUPITRE_PROCESS_<ID>_URLthe port and address of each process of the project
PUPITRE_PROCESS, PUPITRE_PROCESS_DIRthe process’s id and folder
PUPITRE_HOST, PUPITRE_PORTwhere the dev server must listen
PUPITRE_URLits public address when there is one, its local one otherwise
PUPITRE_LOCAL_URL, PUPITRE_PUBLIC_URLeach of the two; the public one is absent until something is published
PUPITRE_ROUTE_<LABEL>_PORT, PUPITRE_ROUTE_<LABEL>_URLthe port and address of each route of the process

<ID> and <LABEL> are upper-cased, a dash becoming an underscore: process api-v2 gives PUPITRE_PROCESS_API_V2_URL. A client writes for instance vite --host $PUPITRE_HOST --port $PUPITRE_PORT, or points at its API with PUPITRE_PROCESS_API_URL.

A process the app starts receives all of them at start and keeps them: when they change, the app flags the process, and a restart applies the new ones. A terminal receives them for the folder it stands in, at every prompt. PORT and HOST are never set, so your repository’s .env.local stays in charge; no secret goes through them.

Exposure

By default a project listens on localhost and the app reaches it through the SSH session it already holds. That is a state, not a module: tick no exposure and nothing is published. With exposure.cloudflare installed, you can give a project a route on your domain — one tunnel for the machine, one route per project, DNS and certificate handled; exposure.caddy does the same for a domain outside Cloudflare.

A published address opens only with an access key: the project is protected as soon as it is declared, and a process can be left public for a webhook receiver. The app adds this computer’s key to every address it opens. See access to addresses. A script on the server that calls its own project goes through PUPITRE_LOCAL_URL: PUPITRE_PUBLIC_URL asks it for a key like any client.