@mhome/core-protocol 1.16.0 → 1.17.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.
@@ -9,48 +9,37 @@ This is a direct development cutover. There is one report model and no legacy
9
9
  serialization, implicit component selection or alternative compatibility route.
10
10
  Update Foundation consumers together after publishing the matching packages.
11
11
 
12
- ## Declaration, executor and authorization subject
13
-
14
- Each service keeps its source-root `permission.yml`, packages it, and reports its
15
- own requirements. Host stores installed declarations so stopped services remain
16
- visible. A declaration says which feature needs access, not who the OS charges.
17
-
18
- `PermissionObservation.processId` identifies the actual executing process. It is
19
- not a grant identity. A helper may share its responsible application's consent.
20
- A Rust service must obtain a sidecar's observation over its private transport when
21
- that sidecar executes the protected operation; it must not substitute its own
22
- Foundation query. Check the real backend used by that executor, including access
23
- errors. Standalone launches report their actual launch context, which can differ
24
- from Host-managed launches. Reading status must not start an absent sidecar.
25
-
26
- `PermissionSubject` is an authorization scope established for the specific
27
- permission, launch path and packaging profile. Host may group by this subject and
28
- the full `PermissionKey`. Equal states, equal signing teams, a common parent,
29
- service-supplied labels or environment variables alone do not establish sharing.
30
- There is no generic public macOS query in this contract that discovers another
31
- process's TCC grant owner. Verify responsible-application attribution using the
32
- real signed launch chain before enabling a shared subject for a permission.
33
- If attribution is unconfirmed, `subject` is null and that component stays separate.
34
- Different Automation `targetBundleId` values always remain distinct grants.
35
-
36
- A grouped row preserves every `PermissionUse`, its observation and any collection
37
- error. There is deliberately no group-level grant/state that can erase a denial,
38
- an unknown executor or conflicting observations. A stopped service has no current
39
- observation. Do not copy another service's grant into that empty observation.
40
- Host chooses `requestComponentId` from available executors; it is null when none
41
- can handle the request. `supportedActions` describes available mechanisms only.
12
+ ## Declaration and executor
13
+
14
+ Each service keeps its source-root `permission.yml` and packages it. The installed
15
+ package is the sole source of desired permissions. Host loads declarations into
16
+ its installed-service inventory on startup and when packages change. Status reads
17
+ use that memory snapshot; they do not scan packages or maintain a database copy.
18
+ Stopped services retain their requirements. Invalid declarations remain visible.
19
+
20
+ Each `HostPermission` is one component and one permission: `componentId`,
21
+ `componentName`, `permission`, `feature`, `reason`, `observation`, `error`, and
22
+ `supportedActions`. There are no grouped subjects or selected substitute executors.
23
+ `PermissionObservation.processId` identifies the actual executing process, not an
24
+ OS grant identity. A sidecar reports its own observation through its service.
25
+ Reading status must not start an absent sidecar or substitute its parent's grant.
26
+
27
+ A helper can share its responsible application's consent without any grouping
28
+ fields in this protocol. Equal PIDs, signing teams or states alone do not establish
29
+ shared grants. Automation keys remain distinct per target application.
42
30
 
43
31
  ## Transport and actions
44
32
 
45
33
  - `GET /v1/permissions` returns `HostPermissions` for installed requirements,
46
34
  including stopped services and declaration/collection failures.
47
35
  - `POST /internal/permissions/request` performs one explicit request or probe in
48
- the selected service's actual execution context, then refreshes the report.
36
+ the selected service's actual execution context. It returns `{ "ok": true }`
37
+ once submitted, without collecting another snapshot or waiting for user consent.
49
38
  - `POST /internal/permissions/open-settings` opens the platform settings pane.
50
- Opening Settings is not a grant; status must be refreshed afterwards.
39
+ It returns `{ "ok": true }` after opening. Status is queried separately.
51
40
  - Both POSTs take `HostPermissionRequest`. `hostId`, `componentId` and `permission`
52
- are mandatory. The component must declare that exact key. Host derives grouping
53
- itself; the caller cannot assert a subject, locality or an arbitrary Settings URL.
41
+ are mandatory. The component must declare that exact key. The caller cannot assert locality
42
+ or an arbitrary Settings URL.
54
43
  - Require valid local control credentials and matching Host identity. Native
55
44
  Desktop and CLI use Client IPC. Remote Hosts accept status only. Do not send
56
45
  local control credentials to discovered addresses or redirects. A future Hub
@@ -59,18 +48,19 @@ can handle the request. `supportedActions` describes available mechanisms only.
59
48
  PID identifies the reporting service; individual observations can identify its
60
49
  sidecar. The service rejects requests outside its declared requirements.
61
50
 
