When the master changes an account type with "New accounts only", it now
serves the type's old rules as kept rule sets (firm.keptAccountRules), each
for accounts dated inside [openedAt, closedAt). This build applies them:
- getAccountConfig, the one rules lookup trading reads, puts a kept set's
rule fields over the live type when the account's rules date falls in
its window. Id, label, prefix, size and the projection fields stay the
live type's, so every report still names the live type.
- account_assignments gains rules_date and rules_date_manual. A new row is
dated when the machine first sees the account, on the master's clock
when this one runs behind and never before the catalog's last change.
Auto dates come down to the broker's evidence: the end of the first fund
day in Chicago, or a DXtrade registration instant. Evidence is applied
before an account's first trade. Rows from before the migration get its
time; unpinning keeps the row and its date. Backups carry both columns.
- An account on a kept set stays on its type while its prefix and cached
size still match, so a tie-break flip can't move it off its old rules.
- Kept entries parse through the same normalizer as live types (now
normalizeAccountType in lib/kept-rules.ts); a malformed one rejects the
sync like a malformed type. The cached catalog records the parser that
stored it, and a cache from an older parser is fetched in full rather
than trusted with ?since= (this also brings in maxConcurrentAccounts).
- Reports add keptRuleIds per firm and live counts per kept set per type.
- The dashboard row and the account page show the rules date next to the
account name, with an "old rules" chip saying what differs. Clicking
the date edits it inline (PUT /api/accounts/:id/rules-date), previewing
which rules the date lands on.
Also: the master's per-firm cap on accounts traded at once
(maxConcurrentAccounts), applied on top of the machine's own max
concurrent when a batch is drawn and in copy trading.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/db.ts switched to WAL at module load before setting busy_timeout.
next build imports it from several workers while the live server holds
the same file, and a journal-mode change needs the file to itself — so
the pragma failed instantly with SQLITE_BUSY and took the whole build
down ("Failed to collect page data"), blocking updates on any box whose
database was still in rollback mode. The timeout is now set first and
the WAL switch is wrapped: a held file keeps its current mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four features:
- Backup: lib/backup-sync.ts pushes the full settings table and firm
credentials (the deliberate exception to "credentials never leave the
machine") to the master's bearer-gated /api/instances/backup — every
5 minutes when the content hash changed, within seconds of a settings
or credential edit, and on the new backup-now command. Enabled by the
master_backup_token setting.
- Restore: a dead VPS's replacement pulls the backup and adopts its
uuid/name, so roster, payouts and card links carry on. Pre-boot via
seed-settings.js --restore-from (setup-linux.sh prompts for it) or
the Settings page's restore picker (lib/backup-restore.ts). Stop the
old machine first; the master 409s a live-name rename.
- Proxy: the proxy_url setting routes BROKER traffic only — TopstepX
REST + SignalR hub, Tradovate REST + websocket, Yahoo quotes — via
lib/outbound-proxy.ts (axios agents, undici dispatcher, ws agent).
The master link, reporter, catalog and backup push stay direct, so a
dead proxy cannot take down the control channel. Restart to apply.
- Multi-instance: AUTOFIRMER_DB moves the SQLite file so one checkout
runs N instances; scripts/instances.json (per-device, gitignored)
drives start-all/update-check, and add-instance.sh / remove-instance.sh
manage the extra PM2 processes. Absent registry = single-instance
behavior, unchanged. Instance 1 keeps port 3000 for the clicker and
extension.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Standalone batch file: clones (or pulls), installs, builds, seeds the
reporter settings, and writes a start-autofirmer.bat. Prompts for the master
dashboard URL and instance name, defaulting to %COMPUTERNAME%.
Two install hazards it handles:
- better-sqlite3 has no prebuilt binary above Node 22, so install silently
falls back to node-gyp and dies without Visual Studio Build Tools. The
script warns first and names the fix if install fails anyway
- playwright is declared but referenced nowhere in the source, so its
postinstall browser download is skipped
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>