spawnfile 0.1.14 → 0.1.17
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 +60 -159
- package/dist/audit/checksShared.d.ts +1 -1
- package/dist/auth/importers.d.ts +10 -0
- package/dist/auth/importers.js +29 -4
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/paths.d.ts +1 -1
- package/dist/auth/profileStore.js +9 -9
- package/dist/auth/targetSecretSourceFsIdentity.d.ts +40 -0
- package/dist/auth/targetSecretSourceFsIdentity.js +42 -0
- package/dist/auth/targetSecretSourceFsPublish.d.ts +2 -0
- package/dist/auth/targetSecretSourceFsPublish.js +3 -1
- package/dist/auth/targetSecretSourceFsPublishImmutable.d.ts +2 -1
- package/dist/auth/targetSecretSourceFsPublishImmutable.js +104 -71
- package/dist/auth/targetSecretSourceFsRead.js +5 -14
- package/dist/auth/trainingAuth.d.ts +21 -0
- package/dist/auth/trainingAuth.js +86 -0
- package/dist/auth/types.d.ts +1 -1
- package/dist/cli/authCommands.d.ts +1 -1
- package/dist/cli/authCommands.js +9 -0
- package/dist/cli/canaryCutoverCommand.d.ts +2 -0
- package/dist/cli/canaryCutoverCommand.js +30 -0
- package/dist/cli/capabilitiesCommand.d.ts +3 -0
- package/dist/cli/capabilitiesCommand.js +12 -0
- package/dist/cli/capabilitiesReceipt.d.ts +56 -0
- package/dist/cli/capabilitiesReceipt.js +56 -0
- package/dist/cli/compileBuildCommands.js +13 -0
- package/dist/cli/composedLifecycleContractSet.d.ts +21 -0
- package/dist/cli/composedLifecycleContractSet.js +204 -0
- package/dist/cli/devCommands.js +2 -0
- package/dist/cli/downCommand.js +19 -8
- package/dist/cli/evidenceExportHelperCommand.d.ts +13 -0
- package/dist/cli/evidenceExportHelperCommand.js +51 -0
- package/dist/cli/index.js +12 -3
- package/dist/cli/lifecycleMachine.d.ts +7 -1
- package/dist/cli/lifecycleMachine.js +2 -2
- package/dist/cli/paideiaDelegation.d.ts +17 -0
- package/dist/cli/paideiaDelegation.js +210 -0
- package/dist/cli/paideiaSupervisor.d.ts +17 -0
- package/dist/cli/paideiaSupervisor.js +48 -0
- package/dist/cli/productStateCloneCommand.d.ts +2 -0
- package/dist/cli/productStateCloneCommand.js +34 -0
- package/dist/cli/runCli.d.ts +13 -3
- package/dist/cli/runCli.js +37 -9
- package/dist/cli/targetCommands.d.ts +4 -7
- package/dist/cli/targetCommands.js +16 -46
- package/dist/cli/targetConfigPreparedPlan.d.ts +3 -0
- package/dist/cli/targetConfigPreparedPlan.js +74 -0
- package/dist/cli/targetConfigResolver.d.ts +3 -0
- package/dist/cli/targetConfigResolver.js +111 -0
- package/dist/cli/targetConfigResolverCommand.d.ts +6 -0
- package/dist/cli/targetConfigResolverCommand.js +43 -0
- package/dist/cli/targetConfigResolverContracts.d.ts +56 -0
- package/dist/cli/targetConfigResolverContracts.js +30 -0
- package/dist/cli/targetConfigResolverDocker.d.ts +4 -0
- package/dist/cli/targetConfigResolverDocker.js +47 -0
- package/dist/cli/targetConfigResolverValidation.d.ts +10 -0
- package/dist/cli/targetConfigResolverValidation.js +94 -0
- package/dist/cli/targetDefaultAuthorities.d.ts +2 -0
- package/dist/cli/targetDefaultAuthorities.js +3 -0
- package/dist/cli/targetDefaultConfig.d.ts +7 -0
- package/dist/cli/targetDefaultConfig.js +22 -5
- package/dist/cli/targetDefaultConfigStdin.js +7 -1
- package/dist/cli/targetDefaultHandlerFactory.js +23 -2
- package/dist/cli/targetDefaultHandlers.d.ts +2 -2
- package/dist/cli/targetProductionCommands.d.ts +5 -0
- package/dist/cli/targetProductionCommands.js +45 -0
- package/dist/cli/trainCommand.d.ts +3 -0
- package/dist/cli/trainCommand.js +61 -0
- package/dist/cli/upCommand.js +35 -6
- package/dist/cli/upLifecycleRecovery.d.ts +2 -2
- package/dist/cli/upLifecycleRecovery.js +188 -44
- package/dist/cli/usageCommand.d.ts +40 -0
- package/dist/cli/usageCommand.js +284 -0
- package/dist/cli/usageCommandLive.d.ts +84 -0
- package/dist/cli/usageCommandLive.js +95 -0
- package/dist/cli/usageDeploymentSource.d.ts +9 -0
- package/dist/cli/usageDeploymentSource.js +22 -0
- package/dist/cli/workspaceResourceMigrationCommand.d.ts +8 -0
- package/dist/cli/workspaceResourceMigrationCommand.js +36 -0
- package/dist/compiler/buildCompilePlanTeams.d.ts +1 -1
- package/dist/compiler/buildCompilePlanTeams.js +6 -1
- package/dist/compiler/compilePlanHelpers.js +3 -0
- package/dist/compiler/compileProject.d.ts +2 -0
- package/dist/compiler/compileProject.js +5 -1
- package/dist/compiler/compileProjectSupport.js +9 -0
- package/dist/compiler/containerArtifacts.d.ts +3 -1
- package/dist/compiler/containerArtifacts.js +92 -40
- package/dist/compiler/containerArtifactsPlans.d.ts +1 -1
- package/dist/compiler/containerArtifactsPlans.js +24 -7
- package/dist/compiler/containerArtifactsRender.d.ts +2 -1
- package/dist/compiler/containerArtifactsRender.js +91 -58
- package/dist/compiler/containerArtifactsTypes.d.ts +8 -0
- package/dist/compiler/containerBackedMountRender.d.ts +56 -0
- package/dist/compiler/containerBackedMountRender.js +101 -0
- package/dist/compiler/containerDaimonBrokerRender.d.ts +112 -0
- package/dist/compiler/containerDaimonBrokerRender.js +191 -0
- package/dist/compiler/containerDaimonGrokWorkerProvisioning.d.ts +38 -0
- package/dist/compiler/containerDaimonGrokWorkerProvisioning.js +108 -0
- package/dist/compiler/containerDaimonGrokWorkerRender.d.ts +172 -0
- package/dist/compiler/containerDaimonGrokWorkerRender.js +268 -0
- package/dist/compiler/containerDaimonOwnershipGuardRender.d.ts +12 -0
- package/dist/compiler/containerDaimonOwnershipGuardRender.js +191 -0
- package/dist/compiler/containerDaimonUidEntrypointRender.d.ts +24 -0
- package/dist/compiler/containerDaimonUidEntrypointRender.js +297 -0
- package/dist/compiler/containerEntrypointRender.d.ts +7 -0
- package/dist/compiler/containerEntrypointRender.js +28 -22
- package/dist/compiler/containerPersistentMounts.d.ts +30 -0
- package/dist/compiler/containerPersistentMounts.js +66 -0
- package/dist/compiler/containerReadinessPaths.d.ts +1 -0
- package/dist/compiler/containerReadinessPaths.js +1 -0
- package/dist/compiler/containerRuntimeLinkMaterializer.d.ts +2 -0
- package/dist/compiler/containerRuntimeLinkMaterializer.js +12 -0
- package/dist/compiler/containerRuntimeReadinessRender.d.ts +7 -0
- package/dist/compiler/containerRuntimeReadinessRender.js +40 -0
- package/dist/compiler/containerStateOwnershipRender.d.ts +4 -0
- package/dist/compiler/containerStateOwnershipRender.js +75 -0
- package/dist/compiler/containerTargetResources.d.ts +22 -1
- package/dist/compiler/containerTargetResources.js +94 -2
- package/dist/compiler/containerVolumeBootstrap.d.ts +3 -0
- package/dist/compiler/containerVolumeBootstrap.js +3 -0
- package/dist/compiler/containerWorkspaceResourceRender.js +49 -1
- package/dist/compiler/daimonTelemetryArtifacts.d.ts +2 -2
- package/dist/compiler/daimonTelemetryArtifacts.js +5 -10
- package/dist/compiler/deploymentLineage.d.ts +40 -0
- package/dist/compiler/deploymentLineage.js +50 -0
- package/dist/compiler/index.d.ts +3 -0
- package/dist/compiler/index.js +3 -0
- package/dist/compiler/initProject.d.ts +2 -0
- package/dist/compiler/initProject.js +39 -1
- package/dist/compiler/localMoltnetAuthority.d.ts +64 -0
- package/dist/compiler/localMoltnetAuthority.js +177 -0
- package/dist/compiler/memoryArtifacts.d.ts +1 -1
- package/dist/compiler/memoryArtifacts.js +108 -32
- package/dist/compiler/modelEnv.d.ts +5 -0
- package/dist/compiler/modelEnv.js +8 -2
- package/dist/compiler/moltnetArtifactPaths.d.ts +17 -12
- package/dist/compiler/moltnetArtifactPaths.js +17 -15
- package/dist/compiler/moltnetArtifactTypes.d.ts +10 -0
- package/dist/compiler/moltnetArtifacts.d.ts +1 -1
- package/dist/compiler/moltnetArtifacts.js +52 -12
- package/dist/compiler/moltnetBinaries.d.ts +16 -4
- package/dist/compiler/moltnetBinaries.js +46 -6
- package/dist/compiler/moltnetClientConfig.js +1 -1
- package/dist/compiler/moltnetConfigLowering.d.ts +3 -1
- package/dist/compiler/moltnetConfigLowering.js +30 -6
- package/dist/compiler/moltnetNodeConfig.js +1 -1
- package/dist/compiler/moltnetReleaseAuthority.d.ts +24 -1
- package/dist/compiler/moltnetReleaseAuthority.js +33 -4
- package/dist/compiler/moltnetRepresentativeResolution.d.ts +1 -1
- package/dist/compiler/moltnetRepresentativeResolution.js +2 -2
- package/dist/compiler/moltnetResolution.js +9 -15
- package/dist/compiler/moltnetRoomMemberships.js +20 -5
- package/dist/compiler/moltnetRoomPolicyCompatibility.d.ts +1 -1
- package/dist/compiler/moltnetRoomPolicyCompatibility.js +3 -1
- package/dist/compiler/moltnetRuntimeConfig.d.ts +1 -1
- package/dist/compiler/moltnetRuntimeConfig.js +22 -2
- package/dist/compiler/moltnetServerPlans.js +19 -0
- package/dist/compiler/organizationExternalParticipants.d.ts +3 -0
- package/dist/compiler/organizationExternalParticipants.js +176 -0
- package/dist/compiler/organizationIdentity.d.ts +3 -9
- package/dist/compiler/organizationIdentity.js +37 -270
- package/dist/compiler/organizationIdentityGraph.d.ts +15 -0
- package/dist/compiler/organizationIdentityGraph.js +115 -0
- package/dist/compiler/organizationReadyEvidence.d.ts +8 -0
- package/dist/compiler/organizationReadyEvidence.js +23 -3
- package/dist/compiler/runProject.d.ts +4 -2
- package/dist/compiler/runProject.js +32 -6
- package/dist/compiler/runProjectAuth.d.ts +3 -1
- package/dist/compiler/runProjectAuth.js +19 -3
- package/dist/compiler/runProjectDaimonCodexDocker.d.ts +3 -0
- package/dist/compiler/runProjectDaimonCodexDocker.js +36 -0
- package/dist/compiler/runProjectDocker.d.ts +8 -4
- package/dist/compiler/runProjectDocker.js +43 -18
- package/dist/compiler/runProjectDockerDaimonGuards.d.ts +15 -0
- package/dist/compiler/runProjectDockerDaimonGuards.js +122 -0
- package/dist/compiler/runProjectDockerReservation.d.ts +3 -0
- package/dist/compiler/runProjectDockerReservation.js +118 -0
- package/dist/compiler/runProjectLifecycle.js +1 -1
- package/dist/compiler/training/broker/declaration.d.ts +45 -0
- package/dist/compiler/training/broker/declaration.js +55 -0
- package/dist/compiler/training/broker/entrypoint.d.ts +39 -0
- package/dist/compiler/training/broker/entrypoint.js +111 -0
- package/dist/compiler/training/broker/index.d.ts +10 -0
- package/dist/compiler/training/broker/index.js +10 -0
- package/dist/compiler/training/broker/main.d.ts +2 -0
- package/dist/compiler/training/broker/main.js +14 -0
- package/dist/compiler/training/broker/paths.d.ts +196 -0
- package/dist/compiler/training/broker/paths.js +241 -0
- package/dist/compiler/training/broker/processes.d.ts +42 -0
- package/dist/compiler/training/broker/processes.js +99 -0
- package/dist/compiler/training/broker/projection.d.ts +51 -0
- package/dist/compiler/training/broker/projection.js +76 -0
- package/dist/compiler/training/broker/provisioning.d.ts +87 -0
- package/dist/compiler/training/broker/provisioning.js +213 -0
- package/dist/compiler/training/broker/receipt.d.ts +77 -0
- package/dist/compiler/training/broker/receipt.js +110 -0
- package/dist/compiler/training/broker/registration.d.ts +41 -0
- package/dist/compiler/training/broker/registration.js +102 -0
- package/dist/compiler/training/broker/runtime.d.ts +25 -0
- package/dist/compiler/training/broker/runtime.js +173 -0
- package/dist/compiler/training/broker/seccompRoutes.d.ts +31 -0
- package/dist/compiler/training/broker/seccompRoutes.js +36 -0
- package/dist/compiler/training/broker/supervisor.d.ts +109 -0
- package/dist/compiler/training/broker/supervisor.js +173 -0
- package/dist/compiler/training/container/contract.d.ts +31 -0
- package/dist/compiler/training/container/contract.js +33 -0
- package/dist/compiler/training/container/index.d.ts +4 -0
- package/dist/compiler/training/container/index.js +2 -0
- package/dist/compiler/training/container/launch.d.ts +18 -0
- package/dist/compiler/training/container/launch.js +173 -0
- package/dist/compiler/training/container/prepare.d.ts +8 -0
- package/dist/compiler/training/container/prepare.js +80 -0
- package/dist/compiler/training/container/process.d.ts +14 -0
- package/dist/compiler/training/container/process.js +70 -0
- package/dist/compiler/training/container/security.d.ts +36 -0
- package/dist/compiler/training/container/security.js +58 -0
- package/dist/compiler/training/context.d.ts +7 -0
- package/dist/compiler/training/context.js +93 -0
- package/dist/compiler/training/contract.d.ts +142 -0
- package/dist/compiler/training/contract.js +34 -0
- package/dist/compiler/training/index.d.ts +4 -0
- package/dist/compiler/training/index.js +3 -0
- package/dist/compiler/training/preparation/assets/Dockerfile +98 -0
- package/dist/compiler/training/preparation/assets/package-lock.json +3787 -0
- package/dist/compiler/training/preparation/broker.d.ts +21 -0
- package/dist/compiler/training/preparation/broker.js +43 -0
- package/dist/compiler/training/preparation/contextModes.d.ts +12 -0
- package/dist/compiler/training/preparation/contextModes.js +38 -0
- package/dist/compiler/training/preparation/contract.d.ts +182 -0
- package/dist/compiler/training/preparation/contract.js +94 -0
- package/dist/compiler/training/preparation/copyAssets.d.ts +1 -0
- package/dist/compiler/training/preparation/copyAssets.js +8 -0
- package/dist/compiler/training/preparation/daimonParent.d.ts +42 -0
- package/dist/compiler/training/preparation/daimonParent.js +114 -0
- package/dist/compiler/training/preparation/files.d.ts +25 -0
- package/dist/compiler/training/preparation/files.js +85 -0
- package/dist/compiler/training/preparation/image.d.ts +28 -0
- package/dist/compiler/training/preparation/image.js +102 -0
- package/dist/compiler/training/preparation/index.d.ts +3 -0
- package/dist/compiler/training/preparation/index.js +2 -0
- package/dist/compiler/training/preparation/inputs.d.ts +23 -0
- package/dist/compiler/training/preparation/inputs.js +93 -0
- package/dist/compiler/training/preparation/prepare.d.ts +33 -0
- package/dist/compiler/training/preparation/prepare.js +186 -0
- package/dist/compiler/training/preparation/scratch.d.ts +19 -0
- package/dist/compiler/training/preparation/scratch.js +48 -0
- package/dist/compiler/training/repair/compatibility.d.ts +11 -0
- package/dist/compiler/training/repair/compatibility.js +34 -0
- package/dist/compiler/training/repair/contract.d.ts +506 -0
- package/dist/compiler/training/repair/contract.js +31 -0
- package/dist/compiler/training/repair/index.d.ts +3 -0
- package/dist/compiler/training/repair/index.js +3 -0
- package/dist/compiler/training/repair/prepare.d.ts +230 -0
- package/dist/compiler/training/repair/prepare.js +74 -0
- package/dist/compiler/training/repair/witness.d.ts +203 -0
- package/dist/compiler/training/repair/witness.js +42 -0
- package/dist/compiler/types.d.ts +2 -0
- package/dist/compiler/upProject.d.ts +26 -1
- package/dist/compiler/upProject.js +117 -178
- package/dist/compiler/upProjectHandoff.d.ts +62 -0
- package/dist/compiler/upProjectHandoff.js +204 -0
- package/dist/compiler/upReceipt.js +49 -4
- package/dist/compiler/updateProjectModels.js +1 -0
- package/dist/compiler/view/types.d.ts +1 -1
- package/dist/compiler/workspaceBundleArtifacts.d.ts +3 -0
- package/dist/compiler/workspaceBundleArtifacts.js +106 -0
- package/dist/compiler/workspaceResources.d.ts +4 -1
- package/dist/compiler/workspaceResources.js +12 -7
- package/dist/deployment/artifactsExportPlan.js +51 -1
- package/dist/deployment/canaryCutover.d.ts +42 -0
- package/dist/deployment/canaryCutover.js +140 -0
- package/dist/deployment/candidateDiagnostics.d.ts +15 -0
- package/dist/deployment/candidateDiagnostics.js +90 -0
- package/dist/deployment/dockerLogs.js +4 -1
- package/dist/deployment/dockerManager.js +9 -3
- package/dist/deployment/dockerProbeGateway.d.ts +17 -0
- package/dist/deployment/dockerProbeGateway.js +17 -2
- package/dist/deployment/downDeployment.d.ts +5 -0
- package/dist/deployment/downDeployment.js +109 -8
- package/dist/deployment/downReceiptTypes.d.ts +17 -2
- package/dist/deployment/downReceiptTypes.js +17 -2
- package/dist/deployment/index.d.ts +4 -0
- package/dist/deployment/index.js +4 -0
- package/dist/deployment/lifecycleCompletionContracts.d.ts +66 -0
- package/dist/deployment/lifecycleCompletionContracts.js +34 -0
- package/dist/deployment/lifecycleCompletionPaths.d.ts +4 -1
- package/dist/deployment/lifecycleCompletionPaths.js +3 -0
- package/dist/deployment/lifecycleCompletionPublication.d.ts +2 -0
- package/dist/deployment/lifecycleCompletionPublication.js +18 -4
- package/dist/deployment/lifecycleCompletionStore.d.ts +1 -1
- package/dist/deployment/lifecycleCompletionStore.js +7 -7
- package/dist/deployment/lifecycleUpRecords.d.ts +11 -0
- package/dist/deployment/lifecycleUpRecords.js +127 -0
- package/dist/deployment/native/AGENTS.md +11 -0
- package/dist/deployment/native/rename-noreplace-arm64 +0 -0
- package/dist/deployment/native/rename-noreplace-arm64.provenance.json +1 -0
- package/dist/deployment/native/rename-noreplace-x64 +0 -0
- package/dist/deployment/native/rename-noreplace-x64.provenance.json +1 -0
- package/dist/deployment/noReplaceActivation.d.ts +17 -0
- package/dist/deployment/noReplaceActivation.js +50 -0
- package/dist/deployment/organizationHandoffAuthorityFsBudget.d.ts +110 -0
- package/dist/deployment/organizationHandoffAuthorityFsBudget.js +156 -0
- package/dist/deployment/organizationHandoffAuthorityFsClient.js +82 -27
- package/dist/deployment/organizationHandoffAuthorityFsElection.d.ts +21 -0
- package/dist/deployment/organizationHandoffAuthorityFsElection.js +57 -0
- package/dist/deployment/organizationHandoffAuthorityFsWorker.js +254 -108
- package/dist/deployment/organizationHandoffAuthorityFsWorkerStall.fixture.d.ts +1 -0
- package/dist/deployment/organizationHandoffAuthorityFsWorkerStall.fixture.js +16 -0
- package/dist/deployment/organizationHandoffAuthorityStore.js +14 -7
- package/dist/deployment/organizationHandoffAuthorityTypes.d.ts +2 -2
- package/dist/deployment/organizationHandoffAuthorityTypes.js +2 -1
- package/dist/deployment/organizationReady.d.ts +2 -0
- package/dist/deployment/organizationReady.js +36 -4
- package/dist/deployment/productStateClone.d.ts +43 -0
- package/dist/deployment/productStateClone.js +319 -0
- package/dist/deployment/upLifecycleRecoveryState.d.ts +23 -0
- package/dist/deployment/upLifecycleRecoveryState.js +12 -0
- package/dist/deployment/upReceiptTypes.d.ts +32 -6
- package/dist/deployment/upReceiptTypes.js +29 -2
- package/dist/deployment/workspaceResourceMigration.d.ts +55 -0
- package/dist/deployment/workspaceResourceMigration.js +315 -0
- package/dist/dev/project.d.ts +7 -0
- package/dist/dev/project.js +9 -0
- package/dist/distribution/consumeImage.d.ts +2 -0
- package/dist/distribution/consumeImage.js +98 -73
- package/dist/distribution/consumeImageDaimonDocker.d.ts +3 -0
- package/dist/distribution/consumeImageDaimonDocker.js +45 -0
- package/dist/distribution/consumeImageLifecycle.d.ts +17 -0
- package/dist/distribution/consumeImageLifecycle.js +231 -0
- package/dist/distribution/consumeImageSupport.d.ts +12 -0
- package/dist/distribution/consumeImageSupport.js +15 -0
- package/dist/distribution/distributionReportSchema.d.ts +11 -1
- package/dist/distribution/distributionReportSchema.js +15 -2
- package/dist/distribution/dockerRunner.d.ts +6 -1
- package/dist/distribution/dockerRunner.js +31 -4
- package/dist/distribution/imageRuntimeAuth.d.ts +9 -1
- package/dist/distribution/imageRuntimeAuth.js +70 -1
- package/dist/distribution/index.d.ts +1 -1
- package/dist/distribution/index.js +1 -1
- package/dist/distribution/preflight.js +5 -1
- package/dist/distribution/types.d.ts +14 -1
- package/dist/distribution/types.js +3 -0
- package/dist/evidenceExportHelper/boundedExecutor.d.ts +3 -0
- package/dist/evidenceExportHelper/boundedExecutor.js +33 -0
- package/dist/evidenceExportHelper/helperProgram.mjs +156 -0
- package/dist/evidenceExportHelper/index.d.ts +4 -0
- package/dist/evidenceExportHelper/index.js +4 -0
- package/dist/evidenceExportHelper/preparedAuthority.d.ts +64 -0
- package/dist/evidenceExportHelper/preparedAuthority.js +228 -0
- package/dist/evidenceExportHelper/preparedBuilder.d.ts +10 -0
- package/dist/evidenceExportHelper/preparedBuilder.js +183 -0
- package/dist/evidenceExportHelper/preparedBuilderTypes.d.ts +19 -0
- package/dist/evidenceExportHelper/preparedBuilderTypes.js +1 -0
- package/dist/evidenceExportHelper/recipe.d.ts +7 -0
- package/dist/evidenceExportHelper/recipe.js +83 -0
- package/dist/filesystem/io.d.ts +2 -0
- package/dist/filesystem/io.js +16 -1
- package/dist/manifest/executionSchemas.d.ts +27 -0
- package/dist/manifest/executionSchemas.js +21 -2
- package/dist/manifest/mcpSchemas.d.ts +1 -0
- package/dist/manifest/mcpSchemas.js +3 -0
- package/dist/manifest/renderSpawnfileNetworks.js +11 -0
- package/dist/manifest/renderSpawnfileWorkspace.js +5 -0
- package/dist/manifest/scheduleSchemas.d.ts +3 -1
- package/dist/manifest/scheduleSchemas.js +91 -5
- package/dist/manifest/schemas.d.ts +237 -12
- package/dist/manifest/schemas.js +5 -2
- package/dist/manifest/teamNetworkAccessSchemas.d.ts +5 -1
- package/dist/manifest/teamNetworkAccessSchemas.js +11 -0
- package/dist/manifest/teamNetworkAuthSchemas.d.ts +1 -1
- package/dist/manifest/teamNetworkSchemas.d.ts +17 -3
- package/dist/manifest/teamNetworkSchemas.js +19 -1
- package/dist/manifest/teamNetworkServerSchemas.d.ts +8 -3
- package/dist/manifest/teamNetworkServerSchemas.js +18 -2
- package/dist/manifest/workspaceSchemas.d.ts +16 -0
- package/dist/manifest/workspaceSchemas.js +11 -0
- package/dist/report/types.d.ts +34 -4
- package/dist/runtime/common.d.ts +28 -1
- package/dist/runtime/common.js +33 -3
- package/dist/runtime/container.js +49 -33
- package/dist/runtime/daimon/adapter.d.ts +14 -0
- package/dist/runtime/daimon/adapter.js +243 -0
- package/dist/runtime/daimon/attention.d.ts +8 -0
- package/dist/runtime/daimon/attention.js +32 -0
- package/dist/runtime/daimon/config.d.ts +25 -0
- package/dist/runtime/daimon/config.js +272 -0
- package/dist/runtime/daimon/contractManifest.d.ts +295 -0
- package/dist/runtime/daimon/contractManifest.js +319 -0
- package/dist/runtime/daimon/grokModel.d.ts +19 -0
- package/dist/runtime/daimon/grokModel.js +33 -0
- package/dist/runtime/daimon/grokWorkerConfigBytes.d.ts +26 -0
- package/dist/runtime/daimon/grokWorkerConfigBytes.js +36 -0
- package/dist/runtime/daimon/grokWorkerContract.d.ts +22 -0
- package/dist/runtime/daimon/grokWorkerContract.js +48 -0
- package/dist/runtime/daimon/memory.d.ts +57 -0
- package/dist/runtime/daimon/memory.js +125 -0
- package/dist/runtime/daimon/runAuth.d.ts +51 -0
- package/dist/runtime/daimon/runAuth.js +279 -0
- package/dist/runtime/daimon/runtimeIdentity.d.ts +22 -0
- package/dist/runtime/daimon/runtimeIdentity.js +22 -0
- package/dist/runtime/daimon/scheduleAuthority.d.ts +3 -0
- package/dist/runtime/daimon/scheduleAuthority.js +17 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/install.d.ts +3 -0
- package/dist/runtime/install.js +3 -0
- package/dist/runtime/localDaimonAuthority.d.ts +11 -0
- package/dist/runtime/localDaimonAuthority.js +77 -0
- package/dist/runtime/mnemeMcp.d.ts +13 -0
- package/dist/runtime/mnemeMcp.js +26 -0
- package/dist/runtime/openclaw/adapter.js +2 -2
- package/dist/runtime/pi/adapter.d.ts +0 -1
- package/dist/runtime/pi/adapter.js +2 -6
- package/dist/runtime/pi/appCliSource.js +1 -1
- package/dist/runtime/pi/appScheduleSource.js +6 -2
- package/dist/runtime/pi/appTemplateTypes.d.ts +1 -1
- package/dist/runtime/picoclaw/adapter.js +2 -2
- package/dist/runtime/registry.d.ts +3 -0
- package/dist/runtime/registry.js +19 -2
- package/dist/runtime/scheduleUtils.d.ts +1 -0
- package/dist/runtime/scheduleUtils.js +5 -1
- package/dist/runtime/types.d.ts +25 -0
- package/dist/runtime/usageLedger.d.ts +158 -0
- package/dist/runtime/usageLedger.js +269 -0
- package/dist/runtime/usageLedgerRead.d.ts +48 -0
- package/dist/runtime/usageLedgerRead.js +102 -0
- package/dist/runtime/usageRequestLedger.d.ts +42 -0
- package/dist/runtime/usageRequestLedger.js +72 -0
- package/dist/shared/daimonCodexDocker.d.ts +32 -0
- package/dist/shared/daimonCodexDocker.js +120 -0
- package/dist/shared/daimonGrokSeccompProfile.d.ts +10 -0
- package/dist/shared/daimonGrokSeccompProfile.js +11 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +2 -0
- package/dist/shared/types.d.ts +1 -1
- package/dist/shared/volumeNames.d.ts +6 -0
- package/dist/shared/volumeNames.js +14 -0
- package/dist/target/dockerArtifactsProvider.js +34 -7
- package/dist/target/dockerBaseImage.d.ts +2 -0
- package/dist/target/dockerBaseImage.js +11 -0
- package/dist/target/dockerCommandExecutor.d.ts +11 -0
- package/dist/target/dockerCommandExecutor.js +53 -2
- package/dist/target/dockerCommandExecutorCore.d.ts +2 -1
- package/dist/target/dockerCommandExecutorCore.js +4 -2
- package/dist/target/dockerPublicArtifactSnapshot.d.ts +3 -3
- package/dist/target/dockerPublicArtifactSnapshot.js +17 -33
- package/dist/target/dockerTarget.d.ts +1 -0
- package/dist/target/dockerTargetBinding.d.ts +1 -2
- package/dist/target/dockerTargetBinding.js +2 -3
- package/dist/target/dockerTargetExecFile.d.ts +19 -0
- package/dist/target/dockerTargetExecFile.js +194 -0
- package/dist/target/dockerWorldServiceProvider.d.ts +4 -0
- package/dist/target/dockerWorldServiceProvider.js +13 -1
- package/dist/target/evidenceExport.d.ts +3 -1
- package/dist/target/evidenceExport.js +12 -3
- package/dist/target/evidenceExportOperationsTestKit.js +3 -3
- package/dist/target/evidenceExportProvider.d.ts +2 -0
- package/dist/target/evidenceExportProvider.js +15 -5
- package/dist/target/publicArtifactSnapshot.d.ts +31 -0
- package/dist/target/publicArtifactSnapshot.js +37 -3
- package/examples/README.md +45 -0
- package/examples/agent-with-subagents/AGENTS.md +3 -0
- package/examples/agent-with-subagents/Spawnfile +30 -0
- package/examples/agent-with-subagents/subagents/critic/AGENTS.md +3 -0
- package/examples/agent-with-subagents/subagents/critic/Spawnfile +15 -0
- package/examples/agent-with-subagents/subagents/researcher/AGENTS.md +3 -0
- package/examples/agent-with-subagents/subagents/researcher/Spawnfile +17 -0
- package/examples/daimon-org/Spawnfile +68 -0
- package/examples/daimon-org/TEAM.md +4 -0
- package/examples/daimon-org/agents/mapper/AGENTS.md +4 -0
- package/examples/daimon-org/agents/mapper/Spawnfile +37 -0
- package/examples/daimon-org/shared/skills/file-note/SKILL.md +9 -0
- package/examples/daimon-org/teams/review/Spawnfile +53 -0
- package/examples/daimon-org/teams/review/agents/reviewer/AGENTS.md +4 -0
- package/examples/daimon-org/teams/review/agents/reviewer/Spawnfile +37 -0
- package/examples/daimon-public-host/AGENTS.md +3 -0
- package/examples/daimon-public-host/Spawnfile +21 -0
- package/examples/distribution-org/Spawnfile +43 -0
- package/examples/distribution-org/TEAM.md +4 -0
- package/examples/distribution-org/agents/coordinator/AGENTS.md +1 -0
- package/examples/distribution-org/agents/coordinator/Spawnfile +20 -0
- package/examples/distribution-org/agents/researcher/AGENTS.md +1 -0
- package/examples/distribution-org/agents/researcher/Spawnfile +20 -0
- package/examples/jungian-daimon-org/Spawnfile +41 -0
- package/examples/jungian-daimon-org/TEAM.md +21 -0
- package/examples/jungian-daimon-org/teams/luna/Spawnfile +47 -0
- package/examples/jungian-daimon-org/teams/luna/TEAM.md +4 -0
- package/examples/jungian-daimon-org/teams/luna/agents/animus/AGENTS.md +10 -0
- package/examples/jungian-daimon-org/teams/luna/agents/animus/Spawnfile +33 -0
- package/examples/jungian-daimon-org/teams/luna/agents/representative/AGENTS.md +21 -0
- package/examples/jungian-daimon-org/teams/luna/agents/representative/Spawnfile +30 -0
- package/examples/jungian-daimon-org/teams/luna/agents/shadow/AGENTS.md +10 -0
- package/examples/jungian-daimon-org/teams/luna/agents/shadow/Spawnfile +33 -0
- package/examples/jungian-daimon-org/teams/selene/Spawnfile +47 -0
- package/examples/jungian-daimon-org/teams/selene/TEAM.md +4 -0
- package/examples/jungian-daimon-org/teams/selene/agents/animus/AGENTS.md +10 -0
- package/examples/jungian-daimon-org/teams/selene/agents/animus/Spawnfile +33 -0
- package/examples/jungian-daimon-org/teams/selene/agents/representative/AGENTS.md +19 -0
- package/examples/jungian-daimon-org/teams/selene/agents/representative/Spawnfile +33 -0
- package/examples/jungian-daimon-org/teams/selene/agents/shadow/AGENTS.md +10 -0
- package/examples/jungian-daimon-org/teams/selene/agents/shadow/Spawnfile +30 -0
- package/examples/mixed-runtime-org/Spawnfile +71 -0
- package/examples/mixed-runtime-org/TEAM.md +8 -0
- package/examples/mixed-runtime-org/agents/analyst/AGENTS.md +8 -0
- package/examples/mixed-runtime-org/agents/analyst/Spawnfile +27 -0
- package/examples/mixed-runtime-org/agents/conductor/AGENTS.md +8 -0
- package/examples/mixed-runtime-org/agents/conductor/Spawnfile +27 -0
- package/examples/mixed-runtime-org/agents/localist/AGENTS.md +7 -0
- package/examples/mixed-runtime-org/agents/localist/Spawnfile +50 -0
- package/examples/moltnet-team-chat/Spawnfile +43 -0
- package/examples/moltnet-team-chat/TEAM.md +5 -0
- package/examples/moltnet-team-chat/agents/coordinator/AGENTS.md +9 -0
- package/examples/moltnet-team-chat/agents/coordinator/Spawnfile +26 -0
- package/examples/moltnet-team-chat/teams/analysis/Spawnfile +23 -0
- package/examples/moltnet-team-chat/teams/analysis/TEAM.md +3 -0
- package/examples/moltnet-team-chat/teams/analysis/agents/observer/AGENTS.md +5 -0
- package/examples/moltnet-team-chat/teams/analysis/agents/observer/Spawnfile +19 -0
- package/examples/moltnet-team-chat/teams/analysis/agents/representative/AGENTS.md +5 -0
- package/examples/moltnet-team-chat/teams/analysis/agents/representative/Spawnfile +19 -0
- package/examples/moltnet-team-chat/teams/field/Spawnfile +41 -0
- package/examples/moltnet-team-chat/teams/field/TEAM.md +3 -0
- package/examples/moltnet-team-chat/teams/field/agents/observer/AGENTS.md +5 -0
- package/examples/moltnet-team-chat/teams/field/agents/observer/Spawnfile +26 -0
- package/examples/moltnet-team-chat/teams/field/agents/representative/AGENTS.md +11 -0
- package/examples/moltnet-team-chat/teams/field/agents/representative/Spawnfile +26 -0
- package/examples/single-agent/AGENTS.md +3 -0
- package/examples/single-agent/HEARTBEAT.md +3 -0
- package/examples/single-agent/IDENTITY.md +3 -0
- package/examples/single-agent/MEMORY.md +3 -0
- package/examples/single-agent/SOUL.md +3 -0
- package/examples/single-agent/Spawnfile +41 -0
- package/examples/single-agent/skills/web_search/SKILL.md +6 -0
- package/moltnet-releases.json +5 -4
- package/package.json +36 -9
- package/runtimes.yaml +5 -2
- package/dist/cli/targetWorldClockCrossProcess.test-helper.d.ts +0 -13
- package/dist/cli/targetWorldClockCrossProcess.test-helper.js +0 -109
- package/dist/deployment/providerRuntimeBoundaryScanner.test-helper.d.ts +0 -8
- package/dist/deployment/providerRuntimeBoundaryScanner.test-helper.js +0 -60
- package/dist/deployment/simfileFixtureBoundary.test-helper.d.ts +0 -8
- package/dist/deployment/simfileFixtureBoundary.test-helper.js +0 -103
- package/dist/ownership/simfileRunOperatorInputs.d.ts +0 -103
- package/dist/ownership/simfileRunOperatorInputs.js +0 -139
- package/dist/target/cleanupRunDocker.test-helper.d.ts +0 -67
- package/dist/target/cleanupRunDocker.test-helper.js +0 -238
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed guard for durable container state.
|
|
3
|
+
*
|
|
4
|
+
* Every path in `container.persistent_mounts[]` is state the compiler expects
|
|
5
|
+
* a host-backed named volume to hold. `spawnfile run`/`spawnfile up` and
|
|
6
|
+
* `spawnfile deploy` mount them; a hand-rolled `docker run` of the same image
|
|
7
|
+
* does not, and nothing in the image declares `VOLUME`, so the container would
|
|
8
|
+
* silently write its whole durable record onto the container's own writable
|
|
9
|
+
* layer and lose it on `docker rm`. That is exactly how a newsroom's message
|
|
10
|
+
* history was destroyed by a routine recreate.
|
|
11
|
+
*
|
|
12
|
+
* These checks make that case loud instead of silent: the entrypoint refuses
|
|
13
|
+
* to start unless each durable path is a real mount point, naming the mount
|
|
14
|
+
* id, the path, and the volume name to attach. `SPAWNFILE_ALLOW_EPHEMERAL_STATE=1`
|
|
15
|
+
* is the deliberate opt-out for throwaway containers.
|
|
16
|
+
*
|
|
17
|
+
* Detection reads `/proc/self/mountinfo` and looks for an EXACT mount point,
|
|
18
|
+
* rather than comparing `stat -c %d` against the parent directory: two nested
|
|
19
|
+
* durable volumes live on the same host filesystem and therefore share a
|
|
20
|
+
* device number, which would make a device comparison report a genuinely
|
|
21
|
+
* backed inner mount as unbacked.
|
|
22
|
+
*
|
|
23
|
+
* The kernel octal-escapes four characters in the mountinfo mount-point field
|
|
24
|
+
* (`show_mountinfo` passes `" \t\n\\"` to `seq_path_root`), so a declared
|
|
25
|
+
* `mount: "/var/lib/my store"` appears there as `/var/lib/my\040store`. The
|
|
26
|
+
* comparison is therefore made against the compiler-escaped form — computed
|
|
27
|
+
* here, where it is exactly testable — while the operator-facing message keeps
|
|
28
|
+
* the real path. Escaping at compile time rather than decoding in shell keeps
|
|
29
|
+
* the check a deterministic string compare with no dependence on how a
|
|
30
|
+
* particular `printf %b` implementation treats `\nnn` vs `\0nnn`. For a path
|
|
31
|
+
* containing none of those four characters both forms are identical.
|
|
32
|
+
*/
|
|
33
|
+
export declare const EPHEMERAL_STATE_OPT_OUT_ENV = "SPAWNFILE_ALLOW_EPHEMERAL_STATE";
|
|
34
|
+
export interface BackedMountRequirement {
|
|
35
|
+
id: string;
|
|
36
|
+
mount_path: string;
|
|
37
|
+
volume_name: string;
|
|
38
|
+
}
|
|
39
|
+
/** The mount-point field exactly as `/proc/self/mountinfo` renders this path. */
|
|
40
|
+
export declare const escapeMountInfoPath: (value: string) => string;
|
|
41
|
+
export declare const createBackedMountShellFunction: () => string[];
|
|
42
|
+
/**
|
|
43
|
+
* One check per durable mount, deduplicated by path and ordered by path so the
|
|
44
|
+
* rendered entrypoint is a deterministic function of the compile plan.
|
|
45
|
+
*/
|
|
46
|
+
export declare const createBackedMountChecks: (mounts: readonly BackedMountRequirement[]) => string[];
|
|
47
|
+
/**
|
|
48
|
+
* The full guard — function plus checks — or nothing at all when the compile
|
|
49
|
+
* declares no durable mounts.
|
|
50
|
+
*/
|
|
51
|
+
export declare const createBackedMountGuard: (mounts: readonly BackedMountRequirement[]) => string[];
|
|
52
|
+
/**
|
|
53
|
+
* Compile-time operator surface: how many durable mounts this organization
|
|
54
|
+
* requires a launcher to attach.
|
|
55
|
+
*/
|
|
56
|
+
export declare const describeBackedMountRequirement: (mounts: readonly BackedMountRequirement[]) => string | null;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed guard for durable container state.
|
|
3
|
+
*
|
|
4
|
+
* Every path in `container.persistent_mounts[]` is state the compiler expects
|
|
5
|
+
* a host-backed named volume to hold. `spawnfile run`/`spawnfile up` and
|
|
6
|
+
* `spawnfile deploy` mount them; a hand-rolled `docker run` of the same image
|
|
7
|
+
* does not, and nothing in the image declares `VOLUME`, so the container would
|
|
8
|
+
* silently write its whole durable record onto the container's own writable
|
|
9
|
+
* layer and lose it on `docker rm`. That is exactly how a newsroom's message
|
|
10
|
+
* history was destroyed by a routine recreate.
|
|
11
|
+
*
|
|
12
|
+
* These checks make that case loud instead of silent: the entrypoint refuses
|
|
13
|
+
* to start unless each durable path is a real mount point, naming the mount
|
|
14
|
+
* id, the path, and the volume name to attach. `SPAWNFILE_ALLOW_EPHEMERAL_STATE=1`
|
|
15
|
+
* is the deliberate opt-out for throwaway containers.
|
|
16
|
+
*
|
|
17
|
+
* Detection reads `/proc/self/mountinfo` and looks for an EXACT mount point,
|
|
18
|
+
* rather than comparing `stat -c %d` against the parent directory: two nested
|
|
19
|
+
* durable volumes live on the same host filesystem and therefore share a
|
|
20
|
+
* device number, which would make a device comparison report a genuinely
|
|
21
|
+
* backed inner mount as unbacked.
|
|
22
|
+
*
|
|
23
|
+
* The kernel octal-escapes four characters in the mountinfo mount-point field
|
|
24
|
+
* (`show_mountinfo` passes `" \t\n\\"` to `seq_path_root`), so a declared
|
|
25
|
+
* `mount: "/var/lib/my store"` appears there as `/var/lib/my\040store`. The
|
|
26
|
+
* comparison is therefore made against the compiler-escaped form — computed
|
|
27
|
+
* here, where it is exactly testable — while the operator-facing message keeps
|
|
28
|
+
* the real path. Escaping at compile time rather than decoding in shell keeps
|
|
29
|
+
* the check a deterministic string compare with no dependence on how a
|
|
30
|
+
* particular `printf %b` implementation treats `\nnn` vs `\0nnn`. For a path
|
|
31
|
+
* containing none of those four characters both forms are identical.
|
|
32
|
+
*/
|
|
33
|
+
export const EPHEMERAL_STATE_OPT_OUT_ENV = "SPAWNFILE_ALLOW_EPHEMERAL_STATE";
|
|
34
|
+
const shellQuote = (value) => `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
35
|
+
const MOUNTINFO_ESCAPES = {
|
|
36
|
+
"\\": "\\134",
|
|
37
|
+
" ": "\\040",
|
|
38
|
+
"\t": "\\011",
|
|
39
|
+
"\n": "\\012"
|
|
40
|
+
};
|
|
41
|
+
/** The mount-point field exactly as `/proc/self/mountinfo` renders this path. */
|
|
42
|
+
export const escapeMountInfoPath = (value) => value.replace(/[\\ \t\n]/gu, (character) => MOUNTINFO_ESCAPES[character]);
|
|
43
|
+
export const createBackedMountShellFunction = () => [
|
|
44
|
+
"require_backed_mount() {",
|
|
45
|
+
' local mount_id="$1"',
|
|
46
|
+
// As mountinfo renders it (octal-escaped), for the comparison only.
|
|
47
|
+
' local mount_field="$2"',
|
|
48
|
+
// The real path, for every operator-facing message.
|
|
49
|
+
' local mount_target="$3"',
|
|
50
|
+
' local mount_volume="$4"',
|
|
51
|
+
" local mount_point",
|
|
52
|
+
` if [ "\${${EPHEMERAL_STATE_OPT_OUT_ENV}:-}" = "1" ]; then`,
|
|
53
|
+
" return 0",
|
|
54
|
+
" fi",
|
|
55
|
+
" if [ ! -r /proc/self/mountinfo ]; then",
|
|
56
|
+
' echo "Durable mount $mount_id at $mount_target cannot be verified: /proc/self/mountinfo is unreadable" >&2',
|
|
57
|
+
" exit 1",
|
|
58
|
+
" fi",
|
|
59
|
+
" while read -r _ _ _ _ mount_point _; do",
|
|
60
|
+
' if [ "$mount_point" = "$mount_field" ]; then',
|
|
61
|
+
" return 0",
|
|
62
|
+
" fi",
|
|
63
|
+
" done < /proc/self/mountinfo",
|
|
64
|
+
' echo "Durable mount $mount_id is not backed by a volume at $mount_target; this container would lose that state when it is removed." >&2',
|
|
65
|
+
` echo "Start this image with 'spawnfile run' or 'spawnfile up', or pass: --mount type=volume,source=\$mount_volume,target=\$mount_target" >&2`,
|
|
66
|
+
` echo "Set ${EPHEMERAL_STATE_OPT_OUT_ENV}=1 to run this container without durable state." >&2`,
|
|
67
|
+
" exit 1",
|
|
68
|
+
"}"
|
|
69
|
+
];
|
|
70
|
+
/**
|
|
71
|
+
* One check per durable mount, deduplicated by path and ordered by path so the
|
|
72
|
+
* rendered entrypoint is a deterministic function of the compile plan.
|
|
73
|
+
*/
|
|
74
|
+
export const createBackedMountChecks = (mounts) => {
|
|
75
|
+
const byPath = new Map();
|
|
76
|
+
for (const mount of mounts) {
|
|
77
|
+
if (!byPath.has(mount.mount_path))
|
|
78
|
+
byPath.set(mount.mount_path, mount);
|
|
79
|
+
}
|
|
80
|
+
return [...byPath.values()]
|
|
81
|
+
.sort((left, right) => left.mount_path.localeCompare(right.mount_path))
|
|
82
|
+
.map((mount) => `require_backed_mount ${shellQuote(mount.id)} ${shellQuote(escapeMountInfoPath(mount.mount_path))} ${shellQuote(mount.mount_path)} ${shellQuote(mount.volume_name)}`);
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* The full guard — function plus checks — or nothing at all when the compile
|
|
86
|
+
* declares no durable mounts.
|
|
87
|
+
*/
|
|
88
|
+
export const createBackedMountGuard = (mounts) => {
|
|
89
|
+
const checks = createBackedMountChecks(mounts);
|
|
90
|
+
return checks.length === 0 ? [] : [...createBackedMountShellFunction(), "", ...checks, ""];
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Compile-time operator surface: how many durable mounts this organization
|
|
94
|
+
* requires a launcher to attach.
|
|
95
|
+
*/
|
|
96
|
+
export const describeBackedMountRequirement = (mounts) => {
|
|
97
|
+
const count = new Set(mounts.map((mount) => mount.mount_path)).size;
|
|
98
|
+
if (count === 0)
|
|
99
|
+
return null;
|
|
100
|
+
return `${count} durable mount${count === 1 ? "" : "s"} must be attached at start: use 'spawnfile run'/'spawnfile up', or pass matching --mount type=volume args. The entrypoint refuses to start otherwise (set ${EPHEMERAL_STATE_OPT_OUT_ENV}=1 to accept state loss).`;
|
|
101
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { RuntimeTargetPlan } from "./containerArtifactsTypes.js";
|
|
2
|
+
import { type DaimonGrokRegistration, type DaimonGrokServiceConfigOptions } from "./containerDaimonGrokWorkerRender.js";
|
|
3
|
+
import { DAIMON_BROKER_UID, DAIMON_FIRST_WORKER_UID, DAIMON_ORGANIZATION_UID } from "../runtime/daimon/runtimeIdentity.js";
|
|
4
|
+
export { DAIMON_BROKER_UID, DAIMON_FIRST_WORKER_UID, DAIMON_ORGANIZATION_UID };
|
|
5
|
+
export declare const DAIMON_BROKER_EXECUTABLE = "/opt/daimon/bin/daimon-engine-broker";
|
|
6
|
+
export declare const DAIMON_BROKER_REGISTRATIONS = "/etc/daimon-engine-broker/registrations.bin";
|
|
7
|
+
export declare const DAIMON_BROKER_SOCKET = "/run/daimon-engine-broker/control.sock";
|
|
8
|
+
export declare const DAIMON_BROKER_BACKEND_SOCKET = "/run/daimon-engine-broker/backend.sock";
|
|
9
|
+
export declare const DAIMON_BROKER_LAUNCHER_SOCKET = "/run/daimon-engine-broker/launcher.sock";
|
|
10
|
+
export declare const DAIMON_BROKER_SERVICE_CONFIG = "/etc/daimon-engine-broker/service.json";
|
|
11
|
+
export declare const DAIMON_BROKER_REALM = "/var/lib/spawnfile/daimon/grok-subscription-realm";
|
|
12
|
+
/**
|
|
13
|
+
* Private temp for the broker and its relay (uid 2100, outside the organization
|
|
14
|
+
* group): shared `/tmp` and `/var/tmp` are `root:2000 1774` in a Grok
|
|
15
|
+
* organization, so any non-root process outside group 2000 needs its own
|
|
16
|
+
* `TMPDIR`. It lives in the broker's `/run` directory, which every worker denies.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DAIMON_BROKER_TMPDIR = "/run/daimon-engine-broker/tmp";
|
|
19
|
+
export { DAIMON_ORGANIZATION_STATE_DIRECTORY, DAIMON_WORKER_ROOT, resolveDaimonGrokRegistrations } from "./containerDaimonGrokWorkerRender.js";
|
|
20
|
+
interface WorkspaceSecurityResource {
|
|
21
|
+
backingPath: string;
|
|
22
|
+
kind: "bundle" | "git" | "volume";
|
|
23
|
+
linkPath: string;
|
|
24
|
+
mode: "mutable" | "readonly";
|
|
25
|
+
resolvedIdentity: string | null;
|
|
26
|
+
}
|
|
27
|
+
export declare const renderDaimonWorkspaceResourceSecurity: (resources: WorkspaceSecurityResource[], owners?: {
|
|
28
|
+
linkUid: number;
|
|
29
|
+
linkGid: number;
|
|
30
|
+
readonlyUid: number;
|
|
31
|
+
readonlyGid: number;
|
|
32
|
+
privilegedUid: number;
|
|
33
|
+
privilegedGid: number;
|
|
34
|
+
}, resourceRoot?: string) => string[];
|
|
35
|
+
/**
|
|
36
|
+
* Fixes ownership and mode of the per-turn usage ledger directory for every
|
|
37
|
+
* Daimon organization, not just ones with a Grok agent. AGY and Codex both
|
|
38
|
+
* write here too (`onTurnUsage` in Daimon's `engineDispatcher.ts`), from the
|
|
39
|
+
* organization-uid runtime process (`uid`/`gid` below, set in
|
|
40
|
+
* `renderDaimonUidEntrypoint`) rather than the privileged broker — so this
|
|
41
|
+
* must run whether or not any Grok registration exists, and the directory
|
|
42
|
+
* must be group-writable (0770), not merely group-readable (0750): a mode
|
|
43
|
+
* that only lets the group list the directory silently defeated every
|
|
44
|
+
* AGY/Codex advisory usage write and, with it, Daimon's wake-fuse token
|
|
45
|
+
* ceiling, which now refuses to start at all if this ledger is missing or
|
|
46
|
+
* unreadable (`wakeFuse.ts`'s `ensureUsageLedgerReadable`).
|
|
47
|
+
*
|
|
48
|
+
* The directory itself is never created here: it is a persistent volume
|
|
49
|
+
* mount (`daimon-grok-usage-ledger` in `config.ts`, unconditional for every
|
|
50
|
+
* Daimon organization) that Docker always materializes before the entrypoint
|
|
51
|
+
* runs — exactly like `DAIMON_WAKE_FUSE_DIRECTORY`, whose own fix-up
|
|
52
|
+
* (`renderDaimonUidEntrypoint`) uses this same three-step order. `chown` to
|
|
53
|
+
* root first, `chmod` next, `chown` to the final owner last — never
|
|
54
|
+
* `install -d`'s create-then-chown-then-chmod order, and never a single
|
|
55
|
+
* `chown owner:group` followed by `chmod`: root only ever chmods a path it
|
|
56
|
+
* currently owns, so it never needs `CAP_FOWNER` (`runProject.ts`'s
|
|
57
|
+
* capability set grants `CAP_CHOWN` but not `CAP_FOWNER`) — chmod-ing
|
|
58
|
+
* *after* the final `chown` hands ownership to the broker uid fails with
|
|
59
|
+
* `EPERM` the moment root no longer owns the path.
|
|
60
|
+
*/
|
|
61
|
+
export declare const renderDaimonUsageLedgerProvisioning: () => string[];
|
|
62
|
+
/**
|
|
63
|
+
* Empties a directory without touching a mount point inside it.
|
|
64
|
+
*
|
|
65
|
+
* `rm -rf` and `find -delete` both fail `EBUSY` on a mount point — it is
|
|
66
|
+
* another filesystem's root, and its entry cannot be unlinked while it is
|
|
67
|
+
* mounted. A deployment that mounts a tmpfs below a directory it later clears
|
|
68
|
+
* would otherwise abort provisioning on a bare `find` failure, which is exactly
|
|
69
|
+
* how a live training launch died. Mount points are left in place and descended
|
|
70
|
+
* into; anything else that cannot be removed is reported by name.
|
|
71
|
+
*
|
|
72
|
+
* Mount points come from `/proc/self/mountinfo` field 5. The kernel
|
|
73
|
+
* octal-escapes space, tab, newline and backslash there; every path this
|
|
74
|
+
* compiler provisions is free of all four, and a path that is not would simply
|
|
75
|
+
* fail to match and be treated as an ordinary directory.
|
|
76
|
+
*/
|
|
77
|
+
export declare const MOUNT_AWARE_CLEAR_HELPER: string[];
|
|
78
|
+
export declare const renderDaimonBrokerProvisioning: (plans: RuntimeTargetPlan[]) => string[];
|
|
79
|
+
/**
|
|
80
|
+
* The root broker provisioning program itself, over already-resolved
|
|
81
|
+
* registrations and workspace resources.
|
|
82
|
+
*
|
|
83
|
+
* Production reaches it through {@link renderDaimonBrokerProvisioning}; the
|
|
84
|
+
* broker-capable training container reaches it directly for its single fixed
|
|
85
|
+
* slot, with `serviceOptions` pointing the turn store at per-slot tmpfs and
|
|
86
|
+
* declaring the evaluator inference ledger. Both callers get byte-identical
|
|
87
|
+
* credential, registration, worker-home, temp and spill provisioning — the
|
|
88
|
+
* whole point of sharing it rather than writing a second root program.
|
|
89
|
+
*
|
|
90
|
+
* It is re-runnable: the shell wrapper removes `/etc/daimon-engine-broker` and
|
|
91
|
+
* `/run/daimon-engine-broker` first, so a slot recycle replays exactly the
|
|
92
|
+
* audited start-up path, credential-journal recovery included.
|
|
93
|
+
*/
|
|
94
|
+
export declare const renderDaimonBrokerProvisioningProgram: (registrations: readonly DaimonGrokRegistration[], workspaceResources: WorkspaceSecurityResource[], serviceOptions?: DaimonGrokServiceConfigOptions,
|
|
95
|
+
/**
|
|
96
|
+
* How the broker's `/etc` and `/run` roots are reset before provisioning.
|
|
97
|
+
* A production organization owns both as ordinary image directories and
|
|
98
|
+
* removes them outright. The training container mounts each as its own
|
|
99
|
+
* tmpfs, which cannot be unlinked, so it empties them instead — the same
|
|
100
|
+
* end state, reached the only way a mount point allows.
|
|
101
|
+
*/
|
|
102
|
+
rootReset?: "remove" | "clear",
|
|
103
|
+
/** Deny targets the worker provisioning creates when absent; see `renderDaimonGrokWorkerProvisioning`. */
|
|
104
|
+
optionalDenyPaths?: readonly string[],
|
|
105
|
+
/**
|
|
106
|
+
* Where the broker and relay get their private `TMPDIR`. Production keeps it
|
|
107
|
+
* inside the control root, which it removes and recreates exactly once. A
|
|
108
|
+
* deployment that re-provisions that root — the training slot recycle — must
|
|
109
|
+
* pass a path outside it: a `tmp/` inside a cleared root is a mount point in
|
|
110
|
+
* the training launch, and clearing a mount point fails `EBUSY`.
|
|
111
|
+
*/
|
|
112
|
+
brokerTmpdir?: string) => string[];
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { DAIMON_GROK_ENGINE_BROKER, DAIMON_GROK_TURN_USAGE_LEDGER } from "../runtime/daimon/contractManifest.js";
|
|
2
|
+
import { resolveDaimonGrokRegistrations } from "./containerDaimonGrokWorkerRender.js";
|
|
3
|
+
import { renderDaimonGrokWorkerProvisioning } from "./containerDaimonGrokWorkerProvisioning.js";
|
|
4
|
+
import { DAIMON_BROKER_UID, DAIMON_FIRST_WORKER_UID, DAIMON_ORGANIZATION_UID } from "../runtime/daimon/runtimeIdentity.js";
|
|
5
|
+
export { DAIMON_BROKER_UID, DAIMON_FIRST_WORKER_UID, DAIMON_ORGANIZATION_UID };
|
|
6
|
+
export const DAIMON_BROKER_EXECUTABLE = "/opt/daimon/bin/daimon-engine-broker";
|
|
7
|
+
export const DAIMON_BROKER_REGISTRATIONS = "/etc/daimon-engine-broker/registrations.bin";
|
|
8
|
+
export const DAIMON_BROKER_SOCKET = "/run/daimon-engine-broker/control.sock";
|
|
9
|
+
export const DAIMON_BROKER_BACKEND_SOCKET = "/run/daimon-engine-broker/backend.sock";
|
|
10
|
+
export const DAIMON_BROKER_LAUNCHER_SOCKET = "/run/daimon-engine-broker/launcher.sock";
|
|
11
|
+
export const DAIMON_BROKER_SERVICE_CONFIG = "/etc/daimon-engine-broker/service.json";
|
|
12
|
+
export const DAIMON_BROKER_REALM = "/var/lib/spawnfile/daimon/grok-subscription-realm";
|
|
13
|
+
/**
|
|
14
|
+
* Private temp for the broker and its relay (uid 2100, outside the organization
|
|
15
|
+
* group): shared `/tmp` and `/var/tmp` are `root:2000 1774` in a Grok
|
|
16
|
+
* organization, so any non-root process outside group 2000 needs its own
|
|
17
|
+
* `TMPDIR`. It lives in the broker's `/run` directory, which every worker denies.
|
|
18
|
+
*/
|
|
19
|
+
export const DAIMON_BROKER_TMPDIR = "/run/daimon-engine-broker/tmp";
|
|
20
|
+
export { DAIMON_ORGANIZATION_STATE_DIRECTORY, DAIMON_WORKER_ROOT, resolveDaimonGrokRegistrations } from "./containerDaimonGrokWorkerRender.js";
|
|
21
|
+
export const renderDaimonWorkspaceResourceSecurity = (resources, owners = { linkUid: 2_000, linkGid: 2_000, readonlyUid: 2_000, readonlyGid: 2_000, privilegedUid: 0, privilegedGid: 0 }, resourceRoot = "/var/lib/spawnfile/resources/") => [
|
|
22
|
+
`const workspaceResources = ${JSON.stringify(resources)};`,
|
|
23
|
+
"const resourceByLink = new Map(workspaceResources.map((resource) => [resource.linkPath, resource])); if (resourceByLink.size !== workspaceResources.length) throw new Error('duplicate worker workspace resource link');",
|
|
24
|
+
`const validateResourceLink = (target, info) => { const resource = resourceByLink.get(target); if (!resource || info.uid !== ${owners.linkUid} || info.gid !== ${owners.linkGid} || info.nlink !== 1) throw new Error('unsafe worker workspace link'); const raw = fs.readlinkSync(target), normalized = require('node:path').posix.normalize(raw); if (!raw.startsWith('/') || normalized !== raw || raw !== resource.backingPath || !raw.startsWith(${JSON.stringify(resourceRoot)})) throw new Error('unsafe worker workspace link target'); const backing = fs.lstatSync(raw); if (!backing.isDirectory() || backing.isSymbolicLink()) throw new Error('unsafe worker workspace resource'); const mode = backing.mode & 0o777; if (resource.kind === 'volume') { const lifecycleOwner = (backing.uid === ${owners.privilegedUid} && backing.gid === ${owners.privilegedGid}) || (backing.uid === ${owners.linkUid} && backing.gid === ${owners.linkGid}); if (!lifecycleOwner || mode !== 0o755 || typeof resource.resolvedIdentity !== 'string') throw new Error('unsafe worker workspace volume'); const expected = Buffer.from(\`${"${resource.resolvedIdentity}"}\\n\`); if (expected.length !== 72) throw new Error('unsafe worker workspace volume identity'); const sentinel = \`${"${raw}"}/.spawnfile-resource-identity\`; let fd; try { fd = fs.openSync(sentinel, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK); const before = fs.fstatSync(fd); if (!before.isFile() || before.isSymbolicLink() || before.nlink !== 1 || before.uid !== ${owners.privilegedUid} || before.gid !== ${owners.privilegedGid} || (before.mode & 0o777) !== 0o644 || before.size !== expected.length) throw new Error('unsafe worker workspace volume identity'); const bytes = fs.readFileSync(fd), after = fs.fstatSync(fd); if (!bytes.equals(expected) || after.dev !== before.dev || after.ino !== before.ino || after.size !== before.size || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs) throw new Error('unsafe worker workspace volume identity'); } finally { expected.fill(0); if (fd !== undefined) fs.closeSync(fd); } } else if (resource.mode === 'readonly') { if (backing.uid !== ${owners.readonlyUid} || backing.gid !== ${owners.readonlyGid} || mode !== 0o555) throw new Error('unsafe readonly worker workspace resource'); } else if (backing.uid !== ${owners.privilegedUid} || backing.gid !== ${owners.privilegedGid} || mode !== 0o755) throw new Error('unsafe mutable worker workspace resource'); };`,
|
|
25
|
+
"const secureWorkspace = (root, uid) => { const visit = (target) => { const info = fs.lstatSync(target); if (info.isSymbolicLink()) { validateResourceLink(target, info); return; } if (info.isDirectory()) { fs.chownSync(target, 0, 0); fs.chmodSync(target, 0o750); for (const name of fs.readdirSync(target)) visit(`${target}/${name}`); fs.chownSync(target, 2000, uid); } else if (info.isFile()) { fs.chownSync(target, 0, 0); fs.chmodSync(target, 0o640); fs.chownSync(target, 2000, uid); } else throw new Error('unsafe worker workspace node'); }; visit(root); };"
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Fixes ownership and mode of the per-turn usage ledger directory for every
|
|
29
|
+
* Daimon organization, not just ones with a Grok agent. AGY and Codex both
|
|
30
|
+
* write here too (`onTurnUsage` in Daimon's `engineDispatcher.ts`), from the
|
|
31
|
+
* organization-uid runtime process (`uid`/`gid` below, set in
|
|
32
|
+
* `renderDaimonUidEntrypoint`) rather than the privileged broker — so this
|
|
33
|
+
* must run whether or not any Grok registration exists, and the directory
|
|
34
|
+
* must be group-writable (0770), not merely group-readable (0750): a mode
|
|
35
|
+
* that only lets the group list the directory silently defeated every
|
|
36
|
+
* AGY/Codex advisory usage write and, with it, Daimon's wake-fuse token
|
|
37
|
+
* ceiling, which now refuses to start at all if this ledger is missing or
|
|
38
|
+
* unreadable (`wakeFuse.ts`'s `ensureUsageLedgerReadable`).
|
|
39
|
+
*
|
|
40
|
+
* The directory itself is never created here: it is a persistent volume
|
|
41
|
+
* mount (`daimon-grok-usage-ledger` in `config.ts`, unconditional for every
|
|
42
|
+
* Daimon organization) that Docker always materializes before the entrypoint
|
|
43
|
+
* runs — exactly like `DAIMON_WAKE_FUSE_DIRECTORY`, whose own fix-up
|
|
44
|
+
* (`renderDaimonUidEntrypoint`) uses this same three-step order. `chown` to
|
|
45
|
+
* root first, `chmod` next, `chown` to the final owner last — never
|
|
46
|
+
* `install -d`'s create-then-chown-then-chmod order, and never a single
|
|
47
|
+
* `chown owner:group` followed by `chmod`: root only ever chmods a path it
|
|
48
|
+
* currently owns, so it never needs `CAP_FOWNER` (`runProject.ts`'s
|
|
49
|
+
* capability set grants `CAP_CHOWN` but not `CAP_FOWNER`) — chmod-ing
|
|
50
|
+
* *after* the final `chown` hands ownership to the broker uid fails with
|
|
51
|
+
* `EPERM` the moment root no longer owns the path.
|
|
52
|
+
*/
|
|
53
|
+
export const renderDaimonUsageLedgerProvisioning = () => {
|
|
54
|
+
const { directoryPath } = DAIMON_GROK_TURN_USAGE_LEDGER;
|
|
55
|
+
return [
|
|
56
|
+
`chown 0:0 ${directoryPath} && chmod 0770 ${directoryPath} && chown ${DAIMON_BROKER_UID}:${DAIMON_ORGANIZATION_UID} ${directoryPath}`,
|
|
57
|
+
`setpriv --clear-groups --reuid ${DAIMON_ORGANIZATION_UID} --regid ${DAIMON_ORGANIZATION_UID} --inh-caps=-all --ambient-caps=-all --bounding-set=-all -- bash -ceu 'probe=${directoryPath}/.daimon-usage-probe; umask 007; : > "$probe"; rm "$probe"'`
|
|
58
|
+
];
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Empties a directory without touching a mount point inside it.
|
|
62
|
+
*
|
|
63
|
+
* `rm -rf` and `find -delete` both fail `EBUSY` on a mount point — it is
|
|
64
|
+
* another filesystem's root, and its entry cannot be unlinked while it is
|
|
65
|
+
* mounted. A deployment that mounts a tmpfs below a directory it later clears
|
|
66
|
+
* would otherwise abort provisioning on a bare `find` failure, which is exactly
|
|
67
|
+
* how a live training launch died. Mount points are left in place and descended
|
|
68
|
+
* into; anything else that cannot be removed is reported by name.
|
|
69
|
+
*
|
|
70
|
+
* Mount points come from `/proc/self/mountinfo` field 5. The kernel
|
|
71
|
+
* octal-escapes space, tab, newline and backslash there; every path this
|
|
72
|
+
* compiler provisions is free of all four, and a path that is not would simply
|
|
73
|
+
* fail to match and be treated as an ordinary directory.
|
|
74
|
+
*/
|
|
75
|
+
export const MOUNT_AWARE_CLEAR_HELPER = [
|
|
76
|
+
"spawnfile_mount_points=$(awk '{print $5}' /proc/self/mountinfo)",
|
|
77
|
+
"spawnfile_is_mount() { printf '%s\\n' \"$spawnfile_mount_points\" | grep -qxF \"$1\"; }",
|
|
78
|
+
"spawnfile_holds_mount() { printf '%s\\n' \"$spawnfile_mount_points\" | grep -qE \"^$(printf '%s' \"$1\" | sed 's/[][\\.*^$/]/\\\\&/g')(/|$)\"; }",
|
|
79
|
+
"spawnfile_clear_tree() {",
|
|
80
|
+
" spawnfile_root=$1",
|
|
81
|
+
" [ -d \"$spawnfile_root\" ] || return 0",
|
|
82
|
+
" for spawnfile_entry in \"$spawnfile_root\"/* \"$spawnfile_root\"/.[!.]* \"$spawnfile_root\"/..?*; do",
|
|
83
|
+
" if [ ! -e \"$spawnfile_entry\" ] && [ ! -L \"$spawnfile_entry\" ]; then continue; fi",
|
|
84
|
+
" if spawnfile_is_mount \"$spawnfile_entry\"; then continue; fi",
|
|
85
|
+
" if spawnfile_holds_mount \"$spawnfile_entry\"; then spawnfile_clear_tree \"$spawnfile_entry\"; continue; fi",
|
|
86
|
+
" rm -rf \"$spawnfile_entry\" || { echo \"cannot clear $spawnfile_entry: it is in use; a mount below a provisioned root must be declared outside it\" >&2; return 1; }",
|
|
87
|
+
" done",
|
|
88
|
+
"}",
|
|
89
|
+
// Removes the target outright, unless it is or contains a mount point: then its contents go and the
|
|
90
|
+
// mount points and the directories holding them stay, because neither can be unlinked while mounted.
|
|
91
|
+
"spawnfile_remove_tree() {",
|
|
92
|
+
" if [ ! -e \"$1\" ] && [ ! -L \"$1\" ]; then return 0; fi",
|
|
93
|
+
" if spawnfile_holds_mount \"$1\"; then spawnfile_clear_tree \"$1\"; return $?; fi",
|
|
94
|
+
" rm -rf \"$1\" || { echo \"cannot remove $1: it is in use\" >&2; return 1; }",
|
|
95
|
+
"}"
|
|
96
|
+
];
|
|
97
|
+
export const renderDaimonBrokerProvisioning = (plans) => {
|
|
98
|
+
const registrations = resolveDaimonGrokRegistrations(plans);
|
|
99
|
+
if (registrations.length === 0)
|
|
100
|
+
return [];
|
|
101
|
+
const workspaceResources = plans
|
|
102
|
+
.filter((plan) => plan.runtimeName === "daimon")
|
|
103
|
+
.flatMap((plan) => plan.resources ?? [])
|
|
104
|
+
.map((resource) => ({
|
|
105
|
+
backingPath: resource.backingPath,
|
|
106
|
+
kind: resource.kind,
|
|
107
|
+
linkPath: resource.linkPath,
|
|
108
|
+
mode: resource.mode,
|
|
109
|
+
resolvedIdentity: "resolvedIdentity" in resource && typeof resource.resolvedIdentity === "string"
|
|
110
|
+
? resource.resolvedIdentity
|
|
111
|
+
: null
|
|
112
|
+
}))
|
|
113
|
+
.sort((left, right) => left.linkPath.localeCompare(right.linkPath));
|
|
114
|
+
return renderDaimonBrokerProvisioningProgram(registrations, workspaceResources);
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* The root broker provisioning program itself, over already-resolved
|
|
118
|
+
* registrations and workspace resources.
|
|
119
|
+
*
|
|
120
|
+
* Production reaches it through {@link renderDaimonBrokerProvisioning}; the
|
|
121
|
+
* broker-capable training container reaches it directly for its single fixed
|
|
122
|
+
* slot, with `serviceOptions` pointing the turn store at per-slot tmpfs and
|
|
123
|
+
* declaring the evaluator inference ledger. Both callers get byte-identical
|
|
124
|
+
* credential, registration, worker-home, temp and spill provisioning — the
|
|
125
|
+
* whole point of sharing it rather than writing a second root program.
|
|
126
|
+
*
|
|
127
|
+
* It is re-runnable: the shell wrapper removes `/etc/daimon-engine-broker` and
|
|
128
|
+
* `/run/daimon-engine-broker` first, so a slot recycle replays exactly the
|
|
129
|
+
* audited start-up path, credential-journal recovery included.
|
|
130
|
+
*/
|
|
131
|
+
export const renderDaimonBrokerProvisioningProgram = (registrations, workspaceResources, serviceOptions = {},
|
|
132
|
+
/**
|
|
133
|
+
* How the broker's `/etc` and `/run` roots are reset before provisioning.
|
|
134
|
+
* A production organization owns both as ordinary image directories and
|
|
135
|
+
* removes them outright. The training container mounts each as its own
|
|
136
|
+
* tmpfs, which cannot be unlinked, so it empties them instead — the same
|
|
137
|
+
* end state, reached the only way a mount point allows.
|
|
138
|
+
*/
|
|
139
|
+
rootReset = "remove",
|
|
140
|
+
/** Deny targets the worker provisioning creates when absent; see `renderDaimonGrokWorkerProvisioning`. */
|
|
141
|
+
optionalDenyPaths,
|
|
142
|
+
/**
|
|
143
|
+
* Where the broker and relay get their private `TMPDIR`. Production keeps it
|
|
144
|
+
* inside the control root, which it removes and recreates exactly once. A
|
|
145
|
+
* deployment that re-provisions that root — the training slot recycle — must
|
|
146
|
+
* pass a path outside it: a `tmp/` inside a cleared root is a mount point in
|
|
147
|
+
* the training launch, and clearing a mount point fails `EBUSY`.
|
|
148
|
+
*/
|
|
149
|
+
brokerTmpdir = DAIMON_BROKER_TMPDIR) => {
|
|
150
|
+
const program = [
|
|
151
|
+
"const crypto = require('node:crypto'); const fs = require('node:fs');",
|
|
152
|
+
`const registrations = ${JSON.stringify(registrations.map(({ agentId, home, slot, uid, workspace }) => ({ agentId, home, slot, uid, workspace })))};`,
|
|
153
|
+
`const executable = '${DAIMON_GROK_ENGINE_BROKER.grokExecutablePath}';`,
|
|
154
|
+
"const digest = crypto.createHash('sha256').update(fs.readFileSync(executable)).digest();",
|
|
155
|
+
`if (!${JSON.stringify([DAIMON_GROK_ENGINE_BROKER.grokCliArtifacts.arm64.sha256, DAIMON_GROK_ENGINE_BROKER.grokCliArtifacts.x64.sha256])}.includes(digest.toString('hex'))) throw new Error('Grok executable is not the manifest-pinned ${DAIMON_GROK_ENGINE_BROKER.grokCliVersion} build');`,
|
|
156
|
+
"const cString = (buffer, offset, length, value) => { const bytes = Buffer.from(value); if (bytes.length < 1 || bytes.length >= length || bytes.includes(0)) throw new Error('invalid broker registration'); bytes.copy(buffer, offset); };",
|
|
157
|
+
`const records = registrations.map((entry) => { const record = Buffer.alloc(692); record.writeUInt32LE(${DAIMON_GROK_ENGINE_BROKER.nativeAbiVersion}, 0); record.writeUInt32LE(entry.slot, 4); record.writeUInt32LE(entry.uid, 8); record.writeUInt32LE(entry.uid, 12); cString(record, 16, 129, entry.agentId); cString(record, 145, 256, entry.workspace); cString(record, 401, 256, entry.home); digest.copy(record, 657); return record; });`,
|
|
158
|
+
"fs.mkdirSync('/etc/daimon-engine-broker', { recursive: true, mode: 0o700 }); fs.chownSync('/etc/daimon-engine-broker', 0, 0); fs.chmodSync('/etc/daimon-engine-broker', 0o700);",
|
|
159
|
+
"fs.writeFileSync('/etc/daimon-engine-broker/registrations.bin', Buffer.concat(records), { mode: 0o400, flag: 'wx' });",
|
|
160
|
+
"fs.chownSync('/etc/daimon-engine-broker/registrations.bin', 0, 0); fs.chmodSync('/etc/daimon-engine-broker/registrations.bin', 0o400);",
|
|
161
|
+
`fs.mkdirSync('${DAIMON_BROKER_REALM}', { recursive: true, mode: 0o700 }); fs.chownSync('${DAIMON_BROKER_REALM}', 0, 0); fs.chmodSync('${DAIMON_BROKER_REALM}', 0o700);`,
|
|
162
|
+
// The usage ledger directory itself is provisioned unconditionally by
|
|
163
|
+
// `renderDaimonUsageLedgerProvisioning` (every Daimon organization writes
|
|
164
|
+
// here, not just Grok ones) before this script's caller reaches the
|
|
165
|
+
// broker startup this function guards; this script only ever reads the
|
|
166
|
+
// path below, for the sandbox denylist.
|
|
167
|
+
`const bootstrap = '/var/lib/spawnfile/daimon/grok-bootstrap-auth', authority = '${DAIMON_BROKER_REALM}/auth.json';`,
|
|
168
|
+
"const readSecure = (file, owner, label) => { const before = fs.lstatSync(file); if (!before.isFile() || before.isSymbolicLink() || (owner !== undefined && (before.uid !== owner || before.gid !== owner)) || (before.mode & 0o777) !== 0o600 || before.nlink !== 1 || before.size < 2 || before.size > 65536) throw new Error(`unsafe broker credential ${label}`); const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK); try { const opened = fs.fstatSync(fd); if (opened.dev !== before.dev || opened.ino !== before.ino) throw new Error(`unsafe broker credential ${label}`); const bytes = Buffer.alloc(opened.size); let offset = 0; while (offset < bytes.length) { const count = fs.readSync(fd, bytes, offset, bytes.length - offset, offset); if (count < 1) throw new Error(`unsafe broker credential ${label}`); offset += count; } return bytes; } finally { fs.closeSync(fd); } };",
|
|
169
|
+
`const atomicOwned = (target, bytes) => { const temporary = \`${"${target}"}.\${process.pid}.\${crypto.randomUUID()}.tmp\`; try { const output = fs.openSync(temporary, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_NOFOLLOW, 0o600); try { let written = 0; while (written < bytes.length) written += fs.writeSync(output, bytes, written, bytes.length - written, written); fs.fchmodSync(output, 0o600); fs.fchownSync(output, 2100, 2100); fs.fsyncSync(output); } finally { fs.closeSync(output); } fs.renameSync(temporary, target); const directory = fs.openSync(require('node:path').dirname(target), fs.constants.O_RDONLY | fs.constants.O_DIRECTORY); try { fs.fsyncSync(directory); } finally { fs.closeSync(directory); } } catch (error) { try { fs.unlinkSync(temporary); } catch {} throw error; } };`,
|
|
170
|
+
"let existing; try { existing = fs.lstatSync(authority); } catch (error) { if (error.code !== 'ENOENT') throw error; } const bootstrapBytes = readSecure(bootstrap, undefined, 'bootstrap'); let bootstrapRecord; try { const root = JSON.parse(bootstrapBytes.toString('utf8')), rows = root && typeof root === 'object' && !Array.isArray(root) ? Object.entries(root).filter(([key, value]) => /^https:\\/\\/auth\\.x\\.ai::/.test(key) && value && typeof value === 'object' && !Array.isArray(value)).map(([, value]) => value) : []; if (rows.length !== 1 || typeof rows[0].key !== 'string' || !rows[0].key.trim() || typeof rows[0].refresh_token !== 'string' || !rows[0].refresh_token.trim() || typeof rows[0].expires_at !== 'string' || !Number.isFinite(Date.parse(rows[0].expires_at))) throw new Error(); bootstrapRecord = true; } catch { bootstrapBytes.fill(0); throw new Error('invalid broker credential bootstrap'); } if (!bootstrapRecord) throw new Error('invalid broker credential bootstrap'); const bootstrapDigest = crypto.createHash('sha256').update(bootstrapBytes).digest('hex');",
|
|
171
|
+
`const journalRoot = '${DAIMON_BROKER_REALM}/.daimon-broker'; let journalRootExists = false; try { const info = fs.lstatSync(journalRoot); if (!info.isDirectory() || info.isSymbolicLink() || info.uid !== 2100 || info.gid !== 2100 || (info.mode & 0o777) !== 0o700) throw new Error('unsafe broker credential journal directory'); fs.chownSync(journalRoot, 0, 0); fs.chmodSync(journalRoot, 0o700); journalRootExists = true; } catch (error) { if (error.code !== 'ENOENT') throw error; }`,
|
|
172
|
+
`try { const journalPath = '${DAIMON_BROKER_REALM}/.daimon-broker/credential-journal.json'; let journal; try { const raw = readSecure(journalPath, 2100, 'recovery journal'); journal = JSON.parse(raw.toString('utf8')); raw.fill(0); } catch (error) { if (error.code !== 'ENOENT') throw error; } const stale = journal?.version === 'noopolis.daimon.broker-credential-journal.v1' && journal.state === 'stale'; const recover = () => { if (!stale || !Number.isSafeInteger(journal.generation) || journal.generation < 0 || !/^[a-f0-9]{64}$/.test(journal.sourceDigest) || journal.sourceDigest !== journal.promotedDigest || bootstrapDigest === journal.sourceDigest) throw new Error('unsafe broker credential recovery'); atomicOwned(authority, bootstrapBytes); const recovered = Buffer.from(\`${"${JSON.stringify({ version: 'noopolis.daimon.broker-credential-journal.v1', state: 'promoted', generation: journal.generation + 1, sourceDigest: journal.sourceDigest, promotedDigest: bootstrapDigest })}"}\\n\`); try { atomicOwned(journalPath, recovered); } finally { recovered.fill(0); } }; if (!existing) { if (stale) recover(); else atomicOwned(authority, bootstrapBytes); } else { const authorityBytes = readSecure(authority, 2100, 'authority'); try { const authorityDigest = crypto.createHash('sha256').update(authorityBytes).digest('hex'); if (stale) { if (authorityDigest !== journal.sourceDigest && authorityDigest !== bootstrapDigest) throw new Error('unsafe broker credential recovery'); if (authorityDigest === bootstrapDigest) { const recovered = Buffer.from(\`${"${JSON.stringify({ version: 'noopolis.daimon.broker-credential-journal.v1', state: 'promoted', generation: journal.generation + 1, sourceDigest: journal.sourceDigest, promotedDigest: bootstrapDigest })}"}\\n\`); try { atomicOwned(journalPath, recovered); } finally { recovered.fill(0); } } else recover(); } } finally { authorityBytes.fill(0); } } } finally { bootstrapBytes.fill(0); }`,
|
|
173
|
+
"if (journalRootExists) { fs.chownSync(journalRoot, 0, 0); fs.chmodSync(journalRoot, 0o700); fs.chownSync(journalRoot, 2100, 2100); }",
|
|
174
|
+
...renderDaimonWorkspaceResourceSecurity(workspaceResources),
|
|
175
|
+
...renderDaimonGrokWorkerProvisioning(registrations, serviceOptions, ...(optionalDenyPaths ? [optionalDenyPaths] : [])),
|
|
176
|
+
`fs.chownSync('${DAIMON_BROKER_REALM}', 0, 0); fs.chmodSync('${DAIMON_BROKER_REALM}', 0o700); fs.chownSync('${DAIMON_BROKER_REALM}', 2100, 2100);`,
|
|
177
|
+
"fs.chmodSync('/etc/daimon-engine-broker', 0o555);"
|
|
178
|
+
].join("\n");
|
|
179
|
+
return [
|
|
180
|
+
"if [ -d /etc/daimon-engine-broker ]; then chmod u+rwx /etc/daimon-engine-broker; fi",
|
|
181
|
+
"if [ -d /run/daimon-engine-broker ]; then chmod u+rwx /run/daimon-engine-broker; fi",
|
|
182
|
+
...(rootReset === "remove"
|
|
183
|
+
? ["rm -rf /etc/daimon-engine-broker /run/daimon-engine-broker"]
|
|
184
|
+
: [...MOUNT_AWARE_CLEAR_HELPER, "for broker_root in /etc/daimon-engine-broker /run/daimon-engine-broker; do spawnfile_clear_tree \"$broker_root\"; done"]),
|
|
185
|
+
`install -d -o root -g ${DAIMON_BROKER_UID} -m 0731 /run/daimon-engine-broker`,
|
|
186
|
+
`install -d -o ${DAIMON_BROKER_UID} -g ${DAIMON_BROKER_UID} -m 0700 ${brokerTmpdir}`,
|
|
187
|
+
"node <<'SPAWNFILE_DAIMON_BROKER_PROVISION'",
|
|
188
|
+
program,
|
|
189
|
+
"SPAWNFILE_DAIMON_BROKER_PROVISION"
|
|
190
|
+
];
|
|
191
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type DaimonGrokRegistration, type DaimonGrokServiceConfigOptions } from "./containerDaimonGrokWorkerRender.js";
|
|
2
|
+
/**
|
|
3
|
+
* Root provisioning for every brokered Grok worker, as lines of the broker's
|
|
4
|
+
* node provisioning program (which already defines `crypto`, `fs`, and
|
|
5
|
+
* `secureWorkspace`).
|
|
6
|
+
*
|
|
7
|
+
* Layout per Daimon's `GROK_ENGINE_BROKER.worker.home` (attested by the broker
|
|
8
|
+
* before every turn): `$GROK_HOME` (`<home>/.grok`) and `$GROK_HOME/sessions`
|
|
9
|
+
* are `root:<worker> 1771`; `config.toml` (Daimon's renderer bytes for the
|
|
10
|
+
* declared model x effort), `sandbox.toml`, and the empty `trusted_folders.toml`,
|
|
11
|
+
* `managed_config.toml` and `requirements.toml` are `root:root 0444`, so the
|
|
12
|
+
* worker can neither write, rename nor unlink any file that decides a turn —
|
|
13
|
+
* trust included; `sessions/sandbox-events.jsonl` is `<worker>:<broker> 0640`.
|
|
14
|
+
*
|
|
15
|
+
* Every path handed to Daimon (workspace, home, profile, events, and each deny
|
|
16
|
+
* entry) must be canonical: it exists, is not a symlink, and resolves to itself.
|
|
17
|
+
* Daimon never resolves these paths, so a symlinked entry would silently mask
|
|
18
|
+
* the wrong inode. Root chmods only paths it owns at that moment (no
|
|
19
|
+
* `CAP_FOWNER`): reclaim, mode, then hand over.
|
|
20
|
+
*/
|
|
21
|
+
export declare const renderDaimonGrokWorkerProvisioning: (registrations: readonly DaimonGrokRegistration[], serviceOptions?: DaimonGrokServiceConfigOptions,
|
|
22
|
+
/**
|
|
23
|
+
* Deny targets this program creates root-owned `0700` when they are absent,
|
|
24
|
+
* so every mask always has an inode. The default is the production
|
|
25
|
+
* container's set; the training container passes its own, because its roots
|
|
26
|
+
* are tmpfs mounts and `/run/secrets` and the shared state roots do not
|
|
27
|
+
* exist there at all.
|
|
28
|
+
*/
|
|
29
|
+
optionalDenyPaths?: readonly string[]) => string[];
|
|
30
|
+
/**
|
|
31
|
+
* Grok 1.0.34 runs every sandbox profile inside bubblewrap, which needs
|
|
32
|
+
* unprivileged user namespaces. Ubuntu 24.04+ hosts (and Colima's default VM)
|
|
33
|
+
* ship `kernel.apparmor_restrict_unprivileged_userns=1`, under which bubblewrap
|
|
34
|
+
* cannot create them even with the pinned seccomp profile — every worker turn
|
|
35
|
+
* would then fail attestation long after startup. The container sees the host
|
|
36
|
+
* kernel's sysctls read-only, so this refuses to start with the fix named.
|
|
37
|
+
*/
|
|
38
|
+
export declare const renderDaimonGrokHostPreflight: (procSys?: string) => string[];
|