@mhome/core-protocol 1.15.11 → 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.
@@ -0,0 +1,113 @@
1
+ # Host permissions contract
2
+
3
+ Host coordinates permissions for this computer and the services it manages. It
4
+ never performs a service's protected business operations on that service's behalf.
5
+ Client/Desktop application permissions remain outside this report. No permission
6
+ request depends on a Space or enables a plugin in a Space.
7
+
8
+ This is a direct development cutover. There is one report model and no legacy
9
+ serialization, implicit component selection or alternative compatibility route.
10
+ Update Foundation consumers together after publishing the matching packages.
11
+
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.
30
+
31
+ ## Transport and actions
32
+
33
+ - `GET /v1/permissions` returns `HostPermissions` for installed requirements,
34
+ including stopped services and declaration/collection failures.
35
+ - `POST /internal/permissions/request` performs one explicit request or probe in
36
+ the selected service's actual execution context. It returns `{ "ok": true }`
37
+ once submitted, without collecting another snapshot or waiting for user consent.
38
+ - `POST /internal/permissions/open-settings` opens the platform settings pane.
39
+ It returns `{ "ok": true }` after opening. Status is queried separately.
40
+ - Both POSTs take `HostPermissionRequest`. `hostId`, `componentId` and `permission`
41
+ are mandatory. The component must declare that exact key. The caller cannot assert locality
42
+ or an arbitrary Settings URL.
43
+ - Require valid local control credentials and matching Host identity. Native
44
+ Desktop and CLI use Client IPC. Remote Hosts accept status only. Do not send
45
+ local control credentials to discovered addresses or redirects. A future Hub
46
+ read can relay a report but cannot relay mutations or decide grants.
47
+ - A service's `GET /internal/permissions` returns `ServicePermissions`. The outer
48
+ PID identifies the reporting service; individual observations can identify its
49
+ sidecar. The service rejects requests outside its declared requirements.
50
+
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.
56
+
57
+ ## Authorization observations
58
+
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.
64
+
65
+ - `system`: passive native authorization query and its timestamp.
66
+ - `platform`: platform consent semantics, not measured resource availability.
67
+ - `probe`: historical evidence from an explicit operation, with its timestamp.
68
+ - `unavailable`: a reliable consent observation could not be obtained.
69
+ - `notRequired`: this native backend has no application-consent step. It does not
70
+ bypass users/groups, D-Bus policies, device rules, sessions, portals or sandboxes.
71
+
72
+ Passive report reads never request authorization, probe Local Network, scan
73
+ Bluetooth, launch an Automation target, restart a service or open Settings.
74
+ Local Network's last explicit probe is historical; ordinary Refresh cannot make
75
+ it a fresh OS setting. Keep cached reports visibly stale when collection fails.
76
+ Missing/malformed declarations remain errors; an empty list is not proof of a
77
+ complete inventory. Do not grant by default on other OSes.
78
+
79
+ ## macOS and Linux
80
+
81
+ macOS keeps Host's existing responsible application and managed process tree.
82
+ Share identity only for validated permission/launch combinations. Services and
83
+ sidecars retain their own valid signatures and required hardened-runtime
84
+ entitlements. Signatures and launch association alone are not proof that an OS
85
+ grant is shared. Unsupported sharing stays a separate request; it never causes
86
+ business operations to migrate into Host. Never modify a signed plist in place.
87
+
88
+ Foundation's macOS status functions remain in-process and passive. Bluetooth's
89
+ explicit request initializes Core Bluetooth to request consent; merely opening
90
+ Settings is not a first-request implementation. When the actual user of BLE is
91
+ Matter's Node sidecar, obtain and request its state there. No Matter.js fork or
92
+ Host BLE broker is implied by this contract.
93
+
94
+ Linux native services use the configured ordinary runtime account and existing
95
+ system interfaces, including BlueZ D-Bus. Foundation reports the absence of a
96
+ native application-consent step as `notRequired`. Resource health belongs to the
97
+ service's own diagnostics and does not affect this permission report.
98
+ An installation preflight collects concrete missing conditions. An administrator
99
+ may authorize a bounded, idempotent setup step for those conditions; service
100
+ manifests must not supply privileged commands. Runtime processes remain ordinary
101
+ users. Account/session refresh requirements remain explicit. New device or
102
+ capability requirements can require additional setup. Container and sandbox
103
+ policies are separate deployment conditions, not automatically inherited grants.
104
+
105
+ ## Acceptance
106
+
107
+ Test passive reads without prompts; real signed allow/deny/revoke/relaunch
108
+ behavior; shared grants across two executors; sidecar and stopped-service observations; conflicting per-component states;
109
+ per-target Automation; cross-host mutation rejection; required component
110
+ selection; incomplete manifests; bounded collection; native request deduplication; and
111
+ Linux `notRequired` without any resource observation. Test Linux setup first as
112
+ a dry-run and verify effective access in the intended ordinary runtime session.
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.15.11",
3
+ "contractVersion": "1.17.0",
4
4
  "protocols": {
5
5
  "agentGateway": {
6
6
  "submitTarget": "/agent/submit",
@@ -76,6 +76,12 @@
76
76
  "failed",
77
77
  "unknown"
78
78
  ]
79
+ },
80
+ "hostPermissions": {
81
+ "schema": "schema/host-permissions.schema.json",
82
+ "statusPath": "/v1/permissions",
83
+ "requestPath": "/internal/permissions/request",
84
+ "settingsPath": "/internal/permissions/open-settings"
79
85
  }
