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
+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));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user