Symphonix Documentation · Core 0.61.0

Bringing the three up

What to start, in what order, and the two traps that cost an evening.

This is the engineering reference, rendered from the repository

It is the same file the platform's own tests are written against — LGFlow/docs/guides/bringing-it-up.md — rendered here rather than copied, so it cannot fall behind the software it describes. It is written for somebody working on the platform and uses the internal project names: LGFlow is Symphonix Core, LGStudio is Symphonix Studio, and LGMonitor or LGStudioMonitor is the Symphonix Control Panel. Names that begin LGFlow and appear in code — LGFlowValidationError and the rest — are exported identifiers and are spelled exactly as the software spells them.

What to start, in what order, and the two traps that cost an evening.

The three pieces are independent processes. Each works alone; together they make a working environment, and the order matters for a reason given below.

LGMonitor   the board      127.0.0.1:5180   who people are, what every agent is doing
LGStudio    the editor     127.0.0.1:5174   where flows are written and deployed
the agent   a deployment   printed on start the thing that answers people

Only the first two have a port of their own. An agent takes PORT, and with nothing set it computes Number(process.env.PORT || 0) — which asks the operating system for a free one, so it is different on every start and the agent prints the address it got. The 127.0.0.1:8080 further down this page is the container's, and it is 8080 because the generated Dockerfile sets ENV PORT=8080 and the docker run line publishes it. *This table said 8080 flatly until 2026-11-04, as did step 9 of the document-agent guide, and a reader who ran the agent from a folder and typed it got nothing.*

Before anything#

Docker Desktop must be running if the agent runs as an image. Check with docker version: a Server section in the answer means the engine is up. Only a Client section means Desktop has not finished starting.

Node 20.11 or later, and Node 24 if an agent here will stop for a person — a form, an ask node or an approval. From 0.26.6 that wait is kept in node:sqlite, which arrived long after 20.11; on an older Node the agent still runs and says Waiting: in memory on start, which means a restart loses whatever somebody had half-filled in. Nothing else is installed — these three have almost no dependency tree between them.

1 · The board, first#

cd C:\Dev\Apps\LGStudioMonitor
node index.js --host 0.0.0.0 --port 5180

It stays in the foreground. Ctrl+C stops it.

--host 0.0.0.0 is only for the container. Inside a container 127.0.0.1 is the container's own loopback, so an agent there cannot reach a board bound to this machine's. Binding wider is what lets it in — and the board refuses to do it without a token:

Listening beyond 127.0.0.1 without a token would let anyone who can reach the port terminate your agents. Set MONITOR_TOKEN.

The token lives in that folder's .env as MONITOR_TOKEN, so the command line carries no secret. The agent sends the same value; both must match.

If no container is involved, drop the flag — node index.js --port 5180 — and the board stays on loopback, which is the safer default and needs no token.

Check it: open http://127.0.0.1:5180. The console should also say Runner registered — starting the board starts a runner for this machine, which is what lets the board stop and start agents on it.

2 · The studio, second#

cd C:\Dev\Apps\LGStudio
npm start

It serves flows from ../LGFlow — no flag needed when the two sit side by side. Open http://127.0.0.1:5174.

Three steps, not two: start the board → refresh the page → sign in. The studio asks the board whether to require a sign-in at all (STUDIO_SIGN_IN, default when-known: ask while a board is reachable and has people). If the board is not answering when the page loads, the studio opens with no door, and a tab that was already open before the board came up keeps whatever it was showing.

The console prints one line worth reading every time:

sign in:   when-known, through http://127.0.0.1:5180

That line, and not the screen, is what says which mode it came up in. To make an unreachable board mean nobody gets in rather than everybody does, put STUDIO_SIGN_IN=required in the studio's .env.

3 · The agent, last#

An image that already exists:

cd C:\Dev\Apps\sqlengine
docker start sqlengine

A fresh one, or after a redeploy:

docker build -t sqlengine .
docker run --name sqlengine --env-file .env -p 127.0.0.1:8080:8080 -v sqlengine-state:/app/.lgflow sqlengine

Check it: docker ps should say healthy, not just up — the image has a HEALTHCHECK that asks the agent's /health, which is a different question from whether the process exists. And docker logs sqlengine should end with:

Monitor: registered with ws://host.docker.internal:5180/agent

No such line means the board was not reachable when it started. Then open http://localhost:8080.

-p 127.0.0.1:8080:8080 publishes to this machine only. Inside the container the agent listens on every interface — it has to — so that prefix is the boundary the loopback bind used to be. Dropping it hands the agent to the network.

-v sqlengine-state:/app/.lgflow is where the journal, the file store and the lock live. Without it the history of every run disappears when the container is replaced, which is most of what the board can show about it.

Every address on this machine, seen from inside a container#

127.0.0.1 means the container. Anything on this machine is host.docker.internal, and that name also resolves on Windows itself, so one value works in both places. It affects, in the agent's .env:

MONITOR_URLws://host.docker.internal:5180/agent
PGHOST and anything else pointing at a local servicehost.docker.internal

The service must also accept the connection: Postgres has to listen beyond loopback (listen_addresses) and pg_hba.conf has to allow the address Docker's NAT presents.

Two rules that cost an evening#

Never run the studio's Play and the container at the same time. They are two copies of one flow. The board notices, refuses to register the second, and that copy stands down — it writes a pause, stops its clocks, and refuses at its door. Nothing ever undoes it when the other copy goes away. The symptom is a sign-in refused with that is not this agent's own address, which describes a different problem entirely.

If it happens:

docker exec sqlengine rm -f /app/.lgflow/paused.json
docker restart sqlengine

Make sure nothing is serving that flow from the folder first — node run.js in a terminal, or Play in the studio.

Test an image through its own address, not through the studio. Play's open chat opens the copy Play started, from the folder. For a container, go to http://localhost:8080 directly.

Stopping#

The board and the studioCtrl+C in their windows
The agentdocker stop sqlengine — a graceful stop, which removes its lock. docker rm -f kills it outright and leaves the lock behind, and the next start waits 20 seconds for the heartbeat to go quiet before taking the folder

When something looks wrong, three questions in this order#

  1. Is the board up? http://127.0.0.1:5180. Almost everything confusing downstream — a studio with no sign-in, an agent that answers nobody — is this.
  2. Did the agent register? docker logs sqlengine for the Monitor: registered line.
  3. Is there a second copy? docker ps and, in the studio, whether Play is running.