Backups push automatically; the token is only for restore
Matches the master's tokenless-push change: pushBackup no longer waits for master_backup_token — every instance with a uuid backs up on its own, and the 5-minute tick force-pushes so a poisoned master copy self-heals. The token now only gates pulling a backup back (restore), so setup-linux.sh asks for it only when restoring and the Settings help text says so. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8154f25bb9
commit
ba8bd50da0
@@ -347,8 +347,8 @@ export default function SettingsPage() {
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-800">Backup token</p>
|
||||
<p className="text-xs text-slate-400 mt-0.5">
|
||||
Enables encrypted settings + credentials backups to the master
|
||||
(its INSTANCE_BACKUP_TOKEN). Blank = backups off.
|
||||
Backups push automatically — this token (the master's
|
||||
INSTANCE_BACKUP_TOKEN) is only needed to restore one.
|
||||
</p>
|
||||
<p className="text-xs text-slate-400 mt-0.5">
|
||||
{backupStatus.at
|
||||
|
||||
+17
-14
@@ -5,9 +5,11 @@
|
||||
* instance's uuid.
|
||||
*
|
||||
* This is the deliberate exception to "credentials never leave the machine":
|
||||
* the payload goes over HTTPS to the bearer-gated /api/instances/backup and is
|
||||
* AES-encrypted at rest on the master. It never rides the unauthenticated
|
||||
* control socket.
|
||||
* the payload goes over HTTPS to /api/instances/backup and is AES-encrypted
|
||||
* at rest on the master. It never rides the control socket. Pushing needs no
|
||||
* token — every instance backs up automatically once it has its uuid; only
|
||||
* PULLING a backup back out (lib/backup-restore.ts) is token-gated, because
|
||||
* that is the direction credentials come out in the clear.
|
||||
*
|
||||
* Push policy: on bootstrap, then every POLL_INTERVAL, but only when the
|
||||
* content actually changed (hash comparison) — plus an immediate debounced
|
||||
@@ -85,19 +87,20 @@ function contentHash(payload: BackupPayload): string {
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
function preconditions(): { masterUrl: string; id: string; token: string } | null {
|
||||
function preconditions(): { masterUrl: string; id: string } | null {
|
||||
const masterUrl = getSetting('master_dashboard_url');
|
||||
const token = getSetting('master_backup_token');
|
||||
// No uuid yet = the master has never said welcome; there is nothing stable
|
||||
// to key the backup by, so wait for the first hello to complete.
|
||||
// to key the backup by, so wait for the first hello to complete. No token
|
||||
// needed: pushes are open on the master (only PULLING a backup is
|
||||
// token-gated), so every instance backs up automatically.
|
||||
const id = getSetting('instance_id');
|
||||
if (!masterUrl || !token || !id) return null;
|
||||
return { masterUrl, id, token };
|
||||
if (!masterUrl || !id) return null;
|
||||
return { masterUrl, id };
|
||||
}
|
||||
|
||||
export async function pushBackup(force = false): Promise<{ ok: boolean; skipped?: boolean; error?: string }> {
|
||||
const pre = preconditions();
|
||||
if (!pre) return { ok: false, skipped: true, error: 'master url, backup token or instance uuid not set' };
|
||||
if (!pre) return { ok: false, skipped: true, error: 'master url or instance uuid not set' };
|
||||
|
||||
const payload = collectBackup();
|
||||
const hash = contentHash(payload);
|
||||
@@ -113,10 +116,7 @@ export async function pushBackup(force = false): Promise<{ ok: boolean; skipped?
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
authorization: `Bearer ${pre.token}`,
|
||||
},
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
id: pre.id,
|
||||
name: getSetting('instance_name') ?? '',
|
||||
@@ -151,7 +151,10 @@ export function scheduleBackupPush(): void {
|
||||
export function startBackupSync(): void {
|
||||
stopBackupSync();
|
||||
const tick = async () => {
|
||||
await pushBackup();
|
||||
// Forced: pushes need no token, so anyone who reaches the master and
|
||||
// knows this uuid could overwrite our backup there. Re-asserting the
|
||||
// real state every tick bounds that poisoning window to 5 minutes.
|
||||
await pushBackup(true);
|
||||
// Jitter so a fleet restarting together doesn't push in lockstep.
|
||||
g.__backupSyncTimer = setTimeout(tick, POLL_INTERVAL_MS + Math.floor(Math.random() * 30_000));
|
||||
};
|
||||
|
||||
@@ -197,8 +197,9 @@ seedSetting.run('twocaptcha_api_key', '');
|
||||
// Blank falls back to the proxy hardcoded in app/api/autobuyer/captcha; `none`
|
||||
// solves without one.
|
||||
seedSetting.run('twocaptcha_proxy', '');
|
||||
// Bearer for pushing/pulling the encrypted settings+credentials backup on the
|
||||
// master (its INSTANCE_BACKUP_TOKEN). Blank = backups off.
|
||||
// Bearer for PULLING an encrypted settings+credentials backup back from the
|
||||
// master (its INSTANCE_BACKUP_TOKEN) — pushing needs no token and happens
|
||||
// automatically. Blank = restore unavailable from this machine's UI.
|
||||
seedSetting.run('master_backup_token', '');
|
||||
// Routes broker traffic (TopstepX, Tradovate, Yahoo quotes) through a proxy:
|
||||
// http(s):// or socks5(h)://, user:pass ok. The master link stays direct on
|
||||
|
||||
+5
-4
@@ -86,10 +86,11 @@ read -r -p " Master dashboard URL [$DEFAULT_MASTER]: " MASTER_URL
|
||||
MASTER_URL="${MASTER_URL:-$DEFAULT_MASTER}"
|
||||
read -r -p " Instance name [$(hostname)]: " INSTANCE
|
||||
INSTANCE="${INSTANCE:-$(hostname)}"
|
||||
read -r -p " Backup token (blank = no master backups): " BACKUP_TOKEN
|
||||
RESTORE_FROM=""
|
||||
if [ -n "$BACKUP_TOKEN" ]; then
|
||||
read -r -p " Restore a dead instance? Its uuid (blank = fresh install): " RESTORE_FROM
|
||||
# Backups push automatically; the token is only needed to pull one back.
|
||||
read -r -p " Restore a dead instance? Its uuid (blank = fresh install): " RESTORE_FROM
|
||||
BACKUP_TOKEN=""
|
||||
if [ -n "$RESTORE_FROM" ]; then
|
||||
read -r -p " Backup token (the master's INSTANCE_BACKUP_TOKEN): " BACKUP_TOKEN
|
||||
fi
|
||||
echo
|
||||
info "installing to: $TARGET"
|
||||
|
||||
Reference in New Issue
Block a user