62
- Host coalesces in-flight requests for an established shared subject and key, or
63
- for the individual executor and key when sharing is unknown. Transport timeout
64
- must not start a duplicate native request. Refresh all affected service reports
65
- when an operation completes. Bound collection concurrency and the whole snapshot
66
- latency; a stalled executor must not delay every other service indefinitely.
51
+ Foundation owns native request deduplication in the actual executor. Transport
52
+ timeouts do not release that ownership. UI polls status every two seconds while
53
+ managing permissions and refreshes on focus; a pending read is shared. CLI batch
54
+ requests query once after submission. Bound collection concurrency and the whole
55
+ snapshot latency so a stalled executor cannot hold every other service indefinitely.
67
56
 
68
- ## Consent and actual access
57
+ ## Authorization observations
69
58
 
70
- `PermissionObservation.state` describes application consent. `access` is a
71
- separate backend observation. A successful consent check does not establish that
72
- a device is powered on, present, correctly configured or usable. A successful
73
- scan does not prove that every connection or operation will succeed.
59
+ `PermissionObservation.state` describes application consent only. The report
60
+ contains declared requirements and authorization observations; it does not collect
61
+ or carry resource availability or business-operation results. `granted` and
62
+ `notRequired` satisfy a requirement when the observation has valid evidence and
63
+ no errors. Missing, denied or unreadable observations remain unresolved.
74
64
 
75
65
  - `system`: passive native authorization query and its timestamp.
76
66
  - `platform`: platform consent semantics, not measured resource availability.
@@ -78,17 +68,13 @@ scan does not prove that every connection or operation will succeed.
78
68
  - `unavailable`: a reliable consent observation could not be obtained.
79
69
  - `notRequired`: this native backend has no application-consent step. It does not
80
70
  bypass users/groups, D-Bus policies, device rules, sessions, portals or sandboxes.
81
- - `access`: the actual backend's checked condition, timestamp and result. Missing
82
- access evidence is unknown, not proof of success. Its detail describes only the
83
- operation/condition actually observed. Do not relabel a hardware failure as OS
84
- user denial or a successful consent query as a successful resource probe.
85
71
 
86
72
  Passive report reads never request authorization, probe Local Network, scan
87
73
  Bluetooth, launch an Automation target, restart a service or open Settings.
88
74
  Local Network's last explicit probe is historical; ordinary Refresh cannot make
89
75
  it a fresh OS setting. Keep cached reports visibly stale when collection fails.
90
76
  Missing/malformed declarations remain errors; an empty list is not proof of a
91
- complete inventory or successful access. Do not grant by default on other OSes.
77
+ complete inventory. Do not grant by default on other OSes.
92
78
 
93
79
  ## macOS and Linux
94
80
 
@@ -107,8 +93,8 @@ Host BLE broker is implied by this contract.
107
93
 
108
94
  Linux native services use the configured ordinary runtime account and existing
109
95
  system interfaces, including BlueZ D-Bus. Foundation reports the absence of a
110
- native application-consent step separately from the backend's access results.
111
- Host and services must not claim access merely because there is no macOS TCC.
96
+ native application-consent step as `notRequired`. Resource health belongs to the
97
+ service's own diagnostics and does not affect this permission report.
112
98
  An installation preflight collects concrete missing conditions. An administrator
113
99
  may authorize a bounded, idempotent setup step for those conditions; service
114
100
  manifests must not supply privileged commands. Runtime processes remain ordinary
@@ -119,10 +105,9 @@ policies are separate deployment conditions, not automatically inherited grants.
119
105
  ## Acceptance
120
106
 
121
107
  Test passive reads without prompts; real signed allow/deny/revoke/relaunch
122
- behavior; shared grants across two executors; independent/unknown subjects;
123
- sidecar and stopped-service observations; conflicting per-service states;
108
+ behavior; shared grants across two executors; sidecar and stopped-service observations; conflicting per-component states;
124
109
  per-target Automation; cross-host mutation rejection; required component
125
- selection; incomplete manifests; bounded collection; coalesced requests; and
126
- Linux policy denial versus missing/disabled resources. Test Linux setup first as
110
+ selection; incomplete manifests; bounded collection; native request deduplication; and
111
+ Linux `notRequired` without any resource observation. Test Linux setup first as
127
112
  a dry-run and verify effective access in the intended ordinary runtime session.
128
113
  Signing fixtures and mocked status tests do not prove TCC attribution.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.core.v1",
