@mhome/app-facade-protocol 1.34.0 → 1.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -119,20 +119,18 @@ Candidates come from the shared discovery (`/local/pod/discovery/*`) with
119
119
  | `/local/host/provision/code` | `{"sessionId","code"}` | session snapshot |
120
120
  | `/local/host/provision/wifi` | `{"sessionId","ssid","password"?}` | session snapshot |
121
121
  | `/local/host/provision/wifi/scan` | `{"sessionId"}` | session snapshot |
122
- | `/local/host/provision/cancel` | `{"sessionId"}` | session snapshot |
123
- | `/local/host/provision/renew` | `{"sessionId"}` | session snapshot |
124
122
  | `/local/host/provision/status` | `{}` | `{"session": snapshot or null}` |
125
123
 
126
124
  Event `/local/host/provision/changed` carries a session snapshot. One session
127
125
  per native Client, and none while a pod session is active; discovery scanning
128
126
  pauses during either. Rejected requests use the error envelope and the stable
129
127
  `details.reason` values of `pod-commissioning-v1.md` section 7. The session is
130
- owned through the same 30-second lease, renewed with
131
- `/local/host/provision/renew` every 10 seconds; an expired lease cancels it.
128
+ owned by the native core until success, failure or the session deadline.
129
+ There is no cancellation or renewal endpoint; leaving the UI does not end it.
132
130
 
133
131
  Session states, in order: `connecting`, `awaiting_code`, `securing`,
134
132
  `reading_info`, `awaiting_wifi`, `joining_wifi`, `completed`; terminal `failed`,
135
- `cancelled`, `timed_out`. `completed` carries `host`, the reported `addresses`
133
+ `timed_out`. `completed` carries `host`, the reported `addresses`
136
134
  (IPv4 only, possibly empty) and `claimToken` from the `finish` response; the
137
135
  commissioner sends `finish` (up to 3 times while the secure session lasts)
138
136
  before reporting it. `claimToken` is absent when `finish` could not be
@@ -8,7 +8,8 @@ call the `/local/pod/*` targets of their native Client.
8
8
 
9
9
  Commissioning order is fixed: secure BLE session, Wi-Fi joined, user
10
10
  authorization, credential issue, delivery, activation. A credential is issued only
11
- after the pod reports a joined network. Any failure after issue revokes it.
11
+ after the pod reports a joined network. Failed setup releases the connection;
12
+ pending cloud credentials expire, and a fresh successful claim cleans up old credentials.
12
13
 
13
14
  ## 1. Device identity
14
15
 
@@ -24,7 +25,7 @@ after the pod reports a joined network. Any failure after issue revokes it.
24
25
  ## 2. BLE advertising
25
26
 
26
27
  Pods advertise only while unprovisioned, while re-commissioning after logout or
27
- revocation, or inside a re-provision window. A commissioned pod does not advertise.
28
+ credential invalidation, or inside a re-provision window. A commissioned pod does not advertise.
28
29
 
29
30
  - Service UUID (128-bit, shared by every MeowLink commissionable device):
30
31
  `a17a7ad5-9b2f-4010-9cf0-aa15ac54c40e`.
@@ -157,7 +158,7 @@ unprovisioned ──connect──▶ session_open ──handshake──▶ secur
157
158
  secured ──prov-config apply──▶ wifi_joining ──▶ wifi_connected | wifi_failed
158
159
  wifi_failed ──retry──▶ wifi_joining
159
160
  wifi_connected ──deliver──▶ activating ──time sync, refresh, commit──▶ commissioned | failed
