@xemahq/xema 0.25.1 → 0.27.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/README.md +70 -37
- package/dist/assets/appliance-base/images.lock.json +126 -126
- package/dist/assets/appliance-lean/images.lock.json +123 -123
- package/dist/assets/cloud/images.lock.json +126 -126
- package/dist/assets/core/images.lock.json +120 -120
- package/dist/assets/distribution-assets.manifest.json +15 -16
- package/dist/assets/minimal/images.lock.json +108 -108
- package/dist/assets/oss/images.lock.json +126 -126
- package/dist/assets/starter/images.lock.json +123 -123
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/dev.js +1 -0
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/down.d.ts.map +1 -1
- package/dist/commands/down.js +7 -1
- package/dist/commands/down.js.map +1 -1
- package/dist/commands/edition.d.ts +3 -0
- package/dist/commands/edition.d.ts.map +1 -0
- package/dist/commands/edition.js +46 -0
- package/dist/commands/edition.js.map +1 -0
- package/dist/commands/init.js +2 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/serve.js +1 -0
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/up.js +1 -0
- package/dist/commands/up.js.map +1 -1
- package/dist/dev/env-defaults.d.ts.map +1 -1
- package/dist/dev/env-defaults.js +6 -1
- package/dist/dev/env-defaults.js.map +1 -1
- package/dist/dev/infra-preflight.d.ts +3 -2
- package/dist/dev/infra-preflight.d.ts.map +1 -1
- package/dist/dev/infra-preflight.js +18 -8
- package/dist/dev/infra-preflight.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +54179 -0
- package/dist/edition/verify-image-evidence.bundle.provenance.json +2871 -0
- package/dist/runtime/backing-registration.d.ts.map +1 -1
- package/dist/runtime/backing-registration.js +7 -2
- package/dist/runtime/backing-registration.js.map +1 -1
- package/dist/runtime/config/schema.d.ts +1 -0
- package/dist/runtime/config/schema.d.ts.map +1 -1
- package/dist/runtime/custody/local-custody.d.ts +12 -0
- package/dist/runtime/custody/local-custody.d.ts.map +1 -0
- package/dist/runtime/custody/local-custody.js +22 -0
- package/dist/runtime/custody/local-custody.js.map +1 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.d.ts +18 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.js +50 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.js.map +1 -0
- package/dist/runtime/custody/openbao-custody-store.d.ts +18 -0
- package/dist/runtime/custody/openbao-custody-store.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-custody-store.js +55 -0
- package/dist/runtime/custody/openbao-custody-store.js.map +1 -0
- package/dist/runtime/custody/openbao-http-client.d.ts +34 -0
- package/dist/runtime/custody/openbao-http-client.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-http-client.js +154 -0
- package/dist/runtime/custody/openbao-http-client.js.map +1 -0
- package/dist/runtime/custody/openbao-local-constants.d.ts +22 -0
- package/dist/runtime/custody/openbao-local-constants.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-local-constants.js +48 -0
- package/dist/runtime/custody/openbao-local-constants.js.map +1 -0
- package/dist/runtime/custody/openbao-local-runner.d.ts +16 -0
- package/dist/runtime/custody/openbao-local-runner.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-local-runner.js +86 -0
- package/dist/runtime/custody/openbao-local-runner.js.map +1 -0
- package/dist/runtime/edition/run-entry.d.ts +9 -0
- package/dist/runtime/edition/run-entry.d.ts.map +1 -0
- package/dist/runtime/edition/run-entry.js +36 -0
- package/dist/runtime/edition/run-entry.js.map +1 -0
- package/dist/runtime/edition/verb-map.d.ts +60 -0
- package/dist/runtime/edition/verb-map.d.ts.map +1 -0
- package/dist/runtime/edition/verb-map.js +146 -0
- package/dist/runtime/edition/verb-map.js.map +1 -0
- package/dist/runtime/env-seeder.d.ts +3 -0
- package/dist/runtime/env-seeder.d.ts.map +1 -1
- package/dist/runtime/env-seeder.js +20 -3
- package/dist/runtime/env-seeder.js.map +1 -1
- package/dist/runtime/launch.d.ts +3 -1
- package/dist/runtime/launch.d.ts.map +1 -1
- package/dist/runtime/launch.js +37 -8
- package/dist/runtime/launch.js.map +1 -1
- package/dist/runtime/paths.d.ts +1 -2
- package/dist/runtime/paths.d.ts.map +1 -1
- package/dist/runtime/paths.js +0 -10
- package/dist/runtime/paths.js.map +1 -1
- package/dist/runtime/runner/chart-local-provenance.d.ts +20 -0
- package/dist/runtime/runner/chart-local-provenance.d.ts.map +1 -0
- package/dist/runtime/runner/chart-local-provenance.js +114 -0
- package/dist/runtime/runner/chart-local-provenance.js.map +1 -0
- package/dist/runtime/runner/chart-preflight.d.ts +8 -8
- package/dist/runtime/runner/chart-preflight.d.ts.map +1 -1
- package/dist/runtime/runner/chart-preflight.js +30 -69
- package/dist/runtime/runner/chart-preflight.js.map +1 -1
- package/dist/runtime/runner/chart-pull.d.ts +14 -0
- package/dist/runtime/runner/chart-pull.d.ts.map +1 -0
- package/dist/runtime/runner/chart-pull.js +96 -0
- package/dist/runtime/runner/chart-pull.js.map +1 -0
- package/dist/runtime/runner/chart-resolver.d.ts +4 -12
- package/dist/runtime/runner/chart-resolver.d.ts.map +1 -1
- package/dist/runtime/runner/chart-resolver.js +16 -62
- package/dist/runtime/runner/chart-resolver.js.map +1 -1
- package/dist/runtime/runner/chart-set-lock.d.ts +6 -20
- package/dist/runtime/runner/chart-set-lock.d.ts.map +1 -1
- package/dist/runtime/runner/chart-set-lock.js +55 -99
- package/dist/runtime/runner/chart-set-lock.js.map +1 -1
- package/dist/runtime/runner/helm-cli.d.ts +3 -1
- package/dist/runtime/runner/helm-cli.d.ts.map +1 -1
- package/dist/runtime/runner/helm-cli.js +9 -7
- package/dist/runtime/runner/helm-cli.js.map +1 -1
- package/dist/runtime/runner/helm-runner.d.ts +4 -4
- package/dist/runtime/runner/helm-runner.d.ts.map +1 -1
- package/dist/runtime/runner/helm-runner.js +18 -16
- package/dist/runtime/runner/helm-runner.js.map +1 -1
- package/dist/runtime/sources/kinds/git.d.ts +1 -2
- package/dist/runtime/sources/kinds/git.d.ts.map +1 -1
- package/dist/runtime/sources/kinds/git.js +8 -5
- package/dist/runtime/sources/kinds/git.js.map +1 -1
- package/dist/runtime/sources/manifest-walk.d.ts +3 -1
- package/dist/runtime/sources/manifest-walk.d.ts.map +1 -1
- package/dist/runtime/sources/manifest-walk.js +8 -3
- package/dist/runtime/sources/manifest-walk.js.map +1 -1
- package/dist/runtime/sources/schema.d.ts +3 -0
- package/dist/runtime/sources/schema.d.ts.map +1 -1
- package/dist/runtime/sources/schema.js +6 -1
- package/dist/runtime/sources/schema.js.map +1 -1
- package/dist/runtime/supervisor.d.ts +3 -0
- package/dist/runtime/supervisor.d.ts.map +1 -1
- package/dist/runtime/supervisor.js +2 -0
- package/dist/runtime/supervisor.js.map +1 -1
- package/edition/README.md +315 -0
- package/edition/assemble-producer-input.mjs +1251 -0
- package/edition/authorization-base.mjs +347 -0
- package/edition/biome-content.mjs +268 -0
- package/edition/build-biome-index.mjs +175 -0
- package/edition/build-platform-service-index.mjs +288 -0
- package/edition/build-runtime-image-source-index.mjs +821 -0
- package/edition/built-verifier.mjs +55 -0
- package/edition/catalogue-edition.mjs +49 -0
- package/edition/catalogue-fetch.mjs +64 -0
- package/edition/catalogue.mjs +135 -0
- package/edition/chart-set-source-commit.mjs +92 -0
- package/edition/check-committed-image-evidence.mjs +383 -0
- package/edition/checkout-indexed-source-repositories.mjs +105 -0
- package/edition/comment-blanker.mjs +75 -0
- package/edition/connector-source-authority.mjs +60 -0
- package/edition/customer-definition.mjs +183 -0
- package/edition/customer-flags.mjs +7 -0
- package/edition/develop-customer-edition.mjs +99 -0
- package/edition/develop-edition-resolve.mjs +65 -0
- package/edition/develop-edition.mjs +274 -0
- package/edition/develop-freeze.mjs +173 -0
- package/edition/develop-image-coordinate.mjs +54 -0
- package/edition/develop-seal.mjs +77 -0
- package/edition/develop-snapshot-inputs.mjs +104 -0
- package/edition/dist-check.mjs +986 -0
- package/edition/distribution-evidence-set.mjs +987 -0
- package/edition/distribution-paths.mjs +191 -0
- package/edition/edition-artifact.mjs +270 -0
- package/edition/edition-biome-manifests.mjs +85 -0
- package/edition/edition-channel.mjs +6 -0
- package/edition/edition-chart-set.mjs +93 -0
- package/edition/edition-config.mjs +207 -0
- package/edition/edition-constants.mjs +57 -0
- package/edition/edition-contract.json +23 -0
- package/edition/edition-list.mjs +35 -0
- package/edition/edition-locks.mjs +69 -0
- package/edition/edition-protocol.mjs +228 -0
- package/edition/edition-workspace.mjs +176 -0
- package/edition/evidence-checkout.mjs +418 -0
- package/edition/evidence-grammars.mjs +16 -0
- package/edition/evidence-trust-pin.mjs +304 -0
- package/edition/evidence-trust-policy.mjs +256 -0
- package/edition/external-source-walk.mjs +463 -0
- package/edition/first-party-source-ids.mjs +59 -0
- package/edition/fleet-coordinate-finality.mjs +350 -0
- package/edition/git-source-auth.mjs +52 -0
- package/edition/image-evidence-replay-state.mjs +242 -0
- package/edition/image-evidence.mjs +1225 -0
- package/edition/kernel-contract-authority.mjs +443 -0
- package/edition/lib.mjs +3018 -0
- package/edition/no-follow-file.mjs +94 -0
- package/edition/oci-evidence-verbs.mjs +392 -0
- package/edition/oci-image-source-evidence.mjs +239 -0
- package/edition/oras.mjs +648 -0
- package/edition/pin-source.mjs +227 -0
- package/edition/plan-image-evidence-refresh.mjs +408 -0
- package/edition/platform-lock-readability.mjs +892 -0
- package/edition/platform-service-class.mjs +18 -0
- package/edition/platform-service-source-authority.mjs +105 -0
- package/edition/positive-closure.mjs +162 -0
- package/edition/produce-distribution-completeness-evidence.mjs +311 -0
- package/edition/produce-image-delivery-entitlement-evidence.mjs +294 -0
- package/edition/produce-image-source-evidence.mjs +765 -0
- package/edition/producer-evidence-bundle.mjs +157 -0
- package/edition/registry-credentials.mjs +193 -0
- package/edition/release-catalogue.mjs +39 -0
- package/edition/release-channel-guard.mjs +99 -0
- package/edition/release-edition-artifact.mjs +158 -0
- package/edition/remedy-command.mjs +89 -0
- package/edition/repin-evidence-trust.mjs +101 -0
- package/edition/required-resource-provider.mjs +58 -0
- package/edition/resolve-chart-set.mjs +415 -0
- package/edition/resolve-customer-edition.mjs +288 -0
- package/edition/resolve-distribution.mjs +2318 -0
- package/edition/resolve-edition.mjs +186 -0
- package/edition/resolve-images.mjs +1606 -0
- package/edition/seal-image-digests.mjs +855 -0
- package/edition/sealed-evidence-accept.mjs +82 -0
- package/edition/source-authority.mjs +725 -0
- package/edition/source-closure.mjs +116 -0
- package/edition/strict-json.mjs +121 -0
- package/edition/string-order.mjs +5 -0
- package/edition/trust-policy-arming.mjs +120 -0
- package/edition/trusted-executable.mjs +166 -0
- package/edition/verifier-binding.mjs +45 -0
- package/edition/verifier-files.mjs +8 -0
- package/edition/verifier-kernel-schemas.mjs +22 -0
- package/edition/verifier-provenance.mjs +64 -0
- package/edition/verify-image-evidence.mjs +430 -0
- package/edition/verify-image-platforms.mjs +495 -0
- package/edition/why.mjs +56 -0
- package/edition/workspace-sections.mjs +195 -0
- package/examples/06-biome-sources.yaml +1 -0
- package/package.json +18 -7
- package/dist/commands/dist.d.ts +0 -16
- package/dist/commands/dist.d.ts.map +0 -1
- package/dist/commands/dist.js +0 -182
- package/dist/commands/dist.js.map +0 -1
- package/dist/runtime/distribution/available-biome.d.ts +0 -10
- package/dist/runtime/distribution/available-biome.d.ts.map +0 -1
- package/dist/runtime/distribution/available-biome.js +0 -154
- package/dist/runtime/distribution/available-biome.js.map +0 -1
- package/dist/runtime/distribution/compose.d.ts +0 -23
- package/dist/runtime/distribution/compose.d.ts.map +0 -1
- package/dist/runtime/distribution/compose.js +0 -48
- package/dist/runtime/distribution/compose.js.map +0 -1
- package/dist/runtime/runner/chart-trust.d.ts +0 -57
- package/dist/runtime/runner/chart-trust.d.ts.map +0 -1
- package/dist/runtime/runner/chart-trust.js +0 -270
- package/dist/runtime/runner/chart-trust.js.map +0 -1
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { lstatSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { kernelRepoDir } from './edition-workspace.mjs';
|
|
6
|
+
import { COMMIT_PATTERN } from './evidence-grammars.mjs';
|
|
7
|
+
|
|
8
|
+
const GIT_ENV = Object.freeze({
|
|
9
|
+
GIT_CONFIG_NOSYSTEM: '1',
|
|
10
|
+
GIT_CONFIG_GLOBAL: '/dev/null',
|
|
11
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
12
|
+
GIT_NO_REPLACE_OBJECTS: '1',
|
|
13
|
+
GIT_OPTIONAL_LOCKS: '0',
|
|
14
|
+
GIT_PAGER: 'cat',
|
|
15
|
+
LC_ALL: 'C',
|
|
16
|
+
PATH: '/usr/bin:/bin',
|
|
17
|
+
});
|
|
18
|
+
const GIT_SAFE_CONFIG = Object.freeze([
|
|
19
|
+
'-c',
|
|
20
|
+
'core.fsmonitor=false',
|
|
21
|
+
'-c',
|
|
22
|
+
'core.hooksPath=/dev/null',
|
|
23
|
+
'-c',
|
|
24
|
+
'core.attributesFile=/dev/null',
|
|
25
|
+
'--no-pager',
|
|
26
|
+
]);
|
|
27
|
+
const GIT_TREE_RECORD_PATTERN =
|
|
28
|
+
/^(100644|100755|120000|160000) (blob|commit) ([a-f0-9]{40,64})\t(.+)$/;
|
|
29
|
+
|
|
30
|
+
function git(repositoryDirectory, args, encoding = 'utf8') {
|
|
31
|
+
return execFileSync(
|
|
32
|
+
'/usr/bin/git',
|
|
33
|
+
[...GIT_SAFE_CONFIG, '-C', repositoryDirectory, ...args],
|
|
34
|
+
{
|
|
35
|
+
encoding,
|
|
36
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
37
|
+
env: GIT_ENV,
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function exactHeadCommit(repositoryDirectory) {
|
|
43
|
+
const commit = git(repositoryDirectory, [
|
|
44
|
+
'rev-parse',
|
|
45
|
+
'--verify',
|
|
46
|
+
'HEAD^{commit}',
|
|
47
|
+
]).trim();
|
|
48
|
+
if (!COMMIT_PATTERN.test(commit)) {
|
|
49
|
+
throw new Error('kernel contract repository HEAD is not an exact commit');
|
|
50
|
+
}
|
|
51
|
+
return commit;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** True when `ancestor` is reachable from `descendant`. Never throws. */
|
|
55
|
+
function isAncestor(repositoryDirectory, ancestor, descendant) {
|
|
56
|
+
try {
|
|
57
|
+
git(repositoryDirectory, ['merge-base', '--is-ancestor', ancestor, descendant]);
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function assertCommitExists(repositoryDirectory, sourceCommit) {
|
|
65
|
+
if (!COMMIT_PATTERN.test(sourceCommit)) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`kernel contract authority has invalid source commit "${String(sourceCommit)}"`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
git(repositoryDirectory, ['cat-file', '-e', `${sourceCommit}^{commit}`]);
|
|
72
|
+
} catch {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`kernel contract authority commit ${sourceCommit} is absent from the local Git object database`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function exactOrigin(repositoryDirectory, schemas) {
|
|
80
|
+
const origin = git(repositoryDirectory, [
|
|
81
|
+
'remote',
|
|
82
|
+
'get-url',
|
|
83
|
+
'origin',
|
|
84
|
+
]).trim();
|
|
85
|
+
if (origin !== schemas.XEMA_KERNEL_CONTRACT_REPOSITORY_URL) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`kernel contract repository origin must be exactly ${schemas.XEMA_KERNEL_CONTRACT_REPOSITORY_URL}`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return origin;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const GIT_MAX_BUFFER = 1024 * 1024 * 1024;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The blob record of every tree entry at `sourceCommit`, by path: ONE
|
|
97
|
+
* `ls-tree -r`, however many authority sources there are. A path absent from
|
|
98
|
+
* the map is absent from the commit or is not a blob.
|
|
99
|
+
*/
|
|
100
|
+
function gitTreeAtCommit(repositoryDirectory, sourceCommit) {
|
|
101
|
+
const records = execFileSync(
|
|
102
|
+
'/usr/bin/git',
|
|
103
|
+
[...GIT_SAFE_CONFIG, '-C', repositoryDirectory, 'ls-tree', '-r', '-z', sourceCommit],
|
|
104
|
+
{ encoding: 'buffer', stdio: ['ignore', 'pipe', 'pipe'], env: GIT_ENV, maxBuffer: GIT_MAX_BUFFER },
|
|
105
|
+
)
|
|
106
|
+
.toString('utf8')
|
|
107
|
+
.split('\0')
|
|
108
|
+
.filter(Boolean);
|
|
109
|
+
const tree = new Map();
|
|
110
|
+
for (const record of records) {
|
|
111
|
+
const match = GIT_TREE_RECORD_PATTERN.exec(record);
|
|
112
|
+
if (match) {tree.set(match[4], { mode: match[1], type: match[2], objectId: match[3] });}
|
|
113
|
+
}
|
|
114
|
+
return tree;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function missingSource(expectedPath) {
|
|
118
|
+
return new Error(
|
|
119
|
+
`kernel contract authority source "${expectedPath}" is missing or is not an exact Git blob`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* `{path, mode, objectId}` of each of `paths` at `sourceCommit`, in order.
|
|
125
|
+
* Refuses a path that is missing or is not a blob.
|
|
126
|
+
*/
|
|
127
|
+
function gitBlobRecordsAtCommit(repositoryDirectory, sourceCommit, paths) {
|
|
128
|
+
const tree = gitTreeAtCommit(repositoryDirectory, sourceCommit);
|
|
129
|
+
return paths.map((path) => {
|
|
130
|
+
const entry = tree.get(path);
|
|
131
|
+
if (entry === undefined || entry.type !== 'blob') {throw missingSource(path);}
|
|
132
|
+
return { path, mode: entry.mode, objectId: entry.objectId };
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* {@link gitBlobRecordsAtCommit} with each blob's exact bytes, read by ONE
|
|
138
|
+
* `cat-file --batch` and matched to the object id it was asked for.
|
|
139
|
+
*/
|
|
140
|
+
function gitBlobsAtCommit(repositoryDirectory, sourceCommit, paths) {
|
|
141
|
+
const records = gitBlobRecordsAtCommit(repositoryDirectory, sourceCommit, paths);
|
|
142
|
+
if (records.length === 0) {return [];}
|
|
143
|
+
const output = execFileSync(
|
|
144
|
+
'/usr/bin/git',
|
|
145
|
+
[...GIT_SAFE_CONFIG, '-C', repositoryDirectory, 'cat-file', '--batch'],
|
|
146
|
+
{
|
|
147
|
+
input: Buffer.from(records.map((record) => `${record.objectId}\n`).join(''), 'utf8'),
|
|
148
|
+
encoding: 'buffer',
|
|
149
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
150
|
+
env: GIT_ENV,
|
|
151
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
let offset = 0;
|
|
155
|
+
const sources = records.map((record) => {
|
|
156
|
+
const headerEnd = output.indexOf(0x0a, offset);
|
|
157
|
+
const header = headerEnd === -1 ? '' : output.subarray(offset, headerEnd).toString('utf8');
|
|
158
|
+
const match = /^([a-f0-9]{40,64}) blob (0|[1-9][0-9]*)$/.exec(header);
|
|
159
|
+
if (!match || match[1] !== record.objectId) {throw missingSource(record.path);}
|
|
160
|
+
const size = Number(match[2]);
|
|
161
|
+
const start = headerEnd + 1;
|
|
162
|
+
if (!Number.isSafeInteger(size) || start + size >= output.length || output[start + size] !== 0x0a) {
|
|
163
|
+
throw missingSource(record.path);
|
|
164
|
+
}
|
|
165
|
+
offset = start + size + 1;
|
|
166
|
+
return { ...record, blob: Buffer.from(output.subarray(start, start + size)) };
|
|
167
|
+
});
|
|
168
|
+
if (offset !== output.length) {
|
|
169
|
+
throw new Error('kernel contract authority sources: git returned more than was asked for');
|
|
170
|
+
}
|
|
171
|
+
return sources;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function exactAuthorityJson(value) {
|
|
175
|
+
return JSON.stringify(value);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function assertKernelContractAuthorityEqual(actual, expected, label) {
|
|
179
|
+
if (exactAuthorityJson(actual) !== exactAuthorityJson(expected)) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`${label} does not exactly match kernel contract authority`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return actual;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function readKernelContractAuthorityAtCommit({
|
|
188
|
+
repositoryDirectory = kernelRepoDir(),
|
|
189
|
+
sourceCommit,
|
|
190
|
+
schemas,
|
|
191
|
+
}) {
|
|
192
|
+
if (!schemas) {
|
|
193
|
+
throw new Error('trusted kernel distribution schemas are required');
|
|
194
|
+
}
|
|
195
|
+
exactOrigin(repositoryDirectory, schemas);
|
|
196
|
+
assertCommitExists(repositoryDirectory, sourceCommit);
|
|
197
|
+
const sources = gitBlobsAtCommit(
|
|
198
|
+
repositoryDirectory,
|
|
199
|
+
sourceCommit,
|
|
200
|
+
schemas.XEMA_KERNEL_CONTRACT_SOURCE_PATHS,
|
|
201
|
+
);
|
|
202
|
+
const contentDigest =
|
|
203
|
+
await schemas.computeKernelContractAuthorityContentDigest(sources);
|
|
204
|
+
return {
|
|
205
|
+
schemaVersion: 1,
|
|
206
|
+
generatorIdentity: schemas.XEMA_KERNEL_CONTRACT_AUTHORITY_GENERATOR,
|
|
207
|
+
repositoryUrl: schemas.XEMA_KERNEL_CONTRACT_REPOSITORY_URL,
|
|
208
|
+
sourceCommit,
|
|
209
|
+
sourcePaths: [...schemas.XEMA_KERNEL_CONTRACT_SOURCE_PATHS],
|
|
210
|
+
contentHashAlgorithm:
|
|
211
|
+
schemas.KernelContractContentHashAlgorithm.LengthFramedGitPathModeBlobV1,
|
|
212
|
+
contentDigest,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function assertExactCleanKernelContractCheckout({
|
|
217
|
+
repositoryDirectory = kernelRepoDir(),
|
|
218
|
+
sourceCommit,
|
|
219
|
+
schemas,
|
|
220
|
+
}) {
|
|
221
|
+
exactOrigin(repositoryDirectory, schemas);
|
|
222
|
+
const head = exactHeadCommit(repositoryDirectory);
|
|
223
|
+
if (head !== sourceCommit) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`kernel contract repository HEAD ${head} does not match authority commit ${sourceCommit}`,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
const status = git(
|
|
229
|
+
repositoryDirectory,
|
|
230
|
+
['status', '--porcelain=v1', '-z', '--untracked-files=all'],
|
|
231
|
+
'buffer',
|
|
232
|
+
);
|
|
233
|
+
if (status.byteLength !== 0) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
'kernel contract repository must be exactly clean for trusted generation',
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
const tracked = git(repositoryDirectory, ['ls-files', '-v', '-z'], 'buffer')
|
|
239
|
+
.toString('utf8')
|
|
240
|
+
.split('\0')
|
|
241
|
+
.filter(Boolean);
|
|
242
|
+
const hidden = tracked.find((record) => !record.startsWith('H '));
|
|
243
|
+
if (hidden) {
|
|
244
|
+
throw new Error(
|
|
245
|
+
`kernel contract repository has skip-worktree, assume-unchanged, or unexpected index state: ${hidden.slice(2)}`,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
// ONE index listing and ONE batched blob read for the whole source set.
|
|
249
|
+
const indexByPath = new Map();
|
|
250
|
+
for (const record of git(repositoryDirectory, ['ls-files', '--stage', '-v', '-z'], 'buffer')
|
|
251
|
+
.toString('utf8')
|
|
252
|
+
.split('\0')
|
|
253
|
+
.filter(Boolean)) {
|
|
254
|
+
const path = record.slice(record.indexOf('\t') + 1);
|
|
255
|
+
indexByPath.set(path, [...(indexByPath.get(path) ?? []), record]);
|
|
256
|
+
}
|
|
257
|
+
const committed = new Map(
|
|
258
|
+
gitBlobsAtCommit(
|
|
259
|
+
repositoryDirectory,
|
|
260
|
+
sourceCommit,
|
|
261
|
+
schemas.XEMA_KERNEL_CONTRACT_SOURCE_PATHS,
|
|
262
|
+
).map((source) => [source.path, source]),
|
|
263
|
+
);
|
|
264
|
+
for (const path of schemas.XEMA_KERNEL_CONTRACT_SOURCE_PATHS) {
|
|
265
|
+
const indexRecords = indexByPath.get(path) ?? [];
|
|
266
|
+
const indexMatch =
|
|
267
|
+
indexRecords.length === 1
|
|
268
|
+
? /^H (100644) ([a-f0-9]{40,64}) 0\t(.+)$/.exec(indexRecords[0])
|
|
269
|
+
: null;
|
|
270
|
+
if (!indexMatch || indexMatch[3] !== path) {
|
|
271
|
+
throw new Error(
|
|
272
|
+
`kernel contract source has hidden flags or unexpected index state: ${path}`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
const source = committed.get(path);
|
|
276
|
+
const worktreePath = join(repositoryDirectory, path);
|
|
277
|
+
const stats = lstatSync(worktreePath);
|
|
278
|
+
if (
|
|
279
|
+
source.mode !== schemas.XEMA_KERNEL_CONTRACT_SOURCE_MODE ||
|
|
280
|
+
source.objectId !== indexMatch[2] ||
|
|
281
|
+
!stats.isFile() ||
|
|
282
|
+
stats.isSymbolicLink() ||
|
|
283
|
+
(stats.mode & 0o777) !== 0o644 ||
|
|
284
|
+
!readFileSync(worktreePath).equals(source.blob)
|
|
285
|
+
) {
|
|
286
|
+
throw new Error(
|
|
287
|
+
`kernel contract source does not exactly match its committed Git mode/blob: ${path}`,
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return head;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export async function buildTrustedKernelContractAuthority({
|
|
295
|
+
repositoryDirectory = kernelRepoDir(),
|
|
296
|
+
schemas,
|
|
297
|
+
}) {
|
|
298
|
+
const sourceCommit = exactHeadCommit(repositoryDirectory);
|
|
299
|
+
assertExactCleanKernelContractCheckout({
|
|
300
|
+
repositoryDirectory,
|
|
301
|
+
sourceCommit,
|
|
302
|
+
schemas,
|
|
303
|
+
});
|
|
304
|
+
const authority = await readKernelContractAuthorityAtCommit({
|
|
305
|
+
repositoryDirectory,
|
|
306
|
+
sourceCommit,
|
|
307
|
+
schemas,
|
|
308
|
+
});
|
|
309
|
+
assertExactCleanKernelContractCheckout({
|
|
310
|
+
repositoryDirectory,
|
|
311
|
+
sourceCommit,
|
|
312
|
+
schemas,
|
|
313
|
+
});
|
|
314
|
+
return authority;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Is the pinned authority on the kernel history we ship — and has the kernel's
|
|
319
|
+
* contract surface moved since it?
|
|
320
|
+
*
|
|
321
|
+
* THIS USED TO REFUSE ON ANY CONTENT DRIFT, and that made the image-contract
|
|
322
|
+
* pin an input every kernel commit could invalidate. Measured 2026-10-01:
|
|
323
|
+
* `dist-check` was red with "the kernel contract surface CHANGED" because
|
|
324
|
+
* kernel `a348e71` added one member, `ComposedProcess`, to
|
|
325
|
+
* `RuntimeImageSubjectKind` — a kind no committed image lock carries and this
|
|
326
|
+
* repository never mints. The printed remedy, `xema edition index runtime-image-source
|
|
327
|
+
* --repin-contract-authority`, rewrites `contractAuthority` in EVERY `images.lock.json`,
|
|
328
|
+
* which changes each lock's `normalizedImageLockDigest` (bound by the signed
|
|
329
|
+
* completeness statement) and orphans every signed producer statement (whose
|
|
330
|
+
* `predicate.contractAuthority` must equal the pin). Nine sealed editions, plus
|
|
331
|
+
* every customer edition minted under the same pin, would have lost their
|
|
332
|
+
* evidence for a change that altered no image. The same would follow from a
|
|
333
|
+
* new export in `distribution/index.ts`, which is one of the authority's
|
|
334
|
+
* source paths and is touched by every distribution-contract addition.
|
|
335
|
+
*
|
|
336
|
+
* Owner ruling, 2026-10-01: an unrelated change must not force historical
|
|
337
|
+
* editions to re-resolve or re-seal their image lists unless IMAGE SEMANTICS
|
|
338
|
+
* changed. The pin is a DELIBERATE image-contract version, advanced by the
|
|
339
|
+
* repin transaction when an edition needs semantics the pinned contract cannot
|
|
340
|
+
* describe — never by kernel HEAD moving underneath it.
|
|
341
|
+
*
|
|
342
|
+
* So this now REFUSES only what is a defect:
|
|
343
|
+
* - a pin that is not an ancestor of kernel HEAD (a different history — see
|
|
344
|
+
* below), and
|
|
345
|
+
* - (in the caller) a pin whose recorded digest does not re-derive from its
|
|
346
|
+
* own commit's blobs.
|
|
347
|
+
* Drift is RETURNED as the list of moved source paths, and the caller reports
|
|
348
|
+
* it. It is not silent: it is printed on every check, naming each path.
|
|
349
|
+
*
|
|
350
|
+
* What keeps a stale pin from LYING about an image lock: the standalone
|
|
351
|
+
* verifier is built from kernel source that descends from the pin and strict-
|
|
352
|
+
* parses every image lock and producer statement, and producer evidence is
|
|
353
|
+
* refused unless its signed `contractAuthority` equals the pin. A lock that
|
|
354
|
+
* used semantics newer than the pin therefore fails closed at verification,
|
|
355
|
+
* loudly — and that is the moment a repin is genuinely owed.
|
|
356
|
+
*
|
|
357
|
+
* The pin must still be an ancestor of HEAD. A pin on a different history is
|
|
358
|
+
* not "behind", it is unrelated, and recomputing a digest across a fork would
|
|
359
|
+
* compare content from a branch nobody is shipping.
|
|
360
|
+
*/
|
|
361
|
+
async function assessAuthorityLineage({ authority, repositoryDirectory, schemas }) {
|
|
362
|
+
const head = exactHeadCommit(repositoryDirectory);
|
|
363
|
+
if (head === authority.sourceCommit) {return { head, changedPaths: [] };}
|
|
364
|
+
|
|
365
|
+
if (!isAncestor(repositoryDirectory, authority.sourceCommit, head)) {
|
|
366
|
+
throw new Error(
|
|
367
|
+
`kernel contract authority commit ${authority.sourceCommit} is not an ancestor of kernel HEAD ` +
|
|
368
|
+
`${head}. The pin is on a different history (a force push, or a checkout of another branch), ` +
|
|
369
|
+
'so it cannot be compared by content — repin deliberately against the history you intend to ship.',
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const atHead = await readKernelContractAuthorityAtCommit({
|
|
374
|
+
repositoryDirectory,
|
|
375
|
+
sourceCommit: head,
|
|
376
|
+
schemas,
|
|
377
|
+
});
|
|
378
|
+
if (atHead.contentDigest === authority.contentDigest) {
|
|
379
|
+
return { head, changedPaths: [] };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const changedPaths = [];
|
|
383
|
+
const pinned = gitBlobRecordsAtCommit(repositoryDirectory, authority.sourceCommit, authority.sourcePaths);
|
|
384
|
+
const current = gitBlobRecordsAtCommit(repositoryDirectory, head, authority.sourcePaths);
|
|
385
|
+
for (const [index, path] of authority.sourcePaths.entries()) {
|
|
386
|
+
if (pinned[index].objectId !== current[index].objectId) {changedPaths.push(path);}
|
|
387
|
+
}
|
|
388
|
+
return { head, changedPaths };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** One line naming the drift, for callers that report it. */
|
|
392
|
+
export function describeKernelContractAuthorityDrift({ authority, head, changedPaths }) {
|
|
393
|
+
return (
|
|
394
|
+
`kernel contract authority ${authority.sourceCommit} is behind kernel HEAD ${head}: ` +
|
|
395
|
+
`${changedPaths.length} contract source path(s) moved (${changedPaths.join(', ')}). ` +
|
|
396
|
+
'This is NOT a refusal — image locks stay pinned to the contract they were minted ' +
|
|
397
|
+
'under. Repin (`xema edition index runtime-image-source --repin-contract-authority`) only ' +
|
|
398
|
+
'when an edition needs image semantics the pinned contract cannot describe; that ' +
|
|
399
|
+
'repin re-binds every edition\'s image evidence.'
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export async function validateKernelContractAuthorityFromGit({
|
|
404
|
+
authority,
|
|
405
|
+
repositoryDirectory = kernelRepoDir(),
|
|
406
|
+
schemas,
|
|
407
|
+
requireHeadMatch = true,
|
|
408
|
+
requireCleanCheckout = false,
|
|
409
|
+
// Called with `{ authority, head, changedPaths }` when the pin is an ancestor
|
|
410
|
+
// of HEAD but the contract surface has moved since it. Reporting, never
|
|
411
|
+
// refusing — see `assessAuthorityLineage`.
|
|
412
|
+
onDrift = () => {},
|
|
413
|
+
}) {
|
|
414
|
+
if (!authority || typeof authority !== 'object') {
|
|
415
|
+
throw new Error('kernel contract authority is required');
|
|
416
|
+
}
|
|
417
|
+
if (requireHeadMatch) {
|
|
418
|
+
const { head, changedPaths } = await assessAuthorityLineage({
|
|
419
|
+
authority,
|
|
420
|
+
repositoryDirectory,
|
|
421
|
+
schemas,
|
|
422
|
+
});
|
|
423
|
+
if (changedPaths.length > 0) {onDrift({ authority, head, changedPaths });}
|
|
424
|
+
}
|
|
425
|
+
if (requireCleanCheckout) {
|
|
426
|
+
assertExactCleanKernelContractCheckout({
|
|
427
|
+
repositoryDirectory,
|
|
428
|
+
sourceCommit: authority.sourceCommit,
|
|
429
|
+
schemas,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
const expected = await readKernelContractAuthorityAtCommit({
|
|
433
|
+
repositoryDirectory,
|
|
434
|
+
sourceCommit: authority.sourceCommit,
|
|
435
|
+
schemas,
|
|
436
|
+
});
|
|
437
|
+
assertKernelContractAuthorityEqual(
|
|
438
|
+
authority,
|
|
439
|
+
expected,
|
|
440
|
+
'runtime image source index',
|
|
441
|
+
);
|
|
442
|
+
return authority;
|
|
443
|
+
}
|