1.9 KiB
Harbor state recovery
Harbor keeps the existing data paths and volumes. state.json now uses schemaVersion: 2; subscription cache, generated sing-box config and HWID keep their existing filenames. Schema v2 adds locally managed domain routing rules; an absent field is migrated to an empty custom list while the built-in .ru rule remains in code.
Atomic writes
Persistent files are written to a unique temporary file in the same directory, flushed with fsync, closed and atomically renamed over the target. A failure before rename leaves the previous target untouched and removes the temporary file.
Migration
On startup, a legacy state.json without schemaVersion, or a v1 state without local routing rules, is normalized and migrated to the current schema. Before replacement Harbor saves the original beside it:
state.json.backup-v0-2026-07-11T12-00-00-000Z
The v1 migration preserves existing fields, adds normalized revision, selection and server fields, and does not rename the volume. Older Harbor builds ignore the additional schemaVersion field, but the backup is the safest rollback source.
Corrupt JSON
If state.json cannot be parsed, Harbor renames the exact damaged bytes to:
state.json.corrupt-2026-07-11T12-00-00-000Z
It then creates a valid empty v1 state and reports storage-recovery through snapshot.operation. A corrupt subscription cache is preserved with the same suffix and reported in control logs.
Recovery should be performed while Harbor is stopped:
- Copy the whole data directory before changing anything.
- Inspect a backup with
jq . <backup-file>. - Restore only a valid JSON backup to the original filename.
- Start Harbor and verify
GET /api/statebefore applying or importing anything.
Generated config rollback also uses the atomic writer. No automatic recovery tries to guess missing subscription credentials or repair semantically invalid sing-box configuration.