# Harbor state recovery Harbor keeps the existing data directory and `state.json` path. The current persisted format is `schemaVersion: 6`: schema v2 introduced local route rules, v3 added rule enabled state, v4 added stable server IDs, v5 embeds the canonical `profiles[]` collection with desired/applied profile identity, and v6 adds an explicit `vpn` or `direct` outbound to every route rule. ## 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. Profile/server switching prepares candidate config and runtime before the final state publication. If any later step fails, Harbor restores the previous config, runtime and canonical state. ## Migration to profiles On startup, a legacy state is normalized before the process starts. Harbor creates one profile named `Основной`, moves the provider URL/config and metadata into it, and preserves unambiguous desired/applied server identity. A legacy state explicitly marked stopped clears stale applied residue. Before replacing state Harbor saves the original beside it: ```text state.json.backup-v4-2026-08-11T12-00-00-000Z ``` After a valid profile has been committed, the raw legacy cache is saved and removed as a second owner: ```text subscription-cache.json.backup-v1-2026-08-11T12-00-00-000Z ``` An invalid legacy provider config is backed up but not started. Harbor removes stale generated config and returns to a stopped first-run state. ## Migration to ordered VPN/Direct rules When schemas v0-v5 are read, Harbor preserves the order of `routeRules` and `appliedRouteRules` and adds `outbound: "direct"` to legacy entries before atomically committing schema v6. The original file is preserved using its actual source version, for example: ```text state.json.backup-v5-2026-08-17T12-00-00-000Z ``` After migration, malformed schema-v6 rules are rejected; Harbor does not reinterpret a missing or unknown outbound as direct. ## Corrupt JSON If `state.json` cannot be parsed, Harbor renames the exact damaged bytes to: ```text state.json.corrupt-2026-08-11T12-00-00-000Z ``` It then creates a valid empty current-schema state and reports storage recovery. A corrupt legacy subscription cache is preserved with the same suffix and is never used to start stale config. ## Manual recovery and downgrade Perform recovery while Harbor is stopped: 1. Copy the whole data directory. 2. Inspect the intended backup with `jq . `. 3. Restore only matching state/cache backups to their original filenames. 4. Start Harbor and verify `GET /api/state` before applying a profile. A pre-v6 binary cannot interpret the explicit ordered VPN/Direct rule contract. Restore `state.json.backup-v-*` matching the rollback binary; deploying old code over schema v6 is not safe. A rollback to pre-v5 additionally requires the matching state and subscription-cache backups because that binary cannot interpret canonical profiles.