arcane-os 0.1.0-dev.5 → 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.
@@ -16,12 +16,25 @@ import path from 'node:path';
16
16
  import {pathToFileURL} from 'node:url';
17
17
  import {setTimeout as delay} from 'node:timers/promises';
18
18
  import {isDeepStrictEqual} from 'node:util';
19
+ import {withWorkspaceOperationLock} from '../workspace-operation-lock.mjs';
20
+ import {generateImportMap} from '../import-map.mjs';
21
+ import {authenticateRuntimeReceipt,verifyRuntime} from '../runtime.mjs';
22
+ import {
23
+ authenticateSdkBrowserRuntimeReceipt,
24
+ verifySdkBrowserRuntime
25
+ } from '../sdk-browser-runtime.mjs';
26
+ import {
27
+ authenticateWorkspaceRuntimeReceipt,
28
+ verifyWorkspaceRuntime
29
+ } from '../workspace-runtime.mjs';
19
30
 
20
31
  export const ROOT_CONFIG_NAME='arcane-packager.json';
21
32
  export const APP_CONFIG_NAME='arcane-package.json';
22
33
  export const RELEASE_MANIFEST_NAME='ARCANE_APP_RELEASE.json';
23
34
  export const PACKAGER_VERSION='arcane-app-packager-v1';
24
35
 
36
+ const RUNTIME_AUTHORITIES_NAME='ARCANE_RUNTIME_AUTHORITIES.json';
37
+
25
38
  const RENAME_RETRY_CODES=new Set(['EACCES','EBUSY','EPERM']);
26
39
  const RENAME_RETRY_LIMIT=20;
27
40
  const RENAME_RETRY_DELAY_MS=250;
@@ -135,6 +148,7 @@ const OLLAMA_MODEL_IDENTIFIER=
135
148
  const MODEL_DEFINITION_PATTERN=/^(?:Modelfile|[A-Za-z0-9][A-Za-z0-9._-]{0,118}\.Modelfile)$/;
136
149
  const MAX_LOCAL_AI_MODELS=64;
137
150
  const MAX_MODEL_DEFINITION_BYTES=512*1024;
151
+ const SHA256_PATTERN=/^[a-f0-9]{64}$/u;
138
152
 
139
153
  function fail(message,code){
140
154
  const error=new Error(message);
@@ -1015,6 +1029,413 @@ async function assertValidatedDescriptorAuthority(validation,descriptorAuthority
1015
1029
  }
1016
1030
  }
1017
1031
 
