Gateway: status route with per-route results, test push, reconnect and unpair
Gateway / check (push) Successful in 9s
Gateway / image (push) Successful in 24s

This commit is contained in:
gib committed 2026-11-01 15:04:30 -05:00
1 parent e6fd2b8f51
commit 9707daf1fe
9 files changed
+398 -55

No files matched your search

+62 -1
View File
@@ -42,6 +42,13 @@ PUT /v1/device/push
Debug builds use `sandbox`, TestFlight and App Store builds use `production`.
To unpair, the watch revokes its own token:
```
DELETE /v1/device
204
```
## Feed
```
@@ -163,7 +170,7 @@ The new thread uses the project's default model, or the host's, and the host's d
The gateway pushes when a thread starts waiting for you, finishes, or fails. Each push has an
`aps.category` and the custom keys `hostId`, `threadId` (absent on `host` pushes), and, for
approvals and questions, `requestId`.
approvals and questions, `requestId`. A `test` push carries no custom keys.
| Category | When | Actions the watch registers |
| --- | --- | --- |
@@ -172,6 +179,60 @@ approvals and questions, `requestId`.
| `finished` | A turn completed | Reply (text input), Open |
| `failed` | A turn failed | Open |
| `host` | A host's T3 token expires within 7 days, or was rejected | none |
| `test` | The watch asked for a test push | none |
## Status
Everything the settings screens show: each host and its routes, whether the gateway can push, and
whether this watch registered for pushes.
```
GET /v1/status
200 {
"hosts": [HostStatusDetail],
"gateway": { "startedAt": "...", "pushConfigured": true, "lastPush": PushResult | null },
"device": { "name": "Apple Watch", "pairedAt": "...", "pushRegistered": true, "apnsEnvironment": "production" }
}
HostStatusDetail {
"id": "...", "label": "Laptop", "status": "online", "error": null,
"activeUrl": "http://laptopvpn.gib:3773",
"routes": [
{ "url": "http://laptop.gib:3773", "kind": "direct", "result": "failed", "error": "no answer in 5 s", "checkedAt": "..." },
{ "url": "http://laptopvpn.gib:3773", "kind": "vpn", "result": "ok", "error": null, "checkedAt": "..." },
{ "url": "https://t3laptop.gbrown.org", "kind": "public", "result": "untried", "error": null, "checkedAt": null }
],
"onlineSince": "...", "lastOnlineAt": null, "nextRetryAt": null,
"tokenExpiresAt": "2026-12-01T00:00:00.000Z", "serverVersion": "0.1.5"
}
PushResult { "ok": true, "at": "...", "status": 200, "reason": null }
```
`routes` are in the order the gateway tries them, and their results come from its latest connect.
A route is `untried` when an earlier one answered first. `kind` is a guess from the URL: `public`
for HTTPS, `vpn` when the hostname contains "vpn", and `direct` otherwise. `activeUrl` is null unless
the host is online. `nextRetryAt` is set while the gateway waits to try again. `lastPush` is Apple's
answer to the most recent push to any watch. A `status` of 0 means the push never reached Apple.
```
POST /v1/hosts/:hostId/reconnect
202
```
Connects again from the first route, skipping any backoff wait. Use it when a better route may be
back, such as the LAN after getting home. The gateway otherwise stays on a working fallback until
that connection drops.
```
POST /v1/device/test-push
200 PushResult
```
Sends a test notification to the watch asking and returns Apple's answer. A failure names its cause,
for example `BadDeviceToken`, which usually means a push environment mismatch.
## Health