@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.
- package/contract/host-permissions.md +113 -0
- package/manifest/core.v1.json +7 -1
- package/package.json +1 -1
- package/protocol.json +2 -1
- package/schema/host-permissions.schema.json +303 -0
|
@@ -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.
|
package/manifest/core.v1.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest": "mhome.core.v1",
|
|
3
|
-
"contractVersion": "1.
|
|
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
package/protocol.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"protocol": "mhome.core",
|
|
3
|
-
"contractVersion": "1.
|
|
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
|
+
}
|