enigma-memory 0.1.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/LICENSE +55 -0
- package/README.md +379 -0
- package/apps/cli/bin/enigma.mjs +1117 -0
- package/apps/desktop/src/app.js +1327 -0
- package/apps/desktop/src/index.html +1373 -0
- package/apps/desktop/src/styles.css +386 -0
- package/apps/gateway/bin/enigma-gateway.mjs +52 -0
- package/apps/gateway/src/server.mjs +1403 -0
- package/apps/native-host/README.md +265 -0
- package/apps/native-host/bin/enigma-native-host.mjs +499 -0
- package/apps/native-host/manifests/com.enigma.native_host.chrome.json +9 -0
- package/apps/native-host/manifests/com.enigma.native_host.edge.json +9 -0
- package/apps/native-host/manifests/com.enigma.native_host.firefox.json +9 -0
- package/apps/relay/bin/enigma-relay.mjs +52 -0
- package/apps/relay/src/server.mjs +1571 -0
- package/apps/verifier/bin/enigma-verify.mjs +240 -0
- package/deploy/docker-compose.production.example.yml +150 -0
- package/deploy/kubernetes/enigma-backend.example.yaml +580 -0
- package/package.json +196 -0
- package/packages/adapters/PACKAGE_CONTRACT.md +47 -0
- package/packages/adapters/src/index.js +183 -0
- package/packages/boundary/src/index.js +468 -0
- package/packages/connectors/src/index.js +533 -0
- package/packages/core/src/index.js +766 -0
- package/packages/enterprise/src/index.js +505 -0
- package/packages/importers/src/index.js +1083 -0
- package/packages/mcp-server/bin/enigma-mcp.mjs +19 -0
- package/packages/mcp-server/src/index.js +1185 -0
- package/packages/mesh/src/index.js +531 -0
- package/packages/metering/PACKAGE_CONTRACT.md +20 -0
- package/packages/metering/src/index.js +246 -0
- package/packages/optimizer/src/index.js +707 -0
- package/packages/passport/src/index.js +651 -0
- package/packages/settlement/PACKAGE_CONTRACT.md +28 -0
- package/packages/settlement/src/index.js +369 -0
- package/packages/storage/PACKAGE_CONTRACT.md +23 -0
- package/packages/storage/src/index.js +392 -0
- package/packages/vault/src/index.js +904 -0
- package/scripts/build-ai-orchestration-plan.mjs +248 -0
- package/scripts/build-cloudflare-pages-release-packet.mjs +287 -0
- package/scripts/build-cloudflare-token-policy.mjs +239 -0
- package/scripts/build-cloudflare-token-request.mjs +314 -0
- package/scripts/build-demo-assets.mjs +263 -0
- package/scripts/build-edge-backend-workers.mjs +353 -0
- package/scripts/build-goal-completion-audit.mjs +398 -0
- package/scripts/build-hosted-probe-worker.mjs +191 -0
- package/scripts/build-operator-acceptance-packet.mjs +254 -0
- package/scripts/build-operator-evidence-starter.mjs +525 -0
- package/scripts/build-production-backend-env-kit.mjs +384 -0
- package/scripts/build-production-dependency-report.mjs +369 -0
- package/scripts/build-production-handoff-packet.mjs +475 -0
- package/scripts/build-production-readiness-manifest.mjs +209 -0
- package/scripts/build-production-status-board.mjs +297 -0
- package/scripts/build-production-storage-migration.mjs +86 -0
- package/scripts/build-production-workplan.mjs +371 -0
- package/scripts/build-review-packet.mjs +594 -0
- package/scripts/check.mjs +419 -0
- package/scripts/cloudflare-ops.mjs +1672 -0
- package/scripts/cloudflare-secret-env.mjs +128 -0
- package/scripts/collect-hosted-backend-live-evidence.mjs +263 -0
- package/scripts/infrastructure-readiness.mjs +944 -0
- package/scripts/install-enigma-local.mjs +270 -0
- package/scripts/memory-optimization-benchmark.mjs +242 -0
- package/scripts/monitor-live-endpoints.mjs +358 -0
- package/scripts/release-audit.mjs +5588 -0
- package/scripts/release-provenance.mjs +250 -0
- package/scripts/run-backend-readiness-smoke.mjs +220 -0
- package/scripts/validate-backup-restore-drill.mjs +166 -0
- package/scripts/validate-cloudflare-credentials.mjs +112 -0
- package/scripts/validate-cloudflare-worker-inspect.mjs +179 -0
- package/scripts/validate-domain-tls.mjs +248 -0
- package/scripts/validate-hosted-backend-live.mjs +319 -0
- package/scripts/validate-incident-drill.mjs +221 -0
- package/scripts/validate-kms-custody.mjs +235 -0
- package/scripts/validate-legal-compliance-approval.mjs +274 -0
- package/scripts/validate-monitoring-alerting.mjs +263 -0
- package/scripts/validate-network-access-policy.mjs +285 -0
- package/scripts/validate-operator-acceptance.mjs +298 -0
- package/scripts/validate-production-manifests.mjs +325 -0
- package/scripts/validate-public-site-security.mjs +252 -0
- package/scripts/validate-security-threat-model.mjs +285 -0
- package/scripts/validate-service-settlement.mjs +256 -0
- package/scripts/validate-support-sla.mjs +190 -0
- package/scripts/validate-tenant-policy-approval.mjs +228 -0
- package/scripts/validate-usage-metering.mjs +224 -0
- package/scripts/validate-whitepaper-claims.mjs +166 -0
- package/specs/boundary-manifest-v1.schema.json +35 -0
- package/specs/capsule-v1.schema.json +55 -0
- package/specs/claim-boundary-manifest-v1.schema.json +22 -0
- package/specs/connector-profile-v1.schema.json +62 -0
- package/specs/deletion-tombstone-v1.schema.json +26 -0
- package/specs/enterprise-policy-v1.schema.json +59 -0
- package/specs/gateway-request-v1.schema.json +32 -0
- package/specs/goal-completion-audit-v1.schema.json +70 -0
- package/specs/hosted-backend-live-evidence-v1.schema.json +147 -0
- package/specs/import-report-v1.schema.json +214 -0
- package/specs/infrastructure-readiness-manifest-v1.schema.json +130 -0
- package/specs/memory-event-v1.schema.json +42 -0
- package/specs/mesh-node-v1.schema.json +126 -0
- package/specs/passport-v1.schema.json +50 -0
- package/specs/production-handoff-packet-v1.schema.json +149 -0
- package/specs/receipt-v1.schema.json +61 -0
- package/specs/relay-record-v1.schema.json +43 -0
- package/specs/state-checkpoint-v1.schema.json +37 -0
- package/specs/trust-bundle-v1.schema.json +56 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
6
|
+
const packageJsonPath = path.join(root, 'package.json');
|
|
7
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
8
|
+
const productionRoots = [
|
|
9
|
+
'packages/adapters/src/',
|
|
10
|
+
'packages/core/src/',
|
|
11
|
+
'packages/vault/src/',
|
|
12
|
+
'packages/passport/src/',
|
|
13
|
+
'packages/boundary/src/',
|
|
14
|
+
'packages/connectors/src/',
|
|
15
|
+
'packages/importers/src/',
|
|
16
|
+
'packages/optimizer/src/',
|
|
17
|
+
'packages/storage/src/',
|
|
18
|
+
'packages/mcp-server/src/',
|
|
19
|
+
'packages/mcp-server/bin/',
|
|
20
|
+
'packages/mesh/src/',
|
|
21
|
+
'packages/enterprise/src/',
|
|
22
|
+
'apps/relay/bin/',
|
|
23
|
+
'apps/relay/src/',
|
|
24
|
+
'apps/gateway/src/',
|
|
25
|
+
'apps/gateway/bin/',
|
|
26
|
+
'apps/desktop/src/',
|
|
27
|
+
'apps/cli/bin/',
|
|
28
|
+
'apps/verifier/bin/',
|
|
29
|
+
'apps/native-host/bin/'
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
function collectProductionModules(rel, out = []) {
|
|
33
|
+
const full = path.join(root, rel);
|
|
34
|
+
if (!fs.existsSync(full)) throw new Error(`Missing production path: ${rel}`);
|
|
35
|
+
const stat = fs.statSync(full);
|
|
36
|
+
if (stat.isDirectory()) {
|
|
37
|
+
for (const entry of fs.readdirSync(full).sort()) {
|
|
38
|
+
collectProductionModules(path.join(rel, entry), out);
|
|
39
|
+
}
|
|
40
|
+
} else if (rel.endsWith('.js') || rel.endsWith('.mjs')) {
|
|
41
|
+
out.push(rel);
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const productionModules = [...new Set(productionRoots.flatMap((rel) => collectProductionModules(rel)))];
|
|
47
|
+
|
|
48
|
+
for (const rel of productionModules) {
|
|
49
|
+
const full = path.join(root, rel);
|
|
50
|
+
if (!fs.existsSync(full)) throw new Error(`Missing production module: ${rel}`);
|
|
51
|
+
await import(pathToFileURL(full).href);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isPlainObject(value) {
|
|
55
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const NODE_SHEBANG = '#!/usr/bin/env node';
|
|
59
|
+
const RAW_MEMORY = 'private launch-code phrase must not leave local memory';
|
|
60
|
+
const SOURCE_ONLY_REVIEW_DOCS = Object.freeze([
|
|
61
|
+
'SECURITY.md',
|
|
62
|
+
'docs/security-threat-model.md',
|
|
63
|
+
'docs/public-api-reference.md',
|
|
64
|
+
'docs/operator-acceptance-packet.md'
|
|
65
|
+
]);
|
|
66
|
+
const REVIEW_DOC_BOUNDARY_PATTERNS = Object.freeze({
|
|
67
|
+
claimBoundary: /claim boundary|proof boundary|non-claims?|does not claim|does not prove/i,
|
|
68
|
+
enigmaScope: /Enigma-(?:controlled|mediated)|Enigma controlled|Enigma mediated|declared boundary operations|vault state|receipts?/i,
|
|
69
|
+
publicProofPrivacy: /raw memory plaintext|public proof artifacts?|public receipts?|proof examples/i,
|
|
70
|
+
hostedByocPosture: /hosted|BYOC|cloud|deployment/i,
|
|
71
|
+
hostedByocBlocked: /blocked|requires?|do not mark|do not market|before|only after|without/i
|
|
72
|
+
});
|
|
73
|
+
const HOSTED_BYOC_BLOCKERS = Object.freeze({
|
|
74
|
+
credentials: /\b(?:credentials?|cloud account|VPC|cluster|private network|admin access)\b/i,
|
|
75
|
+
domain: /\b(?:domain|DNS|ingress|private endpoints|approved customer networks)\b/i,
|
|
76
|
+
TLS: /\bTLS\b|HTTPS|certificates?/i,
|
|
77
|
+
KMS: /\bKMS\b|BYOK|secrets manager|KMS\/secrets/i,
|
|
78
|
+
storage: /durable storage|\bstorage\b|persistence/i,
|
|
79
|
+
monitoring: /\bmonitoring\b|observable services?|logging\/SIEM/i,
|
|
80
|
+
backup: /\bbackups?\b|backup\/restore|restore rehearsal/i
|
|
81
|
+
});
|
|
82
|
+
const DANGEROUS_REVIEW_CLAIM_PATTERNS = Object.freeze({
|
|
83
|
+
'provider deletion proof': /provider[- ](?:native|side)? deletion|provider deletion|provider[- ]native deletion proof|provider deletion proof|complete provider deletion|provider deletion certification|closed provider[^.\n]{0,80}deleted|external provider[^.\n]{0,80}deleted/i,
|
|
84
|
+
'model forgetting': /model[- ]forgetting|model forgetting|model-weight forgetting|model weights?[^.\n]{0,80}(?:forgot|forget|deleted|erased)/i,
|
|
85
|
+
'semantic forgetting': /semantic[- ]forgetting|semantic forgetting|semantic-paraphrase absence/i,
|
|
86
|
+
'compliance status': /\b(?:SOC 2|ISO 27001|HIPAA|GDPR|PCI DSS)\b|compliance[- ]status|(?:certified|approved|validated)\s+(?:compliant|compliance)|compliance[- ](?:certified|approved|validated|ready)/i,
|
|
87
|
+
'tamper-proof hardware': /tamper[- ]proof|tamperproof|hardware[^.\n]{0,80}(?:cannot be tampered|prevents tampering|prevents extraction)/i,
|
|
88
|
+
'raw memory public proof': /raw[- ]memory[- ]on[- ]chain|raw memory in (?:public proof|receipts|relay|witness|SIEM)/i
|
|
89
|
+
});
|
|
90
|
+
const EXPLICIT_NEGATIVE_CONTEXT = /\b(?:do not|does not|cannot|can not|must not|should not|not|no|never|without|unless|before|subject to|avoid|decline|prohibited|non-claims?|Not approved|requires?|only after|should remain available without|must stay|trigger incident response|leakage|mitigation)\b/i;
|
|
91
|
+
|
|
92
|
+
function normalizePackageRel(rel) {
|
|
93
|
+
return String(rel).replace(/\\/g, '/').replace(/^\.\//, '');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function packageFileExists(rel) {
|
|
97
|
+
return fs.existsSync(path.join(root, normalizePackageRel(rel)));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function packageFilesCover(files, rel) {
|
|
101
|
+
const normalizedRel = normalizePackageRel(rel);
|
|
102
|
+
return files.some((entry) => {
|
|
103
|
+
const normalizedEntry = normalizePackageRel(entry);
|
|
104
|
+
if (normalizedEntry.endsWith('/')) return normalizedRel.startsWith(normalizedEntry);
|
|
105
|
+
const fullEntry = path.join(root, normalizedEntry);
|
|
106
|
+
return normalizedRel === normalizedEntry || (fs.existsSync(fullEntry) && fs.statSync(fullEntry).isDirectory() && normalizedRel.startsWith(`${normalizedEntry}/`));
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function readRequiredSourceDoc(rel) {
|
|
111
|
+
const normalizedRel = normalizePackageRel(rel);
|
|
112
|
+
const full = path.join(root, normalizedRel);
|
|
113
|
+
if (!fs.existsSync(full)) throw new Error(`Missing production review doc: ${normalizedRel}`);
|
|
114
|
+
return fs.readFileSync(full, 'utf8');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function assertNoDangerousReviewClaims(rel, text) {
|
|
118
|
+
const lines = text.split(/\r?\n/);
|
|
119
|
+
for (const [claim, pattern] of Object.entries(DANGEROUS_REVIEW_CLAIM_PATTERNS)) {
|
|
120
|
+
lines.forEach((line, index) => {
|
|
121
|
+
if (!pattern.test(line)) return;
|
|
122
|
+
const context = lines.slice(Math.max(0, index - 12), Math.min(lines.length, index + 2)).join('\n');
|
|
123
|
+
if (!EXPLICIT_NEGATIVE_CONTEXT.test(context)) {
|
|
124
|
+
throw new Error(`${rel}:${index + 1} mentions ${claim} outside an explicit negative context`);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function validateProductionReviewDocs(pkg) {
|
|
131
|
+
const readme = readRequiredSourceDoc('README.md');
|
|
132
|
+
const reviewTexts = [];
|
|
133
|
+
for (const rel of SOURCE_ONLY_REVIEW_DOCS) {
|
|
134
|
+
const text = readRequiredSourceDoc(rel);
|
|
135
|
+
reviewTexts.push(text);
|
|
136
|
+
if (!readme.includes(rel)) throw new Error(`${rel} must remain an explicit README reference`);
|
|
137
|
+
if (packageFilesCover(pkg.files, rel)) throw new Error(`${rel} is a source-tree review doc, not packaged runtime code`);
|
|
138
|
+
if (text.includes(RAW_MEMORY)) throw new Error(`${rel} must not include the raw memory sentinel in public examples`);
|
|
139
|
+
for (const [requirement, pattern] of Object.entries(REVIEW_DOC_BOUNDARY_PATTERNS)) {
|
|
140
|
+
if (!pattern.test(text)) throw new Error(`${rel} production review doc is missing ${requirement} boundary language`);
|
|
141
|
+
}
|
|
142
|
+
assertNoDangerousReviewClaims(rel, text);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const reviewText = reviewTexts.join('\n');
|
|
146
|
+
for (const [blocker, pattern] of Object.entries(HOSTED_BYOC_BLOCKERS)) {
|
|
147
|
+
if (!pattern.test(reviewText)) throw new Error(`production review docs hosted/BYOC posture is missing ${blocker} blocker language`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function assertNodeShebang(name, rel) {
|
|
152
|
+
const firstLine = fs.readFileSync(path.join(root, normalizePackageRel(rel)), 'utf8').split(/\r?\n/, 1)[0];
|
|
153
|
+
if (firstLine !== NODE_SHEBANG) throw new Error(`package.json bin ${name} must start with ${NODE_SHEBANG}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function validateCloudflareOpsScript(pkg) {
|
|
157
|
+
if (!isPlainObject(pkg.scripts) || pkg.scripts['cloudflare:ops'] !== 'node scripts/cloudflare-ops.mjs') {
|
|
158
|
+
throw new Error('package.json must expose cloudflare:ops as node scripts/cloudflare-ops.mjs');
|
|
159
|
+
}
|
|
160
|
+
if (!packageFileExists('scripts/cloudflare-ops.mjs')) throw new Error('Missing scripts/cloudflare-ops.mjs');
|
|
161
|
+
if (!packageFilesCover(pkg.files, 'scripts/cloudflare-ops.mjs')) {
|
|
162
|
+
throw new Error('scripts/cloudflare-ops.mjs must be covered by files allowlist');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const ops = await import(pathToFileURL(path.join(root, 'scripts/cloudflare-ops.mjs')).href);
|
|
166
|
+
const requiredHelpers = [
|
|
167
|
+
'isValidDomainName',
|
|
168
|
+
'parseUsdAmount',
|
|
169
|
+
'assertRegistrationPriceGuard',
|
|
170
|
+
'buildCloudflareRequestPlan',
|
|
171
|
+
'buildWranglerPagesDeployPlan',
|
|
172
|
+
'parseCloudflareOpsCommand',
|
|
173
|
+
];
|
|
174
|
+
for (const name of requiredHelpers) {
|
|
175
|
+
if (typeof ops[name] !== 'function') throw new Error(`scripts/cloudflare-ops.mjs missing exported helper ${name}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const parsedRegister = ops.parseCloudflareOpsCommand([
|
|
179
|
+
'registrar',
|
|
180
|
+
'register',
|
|
181
|
+
'--domain',
|
|
182
|
+
'example.dev',
|
|
183
|
+
'--max-price-usd',
|
|
184
|
+
'10.11',
|
|
185
|
+
'--confirm-domain',
|
|
186
|
+
'example.dev',
|
|
187
|
+
'--confirm-registration-cost',
|
|
188
|
+
'10.11',
|
|
189
|
+
'--i-understand-this-charges-my-payment-method',
|
|
190
|
+
], { CLOUDFLARE_ACCOUNT_ID: 'acct-check' });
|
|
191
|
+
if (parsedRegister.kind !== 'registrar.register' || parsedRegister.execute !== false) {
|
|
192
|
+
throw new Error('cloudflare registrar register must default to a non-executing dry-run command');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const registerPlan = ops.buildCloudflareRequestPlan({
|
|
196
|
+
operation: 'registrar.register',
|
|
197
|
+
accountId: 'acct-check',
|
|
198
|
+
domain: 'example.dev',
|
|
199
|
+
});
|
|
200
|
+
if (registerPlan.billable !== true || registerPlan.method !== 'POST' || registerPlan.body?.domain_name !== 'example.dev') {
|
|
201
|
+
throw new Error('cloudflare registrar register plan must remain an explicit billable POST plan');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const deployPlan = ops.buildWranglerPagesDeployPlan({
|
|
205
|
+
site: '_public_site',
|
|
206
|
+
projectName: 'enigma-memory',
|
|
207
|
+
});
|
|
208
|
+
if (deployPlan.execute !== false || deployPlan.dryRun !== true || deployPlan.usesShell !== false) {
|
|
209
|
+
throw new Error('cloudflare pages deploy plan must default to dry-run without shell execution');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const guard = ops.assertRegistrationPriceGuard({
|
|
213
|
+
domain: 'example.dev',
|
|
214
|
+
maxPriceUsd: '10.11',
|
|
215
|
+
confirmationDomain: 'example.dev',
|
|
216
|
+
confirmationCostUsd: '10.11',
|
|
217
|
+
iUnderstandThisChargesMyPaymentMethod: true,
|
|
218
|
+
availability: {
|
|
219
|
+
name: 'example.dev',
|
|
220
|
+
registrable: true,
|
|
221
|
+
pricing: { currency: 'USD', registration_cost: '10.11' },
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
if (guard.registrationCostUsd !== '10.11') throw new Error('cloudflare registrar price guard must preserve exact USD confirmation');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function validatePackageMetadata(pkg) {
|
|
228
|
+
if (pkg.private === true) throw new Error('package.json must be publishable, not private');
|
|
229
|
+
if (pkg.name !== 'enigma-memory') {
|
|
230
|
+
throw new Error('package.json name must be enigma-memory for public npm publishing');
|
|
231
|
+
}
|
|
232
|
+
if (typeof pkg.license !== 'string' || pkg.license.length === 0) throw new Error('package.json missing license');
|
|
233
|
+
if (!isPlainObject(pkg.repository) || typeof pkg.repository.url !== 'string') throw new Error('package.json missing repository placeholder');
|
|
234
|
+
if (!Array.isArray(pkg.files) || pkg.files.length === 0) throw new Error('package.json missing files allowlist');
|
|
235
|
+
if (!Array.isArray(pkg.keywords) || pkg.keywords.length === 0) throw new Error('package.json missing keywords');
|
|
236
|
+
|
|
237
|
+
if (!isPlainObject(pkg.scripts) || pkg.scripts['provenance:local'] !== 'node scripts/release-provenance.mjs') {
|
|
238
|
+
throw new Error('package.json must expose provenance:local as node scripts/release-provenance.mjs');
|
|
239
|
+
}
|
|
240
|
+
if (pkg.scripts['review:packet'] !== 'node scripts/build-review-packet.mjs') {
|
|
241
|
+
throw new Error('package.json must expose review:packet as node scripts/build-review-packet.mjs');
|
|
242
|
+
}
|
|
243
|
+
if (!packageFileExists('scripts/release-provenance.mjs')) throw new Error('Missing scripts/release-provenance.mjs');
|
|
244
|
+
if (!packageFilesCover(pkg.files, 'scripts/release-provenance.mjs')) throw new Error('scripts/release-provenance.mjs must be covered by files allowlist');
|
|
245
|
+
if (!packageFileExists('scripts/build-review-packet.mjs')) throw new Error('Missing scripts/build-review-packet.mjs');
|
|
246
|
+
if (!packageFilesCover(pkg.files, 'scripts/build-review-packet.mjs')) throw new Error('scripts/build-review-packet.mjs must be covered by files allowlist');
|
|
247
|
+
|
|
248
|
+
const requiredExports = [
|
|
249
|
+
'.',
|
|
250
|
+
'./adapters',
|
|
251
|
+
'./core',
|
|
252
|
+
'./vault',
|
|
253
|
+
'./passport',
|
|
254
|
+
'./boundary',
|
|
255
|
+
'./mcp-server',
|
|
256
|
+
'./mesh',
|
|
257
|
+
'./enterprise',
|
|
258
|
+
'./connectors',
|
|
259
|
+
'./importers',
|
|
260
|
+
'./optimizer',
|
|
261
|
+
'./storage',
|
|
262
|
+
'./relay',
|
|
263
|
+
'./gateway',
|
|
264
|
+
'./desktop'
|
|
265
|
+
];
|
|
266
|
+
if (!isPlainObject(pkg.exports)) throw new Error('package.json missing exports');
|
|
267
|
+
for (const key of requiredExports) {
|
|
268
|
+
const target = pkg.exports[key];
|
|
269
|
+
if (typeof target !== 'string') throw new Error(`package.json missing export ${key}`);
|
|
270
|
+
if (!packageFileExists(target)) throw new Error(`package.json export ${key} points to a missing file`);
|
|
271
|
+
}
|
|
272
|
+
for (const [key, target] of Object.entries(pkg.exports)) {
|
|
273
|
+
if (typeof target === 'string' && !packageFileExists(target)) throw new Error(`package.json export ${key} points to a missing file`);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const requiredBins = ['enigma', 'enigma-verify', 'enigma-mcp', 'enigma-relay', 'enigma-gateway', 'enigma-native-host'];
|
|
277
|
+
if (!isPlainObject(pkg.bin)) throw new Error('package.json missing bin map');
|
|
278
|
+
for (const key of requiredBins) {
|
|
279
|
+
if (typeof pkg.bin[key] !== 'string') throw new Error(`package.json missing bin ${key}`);
|
|
280
|
+
}
|
|
281
|
+
for (const [name, target] of Object.entries(pkg.bin)) {
|
|
282
|
+
if (typeof target !== 'string' || !packageFileExists(target)) {
|
|
283
|
+
throw new Error(`package.json bin ${name} points to a missing file`);
|
|
284
|
+
}
|
|
285
|
+
if (!packageFilesCover(pkg.files, target)) throw new Error(`package.json bin ${name} target is not covered by files allowlist`);
|
|
286
|
+
assertNodeShebang(name, target);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
validatePackageMetadata(packageJson);
|
|
291
|
+
await validateCloudflareOpsScript(packageJson);
|
|
292
|
+
validateProductionReviewDocs(packageJson);
|
|
293
|
+
|
|
294
|
+
function requirePlainObject(name, value) {
|
|
295
|
+
if (!isPlainObject(value)) throw new Error(`${name} must be an object`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function requireFailClosedObjectSchema(name, schema) {
|
|
299
|
+
requirePlainObject(name, schema);
|
|
300
|
+
if (schema.type !== 'object') throw new Error(`${name} type must be object`);
|
|
301
|
+
if (schema.additionalProperties !== false) throw new Error(`${name} must fail closed on unknown properties`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function decodeJsonPointerToken(token) {
|
|
305
|
+
return token.replaceAll('~1', '/').replaceAll('~0', '~');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function localDefName(ref) {
|
|
309
|
+
if (typeof ref !== 'string' || !ref.startsWith('#/$defs/')) return undefined;
|
|
310
|
+
return decodeJsonPointerToken(ref.slice('#/$defs/'.length));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function resolveLocalRef(name, rootSchema, ref) {
|
|
314
|
+
const defName = localDefName(ref);
|
|
315
|
+
if (defName === undefined) throw new Error(`${name} uses non-local $ref ${String(ref)}`);
|
|
316
|
+
if (!isPlainObject(rootSchema.$defs) || !Object.hasOwn(rootSchema.$defs, defName)) {
|
|
317
|
+
throw new Error(`${name} references unknown $defs schema ${ref}`);
|
|
318
|
+
}
|
|
319
|
+
return rootSchema.$defs[defName];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function schemaDeclaresObject(schema) {
|
|
323
|
+
if (schema.type === 'object') return true;
|
|
324
|
+
return Array.isArray(schema.type) && schema.type.includes('object');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function schemaHasStructuredObjectShape(schema) {
|
|
328
|
+
return Object.hasOwn(schema, 'properties') || Object.hasOwn(schema, 'required');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function validateOneOfSchema(name, rootSchema, schema, seen) {
|
|
332
|
+
if (Object.hasOwn(schema, 'type') && !schemaDeclaresObject(schema)) {
|
|
333
|
+
throw new Error(`${name} oneOf container type must include object`);
|
|
334
|
+
}
|
|
335
|
+
if (!Array.isArray(schema.oneOf) || schema.oneOf.length === 0) {
|
|
336
|
+
throw new Error(`${name} oneOf must list schema alternatives`);
|
|
337
|
+
}
|
|
338
|
+
if (!isPlainObject(rootSchema.$defs)) throw new Error(`${name} oneOf schemas must define $defs`);
|
|
339
|
+
|
|
340
|
+
for (const [index, branch] of schema.oneOf.entries()) {
|
|
341
|
+
requirePlainObject(`${name} oneOf[${index}]`, branch);
|
|
342
|
+
if (Object.hasOwn(branch, '$ref')) {
|
|
343
|
+
const ref = branch.$ref;
|
|
344
|
+
const target = resolveLocalRef(`${name} oneOf[${index}]`, rootSchema, ref);
|
|
345
|
+
requireFailClosedObjectSchema(`${name} ${ref}`, target);
|
|
346
|
+
validateSchemaNode(`${name} ${ref}`, rootSchema, target, seen);
|
|
347
|
+
} else {
|
|
348
|
+
requireFailClosedObjectSchema(`${name} oneOf[${index}]`, branch);
|
|
349
|
+
validateSchemaNode(`${name} oneOf[${index}]`, rootSchema, branch, seen);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function validateSchemaNode(name, rootSchema, schema, seen = new WeakSet()) {
|
|
355
|
+
requirePlainObject(name, schema);
|
|
356
|
+
if (seen.has(schema)) return;
|
|
357
|
+
seen.add(schema);
|
|
358
|
+
|
|
359
|
+
if (Object.hasOwn(schema, '$ref')) {
|
|
360
|
+
validateSchemaNode(`${name} ${schema.$ref}`, rootSchema, resolveLocalRef(name, rootSchema, schema.$ref), seen);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (schemaDeclaresObject(schema) && schemaHasStructuredObjectShape(schema) && schema.additionalProperties !== false) {
|
|
365
|
+
throw new Error(`${name} must fail closed on unknown properties`);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (Object.hasOwn(schema, 'oneOf')) validateOneOfSchema(name, rootSchema, schema, seen);
|
|
369
|
+
|
|
370
|
+
if (Object.hasOwn(schema, '$defs')) {
|
|
371
|
+
requirePlainObject(`${name} $defs`, schema.$defs);
|
|
372
|
+
for (const [defName, defSchema] of Object.entries(schema.$defs)) {
|
|
373
|
+
validateSchemaNode(`${name} $defs.${defName}`, rootSchema, defSchema, seen);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (Object.hasOwn(schema, 'properties')) {
|
|
378
|
+
requirePlainObject(`${name} properties`, schema.properties);
|
|
379
|
+
for (const [propertyName, propertySchema] of Object.entries(schema.properties)) {
|
|
380
|
+
validateSchemaNode(`${name} properties.${propertyName}`, rootSchema, propertySchema, seen);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (Object.hasOwn(schema, 'items')) {
|
|
385
|
+
validateSchemaNode(`${name} items`, rootSchema, schema.items, seen);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
for (const combinator of ['allOf', 'anyOf']) {
|
|
389
|
+
if (!Object.hasOwn(schema, combinator)) continue;
|
|
390
|
+
if (!Array.isArray(schema[combinator]) || schema[combinator].length === 0) {
|
|
391
|
+
throw new Error(`${name} ${combinator} must list schemas`);
|
|
392
|
+
}
|
|
393
|
+
for (const [index, nested] of schema[combinator].entries()) {
|
|
394
|
+
validateSchemaNode(`${name} ${combinator}[${index}]`, rootSchema, nested, seen);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (isPlainObject(schema.additionalProperties)) {
|
|
399
|
+
validateSchemaNode(`${name} additionalProperties`, rootSchema, schema.additionalProperties, seen);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const specsDir = path.join(root, 'specs');
|
|
404
|
+
for (const name of fs.readdirSync(specsDir)) {
|
|
405
|
+
if (!name.endsWith('.json')) continue;
|
|
406
|
+
const full = path.join(specsDir, name);
|
|
407
|
+
const parsed = JSON.parse(fs.readFileSync(full, 'utf8'));
|
|
408
|
+
for (const key of ['$schema', '$id', 'title']) {
|
|
409
|
+
if (!(key in parsed)) throw new Error(`${name} missing ${key}`);
|
|
410
|
+
}
|
|
411
|
+
if (Object.hasOwn(parsed, 'oneOf')) {
|
|
412
|
+
validateOneOfSchema(name, parsed, parsed, new WeakSet());
|
|
413
|
+
} else {
|
|
414
|
+
requireFailClosedObjectSchema(name, parsed);
|
|
415
|
+
}
|
|
416
|
+
validateSchemaNode(name, parsed, parsed);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
console.log('enigma check ok');
|