3
- "contractVersion": "1.16.0",
3
+ "contractVersion": "1.17.0",
4
4
  "protocols": {
5
5
  "agentGateway": {
6
6
  "submitTarget": "/agent/submit",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhome/core-protocol",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Build-time protocol artifacts for mhome.core",
5
5
  "license": "MIT",
6
6
  "repository": {
package/protocol.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "protocol": "mhome.core",
3
- "contractVersion": "1.16.0",
3
+ "contractVersion": "1.17.0",
4
4
  "manifest": "manifest/core.v1.json",
5
5
  "schemas": {
6
6
  "hostManagementRequest": "schema/host-management-request.v1.schema.json",
@@ -41,39 +41,6 @@
41
41
  }
42
42
  ]
43
43
  },
44
- "PermissionAccess": {
45
- "type": "object",
46
- "additionalProperties": false,
47
- "required": [
48
- "state",
49
- "observedAtMs",
50
- "detail"
51
- ],
52
- "properties": {
53
- "state": {
54
- "enum": [
55
- "unknown",
56
- "available",
57
- "unavailable"
58
- ]
59
- },
60
- "observedAtMs": {
61
- "anyOf": [
62
- {
63
- "type": "integer",
64
- "minimum": 0
65
- },
66
- {
67
- "type": "null"
68
- }
69
- ]
70
- },
71
- "detail": {
72
- "type": "string",
73
- "minLength": 1
74
- }
75
- }
76
- },
77
44
  "PermissionObservation": {
78
45
  "type": "object",
79
46
  "additionalProperties": false,
@@ -83,7 +50,6 @@
83
50
  "state",
84
51
  "evidence",
85
52
  "observedAtMs",
86
- "access",
87
53
  "error"
88
54
  ],
89
55
  "properties": {
@@ -124,16 +90,6 @@
124
90
  }
125
91
  ]
126
92
  },
127
- "access": {
128
- "anyOf": [
129
- {
130
- "$ref": "#/$defs/PermissionAccess"
131
- },
132
- {
133
- "type": "null"
134
- }
135
- ]
136
- },
137
93
  "error": {
138
94
  "anyOf": [
139
95
  {
@@ -165,34 +121,18 @@
165
121
  }
166
122
  ]
167
123
  },
168
- "PermissionSubject": {
169
- "type": "object",
170
- "additionalProperties": false,
171
- "required": [
172
- "id",
173
- "name"
174
- ],
175
- "properties": {
176
- "id": {
177
- "type": "string",
178
- "minLength": 1
179
- },
180
- "name": {
181
- "type": "string",
182
- "minLength": 1
183
- }
184
- }
185
- },
186
- "PermissionUse": {
124
+ "HostPermission": {
187
125
  "type": "object",
188
126
  "additionalProperties": false,
189
127
  "required": [
190
128
  "componentId",
191
129
  "componentName",
130
+ "permission",
192
131
  "feature",
193
132
  "reason",
194
133
  "observation",
195
- "error"
134
+ "error",
135
+ "supportedActions"
196
136
  ],
197
137
  "properties": {
198
138
  "componentId": {
@@ -203,6 +143,9 @@
203
143
  "type": "string",
204
144
  "minLength": 1
205
145
  },
146
+ "permission": {
147
+ "$ref": "#/$defs/PermissionKey"
148
+ },
206
149
  "feature": {
207
150
  "type": "string",
208
151
  "minLength": 1
@@ -230,50 +173,6 @@
230
173
  "type": "null"
231
174
  }
232
175
  ]
233
- }
234
- }
235
- },
236
- "HostPermission": {
237
- "type": "object",
238
- "additionalProperties": false,
239
- "required": [
240
- "permission",
241
- "subject",
242
- "uses",
243
- "requestComponentId",
244
- "supportedActions"
245
- ],
246
- "properties": {
247
- "permission": {
248
- "$ref": "#/$defs/PermissionKey"
249
- },
250
- "subject": {
251
- "anyOf": [
252
- {
253
- "$ref": "#/$defs/PermissionSubject"
254
- },
255
- {
256
- "type": "null"
257
- }
258
- ]
259
- },
260
- "uses": {
261
- "type": "array",
262
- "minItems": 1,
263
- "items": {
264
- "$ref": "#/$defs/PermissionUse"
265
- }
266
- },
267
- "requestComponentId": {
268
- "anyOf": [
269
- {
270
- "type": "string",
271
- "minLength": 1
272
- },
273
- {
274
- "type": "null"
275
- }
276
- ]
277
176
  },
278
177
  "supportedActions": {
279
178
  "type": "array",
@@ -285,45 +184,7 @@
285
184
  ]
286
185
  }
287
186
  }
288
- },
289
- "allOf": [
290
- {
291
- "if": {
292
- "properties": {
293
- "subject": {
294
- "type": "null"
295
- }
296
- }
297
- },
298
- "then": {
299
- "properties": {
300
- "uses": {
301
- "maxItems": 1
302
- }
303
- }
304
- }
305
- },
306
- {
307
- "if": {
308
- "properties": {
309
- "requestComponentId": {
310
- "type": "null"
311
- }
312
- }
313
- },
314
- "then": {
315
- "properties": {
316
- "supportedActions": {
317
- "not": {
318
- "contains": {
319
- "const": "request"
320
- }
321
- }
322
- }
323
- }
324
- }
325
- }
326
- ]
187
+ }
327
188
  },
328
189
  "PermissionDeclarationError": {
329
190
  "type": "object",