64 lines
3.2 KiB
Markdown
64 lines
3.2 KiB
Markdown
# Harbor application state v1
|
|
|
|
`GET /api/state` is the canonical Harbor domain snapshot. Successful POST and DELETE endpoints return the same snapshot as `state` while retaining their v0 response fields for compatibility.
|
|
|
|
```json
|
|
{
|
|
"apiVersion": 1,
|
|
"revision": 42,
|
|
"generatedAt": "2026-07-11T15:00:00.000Z",
|
|
"mode": "client",
|
|
"subscription": {
|
|
"status": "ready",
|
|
"host": "provider.example/…",
|
|
"fetchedAt": "2026-07-11T14:58:00.000Z",
|
|
"userInfo": {}
|
|
},
|
|
"selection": {
|
|
"desiredServerId": "Amsterdam",
|
|
"appliedServerId": "Amsterdam"
|
|
},
|
|
"connection": {
|
|
"desired": "running",
|
|
"process": "running",
|
|
"startedAt": "2026-07-11T14:59:10.000Z",
|
|
"lastError": null
|
|
},
|
|
"route": {
|
|
"mode": "local-vpn",
|
|
"gatewayAddress": null,
|
|
"lastVerifiedAt": null,
|
|
"reason": "auto"
|
|
},
|
|
"operation": {
|
|
"kind": null,
|
|
"status": "idle",
|
|
"startedAt": null,
|
|
"error": null
|
|
},
|
|
"servers": []
|
|
}
|
|
```
|
|
|
|
The backend owns subscription metadata, servers, desired/applied selection, desired/process connection state, route and current operation. React may keep only unsaved form values, pending selection and visual state. Browser transport freshness is not part of this contract.
|
|
|
|
## Revision rules
|
|
|
|
`revision` is persisted in the existing `state.json` and increases on externally visible transitions, including operation start/completion/failure, import, refresh, forget, apply, start, stop and Gateway Auto changes. `generatedAt` is response metadata and does not change revision by itself.
|
|
|
|
A consumer must eventually apply only snapshots whose revision is at least its current revision. The frontend comparison and stale/offline transport envelope are intentionally handled by TASK-002 and TASK-003.
|
|
|
|
## Desired and applied state
|
|
|
|
`selection.desiredServerId` records the user's requested server. `selection.appliedServerId` changes only after its sing-box configuration has been applied. Likewise, `connection.desired` records intent while `connection.process` reports the observed runtime. A failed operation can therefore leave desired and applied values different without pretending that the request succeeded.
|
|
|
|
Server IDs are currently derived from the existing trimmed subscription tag. Stable IDs across cosmetic renames are deferred to TASK-008.
|
|
|
|
## Compatibility and migration
|
|
|
|
No path, volume or file is renamed. A legacy `state.json` without `revision`, `appliedTag` or `connectionDesired` is normalized on read: revision starts at `0`, the legacy `selectedTag` is treated as both desired and applied, and connection intent is inferred from the existing config. The next domain write stores the added fields. Existing unknown fields remain untouched.
|
|
|
|
During the v0 compatibility window, the snapshot also exposes `selectedTag`, `singboxRunning`, `servers[].tag`, `gatewayAuto` and the other previous GET fields. Mutation responses retain their previous result fields and add `state`. The canonical `subscription` object never contains the full subscription URL.
|
|
|
|
Rollback is code-only: deploy the previous build. The added persisted fields are ignored by the previous implementation, so no data rewrite is needed. Revisions created by the newer build remain harmless integers in `state.json`.
|