A local environment
For: Contributors developing the web UI.
Run the local development environment to work on the web UI with populated pages. It starts a dev server with embedded Temporal, two nodes, a stand-in model API and the Vite development server, then seeds sample records. No model key or paid model call is needed.
This environment binds to loopback on one machine. Use the quickstart to try an agent first.
With Tilt
Tilt runs each part as a resource with its own log and status, rebuilds the binaries and the runtime bundle when Go or runtime files change, and restarts the server and the nodes after.
tilt up # the Tilt UI: http://localhost:10350
tilt up -- --no-docs # without the docs' dev server
tilt up -- --no-web # without the web UI on Vite
tilt down # stop everything; the data stays
The Tilt UI links to the web UI, the Temporal UI and the docs. Tilt's update check and usage statistics are off in the Tiltfile.
To install Tilt, run its official install script, or take a pinned release and check it against the release's checksums.txt:
curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
curl -fsSLO https://github.com/tilt-dev/tilt/releases/download/v0.36.0/tilt.0.36.0.linux.x86_64.tar.gz
echo "9ce610083efc76ffa518ec9b001ddb1711b652adce3333f57ffff6be50ad9719 tilt.0.36.0.linux.x86_64.tar.gz" | sha256sum -c
tar -xzf tilt.0.36.0.linux.x86_64.tar.gz tilt && install tilt ~/.local/bin/tilt
Without Tilt
make dev-ui runs the same parts in one terminal, with their logs in .oz0/dev-env/logs, and prints the addresses once the seed is done. Ctrl-C stops all of it, the nodes' runtimes and plugin servers too.
make dev-ui
It does not rebuild on changes; stop it and run it again. Vite reloads the web UI either way. OZ0_DEV_DOCS=1 make dev-ui also starts the docs' dev server.
What a build serves, and what it leaves
Each build also rebuilds dist/ for your machine's platform, the way make dist PLATFORMS=<os>/<arch> TOOLCHAIN=0 does, whenever the node binary or the runtime changed. The dev server serves dist/ to its nodes as a cluster's edge does, so the nodes update through the same path a real install uses, and you never run make dist by hand.
A build cleans up after itself, so rebuilds do not fill the disk:
dist/runtime/keeps the three newest runtimes. Older tarballs and bundle folders go before the manifest is signed, and older node binaries go when it is published.- The nodes' installed runtimes, in
~/.cache/orchestrator-zero/dev-env/node/runtimes, keep the one just built, any a running process still uses, and the three newest. An install in progress is never touched. - Go's build cache is pruned at most every six hours: entries unused for 72 hours, then the least recently used until it is under 100 GB.
- At the same time, the nodes' uv cache is pruned once it passes 2 GB (
OZ0_DEV_UV_CACHE_MB); the next runtime install then fetches its packages again.
OZ0_DEV_KEEP_RUNTIMES changes how many runtimes stay. make go-cache-prune prunes Go's cache at any time; GO_CACHE_MAX_AGE_HOURS and GO_CACHE_MAX_GB change its limits.
Two addresses, one web UI
The local environment serves the same web UI at two addresses. Both talk to the same dev server and show the same data; what differs is which build of the app you get.
| Address | What it serves | Use it to |
|---|---|---|
http://127.0.0.1:15173 | The app from its sources in web/, on Vite's dev server, which reloads the page when you save a file | Change the web UI |
https://127.0.0.1:18443 | The copy built into the server binary, on the management port, as every install serves it | Check the app as it ships: the production build, its Content-Security-Policy, HTTPS and the secure session cookie |
15173 exists only in a checkout, for working on the web UI. An install has no Vite: its operators open the management port. Without Vite, every change to the UI would wait for a production build, a server build and a restart. If you are not changing the UI, leave Vite out with tilt up -- --no-web or OZ0_DEV_WEB=0 make dev-ui, and use 18443.
The copy on 18443 is only as new as your last make web. The server embeds whatever is in web/dist when it is built, and only make web fills web/dist; tilt up and make dev-ui build the server but not the web UI, so that a change to Go code does not also wait for a production build of the UI.
When 18443 looks older than 15173, or newer after a build on another branch, it is an earlier build of the same app, not a different one. To bring it up to date:
make web # build the web UI into web/dist
tilt trigger build # under Tilt: rebuild the server with it and restart; without Tilt, run make dev-ui again
Sign in
Open the web UI on Vite at http://127.0.0.1:15173 and sign in as admin. The password is in the server's data folder, and works on 18443 too:
cat .oz0/dev-env/server/admin-password
Vite's dev server passes the API calls, the sign-in and the session cookie on to the dev server, so the app runs from its sources while the data comes from the server.
To see the chat drawer with chats in it, sign in as one of the seed's people, alex or maya, operators who see content. Their passwords are made by the seed and kept in the data folder, like the admin's (dev only):
cat .oz0/dev-env/seed/alex-password .oz0/dev-env/seed/maya-password
For the CLI, point it at the dev server:
export OZ0_CONTEXT=.oz0/dev-env/server/operator/context.json
oz0 job list
What the seed makes
| Where | What |
|---|---|
| Tenants | default, and acme with no node of its own |
| Nodes | dev-a (pool=general) and dev-b (pool=batch) |
| Plugins | support (support-triage, support-refunds, support-reporter) and hiring (hiring-screener); support in acme too |
| Jobs | finished ones, a conversation of two turns, one that failed on its step limit, an export that runs for most of a day, a refund that waits for a person's approval, and a job in acme that waits for a node |
| Findings | a possible loop, and a job that spent 90% of its budget |
| Cost | the tenant's monthly budget, 85% spent: the CV parser reports what it costs |
| Rollouts | support 0.2.0 on its canary, soaking for a day |
| Governance | the inventory, with hiring-screener as high risk; a kill switch in acme |
| Chats | alex's pinned chat about an order, one that waits for a person to approve a refund, and one shared with maya; maya's chat about an applicant. The seed makes them by signing in as each person, since a chat is a person's and the CLI acts as its operator |
| Also | teams with a quota, a routine with a fire, a reader account riley, an API key, model aliases, and the audit entries all of it wrote |
A stand-in for the model API, scripts/fakellm, answers every model call from a script, scripts/dev-env/fakellm.json, and the plugins in scripts/dev-env/plugins work on made-up orders and applicants.
The seed only adds what is missing, so it can run again on a seeded server, and does each time the environment starts. What waits and runs ends with time: the approvals are denied after 24 hours (alex's next seed then starts another refund chat), and the export, the wait in acme and the rollout's soak end within a day. The next seed starts a new one of each. To seed a running environment again by hand:
make dev-seed
To start over, stop the environment and delete .oz0/dev-env. A rollout in progress keeps the tenant's other plugin updates waiting; oz0 rollout abort <id> ends it.
Ports
Every port and the data folder can be changed, with environment variables for both ways of running it, or Tilt's arguments, such as tilt up -- --web-port 25173. The defaults stay clear of make dev's ports and of the docs' and website's dev servers.
| Variable | Default | What |
|---|---|---|
OZ0_DEV_WEB_PORT | 15173 | The web UI on Vite, from its sources, for changing it |
OZ0_DEV_MANAGEMENT_PORT | 18443 | Management: the admin API and the web UI built into the server, as of the last make web |
OZ0_DEV_EDGE_API_PORT | 17443 | The edge's HTTPS API, for the nodes |
OZ0_DEV_EDGE_TEMPORAL_PORT | 17233 | The edge's Temporal proxy, for the nodes |
OZ0_DEV_TEMPORAL_PORT | 17234 | The embedded Temporal frontend |
OZ0_DEV_TEMPORAL_UI_PORT | 18233 | The Temporal UI |
OZ0_DEV_FAKELLM_PORT | 18090 | The stand-in for the model API |
OZ0_DEV_DOCS_PORT | 13001 | The docs' dev server |
OZ0_DEV_DIR | .oz0/dev-env | The server's and the nodes' data |
OZ0_DEV_NODE_CACHE | ~/.cache/orchestrator-zero/dev-env/node | uv, Python and runtimes the nodes install, kept between runs |
What reaches the internet
The seed and the platform call nothing outside the machine: the gateway's model calls go to the stand-in. The first start of a node downloads uv and Python, as every node does, and installs the plugins' Python packages; both are cached. The docs' dev server downloads its fonts once.
Test it
make e2e-dev-env runs make dev-ui on free ports and a fresh data folder, checks the seeded data through the API and the web UI through Vite's proxy, stops it with Ctrl-C and checks that nothing is left running. When Tilt is installed, it then runs tilt ci on the same data.