The agent
What the sync agent does on each invocation, and the two ways it is run.
The agent is a single self-contained executable that sits next to a MySQL database. It drains that database's outbox, pushes what it finds to the monitoring server, pulls whatever the other side has queued, and applies it.
Every school runs two of them — one against the on-site database and one against the online
database. They are the same binary; only SYNC_SOURCE differs.
| Side | SYNC_SOURCE | Captures | Applies | How it runs |
|---|---|---|---|---|
| School's on-site database | LOCAL | UP tables | DOWN changes | Resident, --watch |
| Online database | ONLINE | DOWN tables | UP changes | One-shot, under the task scheduler |
An agent knows only its own MySQL connection and the server's URL. It never reaches the other database, so the two hosts need no route between them.
One cycle
An invocation is a single cycle. Under --watch the agent repeats it, waiting the school's
interval between runs unless it was started with an --interval of its own.
Three details are worth knowing:
Inbound is applied before outbound is captured. Applying first means the rows this cycle pushes already reflect anything that just arrived, so the two sides converge in one pass instead of trading corrections.
A failed apply blocks, it does not skip. The agent stops at the first change it cannot write and refuses to acknowledge past it, so the queue stalls with the error visible rather than quietly leaving a gap. Reading a failure covers what to do with one.
Apply order is respected. Changes leave in the order configured on each table, so a parent is always written before the rows that reference it.
Configuration lives on the server
The agent fetches its configuration at the start of every cycle. Table list, directions,
allowed operations and apply order all come from the dashboard, so changing any of them takes
effect without redeploying or restarting the agent. The sync interval is stored there too,
though only a --watch agent acts on it — see Timing.
The only things held locally are the four connection settings in .env: where the server
is, who the school is, its token, and which side this agent is.
Two agents cannot collide
Each cycle takes a MySQL advisory lock named after the database. A run that starts while
another is still working logs another run is still in progress and exits, so an
overlapping schedule is harmless.
What a healthy run looks like
Run from a terminal, a cycle reports only what it actually moved:
15:30:04 DOWN applied 3, skipped 1 · UP pushed 12 1.1sUnder a scheduler — where output is redirected to a log — it writes the full form instead, with every number present so the line survives on its own:
2026-08-05T07:36:00.132Z [agent] PAC001 LOCAL — DOWN applied 0, skipped 0, failed 0 | UP scanned 0, pushed 0, dropped 0 across 1 table(s) | counted 0 table(s) | 911ms| Number | Meaning |
|---|---|
applied | Changes from the other side written to this database |
skipped | Changes an operation rule disallowed, or already applied |
failed | Changes that could not be written — the queue is now blocked |
scanned | Outbox rows found pending |
pushed | Rows sent to the server after collapsing duplicates |
dropped | Outbox rows for tables that are no longer configured, or for an operation the table disallows |
counted | Tables whose row counts were refreshed this cycle |
The process exits 0 on a clean cycle and 1 when anything failed to apply.