1032
+ function packagedRuntimeAuthorities(receipt){
1033
+ const arcane=receipt?.sources?.arcane;
1034
+ const sdkBrowser=receipt?.sources?.sdkBrowser;
1035
+ if(receipt?.kind!=='arcane-workspace-runtime-verification'
1036
+ ||!Number.isSafeInteger(receipt.fileCount)||receipt.fileCount<1
1037
+ ||!Number.isSafeInteger(receipt.totalBytes)||receipt.totalBytes<1
1038
+ ||!SHA256_PATTERN.test(receipt.contentSha256??'')
1039
+ ||!isPlainObject(arcane)||arcane.authority!=='arcane-os-upstream'
1040
+ ||!SHA256_PATTERN.test(arcane.manifestSha256??'')
1041
+ ||!SHA256_PATTERN.test(arcane.contentSha256??'')
1042
+ ||!isPlainObject(arcane.source)
1043
+ ||!isPlainObject(sdkBrowser)||sdkBrowser.authority!=='arcane-os-sdk'
1044
+ ||!SHA256_PATTERN.test(sdkBrowser.manifestSha256??'')
1045
+ ||!SHA256_PATTERN.test(sdkBrowser.contentSha256??'')
1046
+ ||!isPlainObject(sdkBrowser.source)
1047
+ ||!Array.isArray(sdkBrowser.source.dependencies)){
1048
+ fail('The composed workspace runtime receipt is missing its source authorities.');
1049
+ }
1050
+ return immutableJsonCopy({
1051
+ schemaVersion:1,
1052
+ kind:'arcane-app-runtime-authorities',
1053
+ sdkVersion:receipt.sdkVersion,
1054
+ projection:{
1055
+ fileCount:receipt.fileCount,
1056
+ totalBytes:receipt.totalBytes,
1057
+ contentSha256:receipt.contentSha256
1058
+ },
1059
+ sources:{
1060
+ arcane:{
1061
+ authority:arcane.authority,
1062
+ manifestSha256:arcane.manifestSha256,
1063
+ contentSha256:arcane.contentSha256,
1064
+ source:arcane.source
1065
+ },
1066
+ sdkBrowser:{
1067
+ authority:sdkBrowser.authority,
1068
+ manifestSha256:sdkBrowser.manifestSha256,
1069
+ contentSha256:sdkBrowser.contentSha256,
1070
+ source:sdkBrowser.source
1071
+ }
1072
+ }
1073
+ });
1074
+ }
1075
+
1076
+ async function hasExternalRuntimeAdmission(context){
1077
+ const lockPath=path.join(context.workspaceRoot,'arcane.lock.json');
1078
+ try{
1079
+ const info=await lstat(lockPath);
1080
+ if(info.isSymbolicLink()||!info.isFile()){
1081
+ fail('arcane.lock.json must be a real file before runtime provenance can be packaged.');
1082
+ }
1083
+ return true;
1084
+ }catch(error){
1085
+ if(error?.code==='ENOENT')return false;
1086
+ throw error;
1087
+ }
1088
+ }
1089
+
1090
+ async function validateExternalRuntimeAdmission(context,{signal,onEvent}={}){
1091
+ const {validateWorkspace}=await import('../workspace.mjs');
1092
+ const validation=await validateWorkspace({
1093
+ workspaceRoot:context.workspaceRoot,
1094
+ appId:context.config.id,
1095
+ signal,
1096
+ onEvent
1097
+ });
1098
+ if(validation.workspaceMode!=='external'){
1099
+ fail('A workspace with arcane.lock.json must use the external SDK runtime contract.');
1100
+ }
1101
+ return validation;
1102
+ }
1103
+
1104
+ async function integratedWorkspaceCandidate(context){
1105
+ const packagePath=path.join(context.workspaceRoot,'package.json');
1106
+ let info;
1107
+ try{
1108
+ info=await lstat(packagePath);
1109
+ }catch(error){
1110
+ if(error?.code==='ENOENT')return false;
1111
+ throw error;
1112
+ }
1113
+ if(info.isSymbolicLink()||!info.isFile()){
1114
+ fail('workspace package.json must be a real file.');
1115
+ }
1116
+ const document=await readJsonDocument(packagePath,'workspace package.json');
1117
+ return document.value?.name==='arcane-os'&&document.value?.type==='module';
1118
+ }
1119
+
1120
+ function packageRuntimeLocations(context){
1121
+ const installedRoot=path.join(context.workspaceRoot,'node_modules','arcane-os');
1122
+ return Object.freeze({
1123
+ runtimeRoot:path.join(installedRoot,'runtime'),
1124
+ browserRuntimeRoot:path.join(installedRoot,'browser-runtime')
1125
+ });
1126
+ }
1127
+
1128
+ async function authenticatePackageRuntimeVerificationState(context,state,{signal}={}){
1129
+ assertOnlyKeys(
1130
+ state,
1131
+ new Set(['runtimeReceipt','sdkBrowserRuntimeReceipt','workspaceRuntimeReceipt']),
1132
+ 'runtime verification state'
1133
+ );
1134
+ const descriptors=Object.getOwnPropertyDescriptors(state);
1135
+ const required=['runtimeReceipt','sdkBrowserRuntimeReceipt','workspaceRuntimeReceipt'];
1136
+ if(required.some(key=>!Object.hasOwn(descriptors,key)||!Object.hasOwn(descriptors[key],'value'))){
1137
+ fail('The runtime verification state must use fixed receipt references, not accessors.');
1138
+ }
1139
+ const snapshot=Object.freeze({
1140
+ runtimeReceipt:descriptors.runtimeReceipt.value,
1141
+ sdkBrowserRuntimeReceipt:descriptors.sdkBrowserRuntimeReceipt.value,
1142
+ workspaceRuntimeReceipt:descriptors.workspaceRuntimeReceipt.value
1143
+ });
1144
+ if(!snapshot.runtimeReceipt
1145
+ ||!snapshot.sdkBrowserRuntimeReceipt
1146
+ ||!snapshot.workspaceRuntimeReceipt){
1147
+ fail('The runtime verification state must contain all three authenticated receipts.');
1148
+ }
1149
+ const {runtimeRoot,browserRuntimeRoot}=packageRuntimeLocations(context);
1150
+ await authenticateRuntimeReceipt(snapshot.runtimeReceipt,{runtimeRoot,signal});
1151
+ await authenticateSdkBrowserRuntimeReceipt(snapshot.sdkBrowserRuntimeReceipt,{
1152
+ browserRuntimeRoot,
1153
+ signal
1154
+ });
1155
+ await authenticateWorkspaceRuntimeReceipt(snapshot.workspaceRuntimeReceipt,{
1156
+ workspaceRoot:context.workspaceRoot,
1157
+ signal
1158
+ });
1159
+ const workspaceReceipt=snapshot.workspaceRuntimeReceipt;
1160
+ const expectedArcaneSource={
1161
+ authority:'arcane-os-upstream',
1162
+ location:snapshot.runtimeReceipt.canonicalLocation,
1163
+ manifestSha256:snapshot.runtimeReceipt.manifestSha256,
1164
+ contentSha256:snapshot.runtimeReceipt.contentSha256,
1165
+ source:snapshot.runtimeReceipt.source
1166
+ };
1167
+ const expectedBrowserSource={
1168
+ authority:'arcane-os-sdk',
1169
+ location:snapshot.sdkBrowserRuntimeReceipt.canonicalLocation,
1170
+ manifestSha256:snapshot.sdkBrowserRuntimeReceipt.manifestSha256,
1171
+ contentSha256:snapshot.sdkBrowserRuntimeReceipt.contentSha256,
1172
+ source:snapshot.sdkBrowserRuntimeReceipt.source
1173
+ };
1174
+ if(workspaceReceipt.sourceRuntimeLocation!==snapshot.runtimeReceipt.canonicalLocation
1175
+ ||workspaceReceipt.sourceManifestSha256!==snapshot.runtimeReceipt.manifestSha256
1176
+ ||workspaceReceipt.sourceContentSha256!==snapshot.runtimeReceipt.contentSha256
1177
+ ||workspaceReceipt.sourceBrowserRuntimeLocation
1178
+ !==snapshot.sdkBrowserRuntimeReceipt.canonicalLocation
1179
+ ||workspaceReceipt.sourceBrowserManifestSha256
1180
+ !==snapshot.sdkBrowserRuntimeReceipt.manifestSha256
1181
+ ||workspaceReceipt.sourceBrowserContentSha256
1182
+ !==snapshot.sdkBrowserRuntimeReceipt.contentSha256
1183
+ ||workspaceReceipt.sdkVersion!==snapshot.runtimeReceipt.sdkVersion
1184
+ ||workspaceReceipt.sdkVersion!==snapshot.sdkBrowserRuntimeReceipt.sdkVersion
1185
+ ||!isDeepStrictEqual(workspaceReceipt.sources?.arcane,expectedArcaneSource)
1186
+ ||!isDeepStrictEqual(workspaceReceipt.sources?.sdkBrowser,expectedBrowserSource)){
1187
+ fail('The workspace runtime receipt is not bound to the supplied source runtime receipts.');
1188
+ }
1189
+ return snapshot;
1190
+ }
1191
+
1192
+ async function issuePackageRuntimeVerificationState(context,{signal,onEvent}={}){
1193
+ const {runtimeRoot,browserRuntimeRoot}=packageRuntimeLocations(context);
1194
+ const [runtimeReceipt,sdkBrowserRuntimeReceipt]=await Promise.all([
1195
+ verifyRuntime({runtimeRoot,signal,onEvent}),
1196
+ verifySdkBrowserRuntime({browserRuntimeRoot,signal,onEvent})
1197
+ ]);
1198
+ const workspaceRuntimeReceipt=await verifyWorkspaceRuntime({
1199
+ workspaceRoot:context.workspaceRoot,
1200
+ runtimeRoot,
1201
+ runtimeReceipt,
1202
+ browserRuntimeRoot,
1203
+ sdkBrowserRuntimeReceipt,
1204
+ signal,
1205
+ onEvent
1206
+ });
1207
+ return Object.freeze({runtimeReceipt,sdkBrowserRuntimeReceipt,workspaceRuntimeReceipt});
1208
+ }
1209
+
1210
+ async function refreshPackageImportMap(context,{
1211
+ runtimeVerificationState,
1212
+ workspaceOperationLease,
1213
+ signal,
1214
+ onEvent
1215
+ }={}){
1216
+ const external=await hasExternalRuntimeAdmission(context);
1217
+ if(!external&&!await integratedWorkspaceCandidate(context)){
1218
+ if(runtimeVerificationState!==undefined){
1219
+ fail('A runtime verification state cannot be supplied without an external runtime admission.');
1220
+ }
1221
+ return Object.freeze({importMapReceipt:null,runtimeVerificationState:null});
1222
+ }
1223
+ const {validateWorkspace}=await import('../workspace.mjs');
1224
+ const validation=await validateWorkspace({
1225
+ workspaceRoot:context.workspaceRoot,
1226
+ appId:context.config.id,
1227
+ allowMissingManagedImportMap:true,
1228
+ signal,
1229
+ onEvent
1230
+ });
1231
+ if(validation.workspaceMode!=='external'&&runtimeVerificationState!==undefined){
1232
+ fail('A runtime verification state cannot be supplied to an integrated workspace.');
1233
+ }
1234
+ if(validation.workspaceMode==='integrated'
1235
+ &&validation.config.browserRuntimeLayout==='integrated-legacy'){
1236
+ return Object.freeze({
1237
+ importMapReceipt:Object.freeze({skipped:true,workspaceMode:'integrated'}),
1238
+ runtimeVerificationState:null
1239
+ });
1240
+ }
1241
+
1242
+ let workspaceRuntimeReceipt;
1243
+ let authenticatedRuntimeState=null;
1244
+ if(validation.workspaceMode==='external'){
1245
+ authenticatedRuntimeState=runtimeVerificationState===undefined
1246
+ ?await issuePackageRuntimeVerificationState(context,{signal,onEvent})
1247
+ :await authenticatePackageRuntimeVerificationState(
1248
+ context,
1249
+ runtimeVerificationState,
1250
+ {signal}
1251
+ );
1252
+ workspaceRuntimeReceipt=authenticatedRuntimeState.workspaceRuntimeReceipt;
1253
+ }
1254
+ const importMapReceipt=await generateImportMap({
1255
+ workspaceRoot:context.workspaceRoot,
1256
+ appId:context.config.id,
1257
+ appRoot:context.appRoot,
1258
+ entry:context.config.entry,
1259
+ workspaceRuntimeReceipt,
1260
+ workspaceOperationLease,
1261
+ signal,
1262
+ onEvent
1263
+ });
1264
+ return Object.freeze({importMapReceipt,runtimeVerificationState:authenticatedRuntimeState});
1265
+ }
1266
+
1267
+ function authenticatedImportMapReceipt(receipt){
1268
+ if(receipt==null||receipt.skipped===true)return receipt;
1269
+ if(receipt.committed!==true||!Array.isArray(receipt.cleanupWarnings)){
1270
+ fail(
1271
+ 'The generated import-map receipt is incomplete; the package release was not assembled.'
1272
+ );
1273
+ }
1274
+ if(receipt.cleanupWarnings.length!==0){
1275
+ fail(
1276
+ 'The generated import map committed with cleanup warnings; the package release '
1277
+ +`was not assembled: ${receipt.cleanupWarnings.join('; ')}`,
1278
+ 'ARCANE_IMPORT_MAP_CLEANUP_FAILED'
1279
+ );
1280
+ }
1281
+ return receipt;
1282
+ }
1283
+
1284
+ function importMapReceiptFiles(context,receipt){
1285
+ if(receipt==null||receipt.skipped===true)return Object.freeze([]);
1286
+ if(!Array.isArray(receipt.files)||receipt.files.length!==2){
1287
+ fail('The generated import-map receipt does not bind its committed artifact and entry files.');
1288
+ }
1289
+ const expected=Object.freeze([
1290
+ Object.freeze({
1291
+ role:'artifact',
1292
+ path:`apps/${context.config.id}/modules/arcane.importmap.json`
1293
+ }),
1294
+ Object.freeze({
1295
+ role:'entry',
1296
+ path:`apps/${context.config.id}/${context.config.entry}`
1297
+ })
1298
+ ]);
1299
+ const records=[];
1300
+ for(const [index,record] of receipt.files.entries()){
1301
+ assertOnlyKeys(
1302
+ record,
1303
+ new Set(['role','path','bytes','sha256']),
1304
+ `import-map receipt files[${index}]`
1305
+ );
1306
+ const wanted=expected[index];
1307
+ if(record.role!==wanted.role
1308
+ ||normalizeRelativePath(record.path,`import-map receipt files[${index}].path`)
1309
+ !==wanted.path
1310
+ ||!Number.isSafeInteger(record.bytes)||record.bytes<1
1311
+ ||!SHA256_PATTERN.test(record.sha256??'')){
1312
+ fail(`The generated import-map receipt ${wanted.role} record is invalid.`);
1313
+ }
1314
+ records.push(Object.freeze({...record}));
1315
+ }
1316
+ return Object.freeze(records);
1317
+ }
1318
+
1319
+ async function authenticateImportMapPair(context,receipt,{signal}={}){
1320
+ const records=importMapReceiptFiles(context,receipt);
1321
+ for(const record of records){
1322
+ const filePath=resolveInside(
1323
+ context.workspaceRoot,
1324
+ record.path,
1325
+ `import-map receipt ${record.role} path`
1326
+ );
1327
+ let verified;
1328
+ try{
1329
+ verified=await sha256WithIdentity(filePath,{
1330
+ signal,
1331
+ label:`import-map receipt ${record.role}`
1332
+ });
1333
+ }catch(error){
1334
+ fail(
1335
+ `The generated import-map ${record.role} is unavailable after commit: ${error.message}`
1336
+ );
1337
+ }
1338
+ if(verified.identity.bytes!==record.bytes||verified.sha256!==record.sha256){
1339
+ fail(`The generated import-map ${record.role} changed after it was committed.`);
1340
+ }
1341
+ }
1342
+ return records;
1343
+ }
1344
+
1345
+ async function authenticateCollectedImportMapPair(files,records,{signal}={}){
1346
+ for(const record of records){
1347
+ const collected=files.find(file=>file.destination===record.path);
1348
+ if(!collected||collected.bytes!==record.bytes){
1349
+ fail(`The package payload does not contain the committed import-map ${record.role}.`);
1350
+ }
1351
+ const verified=await sha256WithIdentity(collected.source,{
1352
+ signal,
1353
+ expectedIdentity:collected.identity,
1354
+ label:`collected import-map ${record.role}`
1355
+ });
1356
+ if(verified.identity.bytes!==record.bytes||verified.sha256!==record.sha256){
1357
+ fail(`The collected import-map ${record.role} does not match its committed receipt.`);
1358
+ }
1359
+ }
1360
+ }
1361
+
1362
+ function authenticatePackagedImportMapPair(release,records){
1363
+ for(const record of records){
1364
+ const packaged=release.files.find(file=>file.path===record.path);
1365
+ if(!packaged||packaged.bytes!==record.bytes||packaged.sha256!==record.sha256){
1366
+ fail(`The packaged import-map ${record.role} does not match its committed receipt.`);
1367
+ }
1368
+ }
1369
+ }
1370
+
1371
+ async function prepareRuntimeAuthorityState(context,{
1372
+ validation,
1373
+ runtimeVerificationState,
1374
+ signal,
1375
+ onEvent
1376
+ }={}){
1377
+ if(!await hasExternalRuntimeAdmission(context)){
1378
+ if(runtimeVerificationState!==undefined){
1379
+ fail('A runtime verification state cannot be supplied to an integrated workspace.');
1380
+ }
1381
+ return null;
1382
+ }
1383
+ await validateExternalRuntimeAdmission(context,{signal,onEvent});
1384
+
1385
+ let verificationState=null;
1386
+ let receipt=null;
1387
+ if(runtimeVerificationState!==undefined){
1388
+ verificationState=await authenticatePackageRuntimeVerificationState(
1389
+ context,
1390
+ runtimeVerificationState,
1391
+ {signal}
1392
+ );
1393
+ receipt=verificationState.workspaceRuntimeReceipt;
1394
+ }else if(validation?.kind==='arcane-workspace-runtime-verification'){
1395
+ receipt=validation;
1396
+ await authenticateWorkspaceRuntimeReceipt(receipt,{
1397
+ workspaceRoot:context.workspaceRoot,
1398
+ signal
1399
+ });
1400
+ }else{
1401
+ verificationState=await issuePackageRuntimeVerificationState(context,{signal,onEvent});
1402
+ receipt=verificationState.workspaceRuntimeReceipt;
1403
+ }
1404
+ return Object.freeze({
1405
+ receipt,
1406
+ document:packagedRuntimeAuthorities(receipt),
1407
+ verificationState
1408
+ });
1409
+ }
1410
+
1411
+ async function authenticateRuntimeAuthorityState(context,state,{signal,onEvent}={}){
1412
+ if(state===null){
1413
+ if(await hasExternalRuntimeAdmission(context)){
1414
+ fail('External runtime authority admission appeared during package verification.');
1415
+ }
1416
+ return;
1417
+ }
1418
+ if(!await hasExternalRuntimeAdmission(context)){
1419
+ fail('External runtime authority admission disappeared during package verification.');
1420
+ }
1421
+ await validateExternalRuntimeAdmission(context,{signal,onEvent});
1422
+ if(state.verificationState){
1423
+ await authenticatePackageRuntimeVerificationState(
1424
+ context,
1425
+ state.verificationState,
1426
+ {signal}
1427
+ );
1428
+ }else{
1429
+ await authenticateWorkspaceRuntimeReceipt(state.receipt,{
1430
+ workspaceRoot:context.workspaceRoot,
1431
+ signal
1432
+ });
1433
+ }
1434
+ if(!isDeepStrictEqual(packagedRuntimeAuthorities(state.receipt),state.document)){
1435
+ fail('External runtime source authorities changed during package verification.');
1436
+ }
1437
+ }
1438
+
1018
1439
  async function getAppContext({
1019
1440
  workspaceRoot:requestedWorkspaceRoot,
1020
1441
  appId,
@@ -1991,6 +2412,56 @@ async function writeReleaseManifest(root,context,version,{signal,onEvent}={}){
1991
2412
  };
1992
2413
  }
1993
2414
 
2415
+ async function writeRuntimeAuthorities(root,state){
2416
+ if(state==null)return;
2417
+ const authorityPath=path.join(root,RUNTIME_AUTHORITIES_NAME);
2418
+ await writeFile(
2419
+ authorityPath,
2420
+ `${JSON.stringify(state.document,null,2)}\n`,
2421
+ {encoding:'utf8',flag:'wx'}
2422
+ );
2423
+ const authority=await openStableRegularFile(authorityPath,RUNTIME_AUTHORITIES_NAME);
2424
+ await authority.handle.close();
2425
+ }
2426
+
2427
+ async function verifyRuntimeAuthorities(root,state){
2428
+ const authorityPath=path.join(root,RUNTIME_AUTHORITIES_NAME);
2429
+ if(state==null){
2430
+ try{
2431
+ await lstat(authorityPath);
2432
+ fail(`${RUNTIME_AUTHORITIES_NAME} is not allowed without an external runtime authority.`);
2433
+ }catch(error){
2434
+ if(error?.code!=='ENOENT')throw error;
2435
+ }
2436
+ return;
2437
+ }
2438
+ const document=await readJsonDocument(authorityPath,RUNTIME_AUTHORITIES_NAME);
2439
+ if(!isDeepStrictEqual(document.value,state.document)){
2440
+ fail(`${RUNTIME_AUTHORITIES_NAME} does not match the admitted workspace runtime authorities.`);
2441
+ }
2442
+ }
2443
+
2444
+ function verifyRuntimeProjectionAuthority(release,state){
2445
+ if(state==null)return;
2446
+ const projection=release.files
2447
+ .filter(file=>file.path.startsWith('arcane/'))
2448
+ .map(file=>({
2449
+ path:file.path.slice('arcane/'.length),
2450
+ bytes:file.bytes,
2451
+ sha256:file.sha256
2452
+ }));
2453
+ const totalBytes=projection.reduce((total,file)=>total+file.bytes,0);
2454
+ const contentSha256=createHash('sha256')
2455
+ .update(JSON.stringify(projection))
2456
+ .digest('hex');
2457
+ const expected=state.document.projection;
2458
+ if(projection.length!==expected.fileCount
2459
+ ||totalBytes!==expected.totalBytes
2460
+ ||contentSha256!==expected.contentSha256){
2461
+ fail('The packaged arcane runtime does not match its admitted runtime authorities.');
2462
+ }
2463
+ }
2464
+
1994
2465
  function expectedReleaseApp(context,version){
1995
2466
  const {config}=context;
1996
2467
  return {
@@ -2004,7 +2475,10 @@ function expectedReleaseApp(context,version){
2004
2475
  };
2005
2476
  }
2006
2477
 
2007
- async function verifyFreshStaticRelease(root,context,version,releaseState,{signal}={}){
2478
+ async function verifyFreshStaticRelease(root,context,version,releaseState,{
2479
+ runtimeAuthorityState,
2480
+ signal
2481
+ }={}){
2008
2482
  throwIfAborted(signal);
2009
2483
  const {config}=context;
2010
2484
  const expectedApp=expectedReleaseApp(context,version);
@@ -2034,10 +2508,17 @@ async function verifyFreshStaticRelease(root,context,version,releaseState,{signa
2034
2508
  fail(`Package entry files for ${config.id} are invalid.`);
2035
2509
  }
2036
2510
 
2511
+ await verifyRuntimeAuthorities(root,runtimeAuthorityState);
2512
+ verifyRuntimeProjectionAuthority(release,runtimeAuthorityState);
2513
+
2037
2514
  return releaseState;
2038
2515
  }
2039
2516
 
2040
- async function verifyGenericRelease(root,context,version,{signal,onEvent}={}){
2517
+ async function verifyGenericRelease(root,context,version,{
2518
+ runtimeAuthorityState,
2519
+ signal,
2520
+ onEvent
2521
+ }={}){
2041
2522
  const {config}=context;
2042
2523
  const manifestDocument=await readJsonDocument(
2043
2524
  path.join(root,RELEASE_MANIFEST_NAME),
@@ -2076,6 +2557,9 @@ async function verifyGenericRelease(root,context,version,{signal,onEvent}={}){
2076
2557
  fail(`Package entry files for ${config.id} are invalid.`);
2077
2558
  }
2078
2559
 
2560
+ await verifyRuntimeAuthorities(root,runtimeAuthorityState);
2561
+ verifyRuntimeProjectionAuthority(release,runtimeAuthorityState);
2562
+
2079
2563
  return {
2080
2564
  release,
2081
2565
  identities:Object.freeze([
@@ -2118,7 +2602,11 @@ async function loadAdapter(context){
2118
2602
  return module;
2119
2603
  }
2120
2604
 
2121
- async function verifyBuiltPackage(context,outputRoot,version,adapter,{signal,onEvent}={}){
2605
+ async function verifyBuiltPackage(context,outputRoot,version,adapter,{
2606
+ runtimeAuthorityState,
2607
+ signal,
2608
+ onEvent
2609
+ }={}){
2122
2610
  throwIfAborted(signal);
2123
2611
  if(adapter){
2124
2612
  await adapter.verifyArcanePackage({
@@ -2132,7 +2620,11 @@ async function verifyBuiltPackage(context,outputRoot,version,adapter,{signal,onE
2132
2620
  });
2133
2621
  }
2134
2622
 
2135
- return verifyGenericRelease(outputRoot,context,version,{signal,onEvent});
2623
+ return verifyGenericRelease(outputRoot,context,version,{
2624
+ runtimeAuthorityState,
2625
+ signal,
2626
+ onEvent
2627
+ });
2136
2628
  }
2137
2629
 
2138
2630
  async function writeAppVersion(context,version){
@@ -2376,6 +2868,8 @@ async function packageAppUnlocked({
2376
2868
  context:preparedContext,
2377
2869
  sharedPayloadSnapshot,
2378
2870
  authenticatedSharedPayloadState,
2871
+ importMapReceipt,
2872
+ runtimeVerificationState,
2379
2873
  signal,
2380
2874
  onEvent,
2381
2875
  validateSourceState
@@ -2392,10 +2886,16 @@ async function packageAppUnlocked({
2392
2886
  });
2393
2887
  const currentVersion=context.config.version;
2394
2888
  const version=resolveTargetVersion(currentVersion,{bump,exactVersion,preid});
2889
+ const importMapFiles=dryRun
2890
+ ?Object.freeze([])
2891
+ :await authenticateImportMapPair(context,importMapReceipt,{signal});
2395
2892
  const files=await collectPackageFiles(context,{
2396
2893
  signal,
2397
2894
  sharedPayloadState:authenticatedSharedPayloadState
2398
2895
  });
2896
+ if(!dryRun){
2897
+ await authenticateCollectedImportMapPair(files,importMapFiles,{signal});
2898
+ }
2399
2899
  const preview={
2400
2900
  app:appId,
2401
2901
  currentVersion,
@@ -2423,6 +2923,7 @@ async function packageAppUnlocked({
2423
2923
  let promoted=false;
2424
2924
  let operationSucceeded=false;
2425
2925
  let rollbackRestored=false;
2926
+ let runtimeAuthorityState=null;
2426
2927
 
2427
2928
  try{
2428
2929
  await rm(staging,{recursive:true,force:true});
@@ -2468,16 +2969,42 @@ async function packageAppUnlocked({
2468
2969
  }
2469
2970
 
2470
2971
  throwIfAborted(signal);
2972
+ let sourceValidation;
2973
+ if(validateSourceState){
2974
+ sourceValidation=await validateSourceState({signal});
2975
+ await assertValidatedDescriptorAuthority(
2976
+ sourceValidation,
2977
+ context.descriptorAuthority
2978
+ );
2979
+ }
2980
+ runtimeAuthorityState=await prepareRuntimeAuthorityState(context,{
2981
+ validation:sourceValidation,
2982
+ runtimeVerificationState,
2983
+ signal,
2984
+ onEvent
2985
+ });
2986
+ await writeRuntimeAuthorities(staging,runtimeAuthorityState);
2471
2987
  const releaseState=await writeReleaseManifest(staging,context,version,{signal,onEvent});
2472
2988
  const verifiedRelease=adapter
2473
- ?await verifyBuiltPackage(context,staging,version,adapter,{signal,onEvent})
2474
- :await verifyFreshStaticRelease(staging,context,version,releaseState,{signal});
2989
+ ?await verifyBuiltPackage(context,staging,version,adapter,{
2990
+ runtimeAuthorityState,
2991
+ signal,
2992
+ onEvent
2993
+ })
2994
+ :await verifyFreshStaticRelease(staging,context,version,releaseState,{
2995
+ runtimeAuthorityState,
2996
+ signal
2997
+ });
2475
2998
 
2476
2999
  throwIfAborted(signal);
2477
3000
  if(validateSourceState){
2478
3001
  const validation=await validateSourceState({signal});
2479
- await assertValidatedDescriptorAuthority(validation,context.descriptorAuthority);
3002
+ await assertValidatedDescriptorAuthority(
3003
+ validation,
3004
+ context.descriptorAuthority
3005
+ );
2480
3006
  }
3007
+ await authenticateRuntimeAuthorityState(context,runtimeAuthorityState,{signal,onEvent});
2481
3008
  await assertAppDescriptorAuthorityCurrent(context,{signal});
2482
3009
  if(sharedPayloadSnapshot!==undefined){
2483
3010
  await authenticateSharedPayloadSnapshotState(sharedPayloadSnapshot,{
@@ -2486,6 +3013,8 @@ async function packageAppUnlocked({
2486
3013
  signal
2487
3014
  });
2488
3015
  }
3016
+ await authenticateImportMapPair(context,importMapReceipt,{signal});
3017
+ authenticatePackagedImportMapPair(verifiedRelease.release,importMapFiles);
2489
3018
  await assertArtifactState(staging,verifiedRelease.identities,{signal});
2490
3019
  throwIfAborted(signal);
2491
3020
 
@@ -2584,20 +3113,56 @@ export async function packageApp(options){
2584
3113
  signal:options?.signal
2585
3114
  });
2586
3115
  if(options?.dryRun){
3116
+ if(options?.runtimeVerificationState!==undefined){
3117
+ if(!await hasExternalRuntimeAdmission(context)){
3118
+ fail('A runtime verification state cannot be supplied to an integrated workspace.');
3119
+ }
3120
+ await authenticatePackageRuntimeVerificationState(
3121
+ context,
3122
+ options.runtimeVerificationState,
3123
+ {signal:options?.signal}
3124
+ );
3125
+ }
2587
3126
  return packageAppUnlocked({...options,context,authenticatedSharedPayloadState});
2588
3127
  }
2589
-
2590
- await assertSafeDistBoundary(context.workspaceRoot,context.distRoot,{create:true});
2591
- const releaseLock=await acquirePackageLock(context.distRoot,options?.appId);
2592
-
2593
- try{
2594
- return await packageAppUnlocked({...options,context,authenticatedSharedPayloadState});
2595
- }finally{
2596
- await releaseLock();
2597
- }
3128
+ return withWorkspaceOperationLock({
3129
+ workspaceRoot:context.workspaceRoot,
3130
+ operation:'package',
3131
+ workspaceOperationLease:options?.workspaceOperationLease,
3132
+ signal:options?.signal,
3133
+ onEvent:options?.onEvent
3134
+ },async workspaceOperationLease=>{
3135
+ await assertSafeDistBoundary(context.workspaceRoot,context.distRoot,{create:true});
3136
+ const releaseLock=await acquirePackageLock(context.distRoot,options?.appId);
3137
+ try{
3138
+ const refreshed=await refreshPackageImportMap(context,{
3139
+ runtimeVerificationState:options?.runtimeVerificationState,
3140
+ workspaceOperationLease,
3141
+ signal:options?.signal,
3142
+ onEvent:options?.onEvent
3143
+ });
3144
+ const importMapReceipt=authenticatedImportMapReceipt(refreshed.importMapReceipt);
3145
+ const packaged=await packageAppUnlocked({
3146
+ ...options,
3147
+ context,
3148
+ authenticatedSharedPayloadState,
3149
+ importMapReceipt,
3150
+ runtimeVerificationState:refreshed.runtimeVerificationState??undefined
3151
+ });
3152
+ return {...packaged,importMapReceipt};
3153
+ }finally{
3154
+ await releaseLock();
3155
+ }
3156
+ });
2598
3157
  }
2599
3158
 
2600
- export async function verifyApp({workspaceRoot,appId,signal,onEvent}){
3159
+ export async function verifyApp({
3160
+ workspaceRoot,
3161
+ appId,
3162
+ runtimeVerificationState,
3163
+ signal,
3164
+ onEvent
3165
+ }){
2601
3166
  throwIfAborted(signal);
2602
3167
  const context=await getAppContext({
2603
3168
  workspaceRoot,
@@ -2605,14 +3170,20 @@ export async function verifyApp({workspaceRoot,appId,signal,onEvent}){
2605
3170
  bindDescriptorAuthority:true,
2606
3171
  signal
2607
3172
  });
3173
+ const runtimeAuthorityState=await prepareRuntimeAuthorityState(context,{
3174
+ runtimeVerificationState,
3175
+ signal,
3176
+ onEvent
3177
+ });
2608
3178
  const adapter=await loadAdapter(context);
2609
3179
  const releaseState=await verifyBuiltPackage(
2610
3180
  context,
2611
3181
  context.outputRoot,
2612
3182
  context.config.version,
2613
3183
  adapter,
2614
- {signal,onEvent}
3184
+ {runtimeAuthorityState,signal,onEvent}
2615
3185
  );
3186
+ await authenticateRuntimeAuthorityState(context,runtimeAuthorityState,{signal,onEvent});
2616
3187
  const release=releaseState.release;
2617
3188
  const receipt=await issueAppReleaseReceipt(
2618
3189
  context.outputRoot,
@@ -2674,18 +3245,25 @@ export async function bumpVersion(options){
2674
3245
  return bumpVersionUnlocked(options);
2675
3246
  }
2676
3247
 
2677
- await getAppContext({
3248
+ const context=await getAppContext({
2678
3249
  workspaceRoot:options?.workspaceRoot,
2679
3250
  appId:options?.appId
2680
3251
  });
2681
- const releaseLock=await acquireOperationLock(
2682
- options?.workspaceRoot,
2683
- options?.appId
2684
- );
2685
-
2686
- try{
2687
- return await bumpVersionUnlocked(options);
2688
- }finally{
2689
- await releaseLock();
2690
- }
3252
+ return withWorkspaceOperationLock({
3253
+ workspaceRoot:context.workspaceRoot,
3254
+ operation:'version-bump',
3255
+ workspaceOperationLease:options?.workspaceOperationLease,
3256
+ signal:options?.signal,
3257
+ onEvent:options?.onEvent
3258
+ },async()=>{
3259
+ const releaseLock=await acquireOperationLock(
3260
+ context.workspaceRoot,
3261
+ options?.appId
3262
+ );
3263
+ try{
3264
+ return await bumpVersionUnlocked({...options,workspaceRoot:context.workspaceRoot});
3265
+ }finally{
3266
+ await releaseLock();
3267
+ }
3268
+ });
2691
3269
  }