@noopolis/daimon 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +307 -45
- package/dist/contracts/attentionContract.d.ts +117 -0
- package/dist/contracts/attentionContract.d.ts.map +1 -0
- package/dist/contracts/attentionContract.js +14 -0
- package/dist/contracts/canonicalJson.d.ts +3 -0
- package/dist/contracts/canonicalJson.d.ts.map +1 -0
- package/dist/contracts/canonicalJson.js +54 -0
- package/dist/contracts/grokWorkerContract.d.ts +74 -0
- package/dist/contracts/grokWorkerContract.d.ts.map +1 -0
- package/dist/contracts/grokWorkerContract.js +88 -0
- package/dist/contracts/organizationRuntimeContract.d.ts +765 -0
- package/dist/contracts/organizationRuntimeContract.d.ts.map +1 -0
- package/dist/contracts/organizationRuntimeContract.js +121 -0
- package/dist/contracts/runtimeContractManifest.d.ts +1615 -0
- package/dist/contracts/runtimeContractManifest.d.ts.map +1 -0
- package/dist/contracts/runtimeContractManifest.js +181 -0
- package/dist/core/credentialRedaction.d.ts +4 -0
- package/dist/core/credentialRedaction.d.ts.map +1 -0
- package/dist/core/credentialRedaction.js +28 -0
- package/dist/core/siblingBuildFreshness.d.ts +21 -0
- package/dist/core/siblingBuildFreshness.d.ts.map +1 -0
- package/dist/core/siblingBuildFreshness.js +25 -0
- package/dist/core/types.d.ts +10 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/examples/exampleCausalId.d.ts +3 -0
- package/dist/examples/exampleCausalId.d.ts.map +1 -0
- package/dist/examples/exampleCausalId.js +8 -0
- package/dist/examples/jungian-play-org.js +3 -2
- package/dist/examples/jungian-triad-org.js +3 -2
- package/dist/examples/jungianPlayAgent.d.ts +1 -1
- package/dist/examples/jungianPlayAgent.d.ts.map +1 -1
- package/dist/examples/jungianPlayAgent.js +1 -1
- package/dist/examples/jungianProfiles.d.ts +1 -1
- package/dist/examples/jungianProfiles.d.ts.map +1 -1
- package/dist/examples/jungianTriadProfiles.d.ts +1 -1
- package/dist/examples/jungianTriadProfiles.d.ts.map +1 -1
- package/dist/examples/pi-agent.js +3 -2
- package/dist/examples/pi-memory-org.js +6 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/mcp/toolServer.d.ts +31 -0
- package/dist/mcp/toolServer.d.ts.map +1 -0
- package/dist/mcp/toolServer.js +145 -0
- package/dist/observability/causalEvents.d.ts +118 -0
- package/dist/observability/causalEvents.d.ts.map +1 -0
- package/dist/observability/causalEvents.js +223 -0
- package/dist/observability/controlCausal.d.ts +117 -0
- package/dist/observability/controlCausal.d.ts.map +1 -0
- package/dist/observability/controlCausal.js +107 -0
- package/dist/observability/emitCausalFixture.d.ts +25 -0
- package/dist/observability/emitCausalFixture.d.ts.map +1 -0
- package/dist/observability/emitCausalFixture.js +98 -0
- package/dist/observability/index.d.ts +2 -0
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +2 -0
- package/dist/observability/orgObserver.d.ts.map +1 -1
- package/dist/observability/orgObserver.js +3 -3
- package/dist/pi/agyHeadlessResult.d.ts +60 -0
- package/dist/pi/agyHeadlessResult.d.ts.map +1 -0
- package/dist/pi/agyHeadlessResult.js +95 -0
- package/dist/pi/cliChildOutput.d.ts +67 -0
- package/dist/pi/cliChildOutput.d.ts.map +1 -0
- package/dist/pi/cliChildOutput.js +269 -0
- package/dist/pi/cliEngineRun.d.ts +16 -0
- package/dist/pi/cliEngineRun.d.ts.map +1 -0
- package/dist/pi/cliEngineRun.js +27 -0
- package/dist/pi/cliEngineSpawn.d.ts +55 -0
- package/dist/pi/cliEngineSpawn.d.ts.map +1 -0
- package/dist/pi/cliEngineSpawn.js +164 -0
- package/dist/pi/cliEnvironment.d.ts +12 -0
- package/dist/pi/cliEnvironment.d.ts.map +1 -0
- package/dist/pi/cliEnvironment.js +32 -0
- package/dist/pi/cliMcpRegistration.d.ts +61 -0
- package/dist/pi/cliMcpRegistration.d.ts.map +1 -0
- package/dist/pi/cliMcpRegistration.js +75 -0
- package/dist/pi/cliProcess.d.ts +7 -0
- package/dist/pi/cliProcess.d.ts.map +1 -0
- package/dist/pi/cliProcess.js +132 -0
- package/dist/pi/cliSession.d.ts +106 -0
- package/dist/pi/cliSession.d.ts.map +1 -0
- package/dist/pi/cliSession.js +379 -0
- package/dist/pi/cliTurnMetering.d.ts +60 -0
- package/dist/pi/cliTurnMetering.d.ts.map +1 -0
- package/dist/pi/cliTurnMetering.js +60 -0
- package/dist/pi/codexFilesystemRules.d.ts +3 -0
- package/dist/pi/codexFilesystemRules.d.ts.map +1 -0
- package/dist/pi/codexFilesystemRules.js +28 -0
- package/dist/pi/codexHeadlessResult.d.ts +68 -0
- package/dist/pi/codexHeadlessResult.d.ts.map +1 -0
- package/dist/pi/codexHeadlessResult.js +122 -0
- package/dist/pi/codexRolloutUsage.d.ts +95 -0
- package/dist/pi/codexRolloutUsage.d.ts.map +1 -0
- package/dist/pi/codexRolloutUsage.js +221 -0
- package/dist/pi/engineWakeLimits.d.ts +39 -0
- package/dist/pi/engineWakeLimits.d.ts.map +1 -0
- package/dist/pi/engineWakeLimits.js +55 -0
- package/dist/pi/grokHeadlessResult.d.ts +36 -0
- package/dist/pi/grokHeadlessResult.d.ts.map +1 -0
- package/dist/pi/grokHeadlessResult.js +118 -0
- package/dist/pi/grokHomeMcpRegistration.d.ts +21 -0
- package/dist/pi/grokHomeMcpRegistration.d.ts.map +1 -0
- package/dist/pi/grokHomeMcpRegistration.js +57 -0
- package/dist/pi/grokSandbox.d.ts +11 -0
- package/dist/pi/grokSandbox.d.ts.map +1 -0
- package/dist/pi/grokSandbox.js +216 -0
- package/dist/pi/grokStreamUsage.d.ts +31 -0
- package/dist/pi/grokStreamUsage.d.ts.map +1 -0
- package/dist/pi/grokStreamUsage.js +51 -0
- package/dist/pi/index.d.ts +5 -0
- package/dist/pi/index.d.ts.map +1 -1
- package/dist/pi/index.js +5 -0
- package/dist/pi/memoryTools.d.ts +55 -1
- package/dist/pi/memoryTools.d.ts.map +1 -1
- package/dist/pi/memoryTools.js +144 -24
- package/dist/pi/modelRegistry.d.ts +13 -0
- package/dist/pi/modelRegistry.d.ts.map +1 -0
- package/dist/pi/modelRegistry.js +19 -0
- package/dist/pi/piAgentHandle.d.ts +46 -0
- package/dist/pi/piAgentHandle.d.ts.map +1 -0
- package/dist/pi/piAgentHandle.js +351 -0
- package/dist/pi/piAgentWakeSupport.d.ts +103 -0
- package/dist/pi/piAgentWakeSupport.d.ts.map +1 -0
- package/dist/pi/piAgentWakeSupport.js +151 -0
- package/dist/pi/piHarness.d.ts +40 -5
- package/dist/pi/piHarness.d.ts.map +1 -1
- package/dist/pi/piHarness.js +151 -256
- package/dist/pi/prompts.d.ts +10 -0
- package/dist/pi/prompts.d.ts.map +1 -0
- package/dist/pi/prompts.js +63 -0
- package/dist/pi/rawTrainingCapture.d.ts +59 -0
- package/dist/pi/rawTrainingCapture.d.ts.map +1 -0
- package/dist/pi/rawTrainingCapture.js +175 -0
- package/dist/pi/turnCausal.d.ts +73 -0
- package/dist/pi/turnCausal.d.ts.map +1 -0
- package/dist/pi/turnCausal.js +75 -0
- package/dist/pi/turnTrace.d.ts +128 -0
- package/dist/pi/turnTrace.d.ts.map +1 -0
- package/dist/pi/turnTrace.js +138 -0
- package/dist/pi/wakeAcceptance.d.ts +49 -0
- package/dist/pi/wakeAcceptance.d.ts.map +1 -0
- package/dist/pi/wakeAcceptance.js +281 -0
- package/dist/pi/wakeAcceptanceFs.d.ts +44 -0
- package/dist/pi/wakeAcceptanceFs.d.ts.map +1 -0
- package/dist/pi/wakeAcceptanceFs.js +318 -0
- package/dist/pi/wakeAcceptanceSchema.d.ts +75 -0
- package/dist/pi/wakeAcceptanceSchema.d.ts.map +1 -0
- package/dist/pi/wakeAcceptanceSchema.js +265 -0
- package/dist/pi/wakeModes.d.ts +9 -0
- package/dist/pi/wakeModes.d.ts.map +1 -0
- package/dist/pi/wakeModes.js +19 -0
- package/dist/pi/worldNudge.d.ts +22 -0
- package/dist/pi/worldNudge.d.ts.map +1 -0
- package/dist/pi/worldNudge.js +62 -0
- package/dist/pi/worldToolProtocol.d.ts +13 -0
- package/dist/pi/worldToolProtocol.d.ts.map +1 -0
- package/dist/pi/worldToolProtocol.js +49 -0
- package/dist/pi/worldTools.d.ts +32 -0
- package/dist/pi/worldTools.d.ts.map +1 -0
- package/dist/pi/worldTools.js +373 -0
- package/dist/pi/worldTrajectory.d.ts +52 -0
- package/dist/pi/worldTrajectory.d.ts.map +1 -0
- package/dist/pi/worldTrajectory.js +131 -0
- package/dist/runtime/agySubscriptionBootstrap.d.ts +16 -0
- package/dist/runtime/agySubscriptionBootstrap.d.ts.map +1 -0
- package/dist/runtime/agySubscriptionBootstrap.js +80 -0
- package/dist/runtime/agySubscriptionRealm.d.ts +38 -0
- package/dist/runtime/agySubscriptionRealm.d.ts.map +1 -0
- package/dist/runtime/agySubscriptionRealm.js +242 -0
- package/dist/runtime/attention.d.ts +21 -0
- package/dist/runtime/attention.d.ts.map +1 -0
- package/dist/runtime/attention.js +36 -0
- package/dist/runtime/attentionDispatcher.d.ts +66 -0
- package/dist/runtime/attentionDispatcher.d.ts.map +1 -0
- package/dist/runtime/attentionDispatcher.js +271 -0
- package/dist/runtime/cli.d.ts +7 -0
- package/dist/runtime/cli.d.ts.map +1 -0
- package/dist/runtime/cli.js +251 -0
- package/dist/runtime/codexSandboxProjection.d.ts +17 -0
- package/dist/runtime/codexSandboxProjection.d.ts.map +1 -0
- package/dist/runtime/codexSandboxProjection.js +40 -0
- package/dist/runtime/contract-manifest.json +1 -0
- package/dist/runtime/contract-manifest.sha256 +1 -0
- package/dist/runtime/contractManifest.d.ts +8 -0
- package/dist/runtime/contractManifest.d.ts.map +1 -0
- package/dist/runtime/contractManifest.js +4 -0
- package/dist/runtime/engineBrokerCapabilities.d.ts +30 -0
- package/dist/runtime/engineBrokerCapabilities.d.ts.map +1 -0
- package/dist/runtime/engineBrokerCapabilities.js +59 -0
- package/dist/runtime/engineBrokerControlClient.d.ts +44 -0
- package/dist/runtime/engineBrokerControlClient.d.ts.map +1 -0
- package/dist/runtime/engineBrokerControlClient.js +132 -0
- package/dist/runtime/engineBrokerCredentialJournal.d.ts +10 -0
- package/dist/runtime/engineBrokerCredentialJournal.d.ts.map +1 -0
- package/dist/runtime/engineBrokerCredentialJournal.js +17 -0
- package/dist/runtime/engineBrokerInferenceProtocol.d.ts +52 -0
- package/dist/runtime/engineBrokerInferenceProtocol.d.ts.map +1 -0
- package/dist/runtime/engineBrokerInferenceProtocol.js +65 -0
- package/dist/runtime/engineBrokerMcpCallLog.d.ts +160 -0
- package/dist/runtime/engineBrokerMcpCallLog.d.ts.map +1 -0
- package/dist/runtime/engineBrokerMcpCallLog.js +219 -0
- package/dist/runtime/engineBrokerMcpFacade.d.ts +56 -0
- package/dist/runtime/engineBrokerMcpFacade.d.ts.map +1 -0
- package/dist/runtime/engineBrokerMcpFacade.js +343 -0
- package/dist/runtime/engineBrokerNativeClient.d.ts +52 -0
- package/dist/runtime/engineBrokerNativeClient.d.ts.map +1 -0
- package/dist/runtime/engineBrokerNativeClient.js +151 -0
- package/dist/runtime/engineBrokerProtocol.d.ts +135 -0
- package/dist/runtime/engineBrokerProtocol.d.ts.map +1 -0
- package/dist/runtime/engineBrokerProtocol.js +274 -0
- package/dist/runtime/engineBrokerSealLedger.d.ts +70 -0
- package/dist/runtime/engineBrokerSealLedger.d.ts.map +1 -0
- package/dist/runtime/engineBrokerSealLedger.js +123 -0
- package/dist/runtime/engineBrokerService.d.ts +37 -0
- package/dist/runtime/engineBrokerService.d.ts.map +1 -0
- package/dist/runtime/engineBrokerService.js +138 -0
- package/dist/runtime/engineBrokerServiceCli.d.ts +5 -0
- package/dist/runtime/engineBrokerServiceCli.d.ts.map +1 -0
- package/dist/runtime/engineBrokerServiceCli.js +32 -0
- package/dist/runtime/engineBrokerServiceConfig.d.ts +50 -0
- package/dist/runtime/engineBrokerServiceConfig.d.ts.map +1 -0
- package/dist/runtime/engineBrokerServiceConfig.js +92 -0
- package/dist/runtime/engineBrokerSingleflight.d.ts +13 -0
- package/dist/runtime/engineBrokerSingleflight.d.ts.map +1 -0
- package/dist/runtime/engineBrokerSingleflight.js +24 -0
- package/dist/runtime/engineBrokerTurnAccounting.d.ts +76 -0
- package/dist/runtime/engineBrokerTurnAccounting.d.ts.map +1 -0
- package/dist/runtime/engineBrokerTurnAccounting.js +108 -0
- package/dist/runtime/engineBrokerTurnRegistry.d.ts +57 -0
- package/dist/runtime/engineBrokerTurnRegistry.d.ts.map +1 -0
- package/dist/runtime/engineBrokerTurnRegistry.js +158 -0
- package/dist/runtime/engineDispatcher.d.ts +36 -0
- package/dist/runtime/engineDispatcher.d.ts.map +1 -0
- package/dist/runtime/engineDispatcher.js +216 -0
- package/dist/runtime/engineReadiness.d.ts +23 -0
- package/dist/runtime/engineReadiness.d.ts.map +1 -0
- package/dist/runtime/engineReadiness.js +222 -0
- package/dist/runtime/grokAuthenticationError.d.ts +8 -0
- package/dist/runtime/grokAuthenticationError.d.ts.map +1 -0
- package/dist/runtime/grokAuthenticationError.js +20 -0
- package/dist/runtime/grokBrokerCredentialAuthority.d.ts +22 -0
- package/dist/runtime/grokBrokerCredentialAuthority.d.ts.map +1 -0
- package/dist/runtime/grokBrokerCredentialAuthority.js +124 -0
- package/dist/runtime/grokBrokerCredentialReader.d.ts +5 -0
- package/dist/runtime/grokBrokerCredentialReader.d.ts.map +1 -0
- package/dist/runtime/grokBrokerCredentialReader.js +37 -0
- package/dist/runtime/grokBrokerModelPolicy.d.ts +23 -0
- package/dist/runtime/grokBrokerModelPolicy.d.ts.map +1 -0
- package/dist/runtime/grokBrokerModelPolicy.js +28 -0
- package/dist/runtime/grokBrokerProjection.d.ts +106 -0
- package/dist/runtime/grokBrokerProjection.d.ts.map +1 -0
- package/dist/runtime/grokBrokerProjection.js +94 -0
- package/dist/runtime/grokBrokerProxy.d.ts +55 -0
- package/dist/runtime/grokBrokerProxy.d.ts.map +1 -0
- package/dist/runtime/grokBrokerProxy.js +262 -0
- package/dist/runtime/grokBrokerProxyRequest.d.ts +30 -0
- package/dist/runtime/grokBrokerProxyRequest.d.ts.map +1 -0
- package/dist/runtime/grokBrokerProxyRequest.js +74 -0
- package/dist/runtime/grokBrokerRealmLease.d.ts +4 -0
- package/dist/runtime/grokBrokerRealmLease.d.ts.map +1 -0
- package/dist/runtime/grokBrokerRealmLease.js +33 -0
- package/dist/runtime/grokBrokerRefresh.d.ts +2 -0
- package/dist/runtime/grokBrokerRefresh.d.ts.map +1 -0
- package/dist/runtime/grokBrokerRefresh.js +15 -0
- package/dist/runtime/grokBrokerTurnMeter.d.ts +128 -0
- package/dist/runtime/grokBrokerTurnMeter.d.ts.map +1 -0
- package/dist/runtime/grokBrokerTurnMeter.js +251 -0
- package/dist/runtime/grokBrokerWorkerConfig.d.ts +86 -0
- package/dist/runtime/grokBrokerWorkerConfig.d.ts.map +1 -0
- package/dist/runtime/grokBrokerWorkerConfig.js +135 -0
- package/dist/runtime/grokEngineBroker.d.ts +48 -0
- package/dist/runtime/grokEngineBroker.d.ts.map +1 -0
- package/dist/runtime/grokEngineBroker.js +114 -0
- package/dist/runtime/grokEngineBrokerLedger.d.ts +52 -0
- package/dist/runtime/grokEngineBrokerLedger.d.ts.map +1 -0
- package/dist/runtime/grokEngineBrokerLedger.js +114 -0
- package/dist/runtime/grokEngineBrokerMetering.d.ts +51 -0
- package/dist/runtime/grokEngineBrokerMetering.d.ts.map +1 -0
- package/dist/runtime/grokEngineBrokerMetering.js +34 -0
- package/dist/runtime/grokEngineBrokerTurn.d.ts +58 -0
- package/dist/runtime/grokEngineBrokerTurn.d.ts.map +1 -0
- package/dist/runtime/grokEngineBrokerTurn.js +165 -0
- package/dist/runtime/grokInferenceClientConfig.d.ts +41 -0
- package/dist/runtime/grokInferenceClientConfig.d.ts.map +1 -0
- package/dist/runtime/grokInferenceClientConfig.js +62 -0
- package/dist/runtime/grokInferenceGrants.d.ts +77 -0
- package/dist/runtime/grokInferenceGrants.d.ts.map +1 -0
- package/dist/runtime/grokInferenceGrants.js +116 -0
- package/dist/runtime/grokInferenceProxy.d.ts +29 -0
- package/dist/runtime/grokInferenceProxy.d.ts.map +1 -0
- package/dist/runtime/grokInferenceProxy.js +82 -0
- package/dist/runtime/grokInferenceProxyRequest.d.ts +23 -0
- package/dist/runtime/grokInferenceProxyRequest.d.ts.map +1 -0
- package/dist/runtime/grokInferenceProxyRequest.js +63 -0
- package/dist/runtime/grokSlotPreflightReceipt.d.ts +70 -0
- package/dist/runtime/grokSlotPreflightReceipt.d.ts.map +1 -0
- package/dist/runtime/grokSlotPreflightReceipt.js +101 -0
- package/dist/runtime/grokSubscriptionRealm.d.ts +4 -0
- package/dist/runtime/grokSubscriptionRealm.d.ts.map +1 -0
- package/dist/runtime/grokSubscriptionRealm.js +435 -0
- package/dist/runtime/grokSubscriptionRealmTypes.d.ts +26 -0
- package/dist/runtime/grokSubscriptionRealmTypes.d.ts.map +1 -0
- package/dist/runtime/grokSubscriptionRealmTypes.js +1 -0
- package/dist/runtime/grokWorkerAttestation.d.ts +170 -0
- package/dist/runtime/grokWorkerAttestation.d.ts.map +1 -0
- package/dist/runtime/grokWorkerAttestation.js +266 -0
- package/dist/runtime/grokWorkerDenyPlacement.d.ts +80 -0
- package/dist/runtime/grokWorkerDenyPlacement.d.ts.map +1 -0
- package/dist/runtime/grokWorkerDenyPlacement.js +128 -0
- package/dist/runtime/grokWorkerHomeAttestation.d.ts +30 -0
- package/dist/runtime/grokWorkerHomeAttestation.d.ts.map +1 -0
- package/dist/runtime/grokWorkerHomeAttestation.js +69 -0
- package/dist/runtime/grokWorkerSandboxProfile.d.ts +27 -0
- package/dist/runtime/grokWorkerSandboxProfile.d.ts.map +1 -0
- package/dist/runtime/grokWorkerSandboxProfile.js +44 -0
- package/dist/runtime/grokWorkerTmpAttestation.d.ts +55 -0
- package/dist/runtime/grokWorkerTmpAttestation.d.ts.map +1 -0
- package/dist/runtime/grokWorkerTmpAttestation.js +63 -0
- package/dist/runtime/index.d.ts +26 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +23 -0
- package/dist/runtime/inferenceUsageLedger.d.ts +38 -0
- package/dist/runtime/inferenceUsageLedger.d.ts.map +1 -0
- package/dist/runtime/inferenceUsageLedger.js +53 -0
- package/dist/runtime/mcpToolResult.d.ts +127 -0
- package/dist/runtime/mcpToolResult.d.ts.map +1 -0
- package/dist/runtime/mcpToolResult.js +214 -0
- package/dist/runtime/moltnetMachineRead.d.ts +97 -0
- package/dist/runtime/moltnetMachineRead.d.ts.map +1 -0
- package/dist/runtime/moltnetMachineRead.js +183 -0
- package/dist/runtime/native/daimon-engine-broker +0 -0
- package/dist/runtime/organizationRuntime.d.ts +165 -0
- package/dist/runtime/organizationRuntime.d.ts.map +1 -0
- package/dist/runtime/organizationRuntime.js +3 -0
- package/dist/runtime/organizationRuntimeControl.d.ts +42 -0
- package/dist/runtime/organizationRuntimeControl.d.ts.map +1 -0
- package/dist/runtime/organizationRuntimeControl.js +235 -0
- package/dist/runtime/organizationRuntimeHost.d.ts +19 -0
- package/dist/runtime/organizationRuntimeHost.d.ts.map +1 -0
- package/dist/runtime/organizationRuntimeHost.js +359 -0
- package/dist/runtime/organizationRuntimeParsing.d.ts +6 -0
- package/dist/runtime/organizationRuntimeParsing.d.ts.map +1 -0
- package/dist/runtime/organizationRuntimeParsing.js +370 -0
- package/dist/runtime/organizationRuntimeReadiness.d.ts +12 -0
- package/dist/runtime/organizationRuntimeReadiness.d.ts.map +1 -0
- package/dist/runtime/organizationRuntimeReadiness.js +59 -0
- package/dist/runtime/physicalReadiness.d.ts +47 -0
- package/dist/runtime/physicalReadiness.d.ts.map +1 -0
- package/dist/runtime/physicalReadiness.js +142 -0
- package/dist/runtime/portableCredentialAuth.d.ts +6 -0
- package/dist/runtime/portableCredentialAuth.d.ts.map +1 -0
- package/dist/runtime/portableCredentialAuth.js +70 -0
- package/dist/runtime/portableCredentialMaterial.d.ts +8 -0
- package/dist/runtime/portableCredentialMaterial.d.ts.map +1 -0
- package/dist/runtime/portableCredentialMaterial.js +169 -0
- package/dist/runtime/productionAgentTools.d.ts +5 -0
- package/dist/runtime/productionAgentTools.d.ts.map +1 -0
- package/dist/runtime/productionAgentTools.js +328 -0
- package/dist/runtime/runtimeHomeLayout.d.ts +48 -0
- package/dist/runtime/runtimeHomeLayout.d.ts.map +1 -0
- package/dist/runtime/runtimeHomeLayout.js +84 -0
- package/dist/runtime/schedule.d.ts +40 -0
- package/dist/runtime/schedule.d.ts.map +1 -0
- package/dist/runtime/schedule.js +371 -0
- package/dist/runtime/storeCoordination.d.ts +23 -0
- package/dist/runtime/storeCoordination.d.ts.map +1 -0
- package/dist/runtime/storeCoordination.js +94 -0
- package/dist/runtime/toolResultSpill.d.ts +152 -0
- package/dist/runtime/toolResultSpill.d.ts.map +1 -0
- package/dist/runtime/toolResultSpill.js +351 -0
- package/dist/runtime/turnRequestLedger.d.ts +160 -0
- package/dist/runtime/turnRequestLedger.d.ts.map +1 -0
- package/dist/runtime/turnRequestLedger.js +249 -0
- package/dist/runtime/turnUsageLedger.d.ts +143 -0
- package/dist/runtime/turnUsageLedger.d.ts.map +1 -0
- package/dist/runtime/turnUsageLedger.js +203 -0
- package/dist/runtime/wakeAcceptanceProcessIdentity.d.ts +17 -0
- package/dist/runtime/wakeAcceptanceProcessIdentity.d.ts.map +1 -0
- package/dist/runtime/wakeAcceptanceProcessIdentity.js +50 -0
- package/dist/runtime/wakeAcceptanceReconciliation.d.ts +211 -0
- package/dist/runtime/wakeAcceptanceReconciliation.d.ts.map +1 -0
- package/dist/runtime/wakeAcceptanceReconciliation.js +289 -0
- package/dist/runtime/wakeAcceptanceRecord.d.ts +23 -0
- package/dist/runtime/wakeAcceptanceRecord.d.ts.map +1 -0
- package/dist/runtime/wakeAcceptanceRecord.js +56 -0
- package/dist/runtime/wakeAcceptanceRetention.d.ts +12 -0
- package/dist/runtime/wakeAcceptanceRetention.d.ts.map +1 -0
- package/dist/runtime/wakeAcceptanceRetention.js +12 -0
- package/dist/runtime/wakeAcceptanceStore.d.ts +113 -0
- package/dist/runtime/wakeAcceptanceStore.d.ts.map +1 -0
- package/dist/runtime/wakeAcceptanceStore.js +558 -0
- package/dist/runtime/wakeAcceptanceTypes.d.ts +179 -0
- package/dist/runtime/wakeAcceptanceTypes.d.ts.map +1 -0
- package/dist/runtime/wakeAcceptanceTypes.js +95 -0
- package/dist/runtime/wakeFuse.d.ts +69 -0
- package/dist/runtime/wakeFuse.d.ts.map +1 -0
- package/dist/runtime/wakeFuse.js +367 -0
- package/package.json +25 -17
- package/dist/examples/mixed-engine-org.d.ts +0 -2
- package/dist/examples/mixed-engine-org.d.ts.map +0 -1
- package/dist/examples/mixed-engine-org.js +0 -351
- package/dist/examples/mixedEngineCli.d.ts +0 -15
- package/dist/examples/mixedEngineCli.d.ts.map +0 -1
- package/dist/examples/mixedEngineCli.js +0 -174
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { lstat, open } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { assertGrokWorkerDenyPathsPlaceable } from "./grokWorkerDenyPlacement.js";
|
|
6
|
+
import { verifyGrokWorkerHome } from "./grokWorkerHomeAttestation.js";
|
|
7
|
+
import { grokWorkerHomeForProfile, verifyGrokWorkerTmp } from "./grokWorkerTmpAttestation.js";
|
|
8
|
+
import { grokWorkerEventsPathFor } from "./grokWorkerSandboxProfile.js";
|
|
9
|
+
export class GrokWorkerAttestationFailure extends Error {
|
|
10
|
+
failureClass;
|
|
11
|
+
constructor(failureClass) {
|
|
12
|
+
super("Grok worker isolation attestation unavailable");
|
|
13
|
+
this.failureClass = failureClass;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Reads the `deny` list out of a worker sandbox profile, but only after the
|
|
18
|
+
* bytes match `profileSha256` exactly.
|
|
19
|
+
*
|
|
20
|
+
* There is no minimum length, and a populated list is supported. Grok 1.0.13
|
|
21
|
+
* refused to start on any non-empty `deny` (its mode-000 placeholders failed
|
|
22
|
+
* an EACCES "__GROK_INSIDE_BWRAP spoof" check), so deployments rendered
|
|
23
|
+
* `deny = []`. Grok 1.0.34 runs every profile inside bubblewrap and enforces a
|
|
24
|
+
* non-empty list, and since its strict base reads all of `/run`, `/var` and
|
|
25
|
+
* `/tmp`, the deny list is what keeps evaluator and host-bind paths away from
|
|
26
|
+
* the worker. `grokWorkerSandboxProfile.ts` renders these bytes.
|
|
27
|
+
*
|
|
28
|
+
* The integrity guarantee is the hash pin, not the length. `profileSha256`
|
|
29
|
+
* comes from `/etc/daimon-engine-broker/service.json`, which the root
|
|
30
|
+
* provisioning phase writes `0440 root:2100` with `flag: 'wx'` and computes
|
|
31
|
+
* from the same `profileFor()` bytes it writes to the profile; the broker
|
|
32
|
+
* reads that config as uid 2100 and cannot rewrite it. A weaker profile
|
|
33
|
+
* therefore cannot be attested: changing any byte — dropping
|
|
34
|
+
* `restrict_network`, swapping `extends = "strict"` for a permissive base,
|
|
35
|
+
* renaming the profile — changes the digest and fails here. And the deny list
|
|
36
|
+
* this returns is not merely parsed and discarded: it is carried into the
|
|
37
|
+
* snapshot and re-asserted against the kernel's own `ProfileApplied`
|
|
38
|
+
* `deny_paths` by `parseGrokWorkerProfileApplied`, which independently
|
|
39
|
+
* requires `enforced`, `restrict_network`, `platform === "linux/landlock"`
|
|
40
|
+
* and the exact workspace. A length floor would only have caught a Spawnfile
|
|
41
|
+
* rendering bug that produced a trivial profile *and* pinned it consistently
|
|
42
|
+
* — and Spawnfile's own tests pin the rendered bytes.
|
|
43
|
+
*/
|
|
44
|
+
export function parseGrokWorkerSandboxProfile(bytes, profileSha256) {
|
|
45
|
+
// Never copy the caller's bytes: `prepareGrokWorkerAttestation` zeroes the
|
|
46
|
+
// buffer it owns, and a private copy here would survive that.
|
|
47
|
+
const buffer = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
48
|
+
if (createHash("sha256").update(buffer).digest("hex") !== profileSha256)
|
|
49
|
+
throw new Error("Grok worker isolation attestation unavailable");
|
|
50
|
+
const line = buffer.toString("utf8").split("\n").find((entry) => entry.startsWith("deny = "));
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = JSON.parse(line?.slice(7) ?? "null");
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new Error("Grok worker isolation attestation unavailable");
|
|
57
|
+
}
|
|
58
|
+
if (!Array.isArray(parsed) || parsed.some((entry) => typeof entry !== "string") || new Set(parsed).size !== parsed.length)
|
|
59
|
+
throw new Error("Grok worker isolation attestation unavailable");
|
|
60
|
+
return [...parsed].sort();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Pre-launch half of the per-turn attestation. Besides the pinned profile and
|
|
64
|
+
* the events watermark it requires the 1.0.34 layout: events under
|
|
65
|
+
* `$GROK_HOME/sessions/` (the root `sandbox-events.jsonl` stays empty on
|
|
66
|
+
* 1.0.34), and a root-owned read-only worker home whose `config.toml` hashes to
|
|
67
|
+
* the declared renderer output (`grokWorkerHomeAttestation.ts`).
|
|
68
|
+
*
|
|
69
|
+
* It also refuses a deny list bubblewrap could not materialize
|
|
70
|
+
* (`grokWorkerDenyPlacement.ts`), naming the entry and the ancestor that stops
|
|
71
|
+
* it. Without this the worker dies with a bare `bwrap: Can't create file at
|
|
72
|
+
* …: Permission denied` on *every* turn, because one unplaceable entry makes
|
|
73
|
+
* Grok refuse the whole profile. The broker cannot descend into a directory
|
|
74
|
+
* opened to the worker's group alone (`<runtime home>/tool-state` under a
|
|
75
|
+
* `2000:<worker> 0710` home), so an `EACCES` there is left undecided; root
|
|
76
|
+
* provisioning, which holds `CAP_DAC_READ_SEARCH`, is the authority that
|
|
77
|
+
* decides every entry.
|
|
78
|
+
*/
|
|
79
|
+
export async function prepareGrokWorkerAttestation(input, profileOwner = { uid: 0, gid: 0 }) {
|
|
80
|
+
if (input.eventsPath !== grokWorkerEventsPathFor(input.profilePath))
|
|
81
|
+
throw new Error("Grok worker sandbox events must be read from $GROK_HOME/sessions/sandbox-events.jsonl");
|
|
82
|
+
const profile = await secureOpen(input.profilePath, profileOwner.uid, profileOwner.gid, 0o444, 65_536);
|
|
83
|
+
let bytes;
|
|
84
|
+
let denyPaths = [];
|
|
85
|
+
try {
|
|
86
|
+
bytes = await profile.readFile();
|
|
87
|
+
denyPaths = parseGrokWorkerSandboxProfile(bytes, input.profileSha256);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
throw new Error("Grok worker isolation attestation unavailable");
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
bytes?.fill(0);
|
|
94
|
+
await profile.close();
|
|
95
|
+
}
|
|
96
|
+
// The launcher exports TMPDIR=<registered home>/tmp; the profile lives at <home>/.grok/sandbox.toml.
|
|
97
|
+
// Every registered worker's private temp is attested, not only this one's (a sibling's open temp is a shared channel).
|
|
98
|
+
await assertGrokWorkerDenyPathsPlaceable(denyPaths, { uid: input.workerUid, gid: input.workerGid ?? input.workerUid });
|
|
99
|
+
const workers = [{ profilePath: input.profilePath, workerUid: input.workerUid }, ...(input.registeredWorkers ?? [])].map((worker) => ({ home: grokWorkerHomeForProfile(worker.profilePath), uid: worker.workerUid }));
|
|
100
|
+
if (workers.some((worker) => worker.home === undefined))
|
|
101
|
+
throw new Error("Grok worker isolation attestation unavailable");
|
|
102
|
+
await verifyGrokWorkerTmp(workers, profileOwner.tmp);
|
|
103
|
+
await verifyGrokWorkerHome(path.dirname(input.profilePath), input.configSha256);
|
|
104
|
+
const events = await secureOpen(input.eventsPath, input.workerUid, input.brokerGid, 0o640, 16 * 1024 * 1024);
|
|
105
|
+
try {
|
|
106
|
+
const stat = await events.stat();
|
|
107
|
+
return { dev: Number(stat.dev), ino: Number(stat.ino), size: Number(stat.size), denyPaths };
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
await events.close();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** The per-turn attestation input for one registration, carrying every registered worker so sibling temp is attested too. */
|
|
114
|
+
export const grokBrokerAttestationInput = (registration, registrations, configSha256) => ({ ...registration, brokerGid: 2100, configSha256, registeredWorkers: registrations.map((entry) => ({ profilePath: entry.profilePath, workerUid: entry.workerUid })) });
|
|
115
|
+
/**
|
|
116
|
+
* The per-turn isolation guard: every model request of a turn, and the
|
|
117
|
+
* post-turn check, go through the same lock.
|
|
118
|
+
*
|
|
119
|
+
* Why the first verification is trustworthy: the proxy awaits this guard before
|
|
120
|
+
* *every* upstream request, including the first, and the only worker-uid
|
|
121
|
+
* process that exists before request 1 is the Grok the launcher started under
|
|
122
|
+
* the pinned profile — tool children are created only after a model response.
|
|
123
|
+
* So the event accepted at request 1 was written before any tool child could
|
|
124
|
+
* write to the (worker-owned) events file. Later requests must find that exact
|
|
125
|
+
* line, byte for byte, at the same offset: a `ProfileApplied` appended later —
|
|
126
|
+
* which a tool child could forge — is never a substitute, and once a turn has
|
|
127
|
+
* been refused it stays refused.
|
|
128
|
+
*/
|
|
129
|
+
export function createGrokWorkerIsolationGuard(input, before) {
|
|
130
|
+
const lock = {};
|
|
131
|
+
return async () => {
|
|
132
|
+
if (lock.refused !== undefined)
|
|
133
|
+
throw new GrokWorkerAttestationFailure(lock.refused);
|
|
134
|
+
try {
|
|
135
|
+
await verifyGrokWorkerAttestation(input, before, lock);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
const failure = error instanceof GrokWorkerAttestationFailure ? error : new GrokWorkerAttestationFailure("profile_invalid");
|
|
139
|
+
lock.refused = failure.failureClass;
|
|
140
|
+
throw failure;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
export async function verifyGrokWorkerAttestation(input, before, lock) {
|
|
145
|
+
let handle;
|
|
146
|
+
try {
|
|
147
|
+
handle = await secureOpen(input.eventsPath, input.workerUid, input.brokerGid, 0o640, 16 * 1024 * 1024);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
throw new GrokWorkerAttestationFailure("profile_invalid");
|
|
151
|
+
}
|
|
152
|
+
let bytes;
|
|
153
|
+
try {
|
|
154
|
+
const stat = await handle.stat();
|
|
155
|
+
if (Number(stat.dev) !== before.dev || Number(stat.ino) !== before.ino)
|
|
156
|
+
throw new GrokWorkerAttestationFailure("profile_invalid");
|
|
157
|
+
if (Number(stat.size) <= before.size)
|
|
158
|
+
throw new GrokWorkerAttestationFailure("profile_missing");
|
|
159
|
+
const accepted = lock?.accepted;
|
|
160
|
+
const start = accepted === undefined ? before.size : before.size + accepted.offset;
|
|
161
|
+
const length = accepted === undefined ? Number(stat.size) - before.size : accepted.length;
|
|
162
|
+
if (start + length > Number(stat.size))
|
|
163
|
+
throw new GrokWorkerAttestationFailure("profile_invalid");
|
|
164
|
+
bytes = Buffer.alloc(length);
|
|
165
|
+
const read = await handle.read(bytes, 0, bytes.length, start);
|
|
166
|
+
if (read.bytesRead !== bytes.length)
|
|
167
|
+
throw new GrokWorkerAttestationFailure("profile_invalid");
|
|
168
|
+
// The file must not change while it is read: a concurrent writer could
|
|
169
|
+
// otherwise show this check bytes that no single state of the file held.
|
|
170
|
+
const after = await handle.stat();
|
|
171
|
+
if (Number(after.size) !== Number(stat.size) || Number(after.mtimeMs) !== Number(stat.mtimeMs))
|
|
172
|
+
throw new GrokWorkerAttestationFailure("profile_invalid");
|
|
173
|
+
if (accepted !== undefined) {
|
|
174
|
+
if (createHash("sha256").update(bytes).digest("hex") !== accepted.digest)
|
|
175
|
+
throw new GrokWorkerAttestationFailure("profile_invalid");
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const event = locateGrokWorkerProfileApplied(bytes, input.workspace, before.denyPaths);
|
|
179
|
+
if (lock !== undefined)
|
|
180
|
+
lock.accepted = { offset: event.offset, length: event.length, digest: createHash("sha256").update(bytes.subarray(event.offset, event.offset + event.length)).digest("hex") };
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
if (error instanceof GrokWorkerAttestationFailure)
|
|
184
|
+
throw error;
|
|
185
|
+
throw new GrokWorkerAttestationFailure("profile_invalid");
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
bytes?.fill(0);
|
|
189
|
+
await handle.close();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Requires one fully-conforming `ProfileApplied` event *somewhere* in the
|
|
194
|
+
* fresh region — not as its last line.
|
|
195
|
+
*
|
|
196
|
+
* `sandbox-events.jsonl` is not a profile log. Grok (1.0.13 and 1.0.34) writes its whole
|
|
197
|
+
* sandbox event vocabulary there — verified by reading the shipped binary:
|
|
198
|
+
* `ProfileApplied, ApplyFailed, FsViolation, NetViolation, BypassGranted,
|
|
199
|
+
* BypassDenied` (one contiguous enum blob beside the record fields
|
|
200
|
+
* `timestamp, event_type, read_only_paths, deny_paths, operation, target,
|
|
201
|
+
* command, tool_call_id`, emitted from `xai_grok_sandbox::logging`), and its
|
|
202
|
+
* own embedded documentation says so outright: "Sandbox events (profile
|
|
203
|
+
* applied, violations) are logged to `~/.grok/sandbox-events.jsonl`" (1.0.34
|
|
204
|
+
* moved the file to `~/.grok/sessions/`; a 1.0.34 turn logs `ProfileApplied`
|
|
205
|
+
* followed by an `FsViolation` for every denied read).
|
|
206
|
+
*
|
|
207
|
+
* Requiring `ProfileApplied` to be the *last* line therefore failed on the
|
|
208
|
+
* first denied access of any turn: the violation Grok logged next became the
|
|
209
|
+
* last line, this threw, the proxy's per-request isolation guard refused, and
|
|
210
|
+
* the turn died `profile_invalid`. Denials are the normal operating state of
|
|
211
|
+
* a working sandbox, so that check turned the sandbox doing its job into a
|
|
212
|
+
* turn failure.
|
|
213
|
+
*
|
|
214
|
+
* Scanning the region is not weaker. The region is fresh by construction —
|
|
215
|
+
* the caller reads only bytes above the pre-launch dev/ino/size watermark, so
|
|
216
|
+
* nothing here predates this turn and a stale or replayed event below it is
|
|
217
|
+
* unreachable. Both forms are equally forgeable by the same single actor (a
|
|
218
|
+
* worker-uid process, which can only exist by being a Grok that the launcher
|
|
219
|
+
* already started under the pinned profile), and both detect the failure this
|
|
220
|
+
* guard exists for: a Grok that came up without kernel enforcement, which
|
|
221
|
+
* emits no conforming `ProfileApplied` at all.
|
|
222
|
+
*/
|
|
223
|
+
export function parseGrokWorkerProfileApplied(bytes, workspace, denyPaths = []) { locateGrokWorkerProfileApplied(bytes, workspace, denyPaths); }
|
|
224
|
+
/** Byte range (within `bytes`) of the first fully-conforming `ProfileApplied` line. */
|
|
225
|
+
export function locateGrokWorkerProfileApplied(bytes, workspace, denyPaths = []) {
|
|
226
|
+
const expected = JSON.stringify([...denyPaths].sort());
|
|
227
|
+
const buffer = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
228
|
+
for (let offset = 0; offset < buffer.length;) {
|
|
229
|
+
const newline = buffer.indexOf(0x0a, offset);
|
|
230
|
+
const end = newline === -1 ? buffer.length : newline;
|
|
231
|
+
const line = buffer.subarray(offset, end).toString("utf8");
|
|
232
|
+
const lineOffset = offset;
|
|
233
|
+
offset = end + 1;
|
|
234
|
+
if (line.trim() === "")
|
|
235
|
+
continue;
|
|
236
|
+
let event;
|
|
237
|
+
try {
|
|
238
|
+
const parsed = JSON.parse(line);
|
|
239
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
240
|
+
continue;
|
|
241
|
+
event = parsed;
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (event.event_type !== "ProfileApplied")
|
|
247
|
+
continue;
|
|
248
|
+
const observed = Array.isArray(event.deny_paths) ? event.deny_paths.filter((entry) => typeof entry === "string").sort() : [];
|
|
249
|
+
if (event.profile === "daimon-strict" && event.enforced === true && event.restrict_network === true && event.platform === "linux/landlock" && event.workspace === workspace && JSON.stringify(observed) === expected)
|
|
250
|
+
return { offset: lineOffset, length: end - lineOffset };
|
|
251
|
+
}
|
|
252
|
+
throw new Error("Grok worker isolation attestation unavailable");
|
|
253
|
+
}
|
|
254
|
+
async function secureOpen(file, uid, gid, mode, max, links = 1) { try {
|
|
255
|
+
const before = await lstat(file);
|
|
256
|
+
const handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
257
|
+
const stat = await handle.stat();
|
|
258
|
+
if (!stat.isFile() || stat.isSymbolicLink() || Number(stat.uid) !== uid || Number(stat.gid) !== gid || (Number(stat.mode) & 0o777) !== mode || Number(stat.nlink) !== links || Number(stat.size) > max || before.dev !== stat.dev || before.ino !== stat.ino) {
|
|
259
|
+
await handle.close();
|
|
260
|
+
throw new Error();
|
|
261
|
+
}
|
|
262
|
+
return handle;
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
throw new Error("Grok worker isolation attestation unavailable");
|
|
266
|
+
} }
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paths Grok 1.0.34's strict base profile grants read or read-write. A `deny`
|
|
3
|
+
* entry that equals or contains one of them makes Grok refuse the profile
|
|
4
|
+
* outright (verified for `/tmp`, `/var/tmp`, `/run`, `/etc`, `/var` and
|
|
5
|
+
* `sessions`; `/tmp/sub` is accepted), so every entry must sit strictly below
|
|
6
|
+
* each grant it touches.
|
|
7
|
+
*/
|
|
8
|
+
export declare const GROK_WORKER_BASE_PROFILE_GRANTS: readonly ["/bin", "/dev", "/etc", "/lib", "/proc", "/run", "/sbin", "/sys", "/tmp", "/usr", "/var", "/var/tmp"];
|
|
9
|
+
export declare class GrokWorkerDenyPlacementError extends Error {
|
|
10
|
+
readonly denyPath: string;
|
|
11
|
+
readonly reason: string;
|
|
12
|
+
constructor(denyPath: string, reason: string);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The shape half of the deny-placement policy: everything decidable without
|
|
16
|
+
* touching a filesystem, so the profile renderer can refuse a bad entry before
|
|
17
|
+
* its bytes are ever pinned or written.
|
|
18
|
+
*
|
|
19
|
+
* `renderGrokWorkerSandboxProfile` already refuses entries that are relative,
|
|
20
|
+
* non-canonical, `/`, trailing-slashed, or carrying a character TOML or Grok
|
|
21
|
+
* would reinterpret; this adds the base-profile grant rule.
|
|
22
|
+
*/
|
|
23
|
+
export declare function assertGrokWorkerDenyPathShape(denyPath: string): void;
|
|
24
|
+
/** The subset of `lstat` the placement rules read; pure so every refusal is testable without root. */
|
|
25
|
+
export type GrokWorkerDenyPathEntry = Readonly<{
|
|
26
|
+
uid: number;
|
|
27
|
+
gid: number;
|
|
28
|
+
mode: number;
|
|
29
|
+
isDirectory: () => boolean;
|
|
30
|
+
isSymbolicLink: () => boolean;
|
|
31
|
+
}>;
|
|
32
|
+
/** One resolved path component: the entry, or the errno that stopped the walk. */
|
|
33
|
+
export type GrokWorkerDenyPathStep = Readonly<{
|
|
34
|
+
path: string;
|
|
35
|
+
entry?: GrokWorkerDenyPathEntry;
|
|
36
|
+
code?: string;
|
|
37
|
+
}>;
|
|
38
|
+
export type GrokWorkerDenyPathWorker = Readonly<{
|
|
39
|
+
uid: number;
|
|
40
|
+
gid: number;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* POSIX search permission: owner bits win, then group, then other. The worker
|
|
44
|
+
* runs with its supplementary groups cleared, so its primary gid is the only
|
|
45
|
+
* group that can apply.
|
|
46
|
+
*/
|
|
47
|
+
export declare const grokWorkerCanSearch: (entry: GrokWorkerDenyPathEntry, worker: GrokWorkerDenyPathWorker) => boolean;
|
|
48
|
+
/** The `/`-rooted ancestor chain of `denyPath`, deepest last, followed by the entry itself. */
|
|
49
|
+
export declare const grokWorkerDenyPathChain: (denyPath: string) => readonly string[];
|
|
50
|
+
/**
|
|
51
|
+
* The placement half of the policy, as a pure function of an already-walked
|
|
52
|
+
* chain.
|
|
53
|
+
*
|
|
54
|
+
* Grok 1.0.34 materializes every `deny` entry inside bubblewrap **as the worker
|
|
55
|
+
* uid**, by bind-mounting `$GROK_HOME/sandbox-blocked-{file,dir}` over the
|
|
56
|
+
* target. So bwrap must be able to *resolve* the target as that uid: every
|
|
57
|
+
* ancestor directory needs the search bit for it, and the target must already
|
|
58
|
+
* exist — otherwise bwrap tries to create it and needs write on the parent,
|
|
59
|
+
* which a private parent never grants. A single unplaceable entry makes Grok
|
|
60
|
+
* refuse the whole profile, so every turn of that worker fails, not just that
|
|
61
|
+
* path. Verified matrix: `.runtime/grok-deny-placement/EVIDENCE.md`.
|
|
62
|
+
*
|
|
63
|
+
* The walk may legitimately stop early: a caller that is neither root nor the
|
|
64
|
+
* worker (the broker, uid 2100) cannot descend into a directory the worker's
|
|
65
|
+
* own group opens to it alone — `<runtime home>/tool-state` under a
|
|
66
|
+
* `2000:<worker> 0710` runtime home is exactly that. An `EACCES` below an
|
|
67
|
+
* ancestor the *worker* can search is therefore "not decidable from here", not
|
|
68
|
+
* a refusal; every decidable failure still refuses.
|
|
69
|
+
*/
|
|
70
|
+
export declare function assertGrokWorkerDenyPathPlacement(denyPath: string, steps: readonly GrokWorkerDenyPathStep[], worker: GrokWorkerDenyPathWorker): void;
|
|
71
|
+
/** Walks one deny path on the real filesystem, recording what stopped it rather than throwing. */
|
|
72
|
+
export declare function readGrokWorkerDenyPathChain(denyPath: string): Promise<readonly GrokWorkerDenyPathStep[]>;
|
|
73
|
+
/**
|
|
74
|
+
* Fails closed before a worker is ever launched with a profile Grok would
|
|
75
|
+
* refuse. Callers with the widest view run it: root provisioning at container
|
|
76
|
+
* start and on every slot recycle, and the direct (non-broker) path, which runs
|
|
77
|
+
* as the worker uid itself.
|
|
78
|
+
*/
|
|
79
|
+
export declare function assertGrokWorkerDenyPathsPlaceable(denyPaths: readonly string[], worker: GrokWorkerDenyPathWorker): Promise<void>;
|
|
80
|
+
//# sourceMappingURL=grokWorkerDenyPlacement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grokWorkerDenyPlacement.d.ts","sourceRoot":"","sources":["../../src/runtime/grokWorkerDenyPlacement.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,iHAEjC,CAAC;AAIZ,qBAAa,4BAA6B,SAAQ,KAAK;IACzC,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAzC,QAAQ,EAAE,MAAM,EAAW,MAAM,EAAE,MAAM;CAI/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAKpE;AAED,sGAAsG;AACtG,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,OAAO,CAAC;IAC3B,cAAc,EAAE,MAAM,OAAO,CAAC;CAC/B,CAAC,CAAC;AAEH,kFAAkF;AAClF,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,uBAAuB,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEhH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,uBAAuB,EAAE,QAAQ,wBAAwB,KAAG,OAGrE,CAAC;AAEnC,+FAA+F;AAC/F,eAAO,MAAM,uBAAuB,GAAI,UAAU,MAAM,KAAG,SAAS,MAAM,EAGzE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,SAAS,sBAAsB,EAAE,EACxC,MAAM,EAAE,wBAAwB,GAC/B,IAAI,CAqBN;AAED,kGAAkG;AAClG,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,sBAAsB,EAAE,CAAC,CAa9G;AAED;;;;;GAKG;AACH,wBAAsB,kCAAkC,CACtD,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAKf"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { lstat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Paths Grok 1.0.34's strict base profile grants read or read-write. A `deny`
|
|
5
|
+
* entry that equals or contains one of them makes Grok refuse the profile
|
|
6
|
+
* outright (verified for `/tmp`, `/var/tmp`, `/run`, `/etc`, `/var` and
|
|
7
|
+
* `sessions`; `/tmp/sub` is accepted), so every entry must sit strictly below
|
|
8
|
+
* each grant it touches.
|
|
9
|
+
*/
|
|
10
|
+
export const GROK_WORKER_BASE_PROFILE_GRANTS = Object.freeze([
|
|
11
|
+
"/bin", "/dev", "/etc", "/lib", "/proc", "/run", "/sbin", "/sys", "/tmp", "/usr", "/var", "/var/tmp"
|
|
12
|
+
]);
|
|
13
|
+
const within = (candidate, root) => candidate === root || candidate.startsWith(`${root}/`);
|
|
14
|
+
export class GrokWorkerDenyPlacementError extends Error {
|
|
15
|
+
denyPath;
|
|
16
|
+
reason;
|
|
17
|
+
constructor(denyPath, reason) {
|
|
18
|
+
super(`Grok worker sandbox deny path ${JSON.stringify(denyPath)} is not placeable: ${reason}`);
|
|
19
|
+
this.denyPath = denyPath;
|
|
20
|
+
this.reason = reason;
|
|
21
|
+
this.name = "GrokWorkerDenyPlacementError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The shape half of the deny-placement policy: everything decidable without
|
|
26
|
+
* touching a filesystem, so the profile renderer can refuse a bad entry before
|
|
27
|
+
* its bytes are ever pinned or written.
|
|
28
|
+
*
|
|
29
|
+
* `renderGrokWorkerSandboxProfile` already refuses entries that are relative,
|
|
30
|
+
* non-canonical, `/`, trailing-slashed, or carrying a character TOML or Grok
|
|
31
|
+
* would reinterpret; this adds the base-profile grant rule.
|
|
32
|
+
*/
|
|
33
|
+
export function assertGrokWorkerDenyPathShape(denyPath) {
|
|
34
|
+
const grant = GROK_WORKER_BASE_PROFILE_GRANTS.find((candidate) => within(candidate, denyPath));
|
|
35
|
+
if (grant !== undefined) {
|
|
36
|
+
throw new GrokWorkerDenyPlacementError(denyPath, `it equals or contains the base profile grant ${grant}, which Grok refuses`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* POSIX search permission: owner bits win, then group, then other. The worker
|
|
41
|
+
* runs with its supplementary groups cleared, so its primary gid is the only
|
|
42
|
+
* group that can apply.
|
|
43
|
+
*/
|
|
44
|
+
export const grokWorkerCanSearch = (entry, worker) => entry.uid === worker.uid ? (entry.mode & 0o100) !== 0
|
|
45
|
+
: entry.gid === worker.gid ? (entry.mode & 0o010) !== 0
|
|
46
|
+
: (entry.mode & 0o001) !== 0;
|
|
47
|
+
/** The `/`-rooted ancestor chain of `denyPath`, deepest last, followed by the entry itself. */
|
|
48
|
+
export const grokWorkerDenyPathChain = (denyPath) => {
|
|
49
|
+
const components = denyPath.split("/").slice(1);
|
|
50
|
+
return ["/", ...components.map((_, index) => `/${components.slice(0, index + 1).join("/")}`)];
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* The placement half of the policy, as a pure function of an already-walked
|
|
54
|
+
* chain.
|
|
55
|
+
*
|
|
56
|
+
* Grok 1.0.34 materializes every `deny` entry inside bubblewrap **as the worker
|
|
57
|
+
* uid**, by bind-mounting `$GROK_HOME/sandbox-blocked-{file,dir}` over the
|
|
58
|
+
* target. So bwrap must be able to *resolve* the target as that uid: every
|
|
59
|
+
* ancestor directory needs the search bit for it, and the target must already
|
|
60
|
+
* exist — otherwise bwrap tries to create it and needs write on the parent,
|
|
61
|
+
* which a private parent never grants. A single unplaceable entry makes Grok
|
|
62
|
+
* refuse the whole profile, so every turn of that worker fails, not just that
|
|
63
|
+
* path. Verified matrix: `.runtime/grok-deny-placement/EVIDENCE.md`.
|
|
64
|
+
*
|
|
65
|
+
* The walk may legitimately stop early: a caller that is neither root nor the
|
|
66
|
+
* worker (the broker, uid 2100) cannot descend into a directory the worker's
|
|
67
|
+
* own group opens to it alone — `<runtime home>/tool-state` under a
|
|
68
|
+
* `2000:<worker> 0710` runtime home is exactly that. An `EACCES` below an
|
|
69
|
+
* ancestor the *worker* can search is therefore "not decidable from here", not
|
|
70
|
+
* a refusal; every decidable failure still refuses.
|
|
71
|
+
*/
|
|
72
|
+
export function assertGrokWorkerDenyPathPlacement(denyPath, steps, worker) {
|
|
73
|
+
assertGrokWorkerDenyPathShape(denyPath);
|
|
74
|
+
const chain = grokWorkerDenyPathChain(denyPath);
|
|
75
|
+
if (steps.length !== chain.length || steps.some((step, index) => step.path !== chain[index])) {
|
|
76
|
+
throw new GrokWorkerDenyPlacementError(denyPath, "its resolved path chain does not match the entry");
|
|
77
|
+
}
|
|
78
|
+
for (const [index, step] of steps.entries()) {
|
|
79
|
+
const ancestor = index < steps.length - 1;
|
|
80
|
+
if (step.entry === undefined) {
|
|
81
|
+
if (step.code === "ENOENT")
|
|
82
|
+
throw new GrokWorkerDenyPlacementError(denyPath, `${step.path} does not exist; bubblewrap would have to create it as the worker uid`);
|
|
83
|
+
if (step.code !== "EACCES")
|
|
84
|
+
throw new GrokWorkerDenyPlacementError(denyPath, `${step.path} could not be read (${step.code ?? "unknown error"})`);
|
|
85
|
+
// Undecidable from here, and only after every shallower ancestor passed.
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (step.entry.isSymbolicLink())
|
|
89
|
+
throw new GrokWorkerDenyPlacementError(denyPath, `${step.path} is a symlink; bubblewrap refuses to bind over one`);
|
|
90
|
+
if (!ancestor)
|
|
91
|
+
return;
|
|
92
|
+
if (!step.entry.isDirectory())
|
|
93
|
+
throw new GrokWorkerDenyPlacementError(denyPath, `${step.path} is not a directory`);
|
|
94
|
+
if (!grokWorkerCanSearch(step.entry, worker)) {
|
|
95
|
+
throw new GrokWorkerDenyPlacementError(denyPath, `worker uid ${worker.uid} cannot search ${step.path} (${(step.entry.mode & 0o7777).toString(8)} ${step.entry.uid}:${step.entry.gid}); deny that directory itself instead`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Walks one deny path on the real filesystem, recording what stopped it rather than throwing. */
|
|
100
|
+
export async function readGrokWorkerDenyPathChain(denyPath) {
|
|
101
|
+
const steps = [];
|
|
102
|
+
for (const target of grokWorkerDenyPathChain(denyPath)) {
|
|
103
|
+
try {
|
|
104
|
+
steps.push({ path: target, entry: await lstat(target) });
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
steps.push({ path: target, code: error.code });
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const chain = grokWorkerDenyPathChain(denyPath);
|
|
112
|
+
while (steps.length < chain.length)
|
|
113
|
+
steps.push({ path: chain[steps.length], code: steps.at(-1)?.code ?? "EACCES" });
|
|
114
|
+
return steps;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Fails closed before a worker is ever launched with a profile Grok would
|
|
118
|
+
* refuse. Callers with the widest view run it: root provisioning at container
|
|
119
|
+
* start and on every slot recycle, and the direct (non-broker) path, which runs
|
|
120
|
+
* as the worker uid itself.
|
|
121
|
+
*/
|
|
122
|
+
export async function assertGrokWorkerDenyPathsPlaceable(denyPaths, worker) {
|
|
123
|
+
for (const denyPath of denyPaths) {
|
|
124
|
+
if (!path.posix.isAbsolute(denyPath))
|
|
125
|
+
throw new GrokWorkerDenyPlacementError(denyPath, "it is not an absolute path");
|
|
126
|
+
assertGrokWorkerDenyPathPlacement(denyPath, await readGrokWorkerDenyPathChain(denyPath), worker);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type Stats } from "node:fs";
|
|
2
|
+
type Entry = Pick<Stats, "uid" | "mode" | "nlink"> & Partial<Pick<Stats, "dev" | "ino">> & Readonly<{
|
|
3
|
+
isFile(): boolean;
|
|
4
|
+
isDirectory(): boolean;
|
|
5
|
+
isSymbolicLink(): boolean;
|
|
6
|
+
}>;
|
|
7
|
+
/**
|
|
8
|
+
* The worker must not be able to change what Grok reads before its next turn.
|
|
9
|
+
*
|
|
10
|
+
* Grok writes its own state (sessions, hooks, locks, docs) into `$GROK_HOME`,
|
|
11
|
+
* so the directory is worker-group writable — but root-owned and sticky, so a
|
|
12
|
+
* worker can neither rename nor unlink a root-owned file in it. Every file
|
|
13
|
+
* that decides a turn's behaviour is root-owned with no write bit: `config.toml`
|
|
14
|
+
* (model, effort, MCP, skills), `sandbox.toml`, `trusted_folders.toml` (trust
|
|
15
|
+
* would re-enable cwd `AGENTS.md` and project skills), and the managed and
|
|
16
|
+
* requirements layers that can override user config. A missing file fails
|
|
17
|
+
* too: the worker could create it.
|
|
18
|
+
*
|
|
19
|
+
* Pure so every refusal is testable without root.
|
|
20
|
+
*/
|
|
21
|
+
export declare function assertGrokWorkerHomeEntries(entries: Readonly<Record<string, Entry | undefined>>, rootUid?: number): void;
|
|
22
|
+
/** The worker's `config.toml` must be exactly the renderer's bytes for the declared policy. */
|
|
23
|
+
export declare function assertGrokWorkerConfigBytes(bytes: Uint8Array, configSha256: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Attests the worker home layout and that `config.toml` is exactly the
|
|
26
|
+
* renderer's bytes for the declared model policy (`configSha256`).
|
|
27
|
+
*/
|
|
28
|
+
export declare function verifyGrokWorkerHome(grokHome: string, configSha256: string, rootUid?: number): Promise<void>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=grokWorkerHomeAttestation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grokWorkerHomeAttestation.d.ts","sourceRoot":"","sources":["../../src/runtime/grokWorkerHomeAttestation.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAMhD,KAAK,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC,GAAG,QAAQ,CAAC;IAAE,MAAM,IAAI,OAAO,CAAC;IAAC,WAAW,IAAI,OAAO,CAAC;IAAC,cAAc,IAAI,OAAO,CAAA;CAAE,CAAC,CAAC;AAG9K;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC,EAAE,OAAO,GAAE,MAA2B,GAAG,IAAI,CAS5I;AAED,+FAA+F;AAC/F,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAEzF;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAE,MAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAetI"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { lstat, open } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { GROK_ENGINE_BROKER } from "../contracts/runtimeContractManifest.js";
|
|
6
|
+
const HOME = GROK_ENGINE_BROKER.worker.home;
|
|
7
|
+
/**
|
|
8
|
+
* The worker must not be able to change what Grok reads before its next turn.
|
|
9
|
+
*
|
|
10
|
+
* Grok writes its own state (sessions, hooks, locks, docs) into `$GROK_HOME`,
|
|
11
|
+
* so the directory is worker-group writable — but root-owned and sticky, so a
|
|
12
|
+
* worker can neither rename nor unlink a root-owned file in it. Every file
|
|
13
|
+
* that decides a turn's behaviour is root-owned with no write bit: `config.toml`
|
|
14
|
+
* (model, effort, MCP, skills), `sandbox.toml`, `trusted_folders.toml` (trust
|
|
15
|
+
* would re-enable cwd `AGENTS.md` and project skills), and the managed and
|
|
16
|
+
* requirements layers that can override user config. A missing file fails
|
|
17
|
+
* too: the worker could create it.
|
|
18
|
+
*
|
|
19
|
+
* Pure so every refusal is testable without root.
|
|
20
|
+
*/
|
|
21
|
+
export function assertGrokWorkerHomeEntries(entries, rootUid = HOME.directory.uid) {
|
|
22
|
+
const directory = (entry) => entry !== undefined && entry.isDirectory() && !entry.isSymbolicLink() && entry.uid === rootUid
|
|
23
|
+
&& (Number(entry.mode) & 0o002) === 0 && ((Number(entry.mode) & 0o020) === 0 || (Number(entry.mode) & 0o1000) !== 0);
|
|
24
|
+
if (!directory(entries["."]) || !directory(entries[HOME.sessionsDirectory.relativePath]))
|
|
25
|
+
throw unavailable();
|
|
26
|
+
for (const name of HOME.readOnlyFiles.names) {
|
|
27
|
+
const entry = entries[name];
|
|
28
|
+
if (entry === undefined || !entry.isFile() || entry.isSymbolicLink() || entry.uid !== rootUid || entry.nlink !== 1 || (Number(entry.mode) & 0o7222) !== 0)
|
|
29
|
+
throw unavailable();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** The worker's `config.toml` must be exactly the renderer's bytes for the declared policy. */
|
|
33
|
+
export function assertGrokWorkerConfigBytes(bytes, configSha256) {
|
|
34
|
+
if (!/^[0-9a-f]{64}$/u.test(configSha256) || createHash("sha256").update(bytes).digest("hex") !== configSha256)
|
|
35
|
+
throw unavailable();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Attests the worker home layout and that `config.toml` is exactly the
|
|
39
|
+
* renderer's bytes for the declared model policy (`configSha256`).
|
|
40
|
+
*/
|
|
41
|
+
export async function verifyGrokWorkerHome(grokHome, configSha256, rootUid = HOME.directory.uid) {
|
|
42
|
+
const entries = {};
|
|
43
|
+
for (const name of [".", HOME.sessionsDirectory.relativePath, ...HOME.readOnlyFiles.names]) {
|
|
44
|
+
try {
|
|
45
|
+
entries[name] = await lstat(path.join(grokHome, name));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
entries[name] = undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
assertGrokWorkerHomeEntries(entries, rootUid);
|
|
52
|
+
let handle;
|
|
53
|
+
try {
|
|
54
|
+
handle = await open(path.join(grokHome, "config.toml"), constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
55
|
+
const opened = await handle.stat();
|
|
56
|
+
const before = entries["config.toml"];
|
|
57
|
+
// Same inode as the lstat above, as in `secureOpen`: a rename between the two cannot swap in other bytes.
|
|
58
|
+
if (!opened.isFile() || opened.size > 65_536 || opened.dev !== before.dev || opened.ino !== before.ino || opened.uid !== before.uid || opened.mode !== before.mode || opened.nlink !== 1)
|
|
59
|
+
throw unavailable();
|
|
60
|
+
assertGrokWorkerConfigBytes(await handle.readFile(), configSha256);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw unavailable();
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
await handle?.close().catch(() => undefined);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const unavailable = () => new Error("Grok worker isolation attestation unavailable");
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const GROK_WORKER_SANDBOX_PROFILE: "daimon-strict";
|
|
2
|
+
export declare const GROK_WORKER_SANDBOX_EVENTS_RELATIVE_PATH: "sessions/sandbox-events.jsonl";
|
|
3
|
+
/**
|
|
4
|
+
* The only source of `daimon-strict` sandbox profile bytes.
|
|
5
|
+
*
|
|
6
|
+
* Grok 1.0.34 runs every Landlock profile inside bubblewrap, and there a
|
|
7
|
+
* non-empty `deny` list works: each entry is bind-masked for both the shell
|
|
8
|
+
* tool and in-process `read_file` (P0: `/run/paideia` denied, controls intact).
|
|
9
|
+
* The strict base still reads all of `/run`, `/var`, `/tmp` and `/etc`, so the
|
|
10
|
+
* deny list — not Landlock's allowlist — is what keeps evaluator and host-bind
|
|
11
|
+
* paths away from the worker. Which paths to deny is a registration input
|
|
12
|
+
* supplied by the deployment; Daimon only renders, pins, and attests them.
|
|
13
|
+
*
|
|
14
|
+
* Entries are sorted and deduplicated so equal sets render equal bytes (and the
|
|
15
|
+
* same `profileSha256`). A path that is not absolute and canonical, or that
|
|
16
|
+
* carries a character TOML or Grok would reinterpret, is refused — and so is
|
|
17
|
+
* one that equals or contains a base-profile grant, the first half of the
|
|
18
|
+
* deny-placement policy (`grokWorkerDenyPlacement.ts`). The other half —
|
|
19
|
+
* the entry exists and every ancestor is searchable by the worker uid — needs
|
|
20
|
+
* a filesystem, so it is asserted by whoever provisions the paths and, on the
|
|
21
|
+
* direct path, before every turn.
|
|
22
|
+
*/
|
|
23
|
+
export declare function renderGrokWorkerSandboxProfile(denyPaths?: readonly string[]): string;
|
|
24
|
+
export declare const grokWorkerSandboxProfileSha256: (denyPaths?: readonly string[]) => string;
|
|
25
|
+
/** `$GROK_HOME` is the profile's directory; 1.0.34 logs sandbox events under `sessions/`. */
|
|
26
|
+
export declare const grokWorkerEventsPathFor: (profilePath: string) => string;
|
|
27
|
+
//# sourceMappingURL=grokWorkerSandboxProfile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grokWorkerSandboxProfile.d.ts","sourceRoot":"","sources":["../../src/runtime/grokWorkerSandboxProfile.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,2BAA2B,EAAG,eAAwB,CAAC;AACpE,eAAO,MAAM,wCAAwC,EAAG,+BAAwC,CAAC;AAEjG;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,8BAA8B,CAAC,SAAS,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAexF;AAED,eAAO,MAAM,8BAA8B,GAAI,YAAW,SAAS,MAAM,EAAO,KAAG,MACG,CAAC;AAEvF,6FAA6F;AAC7F,eAAO,MAAM,uBAAuB,GAAI,aAAa,MAAM,KAAG,MAC8B,CAAC"}
|