Store the person uuid the master issues, so renames keep our identity

The welcome now carries a personId; it lands in the instance_id setting
and rides on every hello and HTTP report from then on. The master keys
the person by that uuid, so changing instance_name moves our history to
the new name instead of starting a stranger. Read-only in Settings on
purpose — editing it by hand would detach this machine from its person.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-02 12:07:25 -05:00
co-authored by Claude Fable 5
parent de9cbffd83
commit a088381749
6 changed files with 39 additions and 5 deletions
+4
View File
@@ -155,6 +155,10 @@ seedSetting.run('max_concurrent_accounts', '5');
seedSetting.run('tick_interval_seconds', '60');
seedSetting.run('master_dashboard_url', 'https://master.juicerroom.com');
seedSetting.run('instance_name', '');
// The person uuid the master issues in its welcome. Stored, then presented on
// every hello/report — it is what lets a renamed instance_name stay the same
// person on the master. Never edited by hand.
seedSetting.run('instance_id', '');
seedSetting.run('trading_hours', 'full_cme');
// Solves the captchas the AutoBuyer runs into. Blank uses the key hardcoded in
// app/api/autobuyer/captcha; set this (or TWOCAPTCHA_API_KEY) to bill another
+9 -1
View File
@@ -15,7 +15,7 @@
* master you control, and prefer https so it cannot be redirected in transit.
*/
import { getSetting, getInstruments, getRunnerHeartbeat, RUNNER_TIMEOUT_MS } from './db';
import { getSetting, setSetting, getInstruments, getRunnerHeartbeat, RUNNER_TIMEOUT_MS } from './db';
import { runTrade, startScheduler, stopScheduler, getSchedulerStatus } from './auto-trade';
import { FIRMS, automationKey } from './automations';
import { POINT_VALUES } from './trading-logic';
@@ -217,9 +217,14 @@ function connect() {
s.ready = false;
socket.addEventListener('open', () => {
// The stored uuid, once the master has issued one — it, not the name,
// is who this instance is, so renaming instance_name keeps the same
// person (and their accounting history) on the master.
const instanceId = getSetting('instance_id');
socket.send(JSON.stringify({
type: 'hello',
instance,
id: instanceId || undefined,
capabilities: collectCapabilities(),
} satisfies InstanceMessage));
});
@@ -235,6 +240,9 @@ function connect() {
if (msg.type === 'welcome') {
s.ready = true;
s.attempt = 0;
if (msg.personId && msg.personId !== getSetting('instance_id')) {
setSetting('instance_id', msg.personId);
}
console.log(`[master-link] connected to ${url} as "${instance}"`);
return;
}
+5 -2
View File
@@ -106,10 +106,13 @@ export type Command =
| { kind: 'report-now' };
export type MasterMessage =
| { type: 'welcome' }
/** personId is the person's stable uuid; the instance stores it and
* presents it on later hellos, which is what makes a rename follow the
* person. */
| { type: 'welcome'; personId?: string }
| { type: 'command'; id: string; command: Command };
export type InstanceMessage =
| { type: 'hello'; instance: string; capabilities: InstanceCapabilities }
| { type: 'hello'; instance: string; id?: string; capabilities: InstanceCapabilities }
| { type: 'report'; firms: FirmStats[]; scheduler: SchedulerState; capabilities?: InstanceCapabilities }
| { type: 'ack'; id: string; ok: boolean; result?: unknown; error?: string };
+8 -1
View File
@@ -132,7 +132,14 @@ export async function sendReport(): Promise<void> {
await fetch(new URL('/api/report', url), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ instance: instanceName, firms, scheduler }),
body: JSON.stringify({
instance: instanceName,
// Same identity the socket hello carries, so a rename resolves
// to the same person whichever transport the report takes.
id: getSetting('instance_id') || undefined,
firms,
scheduler,
}),
});
} catch {
// Master might be down — silently continue