@holoscript/holosystem 0.2.1 → 0.2.3
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/README.md +547 -0
- package/bin/holosystem.mjs +427 -7
- package/docs/native-build-threat-model.md +150 -0
- package/docs/vm-launch-threat-model.md +91 -0
- package/package.json +3 -2
- package/src/catalog.mjs +20 -1
- package/src/index.mjs +37 -0
- package/src/native-build.mjs +970 -0
- package/src/substrate-debian-release.mjs +852 -0
- package/src/substrate-import-debian.mjs +979 -0
- package/src/substrate-import.mjs +578 -0
- package/src/substrate.mjs +789 -0
- package/src/vm-launch.mjs +927 -0
package/README.md
CHANGED
|
@@ -55,6 +55,553 @@ returns one highest-priority candidate, and carries authority, validation, lease
|
|
|
55
55
|
and spend stop conditions. It selects work; it does not claim a board task,
|
|
56
56
|
publish a package, spend funds, or bypass caller authority.
|
|
57
57
|
|
|
58
|
+
## Substrate Closure
|
|
59
|
+
|
|
60
|
+
The substrate closure replaces an implicit operating-system dependency tower
|
|
61
|
+
with a deterministic, caller-owned infrastructure graph. Every component names
|
|
62
|
+
an exact version, portable source revision, content digest, custody owner,
|
|
63
|
+
dependency edges, installation-script state, and a cryptographically
|
|
64
|
+
authenticated rebuild. External
|
|
65
|
+
components can remain during migration, but they are visible sovereignty
|
|
66
|
+
boundaries rather than hidden transitive dependencies.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx holosystem substrate \
|
|
70
|
+
--input substrate.json \
|
|
71
|
+
--output substrate-lock.json \
|
|
72
|
+
--json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
import { buildSubstrateClosure, createRebuildAttestationPayload } from '@holoscript/holosystem';
|
|
77
|
+
|
|
78
|
+
const component = {
|
|
79
|
+
id: 'holosystem',
|
|
80
|
+
kind: 'runtime',
|
|
81
|
+
version: '1.0.0',
|
|
82
|
+
custody: {
|
|
83
|
+
mode: 'owned',
|
|
84
|
+
owner: 'holoscript',
|
|
85
|
+
trustDomain: 'holoscript-release',
|
|
86
|
+
},
|
|
87
|
+
source: { uri: 'holorepo://holoscript', revision: 'abc123' },
|
|
88
|
+
artifact: { digest: 'sha256:<64 lowercase hex characters>' },
|
|
89
|
+
execution: { installScripts: 'none' },
|
|
90
|
+
requires: [],
|
|
91
|
+
verification: {
|
|
92
|
+
rebuilds: [
|
|
93
|
+
{
|
|
94
|
+
verifier: 'independent-builder',
|
|
95
|
+
digest: 'sha256:<same 64 lowercase hex characters>',
|
|
96
|
+
signature: '<Ed25519 signature in canonical base64>',
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// The independent builder signs this exact UTF-8 payload with its private key.
|
|
103
|
+
const payload = createRebuildAttestationPayload({
|
|
104
|
+
verifier: 'independent-builder',
|
|
105
|
+
component,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const receipt = buildSubstrateClosure({
|
|
109
|
+
root: 'holosystem',
|
|
110
|
+
coverage: {
|
|
111
|
+
includedLayers: ['kernel', 'runtime', 'toolchain'],
|
|
112
|
+
missingLayers: [],
|
|
113
|
+
},
|
|
114
|
+
verificationPolicy: {
|
|
115
|
+
minimumIndependentRebuilds: 1,
|
|
116
|
+
trustRoots: [
|
|
117
|
+
{
|
|
118
|
+
verifier: 'independent-builder',
|
|
119
|
+
trustDomain: 'independent-builders',
|
|
120
|
+
publicKey: '<Ed25519 public key in PEM format>',
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
components: [component],
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The command exits with code `2` and still emits the blocked receipt when it
|
|
129
|
+
finds a missing or unreachable dependency, a cycle, a floating version, a local
|
|
130
|
+
source path, an invalid digest, an untrusted verifier, a forged signature, a
|
|
131
|
+
same-domain rebuild, a present installation script, or too few matching
|
|
132
|
+
rebuilds. The receipt exposes only public-key fingerprints, not the supplied
|
|
133
|
+
PEM text. Installation scripts remain blocked until a later execution layer can
|
|
134
|
+
prove they were forcibly disabled or sandboxed.
|
|
135
|
+
|
|
136
|
+
`coverage.includedLayers` declares what the graph represents, while any known
|
|
137
|
+
gap remains in `coverage.missingLayers` and blocks readiness. Coverage is still
|
|
138
|
+
a caller assertion rather than discovery proof; importers must carry their
|
|
139
|
+
known blind spots into this field.
|
|
140
|
+
|
|
141
|
+
Trust domains are assertions in the caller-owned policy. The signature proves
|
|
142
|
+
that the named key attested to the exact component tuple; it does not prove that
|
|
143
|
+
the builder is organizationally independent or that the component is safe.
|
|
144
|
+
Production trust roots therefore belong to independently governed rebuilders,
|
|
145
|
+
not keys created by the component custodian for the same release.
|
|
146
|
+
|
|
147
|
+
### Import an npm dependency graph
|
|
148
|
+
|
|
149
|
+
`substrate-import` derives the graph from an npm `package-lock.json` instead of
|
|
150
|
+
asking an operator to maintain the dependency edges manually. It supports lock
|
|
151
|
+
formats [v2 and v3](https://docs.npmjs.com/cli/v11/configuring-npm/package-lock-json/),
|
|
152
|
+
preserves nested Node resolution, converts canonical npm SRI
|
|
153
|
+
integrity into content digests, and marks every registry package as external
|
|
154
|
+
custody.
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"root": {
|
|
159
|
+
"id": "demo-app",
|
|
160
|
+
"custody": {
|
|
161
|
+
"mode": "owned",
|
|
162
|
+
"owner": "demo",
|
|
163
|
+
"trustDomain": "demo-release"
|
|
164
|
+
},
|
|
165
|
+
"source": {
|
|
166
|
+
"uri": "https://github.com/example/demo",
|
|
167
|
+
"revision": "demo-revision-1"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"verificationPolicy": {
|
|
171
|
+
"minimumIndependentRebuilds": 1,
|
|
172
|
+
"trustRoots": [
|
|
173
|
+
{
|
|
174
|
+
"verifier": "rebuild-farm",
|
|
175
|
+
"trustDomain": "independent-rebuild-farm",
|
|
176
|
+
"publicKey": "<Ed25519 public key in PEM format>"
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
npx holosystem substrate-import \
|
|
185
|
+
--lock package-lock.json \
|
|
186
|
+
--config substrate-import.json \
|
|
187
|
+
--output substrate-input.json \
|
|
188
|
+
--json
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The command writes the generated closure input to `--output` and emits an
|
|
192
|
+
import receipt on stdout. A valid import has status
|
|
193
|
+
`coverage-and-attestation-required`, or `execution-policy-required` when a
|
|
194
|
+
package declares an installation script; neither means `ready`. Lockfiles prove
|
|
195
|
+
resolution and registry integrity, but not a rebuild, safe lifecycle execution,
|
|
196
|
+
or complete operating-system coverage. After independent builders attach signed
|
|
197
|
+
attestations, run the existing
|
|
198
|
+
`holosystem substrate --input substrate-input.json --json` gate.
|
|
199
|
+
|
|
200
|
+
Production dependencies are imported by default. Set `"includeDev": true` in
|
|
201
|
+
the import configuration to include direct development dependencies. Optional
|
|
202
|
+
branches present in the lock are retained as a conservative superset; missing
|
|
203
|
+
optional dependencies are counted but do not block import. No package or
|
|
204
|
+
install script is executed. `registry.npmjs.org` lockfile indirection is
|
|
205
|
+
preserved as `npm://configured-registry/...` instead of pretending that the
|
|
206
|
+
configured registry endpoint is known. The root component's artifact digest
|
|
207
|
+
represents the canonical lockfile, not a compiled application artifact. Native
|
|
208
|
+
libraries and operating-system packages are not derived yet, so those gaps are
|
|
209
|
+
carried into the generated input and block the closure. Local links,
|
|
210
|
+
missing runtime entries, malformed or weak integrity, non-portable sources, and
|
|
211
|
+
private keys in the trust policy block import without echoing their values.
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
import { importNpmPackageLock } from '@holoscript/holosystem';
|
|
215
|
+
|
|
216
|
+
const imported = importNpmPackageLock({
|
|
217
|
+
lockfile,
|
|
218
|
+
root,
|
|
219
|
+
verificationPolicy,
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Import an installed Debian package graph
|
|
224
|
+
|
|
225
|
+
`substrate-import-debian` converts offline Debian evidence into an
|
|
226
|
+
operating-system substrate graph. It accepts one or more independently anchored
|
|
227
|
+
repository indexes, parses the standard control-stanza format,
|
|
228
|
+
resolves `Depends`, `Pre-Depends`, alternatives, exact version relations, and
|
|
229
|
+
installed virtual-package providers according to the
|
|
230
|
+
[Debian relationship rules](https://www.debian.org/doc/debian-policy/ch-relationships.html),
|
|
231
|
+
and binds each installed package to the SHA-256 or SHA-512 and `Filename` in a
|
|
232
|
+
caller-anchored `Packages` index.
|
|
233
|
+
|
|
234
|
+
The command does not run `dpkg`, `apt`, or any package script. Supply:
|
|
235
|
+
|
|
236
|
+
- the installed `dpkg` status text;
|
|
237
|
+
- each uncompressed repository `Packages` index and its expected digest;
|
|
238
|
+
- a JSON map for every installed `package:architecture`, containing hashes for
|
|
239
|
+
any `preinst`, `postinst`, `prerm`, `postrm`, or `config` scripts;
|
|
240
|
+
- a config containing the repository base, system root, custody, and optional
|
|
241
|
+
rebuild policy.
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"repository": {
|
|
246
|
+
"uri": "https://deb.debian.org/debian/",
|
|
247
|
+
"packagesIndexDigest": "sha256:<digest of the exact Packages input>"
|
|
248
|
+
},
|
|
249
|
+
"root": {
|
|
250
|
+
"id": "demo-linux",
|
|
251
|
+
"version": "12.1-1",
|
|
252
|
+
"custody": {
|
|
253
|
+
"mode": "owned",
|
|
254
|
+
"owner": "demo",
|
|
255
|
+
"trustDomain": "demo-os-release"
|
|
256
|
+
},
|
|
257
|
+
"source": {
|
|
258
|
+
"uri": "https://images.example.com/demo-linux",
|
|
259
|
+
"revision": "snapshot-20260716"
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```json
|
|
266
|
+
{
|
|
267
|
+
"base-files:amd64": {},
|
|
268
|
+
"demo-app:amd64": {
|
|
269
|
+
"postinst": "sha256:<digest of the installed postinst file>"
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
npx holosystem substrate-import-debian \
|
|
276
|
+
--status status \
|
|
277
|
+
--packages Packages \
|
|
278
|
+
--maintainer-scripts maintainer-scripts.json \
|
|
279
|
+
--config debian-substrate-import.json \
|
|
280
|
+
--output debian-substrate-input.json \
|
|
281
|
+
--json
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Real installations usually span base, security, update, or third-party
|
|
285
|
+
archives. Use `--sources` instead of `--packages` for those systems. The source
|
|
286
|
+
manifest is a JSON array; local `packages` paths are read by the CLI but are not
|
|
287
|
+
copied into the receipt.
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
[
|
|
291
|
+
{
|
|
292
|
+
"packages": "Packages.main",
|
|
293
|
+
"uri": "https://deb.debian.org/debian/",
|
|
294
|
+
"packagesIndexDigest": "sha256:<digest>",
|
|
295
|
+
"authentication": {
|
|
296
|
+
"inReleasePath": "InRelease",
|
|
297
|
+
"verifier": {
|
|
298
|
+
"path": "/usr/bin/gpgv",
|
|
299
|
+
"digest": "sha256:<gpgv-binary-digest>"
|
|
300
|
+
},
|
|
301
|
+
"keyrings": [
|
|
302
|
+
{
|
|
303
|
+
"path": "/usr/share/keyrings/debian-archive-keyring.gpg",
|
|
304
|
+
"digest": "sha256:<keyring-digest>"
|
|
305
|
+
}
|
|
306
|
+
],
|
|
307
|
+
"trustedFingerprints": ["<exact-primary-or-signing-key-fingerprint>"],
|
|
308
|
+
"expected": {
|
|
309
|
+
"suite": "stable",
|
|
310
|
+
"codename": "trixie",
|
|
311
|
+
"architecture": "amd64",
|
|
312
|
+
"component": "main"
|
|
313
|
+
},
|
|
314
|
+
"packagesIndexPath": "main/binary-amd64/Packages",
|
|
315
|
+
"maxReleaseAgeSeconds": 604800
|
|
316
|
+
},
|
|
317
|
+
"custody": {
|
|
318
|
+
"owner": "debian-main",
|
|
319
|
+
"trustDomain": "debian-main-archive"
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"packages": "Packages.security",
|
|
324
|
+
"uri": "https://security.debian.org/debian-security/",
|
|
325
|
+
"packagesIndexDigest": "sha256:<digest>",
|
|
326
|
+
"custody": {
|
|
327
|
+
"owner": "debian-security",
|
|
328
|
+
"trustDomain": "debian-security-archive"
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
]
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
npx holosystem substrate-import-debian \
|
|
336
|
+
--status status \
|
|
337
|
+
--sources debian-sources.json \
|
|
338
|
+
--maintainer-scripts maintainer-scripts.json \
|
|
339
|
+
--config debian-substrate-import.json \
|
|
340
|
+
--output debian-substrate-input.json \
|
|
341
|
+
--json
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Authentication paths are operational inputs resolved from the current working
|
|
345
|
+
directory and are not copied into receipts. Use `releasePath` plus
|
|
346
|
+
`releaseSignaturePath` instead of `inReleasePath` for detached `Release.gpg`
|
|
347
|
+
signatures. A Git-for-Windows verifier can set `verifier.pathStyle` to `msys`;
|
|
348
|
+
other verifiers use the default `native` path style. The package never fetches
|
|
349
|
+
or imports keys, so establishing the verifier digest, keyring digest, and exact
|
|
350
|
+
fingerprint allowlist remains a caller-owned trust-bootstrap operation.
|
|
351
|
+
On the first import, omit `minimumReleaseDate`; on later imports, copy the
|
|
352
|
+
accepted `release.date` from the last receipt to reject rollback within the
|
|
353
|
+
freshness window. Multi-signed archives require every signature visible to
|
|
354
|
+
`gpgv` to resolve through the pinned keyring and fingerprint allowlist. Treat
|
|
355
|
+
archive key transitions as explicit configuration updates rather than weakening
|
|
356
|
+
verification. Signing-key compromise, key lifecycle, and the trusted clock
|
|
357
|
+
remain caller-owned boundaries.
|
|
358
|
+
|
|
359
|
+
Every fully installed package becomes an external-custody component and the
|
|
360
|
+
system root depends on all of them, including packages no longer reachable from
|
|
361
|
+
another package. The importer implements Debian's version ordering, including
|
|
362
|
+
epochs and the special tilde order. It fails closed on an index-digest mismatch,
|
|
363
|
+
missing package artifact, unsatisfied or ambiguous dependency, path traversal,
|
|
364
|
+
incomplete maintainer-script evidence, untrusted signature, stale Release,
|
|
365
|
+
distribution identity change, or signed index mismatch.
|
|
366
|
+
|
|
367
|
+
A source without authentication still uses its caller digest only; it does not
|
|
368
|
+
claim that a Debian `Release` or `InRelease` signature was verified and retains
|
|
369
|
+
the blocking `repository-authentication` coverage gap. When every source passes,
|
|
370
|
+
the receipt includes that layer and records the signed Release-to-Packages hash
|
|
371
|
+
chain, pinned verifier and keyring digests, exact signer fingerprints, and replay
|
|
372
|
+
window. Likewise, Debian
|
|
373
|
+
[maintainer scripts execute during package transitions](https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html),
|
|
374
|
+
so any present script still blocks substrate readiness. The generated input
|
|
375
|
+
always marks operating-system coverage as included. Signed rebuild attestations,
|
|
376
|
+
native-build provenance, and the existing `substrate` gate remain required.
|
|
377
|
+
|
|
378
|
+
```js
|
|
379
|
+
import { importDebianPackageSnapshot } from '@holoscript/holosystem';
|
|
380
|
+
|
|
381
|
+
const imported = importDebianPackageSnapshot({
|
|
382
|
+
status,
|
|
383
|
+
sources: [{ packagesIndex, repository, custody }],
|
|
384
|
+
maintainerScripts,
|
|
385
|
+
root,
|
|
386
|
+
verificationPolicy,
|
|
387
|
+
});
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
For a verification-only workflow, call `verifyDebianRepositoryRelease` with the
|
|
391
|
+
same authentication fields plus `packagesIndex` and `packagesIndexDigest`. It
|
|
392
|
+
returns `holoscript.holosystem.debian-release-auth.v1`; `verified` is true only
|
|
393
|
+
when OpenPGP verification, signer pinning, release identity and freshness, and
|
|
394
|
+
the signed Packages checksum and size all pass.
|
|
395
|
+
|
|
396
|
+
### Build a native artifact in a closed executor
|
|
397
|
+
|
|
398
|
+
`native-build` is the first executable bridge from the substrate graph to a
|
|
399
|
+
reproducible native artifact. The initial vocabulary deliberately supports one
|
|
400
|
+
small target: a C11 translation unit compiled by GCC into a Linux AMD64 GNU ELF
|
|
401
|
+
executable. It does not accept a shell command, arbitrary compiler executable,
|
|
402
|
+
free-form flags, lifecycle script, network mode, user id, or weaker isolation
|
|
403
|
+
policy from the plan.
|
|
404
|
+
|
|
405
|
+
Hash the caller-owned source tree first:
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
npx holosystem native-build-source --source ./native-source --json
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Then create a plan using that source digest and a digest-pinned local Docker
|
|
412
|
+
binary plus an immutable OCI manifest reference:
|
|
413
|
+
|
|
414
|
+
```json
|
|
415
|
+
{
|
|
416
|
+
"schema": "holoscript.holosystem.native-build-plan.v1",
|
|
417
|
+
"id": "demo-native-build",
|
|
418
|
+
"source": {
|
|
419
|
+
"digest": "sha256:<source-manifest-digest>",
|
|
420
|
+
"sourceDateEpoch": 1700000000
|
|
421
|
+
},
|
|
422
|
+
"target": {
|
|
423
|
+
"os": "linux",
|
|
424
|
+
"architecture": "amd64",
|
|
425
|
+
"abi": "gnu"
|
|
426
|
+
},
|
|
427
|
+
"executor": {
|
|
428
|
+
"kind": "docker",
|
|
429
|
+
"digest": "sha256:<docker-binary-digest>",
|
|
430
|
+
"image": "docker.io/library/gcc@sha256:<amd64-manifest-digest>"
|
|
431
|
+
},
|
|
432
|
+
"compiler": {
|
|
433
|
+
"family": "gcc",
|
|
434
|
+
"language": "c11",
|
|
435
|
+
"source": "main.c",
|
|
436
|
+
"optimization": "speed"
|
|
437
|
+
},
|
|
438
|
+
"output": {
|
|
439
|
+
"path": "demo",
|
|
440
|
+
"format": "elf-executable"
|
|
441
|
+
},
|
|
442
|
+
"limits": {
|
|
443
|
+
"timeoutSeconds": 60,
|
|
444
|
+
"memoryMiB": 512,
|
|
445
|
+
"cpus": 1,
|
|
446
|
+
"pids": 64,
|
|
447
|
+
"tmpfsMiB": 64
|
|
448
|
+
},
|
|
449
|
+
"rebuilds": 2
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
npx holosystem native-build \
|
|
455
|
+
--plan native-build.json \
|
|
456
|
+
--source ./native-source \
|
|
457
|
+
--executor /absolute/operational/path/to/docker \
|
|
458
|
+
--artifact-dir ./native-output \
|
|
459
|
+
--output native-build-receipt.json \
|
|
460
|
+
--json
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
The first successful run is intentionally `artifact-pin-required` and exits
|
|
464
|
+
with code `2`. It performs two clean builds and reports their common digest but
|
|
465
|
+
does not claim `native-build` coverage. Copy that digest into
|
|
466
|
+
`expectedArtifactDigest`, use a new empty artifact directory, and rerun. Only a
|
|
467
|
+
matching reproducible result returns `verified`, exits `0`, and includes the
|
|
468
|
+
`native-build` layer.
|
|
469
|
+
|
|
470
|
+
The runner verifies an immutable source snapshot before mounting it, checks the
|
|
471
|
+
Docker executable digest, prohibits image pulls and networking, mounts both the
|
|
472
|
+
root filesystem and source read-only, drops all Linux capabilities, enables
|
|
473
|
+
`no-new-privileges`, runs as uid/gid 65534, bounds time, memory, CPU, PIDs and
|
|
474
|
+
temporary storage, and invokes `/usr/local/bin/gcc` directly with generated
|
|
475
|
+
deterministic arguments. It rejects undeclared files, symlink outputs,
|
|
476
|
+
non-AMD64 ELF results, mismatched artifact pins, and differing rebuild digests.
|
|
477
|
+
Compiler logs are represented only by byte counts and hashes; operational host
|
|
478
|
+
paths are not copied into the receipt.
|
|
479
|
+
|
|
480
|
+
```js
|
|
481
|
+
import {
|
|
482
|
+
createNativeRebuildAttestationPayload,
|
|
483
|
+
inspectNativeBuildPlan,
|
|
484
|
+
inspectNativeBuildSource,
|
|
485
|
+
runNativeBuild,
|
|
486
|
+
} from '@holoscript/holosystem';
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
`createNativeRebuildAttestationPayload` joins a verified native receipt to the
|
|
490
|
+
existing `holoscript.holosystem.rebuild-attestation.v1` payload. The independent
|
|
491
|
+
builder still signs that payload outside this package, and the substrate gate
|
|
492
|
+
still verifies the signature against caller-owned trust roots. The receipt
|
|
493
|
+
does not prove the Docker host, compiler-image supply chain, host kernel, CPU,
|
|
494
|
+
or organizational independence; those remain named boundaries.
|
|
495
|
+
|
|
496
|
+
This tracer commences the build/execution layer. It is not a claim that
|
|
497
|
+
HoloSystem already replaces measured boot, UEFI/device firmware, an ISA backend,
|
|
498
|
+
or physical hardware verification. Those require separate target contracts and
|
|
499
|
+
receipts rather than broader process execution in this API.
|
|
500
|
+
The adversary classes, attack specifications, test mapping, and residual trust
|
|
501
|
+
boundary are recorded in the [native-build threat model](./docs/native-build-threat-model.md).
|
|
502
|
+
|
|
503
|
+
### Launch a measured machine VM
|
|
504
|
+
|
|
505
|
+
`vm-launch` extends the executable tracer below the container boundary. It boots
|
|
506
|
+
an AMD64 Linux kernel and initramfs as a full-system q35 guest with QEMU TCG.
|
|
507
|
+
`vm-launch-whpx` is a separately named adapter for QEMU's Windows Hypervisor
|
|
508
|
+
Platform backend. The adapters do not share an accelerator switch and never
|
|
509
|
+
fall back to one another. Both vocabularies are deliberately closed: Windows
|
|
510
|
+
AMD64 host, `qemu-system-x86_64.exe`, q35, 128 MiB, one CPU, and exactly two
|
|
511
|
+
launches. Plans cannot provide a command, argument, environment variable,
|
|
512
|
+
device, network, firmware, monitor, display, or fallback setting.
|
|
513
|
+
|
|
514
|
+
First measure the complete caller-owned QEMU runtime closure and both guest
|
|
515
|
+
artifacts:
|
|
516
|
+
|
|
517
|
+
```bash
|
|
518
|
+
npx holosystem vm-executor --runtime C:/absolute/path/to/qemu-runtime --json
|
|
519
|
+
npx holosystem vm-asset --kind kernel --file C:/absolute/path/to/vmlinuz --json
|
|
520
|
+
npx holosystem vm-asset --kind initrd --file C:/absolute/path/to/initramfs --json
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
Create a plan with those digests and the SHA-256 digest of the exact expected
|
|
524
|
+
serial success signal:
|
|
525
|
+
|
|
526
|
+
```json
|
|
527
|
+
{
|
|
528
|
+
"schema": "holoscript.holosystem.vm-launch-plan.v1",
|
|
529
|
+
"id": "demo-vm-proof",
|
|
530
|
+
"host": { "os": "windows", "architecture": "amd64" },
|
|
531
|
+
"executor": {
|
|
532
|
+
"kind": "qemu-system",
|
|
533
|
+
"binary": "qemu-system-x86_64.exe",
|
|
534
|
+
"binaryDigest": "sha256:<qemu-executable-digest>",
|
|
535
|
+
"runtimeDigest": "sha256:<complete-runtime-manifest-digest>"
|
|
536
|
+
},
|
|
537
|
+
"target": { "architecture": "amd64", "machine": "q35", "accelerator": "tcg" },
|
|
538
|
+
"guest": {
|
|
539
|
+
"kernelDigest": "sha256:<kernel-digest>",
|
|
540
|
+
"initrdDigest": "sha256:<initrd-digest>",
|
|
541
|
+
"expectedConsoleDigest": "sha256:<exact-serial-output-digest>"
|
|
542
|
+
},
|
|
543
|
+
"resources": { "memoryMiB": 128, "cpus": 1, "timeoutSeconds": 30 },
|
|
544
|
+
"launches": 2
|
|
545
|
+
}
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
For WHPX, use schema `holoscript.holosystem.whpx-vm-launch-plan.v1`, set the
|
|
549
|
+
target accelerator to `whpx`, and add `guest.expectedDiagnosticsDigest`. That
|
|
550
|
+
digest pins the exact WHPX diagnostic bytes after removal of only the generated
|
|
551
|
+
private executor-path prefix. The WHPX adapter requires successful explicit
|
|
552
|
+
WHPX execution; feature labels or QEMU's compiled backend list are not treated
|
|
553
|
+
as proof that acceleration is usable.
|
|
554
|
+
|
|
555
|
+
```bash
|
|
556
|
+
npx holosystem vm-launch \
|
|
557
|
+
--plan vm-launch.json \
|
|
558
|
+
--runtime C:/absolute/path/to/qemu-runtime \
|
|
559
|
+
--kernel C:/absolute/path/to/vmlinuz \
|
|
560
|
+
--initrd C:/absolute/path/to/initramfs \
|
|
561
|
+
--output vm-launch-receipt.json \
|
|
562
|
+
--json
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
Use `npx holosystem vm-launch-whpx` with the same four caller-owned artifact
|
|
566
|
+
arguments for the WHPX schema. WHPX requires the Windows Hypervisor Platform
|
|
567
|
+
API exposed to QEMU; see the [QEMU WHPX documentation](https://www.qemu.org/docs/master/system/whpx.html)
|
|
568
|
+
and [Microsoft's Windows Hypervisor Platform API overview](https://learn.microsoft.com/en-us/virtualization/api/).
|
|
569
|
+
|
|
570
|
+
The runner creates a private snapshot and remeasures the complete QEMU closure,
|
|
571
|
+
kernel, and initramfs before and after each launch. It then generates QEMU
|
|
572
|
+
arguments that disable user configuration, default devices, networking, USB,
|
|
573
|
+
display, monitor, and reboot;
|
|
574
|
+
passes only a minimal environment; bounds output and time; and requires both
|
|
575
|
+
launches to produce the pinned serial digest and fixed debug-exit code. TCG
|
|
576
|
+
requires no emulator diagnostics. WHPX requires the normalized diagnostic
|
|
577
|
+
digest pinned by its plan; unexpected bytes block the receipt. Receipts contain
|
|
578
|
+
only digests and byte counts, never raw console output or operational host
|
|
579
|
+
paths.
|
|
580
|
+
|
|
581
|
+
A verified TCG receipt includes `machine-vm-launch`,
|
|
582
|
+
`guest-artifact-measurement`, and `virtual-device-minimization`, reports
|
|
583
|
+
`hardwareBacked: false`, and leaves hardware acceleration missing. A verified
|
|
584
|
+
WHPX receipt additionally includes `hardware-hypervisor-acceleration` and sets
|
|
585
|
+
`hardwareBacked: true` only after two explicit WHPX launches succeed. Both
|
|
586
|
+
adapters leave `host-process-isolation` missing: QEMU still has the ambient
|
|
587
|
+
rights of the Windows process. Receipts therefore expose
|
|
588
|
+
`isolation.hostProcess: "ambient-windows-process"` and
|
|
589
|
+
`isolation.verified: false` even when WHPX acceleration is verified. Neither
|
|
590
|
+
receipt proves an IOMMU, measured boot, firmware authenticity, confidential
|
|
591
|
+
memory, or side-channel resistance. The
|
|
592
|
+
exact claims are in the [VM launch threat model](./docs/vm-launch-threat-model.md).
|
|
593
|
+
|
|
594
|
+
```js
|
|
595
|
+
import {
|
|
596
|
+
inspectVmExecutor,
|
|
597
|
+
inspectVmLaunchAsset,
|
|
598
|
+
inspectVmLaunchPlan,
|
|
599
|
+
inspectWhpxVmLaunchPlan,
|
|
600
|
+
runVmLaunch,
|
|
601
|
+
runWhpxVmLaunch,
|
|
602
|
+
} from '@holoscript/holosystem';
|
|
603
|
+
```
|
|
604
|
+
|
|
58
605
|
## What It Creates
|
|
59
606
|
|
|
60
607
|
The default configuration pins public contracts for:
|