Bringing the three up
What to start, in what order, and the two traps that cost an evening.
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 peopleOnly 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 5180It 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 startIt 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:5180That 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 sqlengineA 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 sqlengineCheck 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/agentNo 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_URL | ws://host.docker.internal:5180/agent |
PGHOST and anything else pointing at a local service | host.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 sqlengineMake 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 studio | Ctrl+C in their windows |
| The agent | docker 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#
- 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. - Did the agent register?
docker logs sqlenginefor theMonitor: registeredline. - Is there a second copy?
docker psand, in the studio, whether Play is running.