160
- any pre-commit state ──timeout/disconnect/cancel──▶ unprovisioned
161
+ any pre-commit state ──timeout/disconnect──▶ unprovisioned
161
162
  ```
162
163
 
163
164
  Runtime states after commit:
@@ -176,8 +177,9 @@ Runtime states after commit:
176
177
  - `needs_recommission`: logout, or refresh rejected because the credential was
177
178
  revoked or the user no longer exists. Credential and local JWTs are erased,
178
179
  Wi-Fi is kept, advertising resumes with the Wi-Fi-configured flag.
179
- - `factory_resetting`: best-effort self-revoke, erase all persisted data
180
- including the identity key, reboot to `unprovisioned`.
180
+ - `factory_resetting`: purely offline, erase all persisted data including the
181
+ identity key and cached Hub tokens, reboot to `unprovisioned`. No cloud
182
+ request is needed. Logout also clears local credentials without cloud revocation.
181
183
 
182
184
  Time sync uses SNTP; if NTP is unreachable the pod may use the `Date` header of a
183
185
  Lion HTTPS response. Activation proofs require a synced clock.
@@ -227,7 +229,7 @@ claims `iss` and `sub` = `podId`, a single-valued `aud` = `pod-token-refresh`,
227
229
  proof can be accepted; a reused `jti` in that window fails with
228
230
  `pod_proof_replayed`.
229
231
 
230
- The first successful refresh moves `pending` to `active` and revokes every other
232
+ Before the first successful refresh moves `pending` to `active`, Lion deletes every other
231
233
  credential with the same `identity.keyId` (whichever user holds it) and the same
232
234
  user's other credentials with the same `deviceId`. A `pending` credential past
233
235
  `activateBefore` is rejected and deleted. Revoked credentials are deleted, so a
@@ -241,17 +243,16 @@ later refresh fails with `pod_credential_invalid`. Response
241
243
  `absent` (schema `schema/pod-credential-status.v1.schema.json`). `absent`
242
244
  covers no such credential, revoked, pending past `activateBefore`, and a
243
245
  credential owned by another user, so the answer never reveals other users'
244
- pods. `activatedAt` (epoch ms) is present when `active`. Commissioners use it to
245
- decide the outcome of an activation they could not observe (section 7).
246
+ pods. `activatedAt` (epoch ms) is present when `active`. It is informational;
247
+ commissioning success requires the device confirmation described in section 7.
246
248
 
247
- ### Revoke
249
+ ### Reset and cleanup
248
250
 
249
- `POST /api/v1/pod/credential/revoke`, body `{"podId"}` with the owning user's
250
- `Authorization`, or `{"podId","refreshToken","proof"}` where the proof uses
251
- `aud` = `pod-credential-revoke` (pod logout and factory reset). Idempotent:
252
- revoking a pod that does not exist or that the caller does not own is a no-op
253
- that also answers `{"ok":true}`, so the answer never reveals other users' pods.
254
- Response `{"ok":true}`.
251
+ There is no public Pod credential revocation endpoint and no App action that
252
+ withdraws a Pod's access without contacting the device. Factory reset executes
253
+ on the Pod itself. Remote reset, when provided, must reach an online Pod; a
254
+ cloud-side deletion is not a successful reset. This contract currently exposes
255
+ no remote reset target. Each setup creates a new Pod credential.
255
256
 
256
257
  ### List and rename
257
258
 
@@ -282,8 +283,11 @@ and unexpired pending pods, each
282
283
  channel. Either close only ends that connection; whether the pod is revoked
283
284
  is decided by its next refresh.
284
285
  - `/api/v1/hub/token/exchange` with a pod access token returns a Hub JWT with
285
- `clientKind` = `pod` and `podId` claims and a 24-hour lifetime. Hub JWTs for
286
- user tokens are unchanged.
286
+ `clientKind` = `pod` and `podId` claims and the same lifetime as a Hub JWT
287
+ for a user token.
288
+ - Pod and ordinary Client local Hub JWTs have the same 100-year lifetime
289
+ (100 * 365 days). There is no per-Pod Hub revocation list or cloud-to-Hub
290
+ revocation sync. Local tokens are cleared on the Pod by reset/logout.
287
291
 
288
292
  ## 6. Pod runtime authentication
289
293
 
@@ -299,11 +303,14 @@ and unexpired pending pods, each
299
303
  re-sends `/focus`; it keeps one Hub JWT and Hub key per Hub and Space.
300
304
  - Local Hub: the Hub identity endpoint `POST /api/v1/hub/identity/get`
301
305
  (`{"hubId","tenantId","scopeId"}`) is public and needs no token. The pod
302
- proves the Hub identity, obtains a Hub JWT from `/api/v1/hub/token/exchange`
303
- over HTTPS with its pod access token and `ScopeId`, and authenticates to the
304
- local Hub only with that Hub JWT (`source=local`, `clientSource=pod`). The
305
- cloud access token is never sent over the local link. The pod exchanges a new
306
- Hub JWT before the current one expires and after a local rejection.
306
+ proves the Hub identity, then authenticates to the local Hub the way a Client
307
+ does: the first local `/auth` carries its pod access token with
308
+ `source=cloud` (`clientSource=pod`, `deviceId`, `tenantId`, `scopeId`,
309
+ `hubId`). The Hub exchanges that token with the cloud and returns a
310
+ long-lived local Hub JWT in `jwtToken`, which the pod stores per Hub. Later
311
+ local `/auth` requests use only that JWT with `source=local`. When the stored
312
+ JWT is about to expire or the Hub rejects it, the pod discards it and
313
+ authenticates with `source=cloud` again.
307
314
 
308
315
  ## 7. Native Client targets
309
316
 
@@ -346,8 +353,6 @@ never show these values or `message` verbatim.
346
353
  | `/local/pod/commission/wifi` | `{"sessionId","ssid","password"?}` | session snapshot |
347
354
  | `/local/pod/commission/wifi/scan` | `{"sessionId"}` | session snapshot |
348
355
  | `/local/pod/commission/authorize` | `{"sessionId","scopeId"}` | session snapshot |
349
- | `/local/pod/commission/cancel` | `{"sessionId"}` | session snapshot |
350
- | `/local/pod/commission/renew` | `{"sessionId"}` | session snapshot |
351
356
  | `/local/pod/commission/status` | `{}` | `{"session": snapshot or null}` |
352
357
  | `/local/pod/bluetooth/settings` | `{}` | `{"opened"}` |
353
358
 
@@ -369,10 +374,10 @@ UIs reconcile with `status` and discovery `list` on start, resume, reconnect and
369
374
  when they become visible, and a snapshot with a lower `revision` than one
370
375
  already seen for the same session is stale.
371
376
 
372
- The session that `start` creates is owned through a 30-second lease. The
373
- caller renews it with `/local/pod/commission/renew` every 10 seconds while it
374
- shows the session. An expired lease cancels the session, except in `delivering`
375
- and `activating`, which run to their conclusion.
377
+ The native core owns the session until success or failure, bounded by
378
+ `expiresAtMs`. There is no user cancellation or session renewal. Leaving the
379
+ page does not end setup; returning to it reads `status`. Waiting for user input
380
+ also counts toward the deadline. Discovery leases still control scanning.
376
381
 
377
382
  Sign-in and Spaces are checked at `start` (`not_signed_in`,
378
383
  `scope_not_offered`) and again when the authorization prompt is built.
@@ -391,7 +396,7 @@ pod's Wi-Fi rather than adding a pod.
391
396
  Session states, in order: `connecting`, `awaiting_code`, `securing`,
392
397
  `reading_info`, `awaiting_wifi`, `joining_wifi`, `awaiting_authorization`,
393
398
  `issuing`, `delivering`, `activating`, `completed`; terminal `failed`,
394
- `cancelled`, `timed_out`. Wi-Fi states are skipped when the pod reports a
399
+ `timed_out`. Wi-Fi states are skipped when the pod reports a
395
400
  configured network and `prov-config` status reports it connected; while that
396
401
  status is `connecting` the commissioner keeps polling, and only after a failure
397
402
  does it reset Wi-Fi (`prov-ctrl`) and offer new credentials. The commissioner
@@ -401,18 +406,15 @@ scan runs waits for it. `revision` increases on every observable change.
401
406
  `expiresAtMs` is 10 minutes after `start` until delivery starts; from
402
407
  `delivering` on it is `activateBefore` plus 30 seconds.
403
408
 
404
- Lion decides the outcome of activation. In `delivering` and `activating`, a
405
- lost BLE link, a failed status exchange or the deadline does not revoke by
406
- itself; the commissioner asks `/api/v1/pod/credential/status`:
407
-
408
- - `active`: `completed` with `podId`.
409
- - `pending`: ask again every 5 seconds until `activateBefore` plus 30 seconds,
410
- then revoke and fail with `activation_failed`.
411
- - `absent`: fail with `activation_failed`.
409
+ Only the device's durable `commissioned` response confirms successful setup.
410
+ Lost BLE delivery confirmation or activation status fails the attempt, even
411
+ when Lion has already activated its credential. Expiry fails or times out the
412
+ attempt. The commissioner does not recover interrupted setup through Lion's
413
+ credential status. The user resets the device and starts a fresh attempt.
412
414
 
413
415
  A refused `deliver` fails with `delivery_failed` carrying the pod's `reason`
414
- (`invalid_payload` or `wrong_state`) in `detail`, and revokes. When the pod
415
- itself reports `failed`, the commissioner revokes and fails with
416
+ (`invalid_payload` or `wrong_state`) in `detail`. When the pod
417
+ itself reports `failed`, the commissioner fails with
416
418
  `activation_failed` carrying the pod's code. `activation_failed` without a pod
417
419
  code carries `not_activated`. The commissioner records `completed` before
418
420
  sending `finish`. Native Clients keep a non-terminal session running while the
@@ -426,10 +428,9 @@ Session error codes: `bluetooth_unavailable`, `device_busy`, `disconnected`,
426
428
  `detail`), `session_timeout`. `code_locked` is terminal: the device shows a new
427
429
  code once it accepts handshakes again. `failed` and `timed_out` always carry `error`. A rejected
428
430
  code returns to `awaiting_code` with `code_rejected`; a failed join returns to
429
- `awaiting_wifi` with the `wifi_*` code. No other state carries `error`. After
430
- issue, every terminal state other than `completed` revokes the credential unless
431
- Lion already reported it `absent`; the revoke uses the account that issued the
432
- credential.
431
+ `awaiting_wifi` with the `wifi_*` code. No other state carries `error`. Terminal states release the link and radio
432
+ without revoking credentials. Reset the Pod and start over after an uncertain
433
+ activation outcome; unused pending cloud credentials expire automatically.
433
434
 
434
435
  The schemas are `schema/pod-discovery.v1.schema.json` and
435
436
  `schema/pod-commission.v1.schema.json`.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.app-facade.v1",
3
- "contractVersion": "1.34.0",
3
+ "contractVersion": "1.36.0",
4
4
  "callSchema": "schema/facade-call.v1.schema.json",
5
5
  "routing": "manifest/routing.v1.json",
6
6
  "routingSchema": "schema/routing.v1.schema.json",
@@ -1,14 +1,12 @@
1
1
  {
2
2
  "manifest": "mhome.host-provision.targets.v1",
3
- "contractVersion": "1.34.0",
3
+ "contractVersion": "1.36.0",
4
4
  "contract": "contract/host-provisioning-v1.md",
5
5
  "localTargets": [
6
6
  "/local/host/provision/start",
7
7
  "/local/host/provision/code",
8
8
  "/local/host/provision/wifi",
9
9
  "/local/host/provision/wifi/scan",
10
- "/local/host/provision/cancel",
11
- "/local/host/provision/renew",
12
10
  "/local/host/provision/status"
13
11
  ],
14
12
  "eventTargets": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.hub.targets.v1",
3
- "contractVersion": "1.34.0",
3
+ "contractVersion": "1.36.0",
4
4
  "appTargets": [
5
5
  "/app/hub/get",
6
6
  "/app/hub/remove",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.pod.targets.v1",
3
- "contractVersion": "1.34.0",
3
+ "contractVersion": "1.36.0",
4
4
  "contract": "contract/pod-commissioning-v1.md",
5
5
  "localTargets": [
6
6
  "/local/pod/discovery/start",
@@ -12,8 +12,6 @@
12
12
  "/local/pod/commission/wifi",
13
13
  "/local/pod/commission/wifi/scan",
14
14
  "/local/pod/commission/authorize",
15
- "/local/pod/commission/cancel",
16
- "/local/pod/commission/renew",
17
15
  "/local/pod/commission/status",
18
16
  "/local/pod/bluetooth/settings"
19
17
  ],
@@ -63,15 +61,12 @@
63
61
  },
64
62
  "session": {
65
63
  "lifetimeMs": 600000,
66
- "leaseMs": 30000,
67
- "renewIntervalMs": 10000,
68
64
  "activationGraceMs": 30000,
69
65
  "credentialStatusPollMs": 5000
70
66
  },
71
67
  "cloud": {
72
68
  "issue": "pod/credential/issue",
73
69
  "status": "pod/credential/status",
74
- "revoke": "pod/credential/revoke",
75
70
  "statusSchema": "schema/pod-credential-status.v1.schema.json"
76
71
  },
77
72
  "activationErrorCodes": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.app-facade.routing.v1",
3
- "contractVersion": "1.34.0",
3
+ "contractVersion": "1.36.0",
4
4
  "targetPrefix": "/app/",
5
5
  "default": {
6
6
  "execution": "scopeMode",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.messaging.targets.v1",
3
- "contractVersion": "1.34.0",
3
+ "contractVersion": "1.36.0",
4
4
  "requestTargets": [
5
5
  "/app/messaging/provider/list",
6
6
  "/app/messaging/provider-account/list",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhome/app-facade-protocol",
3
- "version": "1.34.0",
3
+ "version": "1.36.0",
4
4
  "description": "Build-time protocol artifacts for mhome.app-facade",
5
5
  "license": "MIT",
6
6
  "repository": {
package/protocol.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "protocol": "mhome.app-facade",
3
- "contractVersion": "1.34.0",
3
+ "contractVersion": "1.36.0",
4
4
  "manifest": "manifest/app-facade.v1.json",
5
5
  "schemas": {
6
6
  "capabilityDescriptor": "schema/capability-descriptor.v1.schema.json",
@@ -18,7 +18,6 @@
18
18
  "joining_wifi",
19
19
  "completed",
20
20
  "failed",
21
- "cancelled",
22
21
  "timed_out"
23
22
  ]
24
23
  },
@@ -22,7 +22,6 @@
22
22
  "activating",
23
23
  "completed",
24
24
  "failed",
25
- "cancelled",
26
25
  "timed_out"
27
26
  ]
28
27
  },