80
86
  },
81
87
  "hostManagement": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhome/core-protocol",
3
- "version": "1.15.11",
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,9 +1,10 @@
1
1
  {
2
2
  "protocol": "mhome.core",
3
- "contractVersion": "1.15.11",
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",
7
+ "hostPermissions": "schema/host-permissions.schema.json",
7
8
  "normalizedInbound": "schema/normalized-inbound.v4.schema.json",
8
9
  "messagingCommands": "schema/messaging-commands.v1.schema.json",
9
10
  "interactionFlowNode": "schema/interaction-flow-node.v1.schema.json",
@@ -0,0 +1,303 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.mhome.ai/core/host-permissions.schema.json",
4
+ "title": "Host permission reports and local requests",
5
+ "$defs": {
6
+ "PermissionKey": {
7
+ "oneOf": [
8
+ {
9
+ "type": "object",
10
+ "additionalProperties": false,
11
+ "required": [
12
+ "id"
13
+ ],
14
+ "properties": {
15
+ "id": {
16
+ "enum": [
17
+ "localNetwork",
18
+ "bluetooth",
19
+ "microphone",
20
+ "reminders"
21
+ ]
22
+ }
23
+ }
24
+ },
25
+ {
26
+ "type": "object",
27
+ "additionalProperties": false,
28
+ "required": [
29
+ "id",
30
+ "targetBundleId"
31
+ ],
32
+ "properties": {
33
+ "id": {
34
+ "const": "automation"
35
+ },
36
+ "targetBundleId": {
37
+ "type": "string",
38
+ "minLength": 1
39
+ }
40
+ }
41
+ }
42
+ ]
43
+ },
44
+ "PermissionObservation": {
45
+ "type": "object",
46
+ "additionalProperties": false,
47
+ "required": [
48
+ "permission",
49
+ "processId",
50
+ "state",
51
+ "evidence",
52
+ "observedAtMs",
53
+ "error"
54
+ ],
55
+ "properties": {
56
+ "permission": {
57
+ "$ref": "#/$defs/PermissionKey"
58
+ },
59
+ "processId": {
60
+ "type": "integer",
61
+ "minimum": 1
62
+ },
63
+ "state": {
64
+ "enum": [
65
+ "unknown",
66
+ "notDetermined",
67
+ "granted",
68
+ "notRequired",
69
+ "denied",
70
+ "restricted",
71
+ "unsupported"
72
+ ]
73
+ },
74
+ "evidence": {
75
+ "enum": [
76
+ "system",
77
+ "platform",
78
+ "probe",
79
+ "unavailable"
80
+ ]
81
+ },
82
+ "observedAtMs": {
83
+ "anyOf": [
84
+ {
85
+ "type": "integer",
86
+ "minimum": 0
87
+ },
88
+ {
89
+ "type": "null"
90
+ }
91
+ ]
92
+ },
93
+ "error": {
94
+ "anyOf": [
95
+ {
96
+ "type": "string"
97
+ },
98
+ {
99
+ "type": "null"
100
+ }
101
+ ]
102
+ }
103
+ },
104
+ "allOf": [
105
+ {
106
+ "if": {
107
+ "properties": {
108
+ "evidence": {
109
+ "const": "probe"
110
+ }
111
+ }
112
+ },
113
+ "then": {
114
+ "properties": {
115
+ "observedAtMs": {
116
+ "type": "integer",
117
+ "minimum": 0
118
+ }
119
+ }
120
+ }
121
+ }
122
+ ]
123
+ },
124
+ "HostPermission": {
125
+ "type": "object",
126
+ "additionalProperties": false,
127
+ "required": [
128
+ "componentId",
129
+ "componentName",
130
+ "permission",
131
+ "feature",
132
+ "reason",
133
+ "observation",
134
+ "error",
135
+ "supportedActions"
136
+ ],
137
+ "properties": {
138
+ "componentId": {
139
+ "type": "string",
140
+ "minLength": 1
141
+ },
142
+ "componentName": {
143
+ "type": "string",
144
+ "minLength": 1
145
+ },
146
+ "permission": {
147
+ "$ref": "#/$defs/PermissionKey"
148
+ },
149
+ "feature": {
150
+ "type": "string",
151
+ "minLength": 1
152
+ },
153
+ "reason": {
154
+ "type": "string",
155
+ "minLength": 1
156
+ },
157
+ "observation": {
158
+ "anyOf": [
159
+ {
160
+ "$ref": "#/$defs/PermissionObservation"
161
+ },
162
+ {
163
+ "type": "null"
164
+ }
165
+ ]
166
+ },
167
+ "error": {
168
+ "anyOf": [
169
+ {
170
+ "type": "string"
171
+ },
172
+ {
173
+ "type": "null"
174
+ }
175
+ ]
176
+ },
177
+ "supportedActions": {
178
+ "type": "array",
179
+ "uniqueItems": true,
180
+ "items": {
181
+ "enum": [
182
+ "request",
183
+ "openSettings"
184
+ ]
185
+ }
186
+ }
187
+ }
188
+ },
189
+ "PermissionDeclarationError": {
190
+ "type": "object",
191
+ "additionalProperties": false,
192
+ "required": [
193
+ "componentId",
194
+ "message"
195
+ ],
196
+ "properties": {
197
+ "componentId": {
198
+ "type": "string",
199
+ "minLength": 1
200
+ },
201
+ "message": {
202
+ "type": "string",
203
+ "minLength": 1
204
+ }
205
+ }
206
+ },
207
+ "HostPermissions": {
208
+ "type": "object",
209
+ "additionalProperties": false,
210
+ "required": [
211
+ "hostId",
212
+ "hostVersion",
213
+ "platform",
214
+ "observedAtMs",
215
+ "permissions",
216
+ "declarationErrors"
217
+ ],
218
+ "properties": {
219
+ "hostId": {
220
+ "type": "string",
221
+ "minLength": 1
222
+ },
223
+ "hostVersion": {
224
+ "type": "string",
225
+ "minLength": 1
226
+ },
227
+ "platform": {
228
+ "type": "string",
229
+ "minLength": 1
230
+ },
231
+ "observedAtMs": {
232
+ "type": "integer",
233
+ "minimum": 0
234
+ },
235
+ "permissions": {
236
+ "type": "array",
237
+ "items": {
238
+ "$ref": "#/$defs/HostPermission"
239
+ }
240
+ },
241
+ "declarationErrors": {
242
+ "type": "array",
243
+ "items": {
244
+ "$ref": "#/$defs/PermissionDeclarationError"
245
+ }
246
+ }
247
+ }
248
+ },
249
+ "HostPermissionRequest": {
250
+ "type": "object",
251
+ "additionalProperties": false,
252
+ "required": [
253
+ "hostId",
254
+ "componentId",
255
+ "permission"
256
+ ],
257
+ "properties": {
258
+ "hostId": {
259
+ "type": "string",
260
+ "minLength": 1
261
+ },
262
+ "componentId": {
263
+ "type": "string",
264
+ "minLength": 1
265
+ },
266
+ "permission": {
267
+ "$ref": "#/$defs/PermissionKey"
268
+ }
269
+ }
270
+ },
271
+ "ServicePermissions": {
272
+ "type": "object",
273
+ "additionalProperties": false,
274
+ "required": [
275
+ "processId",
276
+ "permissions"
277
+ ],
278
+ "properties": {
279
+ "processId": {
280
+ "type": "integer",
281
+ "minimum": 1
282
+ },
283
+ "permissions": {
284
+ "type": "array",
285
+ "items": {
286
+ "$ref": "#/$defs/PermissionObservation"
287
+ }
288
+ }
289
+ }
290
+ }
291
+ },
292
+ "oneOf": [
293
+ {
294
+ "$ref": "#/$defs/HostPermissions"
295
+ },
296
+ {
297
+ "$ref": "#/$defs/HostPermissionRequest"
298
+ },
299
+ {
300
+ "$ref": "#/$defs/ServicePermissions"
301
+ }
302
+ ]
303
+ }