@smthrs/platform-node 0.0.0-stage → 1.0.0-rc.4
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/CHANGELOG.md +211 -0
- package/LICENSE +21 -0
- package/README.md +165 -2
- package/bin/darwin-arm64/smithers-jj-export +0 -0
- package/bin/darwin-x64/smithers-jj-export +0 -0
- package/bin/linux-arm64/smithers-jj-export +0 -0
- package/bin/linux-x64/smithers-jj-export +4 -0
- package/bin/win32-x64/smithers-jj-export.exe +0 -0
- package/dist/cjs/AtomicFileSystem.d.ts +147 -0
- package/dist/cjs/AtomicFileSystem.d.ts.map +1 -0
- package/dist/cjs/AtomicFileSystem.js +234 -0
- package/dist/cjs/AtomicFileSystem.js.map +7 -0
- package/dist/cjs/EgressHttpClient.d.ts +106 -0
- package/dist/cjs/EgressHttpClient.d.ts.map +1 -0
- package/dist/cjs/EgressHttpClient.js +202 -0
- package/dist/cjs/EgressHttpClient.js.map +7 -0
- package/dist/cjs/HostLiveness.d.ts +81 -0
- package/dist/cjs/HostLiveness.d.ts.map +1 -0
- package/dist/cjs/HostLiveness.js +49 -0
- package/dist/cjs/HostLiveness.js.map +7 -0
- package/dist/cjs/NodeHost.d.ts +145 -0
- package/dist/cjs/NodeHost.d.ts.map +1 -0
- package/dist/cjs/NodeHost.js +128 -0
- package/dist/cjs/NodeHost.js.map +7 -0
- package/dist/cjs/ProcessConfinement.d.ts +61 -0
- package/dist/cjs/ProcessConfinement.d.ts.map +1 -0
- package/dist/cjs/ProcessConfinement.js +292 -0
- package/dist/cjs/ProcessConfinement.js.map +7 -0
- package/dist/cjs/ProcessReaper.d.ts +457 -0
- package/dist/cjs/ProcessReaper.d.ts.map +1 -0
- package/dist/cjs/ProcessReaper.js +349 -0
- package/dist/cjs/ProcessReaper.js.map +7 -0
- package/dist/cjs/ProcessSandbox.d.ts +391 -0
- package/dist/cjs/ProcessSandbox.d.ts.map +1 -0
- package/dist/cjs/ProcessSandbox.js +774 -0
- package/dist/cjs/ProcessSandbox.js.map +7 -0
- package/dist/cjs/ScopedProcess.d.ts +66 -0
- package/dist/cjs/ScopedProcess.d.ts.map +1 -0
- package/dist/cjs/ScopedProcess.js +87 -0
- package/dist/cjs/ScopedProcess.js.map +7 -0
- package/dist/cjs/index.d.ts +38 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +47 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/AtomicFileSystemExecutable.d.ts +61 -0
- package/dist/cjs/internal/AtomicFileSystemExecutable.d.ts.map +1 -0
- package/dist/cjs/internal/AtomicFileSystemExecutable.js +250 -0
- package/dist/cjs/internal/AtomicFileSystemExecutable.js.map +7 -0
- package/dist/cjs/internal/AtomicFileSystemProtocol.d.ts +78 -0
- package/dist/cjs/internal/AtomicFileSystemProtocol.d.ts.map +1 -0
- package/dist/cjs/internal/AtomicFileSystemProtocol.js +382 -0
- package/dist/cjs/internal/AtomicFileSystemProtocol.js.map +7 -0
- package/dist/cjs/internal/AtomicFileSystemTransport.d.ts +44 -0
- package/dist/cjs/internal/AtomicFileSystemTransport.d.ts.map +1 -0
- package/dist/cjs/internal/AtomicFileSystemTransport.js +197 -0
- package/dist/cjs/internal/AtomicFileSystemTransport.js.map +7 -0
- package/dist/cjs/internal/AtomicHelperError.d.ts +35 -0
- package/dist/cjs/internal/AtomicHelperError.d.ts.map +1 -0
- package/dist/cjs/internal/AtomicHelperError.js +45 -0
- package/dist/cjs/internal/AtomicHelperError.js.map +7 -0
- package/dist/cjs/internal/AtomicHelperFault.d.ts +59 -0
- package/dist/cjs/internal/AtomicHelperFault.d.ts.map +1 -0
- package/dist/cjs/internal/AtomicHelperFault.js +66 -0
- package/dist/cjs/internal/AtomicHelperFault.js.map +7 -0
- package/dist/cjs/internal/PipedProcess.d.ts +35 -0
- package/dist/cjs/internal/PipedProcess.d.ts.map +1 -0
- package/dist/cjs/internal/PipedProcess.js +309 -0
- package/dist/cjs/internal/PipedProcess.js.map +7 -0
- package/dist/cjs/internal/ProcSnapshot.d.ts +85 -0
- package/dist/cjs/internal/ProcSnapshot.d.ts.map +1 -0
- package/dist/cjs/internal/ProcSnapshot.js +123 -0
- package/dist/cjs/internal/ProcSnapshot.js.map +7 -0
- package/dist/cjs/internal/ProcessCleanup.d.ts +64 -0
- package/dist/cjs/internal/ProcessCleanup.d.ts.map +1 -0
- package/dist/cjs/internal/ProcessCleanup.js +62 -0
- package/dist/cjs/internal/ProcessCleanup.js.map +7 -0
- package/dist/cjs/internal/ProcessCleanupPolicyError.d.ts +30 -0
- package/dist/cjs/internal/ProcessCleanupPolicyError.d.ts.map +1 -0
- package/dist/cjs/internal/ProcessCleanupPolicyError.js +33 -0
- package/dist/cjs/internal/ProcessCleanupPolicyError.js.map +7 -0
- package/dist/cjs/internal/ProcessFault.d.ts +63 -0
- package/dist/cjs/internal/ProcessFault.d.ts.map +1 -0
- package/dist/cjs/internal/ProcessFault.js +63 -0
- package/dist/cjs/internal/ProcessFault.js.map +7 -0
- package/dist/cjs/internal/ProcessSupervisor.d.ts +143 -0
- package/dist/cjs/internal/ProcessSupervisor.d.ts.map +1 -0
- package/dist/cjs/internal/ProcessSupervisor.js +685 -0
- package/dist/cjs/internal/ProcessSupervisor.js.map +7 -0
- package/dist/cjs/internal/SupervisorProgram.d.ts +9 -0
- package/dist/cjs/internal/SupervisorProgram.d.ts.map +1 -0
- package/dist/cjs/internal/SupervisorProgram.js +326 -0
- package/dist/cjs/internal/SupervisorProgram.js.map +7 -0
- package/dist/cjs/internal/WindowsProcessJob.d.ts +39 -0
- package/dist/cjs/internal/WindowsProcessJob.d.ts.map +1 -0
- package/dist/cjs/internal/WindowsProcessJob.js +113 -0
- package/dist/cjs/internal/WindowsProcessJob.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/AtomicFileSystem.d.ts +147 -0
- package/dist/esm/AtomicFileSystem.d.ts.map +1 -0
- package/dist/esm/AtomicFileSystem.js +290 -0
- package/dist/esm/AtomicFileSystem.js.map +1 -0
- package/dist/esm/EgressHttpClient.d.ts +106 -0
- package/dist/esm/EgressHttpClient.d.ts.map +1 -0
- package/dist/esm/EgressHttpClient.js +259 -0
- package/dist/esm/EgressHttpClient.js.map +1 -0
- package/dist/esm/HostLiveness.d.ts +81 -0
- package/dist/esm/HostLiveness.d.ts.map +1 -0
- package/dist/esm/HostLiveness.js +68 -0
- package/dist/esm/HostLiveness.js.map +1 -0
- package/dist/esm/NodeHost.d.ts +145 -0
- package/dist/esm/NodeHost.d.ts.map +1 -0
- package/dist/esm/NodeHost.js +162 -0
- package/dist/esm/NodeHost.js.map +1 -0
- package/dist/esm/ProcessConfinement.d.ts +61 -0
- package/dist/esm/ProcessConfinement.d.ts.map +1 -0
- package/dist/esm/ProcessConfinement.js +311 -0
- package/dist/esm/ProcessConfinement.js.map +1 -0
- package/dist/esm/ProcessReaper.d.ts +457 -0
- package/dist/esm/ProcessReaper.d.ts.map +1 -0
- package/dist/esm/ProcessReaper.js +711 -0
- package/dist/esm/ProcessReaper.js.map +1 -0
- package/dist/esm/ProcessSandbox.d.ts +391 -0
- package/dist/esm/ProcessSandbox.d.ts.map +1 -0
- package/dist/esm/ProcessSandbox.js +1043 -0
- package/dist/esm/ProcessSandbox.js.map +1 -0
- package/dist/esm/ScopedProcess.d.ts +66 -0
- package/dist/esm/ScopedProcess.d.ts.map +1 -0
- package/dist/esm/ScopedProcess.js +67 -0
- package/dist/esm/ScopedProcess.js.map +1 -0
- package/dist/esm/index.d.ts +38 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +38 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/AtomicFileSystemExecutable.d.ts +61 -0
- package/dist/esm/internal/AtomicFileSystemExecutable.d.ts.map +1 -0
- package/dist/esm/internal/AtomicFileSystemExecutable.js +350 -0
- package/dist/esm/internal/AtomicFileSystemExecutable.js.map +1 -0
- package/dist/esm/internal/AtomicFileSystemProtocol.d.ts +78 -0
- package/dist/esm/internal/AtomicFileSystemProtocol.d.ts.map +1 -0
- package/dist/esm/internal/AtomicFileSystemProtocol.js +412 -0
- package/dist/esm/internal/AtomicFileSystemProtocol.js.map +1 -0
- package/dist/esm/internal/AtomicFileSystemTransport.d.ts +44 -0
- package/dist/esm/internal/AtomicFileSystemTransport.d.ts.map +1 -0
- package/dist/esm/internal/AtomicFileSystemTransport.js +242 -0
- package/dist/esm/internal/AtomicFileSystemTransport.js.map +1 -0
- package/dist/esm/internal/AtomicHelperError.d.ts +35 -0
- package/dist/esm/internal/AtomicHelperError.d.ts.map +1 -0
- package/dist/esm/internal/AtomicHelperError.js +39 -0
- package/dist/esm/internal/AtomicHelperError.js.map +1 -0
- package/dist/esm/internal/AtomicHelperFault.d.ts +59 -0
- package/dist/esm/internal/AtomicHelperFault.d.ts.map +1 -0
- package/dist/esm/internal/AtomicHelperFault.js +66 -0
- package/dist/esm/internal/AtomicHelperFault.js.map +1 -0
- package/dist/esm/internal/PipedProcess.d.ts +35 -0
- package/dist/esm/internal/PipedProcess.d.ts.map +1 -0
- package/dist/esm/internal/PipedProcess.js +321 -0
- package/dist/esm/internal/PipedProcess.js.map +1 -0
- package/dist/esm/internal/ProcSnapshot.d.ts +85 -0
- package/dist/esm/internal/ProcSnapshot.d.ts.map +1 -0
- package/dist/esm/internal/ProcSnapshot.js +188 -0
- package/dist/esm/internal/ProcSnapshot.js.map +1 -0
- package/dist/esm/internal/ProcessCleanup.d.ts +64 -0
- package/dist/esm/internal/ProcessCleanup.d.ts.map +1 -0
- package/dist/esm/internal/ProcessCleanup.js +45 -0
- package/dist/esm/internal/ProcessCleanup.js.map +1 -0
- package/dist/esm/internal/ProcessCleanupPolicyError.d.ts +30 -0
- package/dist/esm/internal/ProcessCleanupPolicyError.d.ts.map +1 -0
- package/dist/esm/internal/ProcessCleanupPolicyError.js +25 -0
- package/dist/esm/internal/ProcessCleanupPolicyError.js.map +1 -0
- package/dist/esm/internal/ProcessFault.d.ts +63 -0
- package/dist/esm/internal/ProcessFault.d.ts.map +1 -0
- package/dist/esm/internal/ProcessFault.js +64 -0
- package/dist/esm/internal/ProcessFault.js.map +1 -0
- package/dist/esm/internal/ProcessSupervisor.d.ts +143 -0
- package/dist/esm/internal/ProcessSupervisor.d.ts.map +1 -0
- package/dist/esm/internal/ProcessSupervisor.js +689 -0
- package/dist/esm/internal/ProcessSupervisor.js.map +1 -0
- package/dist/esm/internal/SupervisorProgram.d.ts +9 -0
- package/dist/esm/internal/SupervisorProgram.d.ts.map +1 -0
- package/dist/esm/internal/SupervisorProgram.js +310 -0
- package/dist/esm/internal/SupervisorProgram.js.map +1 -0
- package/dist/esm/internal/WindowsProcessJob.d.ts +39 -0
- package/dist/esm/internal/WindowsProcessJob.d.ts.map +1 -0
- package/dist/esm/internal/WindowsProcessJob.js +115 -0
- package/dist/esm/internal/WindowsProcessJob.js.map +1 -0
- package/dist/vendor/adapters.json +1757 -0
- package/dist/vendor/chunk-2Y7WC4KU.js +109 -0
- package/dist/vendor/chunk-3GR5VZ2M.js +118 -0
- package/dist/vendor/chunk-42QKY6T6.js +40 -0
- package/dist/vendor/chunk-54RLBMTZ.js +61 -0
- package/dist/vendor/chunk-5ZMHBXIK.js +55 -0
- package/dist/vendor/chunk-6ME2W7TF.js +28 -0
- package/dist/vendor/chunk-AMQKOQNT.js +34 -0
- package/dist/vendor/chunk-B3FZ67QU.js +59 -0
- package/dist/vendor/chunk-BKTBUBQY.js +437 -0
- package/dist/vendor/chunk-DS7GF46S.js +34 -0
- package/dist/vendor/chunk-DVOAHXPG.js +104 -0
- package/dist/vendor/chunk-DWSHTSVX.js +229 -0
- package/dist/vendor/chunk-EGBNVEEW.js +14 -0
- package/dist/vendor/chunk-EZWY45HP.js +18 -0
- package/dist/vendor/chunk-FNZOJHSV.js +71 -0
- package/dist/vendor/chunk-GXCHI7BM.js +131 -0
- package/dist/vendor/chunk-IIJ52ZU6.js +20 -0
- package/dist/vendor/chunk-J7A4CZDO.js +92 -0
- package/dist/vendor/chunk-JG5TTWPT.js +85 -0
- package/dist/vendor/chunk-JJ24ARBQ.js +394 -0
- package/dist/vendor/chunk-KOZMM5PZ.js +426 -0
- package/dist/vendor/chunk-KUUXSID7.js +18 -0
- package/dist/vendor/chunk-LEUPYT3Q.js +36 -0
- package/dist/vendor/chunk-M32LDTW7.js +18 -0
- package/dist/vendor/chunk-M7CLUILA.js +0 -0
- package/dist/vendor/chunk-MBSKBQ5D.js +21 -0
- package/dist/vendor/chunk-MLKGABMK.js +9 -0
- package/dist/vendor/chunk-NBVRTDTV.js +129 -0
- package/dist/vendor/chunk-OXNGN5H7.js +81 -0
- package/dist/vendor/chunk-QPLDT6A6.js +564 -0
- package/dist/vendor/chunk-QYBKAIA6.js +18 -0
- package/dist/vendor/chunk-RO2R4MJH.js +62 -0
- package/dist/vendor/chunk-SIR62W3Y.js +398 -0
- package/dist/vendor/chunk-TFIKFFSC.js +26 -0
- package/dist/vendor/chunk-TXKLEBBL.js +143 -0
- package/dist/vendor/chunk-UFH5UUZK.js +48 -0
- package/dist/vendor/chunk-VANKHF46.js +96 -0
- package/dist/vendor/chunk-VZBDV3TV.js +18 -0
- package/dist/vendor/chunk-XDKNKFBW.js +22 -0
- package/dist/vendor/chunk-XH4U3JSV.js +249 -0
- package/dist/vendor/chunk-Y35ZAO5U.js +53 -0
- package/dist/vendor/chunk-ZJMRMY57.js +0 -0
- package/dist/vendor/cjs/node/NodeChildProcessSpawner.d.ts +7 -0
- package/dist/vendor/cjs/node/NodeChildProcessSpawner.js +1 -0
- package/dist/vendor/cjs/node/NodeClusterHttp.d.ts +63 -0
- package/dist/vendor/cjs/node/NodeClusterHttp.js +1 -0
- package/dist/vendor/cjs/node/NodeClusterSocket.d.ts +79 -0
- package/dist/vendor/cjs/node/NodeClusterSocket.js +1 -0
- package/dist/vendor/cjs/node/NodeCrypto.d.ts +10 -0
- package/dist/vendor/cjs/node/NodeCrypto.js +1 -0
- package/dist/vendor/cjs/node/NodeFileSystem.d.ts +10 -0
- package/dist/vendor/cjs/node/NodeFileSystem.js +1 -0
- package/dist/vendor/cjs/node/NodeHttpClient.d.ts +179 -0
- package/dist/vendor/cjs/node/NodeHttpClient.js +1 -0
- package/dist/vendor/cjs/node/NodeHttpIncomingMessage.d.ts +62 -0
- package/dist/vendor/cjs/node/NodeHttpIncomingMessage.js +1 -0
- package/dist/vendor/cjs/node/NodeHttpPlatform.d.ts +36 -0
- package/dist/vendor/cjs/node/NodeHttpPlatform.js +1 -0
- package/dist/vendor/cjs/node/NodeHttpServer.d.ts +120 -0
- package/dist/vendor/cjs/node/NodeHttpServer.js +1 -0
- package/dist/vendor/cjs/node/NodeHttpServerRequest.d.ts +29 -0
- package/dist/vendor/cjs/node/NodeHttpServerRequest.js +1 -0
- package/dist/vendor/cjs/node/NodeMultipart.d.ts +46 -0
- package/dist/vendor/cjs/node/NodeMultipart.js +1 -0
- package/dist/vendor/cjs/node/NodeMultipartParser.d.ts +97 -0
- package/dist/vendor/cjs/node/NodeMultipartParser.js +1 -0
- package/dist/vendor/cjs/node/NodePath.d.ts +27 -0
- package/dist/vendor/cjs/node/NodePath.js +1 -0
- package/dist/vendor/cjs/node/NodeRedis.d.ts +74 -0
- package/dist/vendor/cjs/node/NodeRedis.js +1 -0
- package/dist/vendor/cjs/node/NodeRuntime.d.ts +85 -0
- package/dist/vendor/cjs/node/NodeRuntime.js +1 -0
- package/dist/vendor/cjs/node/NodeServices.d.ts +34 -0
- package/dist/vendor/cjs/node/NodeServices.js +1 -0
- package/dist/vendor/cjs/node/NodeSink.d.ts +8 -0
- package/dist/vendor/cjs/node/NodeSink.js +1 -0
- package/dist/vendor/cjs/node/NodeSocket.d.ts +38 -0
- package/dist/vendor/cjs/node/NodeSocket.js +1 -0
- package/dist/vendor/cjs/node/NodeSocketServer.d.ts +8 -0
- package/dist/vendor/cjs/node/NodeSocketServer.js +1 -0
- package/dist/vendor/cjs/node/NodeStdio.d.ts +11 -0
- package/dist/vendor/cjs/node/NodeStdio.js +1 -0
- package/dist/vendor/cjs/node/NodeStream.d.ts +8 -0
- package/dist/vendor/cjs/node/NodeStream.js +1 -0
- package/dist/vendor/cjs/node/NodeTerminal.d.ts +21 -0
- package/dist/vendor/cjs/node/NodeTerminal.js +1 -0
- package/dist/vendor/cjs/node/NodeWorker.d.ts +22 -0
- package/dist/vendor/cjs/node/NodeWorker.js +1 -0
- package/dist/vendor/cjs/node/NodeWorkerRunner.d.ts +12 -0
- package/dist/vendor/cjs/node/NodeWorkerRunner.js +1 -0
- package/dist/vendor/cjs/node/Undici.d.ts +28 -0
- package/dist/vendor/cjs/node/Undici.js +1 -0
- package/dist/vendor/cjs/node/index.d.ts +100 -0
- package/dist/vendor/cjs/node/index.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeChildProcessSpawner.d.ts +57 -0
- package/dist/vendor/cjs/node-shared/NodeChildProcessSpawner.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeClusterSocket.d.ts +22 -0
- package/dist/vendor/cjs/node-shared/NodeClusterSocket.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeCrypto.d.ts +27 -0
- package/dist/vendor/cjs/node-shared/NodeCrypto.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeFileSystem.d.ts +11 -0
- package/dist/vendor/cjs/node-shared/NodeFileSystem.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeHttpCompression.d.ts +36 -0
- package/dist/vendor/cjs/node-shared/NodeHttpCompression.js +1 -0
- package/dist/vendor/cjs/node-shared/NodePath.d.ts +27 -0
- package/dist/vendor/cjs/node-shared/NodePath.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeRuntime.d.ts +47 -0
- package/dist/vendor/cjs/node-shared/NodeRuntime.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeSink.d.ts +69 -0
- package/dist/vendor/cjs/node-shared/NodeSink.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeSocket.d.ts +134 -0
- package/dist/vendor/cjs/node-shared/NodeSocket.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeSocketServer.d.ts +80 -0
- package/dist/vendor/cjs/node-shared/NodeSocketServer.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeStdio.d.ts +12 -0
- package/dist/vendor/cjs/node-shared/NodeStdio.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeStream.d.ts +179 -0
- package/dist/vendor/cjs/node-shared/NodeStream.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeTerminal.d.ts +22 -0
- package/dist/vendor/cjs/node-shared/NodeTerminal.js +1 -0
- package/dist/vendor/cjs/node-shared/index.d.ts +56 -0
- package/dist/vendor/cjs/node-shared/index.js +1 -0
- package/dist/vendor/cjs/node-shared/internal/nodeChildProcessSpawner.d.ts +4 -0
- package/dist/vendor/cjs/node-shared/internal/nodeChildProcessSpawner.js +1 -0
- package/dist/vendor/cjs/node-shared/internal/utils.d.ts +2 -0
- package/dist/vendor/cjs/node-shared/internal/utils.js +1 -0
- package/dist/vendor/cjs/package.json +1 -0
- package/dist/vendor/cjs/platform-node-shared.d.ts +56 -0
- package/dist/vendor/cjs/platform-node-shared.js +1 -0
- package/dist/vendor/cjs/platform-node.d.ts +100 -0
- package/dist/vendor/cjs/platform-node.js +1 -0
- package/dist/vendor/node/LICENSE +21 -0
- package/dist/vendor/node/NodeChildProcessSpawner.d.ts +7 -0
- package/dist/vendor/node/NodeChildProcessSpawner.js +14 -0
- package/dist/vendor/node/NodeClusterHttp.d.ts +63 -0
- package/dist/vendor/node/NodeClusterHttp.js +43 -0
- package/dist/vendor/node/NodeClusterSocket.d.ts +79 -0
- package/dist/vendor/node/NodeClusterSocket.js +30 -0
- package/dist/vendor/node/NodeCrypto.d.ts +10 -0
- package/dist/vendor/node/NodeCrypto.js +8 -0
- package/dist/vendor/node/NodeFileSystem.d.ts +10 -0
- package/dist/vendor/node/NodeFileSystem.js +9 -0
- package/dist/vendor/node/NodeHttpClient.d.ts +179 -0
- package/dist/vendor/node/NodeHttpClient.js +46 -0
- package/dist/vendor/node/NodeHttpIncomingMessage.d.ts +62 -0
- package/dist/vendor/node/NodeHttpIncomingMessage.js +10 -0
- package/dist/vendor/node/NodeHttpPlatform.d.ts +36 -0
- package/dist/vendor/node/NodeHttpPlatform.js +16 -0
- package/dist/vendor/node/NodeHttpServer.d.ts +120 -0
- package/dist/vendor/node/NodeHttpServer.js +46 -0
- package/dist/vendor/node/NodeHttpServerRequest.d.ts +29 -0
- package/dist/vendor/node/NodeHttpServerRequest.js +9 -0
- package/dist/vendor/node/NodeMultipart.d.ts +46 -0
- package/dist/vendor/node/NodeMultipart.js +15 -0
- package/dist/vendor/node/NodeMultipartParser.d.ts +97 -0
- package/dist/vendor/node/NodeMultipartParser.js +11 -0
- package/dist/vendor/node/NodePath.d.ts +27 -0
- package/dist/vendor/node/NodePath.js +12 -0
- package/dist/vendor/node/NodeRedis.d.ts +74 -0
- package/dist/vendor/node/NodeRedis.js +11 -0
- package/dist/vendor/node/NodeRuntime.d.ts +85 -0
- package/dist/vendor/node/NodeRuntime.js +8 -0
- package/dist/vendor/node/NodeServices.d.ts +34 -0
- package/dist/vendor/node/NodeServices.js +23 -0
- package/dist/vendor/node/NodeSink.d.ts +8 -0
- package/dist/vendor/node/NodeSink.js +12 -0
- package/dist/vendor/node/NodeSocket.d.ts +38 -0
- package/dist/vendor/node/NodeSocket.js +31 -0
- package/dist/vendor/node/NodeSocketServer.d.ts +8 -0
- package/dist/vendor/node/NodeSocketServer.js +21 -0
- package/dist/vendor/node/NodeStdio.d.ts +11 -0
- package/dist/vendor/node/NodeStdio.js +10 -0
- package/dist/vendor/node/NodeStream.d.ts +8 -0
- package/dist/vendor/node/NodeStream.js +27 -0
- package/dist/vendor/node/NodeTerminal.d.ts +21 -0
- package/dist/vendor/node/NodeTerminal.js +10 -0
- package/dist/vendor/node/NodeWorker.d.ts +22 -0
- package/dist/vendor/node/NodeWorker.js +9 -0
- package/dist/vendor/node/NodeWorkerRunner.d.ts +12 -0
- package/dist/vendor/node/NodeWorkerRunner.js +7 -0
- package/dist/vendor/node/Undici.d.ts +28 -0
- package/dist/vendor/node/Undici.js +9 -0
- package/dist/vendor/node/index.d.ts +100 -0
- package/dist/vendor/node/index.js +115 -0
- package/dist/vendor/node-shared/LICENSE +21 -0
- package/dist/vendor/node-shared/NodeChildProcessSpawner.d.ts +57 -0
- package/dist/vendor/node-shared/NodeChildProcessSpawner.js +13 -0
- package/dist/vendor/node-shared/NodeClusterSocket.d.ts +22 -0
- package/dist/vendor/node-shared/NodeClusterSocket.js +11 -0
- package/dist/vendor/node-shared/NodeCrypto.d.ts +27 -0
- package/dist/vendor/node-shared/NodeCrypto.js +9 -0
- package/dist/vendor/node-shared/NodeFileSystem.d.ts +11 -0
- package/dist/vendor/node-shared/NodeFileSystem.js +8 -0
- package/dist/vendor/node-shared/NodeHttpCompression.d.ts +36 -0
- package/dist/vendor/node-shared/NodeHttpCompression.js +13 -0
- package/dist/vendor/node-shared/NodePath.d.ts +27 -0
- package/dist/vendor/node-shared/NodePath.js +11 -0
- package/dist/vendor/node-shared/NodeRuntime.d.ts +47 -0
- package/dist/vendor/node-shared/NodeRuntime.js +7 -0
- package/dist/vendor/node-shared/NodeSink.d.ts +69 -0
- package/dist/vendor/node-shared/NodeSink.js +11 -0
- package/dist/vendor/node-shared/NodeSocket.d.ts +134 -0
- package/dist/vendor/node-shared/NodeSocket.js +23 -0
- package/dist/vendor/node-shared/NodeSocketServer.d.ts +80 -0
- package/dist/vendor/node-shared/NodeSocketServer.js +20 -0
- package/dist/vendor/node-shared/NodeStdio.d.ts +12 -0
- package/dist/vendor/node-shared/NodeStdio.js +9 -0
- package/dist/vendor/node-shared/NodeStream.d.ts +179 -0
- package/dist/vendor/node-shared/NodeStream.js +26 -0
- package/dist/vendor/node-shared/NodeTerminal.d.ts +22 -0
- package/dist/vendor/node-shared/NodeTerminal.js +9 -0
- package/dist/vendor/node-shared/index.d.ts +56 -0
- package/dist/vendor/node-shared/index.js +58 -0
- package/dist/vendor/node-shared/internal/nodeChildProcessSpawner.d.ts +4 -0
- package/dist/vendor/node-shared/internal/nodeChildProcessSpawner.js +7 -0
- package/dist/vendor/node-shared/internal/utils.d.ts +2 -0
- package/dist/vendor/node-shared/internal/utils.js +7 -0
- package/dist/vendor/platform-node-shared.d.ts +56 -0
- package/dist/vendor/platform-node-shared.js +58 -0
- package/dist/vendor/platform-node.d.ts +100 -0
- package/dist/vendor/platform-node.js +115 -0
- package/docs/README.md +129 -0
- package/docs/api.md +553 -0
- package/docs/concepts/descriptor-relative-filesystem.md +181 -0
- package/docs/concepts/host-bundle.md +125 -0
- package/docs/concepts/process-containment.md +190 -0
- package/docs/guides/answer-run-ownership.md +87 -0
- package/docs/guides/configure-the-filesystem-helper.md +152 -0
- package/docs/guides/contain-child-processes.md +139 -0
- package/docs/guides/match-files-with-glob.md +151 -0
- package/docs/installation.md +204 -0
- package/docs/quickstart.md +147 -0
- package/docs/troubleshooting.md +130 -0
- package/package.json +202 -3
- package/src/AtomicFileSystem.ts +415 -0
- package/src/EgressHttpClient.ts +327 -0
- package/src/HostLiveness.ts +103 -0
- package/src/NodeHost.ts +253 -0
- package/src/ProcessConfinement.ts +363 -0
- package/src/ProcessReaper.ts +951 -0
- package/src/ProcessSandbox.ts +1307 -0
- package/src/ScopedProcess.ts +124 -0
- package/src/index.ts +44 -0
- package/src/internal/AtomicFileSystemExecutable.ts +379 -0
- package/src/internal/AtomicFileSystemProtocol.ts +502 -0
- package/src/internal/AtomicFileSystemTransport.ts +272 -0
- package/src/internal/AtomicHelperError.ts +50 -0
- package/src/internal/AtomicHelperFault.ts +84 -0
- package/src/internal/PipedProcess.ts +357 -0
- package/src/internal/ProcSnapshot.ts +193 -0
- package/src/internal/ProcessCleanup.ts +99 -0
- package/src/internal/ProcessCleanupPolicyError.ts +30 -0
- package/src/internal/ProcessFault.ts +82 -0
- package/src/internal/ProcessSupervisor.ts +807 -0
- package/src/internal/SupervisorProgram.ts +309 -0
- package/src/internal/WindowsProcessJob.ts +114 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "The descriptor-relative filesystem"
|
|
3
|
+
description: "Why AtomicFileSystem runs every filesystem operation through the smithers-jj-export helper, what confinement that buys, which operations it covers, and what it costs per call."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
`AtomicFileSystem` is `NodeHost`'s filesystem slot, and it is not Effect's
|
|
9
|
+
`NodeFileSystem`. It exists to close one race.
|
|
10
|
+
|
|
11
|
+
## The race it closes
|
|
12
|
+
|
|
13
|
+
A capability check names a path. A path-based operation resolves that path
|
|
14
|
+
again when it runs. Between the two, anything with write access to the
|
|
15
|
+
workspace can replace a component of the path with a symlink, and the operation
|
|
16
|
+
that was authorized for one file performs on another.
|
|
17
|
+
|
|
18
|
+
POSIX has the answer: open a directory, keep the descriptor, and perform the
|
|
19
|
+
final syscall relative to it (`openat(2)`, `renameat(2)`, and the `dir_fd`
|
|
20
|
+
family). A descriptor names an inode, not a name, so a swap after the walk
|
|
21
|
+
cannot redirect anything.
|
|
22
|
+
|
|
23
|
+
Node's JavaScript filesystem API exposes none of that, and no root-handle
|
|
24
|
+
equivalent. So `AtomicFileSystem` delegates each operation to the native
|
|
25
|
+
`smithers-jj-export` helper, which does have it. For ordinary operations and batches, the helper walks
|
|
26
|
+
from the filesystem root to the canonical workspace root using descriptor-relative
|
|
27
|
+
`O_NOFOLLOW | O_DIRECTORY` opens for every component. It verifies the opened
|
|
28
|
+
root's device/inode against the identity captured when the kernel layer was
|
|
29
|
+
composed, then performs the operation relative to a pinned parent descriptor.
|
|
30
|
+
An ancestor symlink swap or replacement of the root by another directory fails
|
|
31
|
+
ordinary operations with `PermissionDenied` before any content is read or changed.
|
|
32
|
+
|
|
33
|
+
## What the helper refuses
|
|
34
|
+
|
|
35
|
+
The confinement rules are strict, and two of them surprise people:
|
|
36
|
+
|
|
37
|
+
- **A regular file with more than one hard link cannot be opened.** A hard link
|
|
38
|
+
is not a symlink, so `O_NOFOLLOW` cannot confine it: the same inode is
|
|
39
|
+
reachable from outside the workspace under another name. Refusing the open is
|
|
40
|
+
the only confinement available.
|
|
41
|
+
- **A symlink is never traversed, opened, renamed, or removed.** Directory
|
|
42
|
+
listing is the single exception. It names a symlink entry and never descends
|
|
43
|
+
through it, because listing resolves nothing and refusing would buy no
|
|
44
|
+
confinement.
|
|
45
|
+
|
|
46
|
+
Two more rules follow from requiring the kind on the descriptor rather than on
|
|
47
|
+
a name:
|
|
48
|
+
|
|
49
|
+
- **Only regular files carry content.** A FIFO, a socket, a device, or a
|
|
50
|
+
directory is refused for every read and write, because the kind is checked on
|
|
51
|
+
the already-open descriptor and cannot be swapped afterwards.
|
|
52
|
+
- **Every open is non-blocking.** A named pipe planted at a path cannot park
|
|
53
|
+
the adapter inside `open()` until some other process opens the other end. A
|
|
54
|
+
write-only open of a reader-less pipe returns a typed failure instead. Node's
|
|
55
|
+
own filesystem waits there indefinitely, which is the one place this adapter
|
|
56
|
+
is deliberately stricter than the implementation it mirrors.
|
|
57
|
+
|
|
58
|
+
The workspace root itself is addressable like any other directory: `exists`,
|
|
59
|
+
`stat`, `readDirectory`, `realPath`, `glob`, and a recursive `makeDirectory`
|
|
60
|
+
all answer for it. Removing it, renaming it or onto it, reading it as a file,
|
|
61
|
+
writing over it, and a non-recursive `makeDirectory` on it stay refused.
|
|
62
|
+
|
|
63
|
+
## Which operations it covers
|
|
64
|
+
|
|
65
|
+
Fifteen operations can be expressed as one descriptor-relative request, and
|
|
66
|
+
those are the ones the adapter implements:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
readFile readFileString writeFile writeFileString exists stat
|
|
70
|
+
readLink realPath makeDirectory readDirectory remove rename glob
|
|
71
|
+
chmod chown
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`chmod` and `chown` change metadata through the descriptor of the resolved
|
|
75
|
+
target (`fchmod`, `fchown`), never through a path. They refuse a symbolic link,
|
|
76
|
+
a hard-linked file and the workspace root, a mode outside `0` to `0o7777`, and
|
|
77
|
+
an owner below `-1`; `-1` leaves a `uid` or `gid` unchanged. The change is
|
|
78
|
+
synced before the call returns. A refusal arrives as a typed failure and the
|
|
79
|
+
target keeps its previous mode and owner.
|
|
80
|
+
|
|
81
|
+
Everything else on Effect's `FileSystem` surface returns a live handle or a
|
|
82
|
+
stream that Node cannot open relative to a pinned descriptor: `open`, `stream`, `sink`, `watch`, `copy`,
|
|
83
|
+
`copyFile`, `link`, `symlink`, `access`, `truncate`, `utimes`, and the `makeTemp*` family.
|
|
84
|
+
Under the kernel decorator each of those fails closed with a typed
|
|
85
|
+
`PermissionDenied` rather than silently reverting to a path-based call. A
|
|
86
|
+
program that needs them reaches for the raw `NodeHost.NodeFileSystem` outside
|
|
87
|
+
the capability boundary.
|
|
88
|
+
|
|
89
|
+
Wrapping that raw layer directly also fails closed, because it carries no
|
|
90
|
+
atomic extension for the kernel to call.
|
|
91
|
+
|
|
92
|
+
## How the helper is started
|
|
93
|
+
|
|
94
|
+
The helper is `smithers-jj-export --atomic-fs`, a Rust binary shipped with the
|
|
95
|
+
package. Three properties of how it is started are load-bearing:
|
|
96
|
+
|
|
97
|
+
- **Absolute path, never a `PATH` lookup.** A binary planted in the working
|
|
98
|
+
directory or on an injected `PATH` would run inside the process that holds
|
|
99
|
+
the pinned root descriptor.
|
|
100
|
+
- **Outside the workspace.** The resolved executable must be an executable
|
|
101
|
+
regular file outside the confined root. A packaged helper is copied out of
|
|
102
|
+
the workspace when the first host layer in the process is built, before any
|
|
103
|
+
flow runs; later layer builds copy nothing.
|
|
104
|
+
- **Inert process.** It starts with an empty environment and the filesystem
|
|
105
|
+
root as its working directory.
|
|
106
|
+
|
|
107
|
+
Both directions of the protocol are length-framed and bounded, so neither a
|
|
108
|
+
large file nor a malfunctioning helper can make the host allocate without
|
|
109
|
+
limit. See
|
|
110
|
+
[Configure the filesystem helper](../guides/configure-the-filesystem-helper.md)
|
|
111
|
+
for the ceilings and how to change them.
|
|
112
|
+
|
|
113
|
+
## What it costs
|
|
114
|
+
|
|
115
|
+
An ordinary operation starts one helper. The guarded filesystem also
|
|
116
|
+
offers `FileSystem.batch(fs)` from `@smthrs/kernel/FileSystem`: one helper
|
|
117
|
+
serves up to 128 read requests against the same root descriptor. Requests may
|
|
118
|
+
stat, list directories, expand host globs, or collect SHA-256 digests, optionally
|
|
119
|
+
with file bytes. The helper exits after that request. There is no persistent
|
|
120
|
+
process.
|
|
121
|
+
|
|
122
|
+
Through the kernel's guarded filesystem, authorization resolves each path
|
|
123
|
+
through the helper too, never through a host pathname: once before the grant
|
|
124
|
+
decision and once after. A guarded operation therefore starts three helpers,
|
|
125
|
+
and so does a guarded batch, whose members are resolved together.
|
|
126
|
+
|
|
127
|
+
- Prefer one recursive `readDirectory` (one helper for the whole tree) to a read
|
|
128
|
+
per entry.
|
|
129
|
+
- Use bounded batches for a wide fan-out. Without a ceiling, an
|
|
130
|
+
`Effect.forEach(files, read, { concurrency: "unbounded" })` over fifty paths
|
|
131
|
+
would start fifty helpers at once, which is why the adapter carries a
|
|
132
|
+
process ceiling at all.
|
|
133
|
+
|
|
134
|
+
Each batch member keeps its own canonical resource and grant check. Denied
|
|
135
|
+
members never enter the helper. Results retain their request index and sort by
|
|
136
|
+
normalized path using JavaScript's UTF-16 order, including Unicode names and
|
|
137
|
+
duplicate requests. Directory and glob results also sort deterministically.
|
|
138
|
+
|
|
139
|
+
The guard captures the root's device/inode at composition. The helper walks to
|
|
140
|
+
that root without following any component symlink and verifies its identity
|
|
141
|
+
before and after measurement. Root loss is a boundary failure, not a collection
|
|
142
|
+
of absent files. Digest reads compare descriptor metadata before and after the
|
|
143
|
+
read, then compare the pathname against that descriptor. Directory walks check
|
|
144
|
+
their opened directories for observed mutations. These checks detect changes
|
|
145
|
+
during a measurement; they do not freeze a workspace or claim a simultaneous
|
|
146
|
+
snapshot of unrelated paths.
|
|
147
|
+
|
|
148
|
+
A workspace symlink is allowed when composed, but the kernel rechecks its
|
|
149
|
+
logical path before and after grants. Retargeting it refuses the batch even
|
|
150
|
+
while the old canonical root remains available to the helper.
|
|
151
|
+
|
|
152
|
+
An entry absent before measurement reports `NotFound`. An entry or ancestor
|
|
153
|
+
that disappears after measurement begins reports `Busy`, so an interrupted
|
|
154
|
+
read cannot become evidence that the file was initially absent.
|
|
155
|
+
|
|
156
|
+
The digest is SHA-256 of the original bytes. No JSON, encoding, or pathname is
|
|
157
|
+
added to its input. Boundary tree and read-set identities remain the engine's
|
|
158
|
+
existing canonical constructions.
|
|
159
|
+
|
|
160
|
+
`AtomicFileSystem.helperSpawns()` exposes a process-local count for diagnostics.
|
|
161
|
+
Elapsed timings from `scripts/observation-bench.ts` are local measurements and
|
|
162
|
+
do not establish a production speed claim.
|
|
163
|
+
|
|
164
|
+
## Errors
|
|
165
|
+
|
|
166
|
+
Errno is normalized to the same reasons `@effect/platform-node` reports, so a
|
|
167
|
+
caller reads the same typed reason it would get from the native filesystem,
|
|
168
|
+
with one addition: a helper failure that carries no errno at all stays
|
|
169
|
+
`PermissionDenied`, so the boundary fails closed.
|
|
170
|
+
|
|
171
|
+
Writes follow Effect's `OpenFlag` contract exactly (`r`, `r+`, `w`, `wx`, `w+`,
|
|
172
|
+
`wx+`, `a`, `ax`, `a+`, `ax+`), and the test suite checks each flag against the
|
|
173
|
+
native Node filesystem. Truncation runs on the opened descriptor rather than
|
|
174
|
+
through `O_TRUNC`, so a hard link is refused before the file is modified.
|
|
175
|
+
|
|
176
|
+
`remove(path, { recursive: true })` walks iteratively with an explicit
|
|
177
|
+
descriptor stack, bounded at 512 levels deep and 100000 entries visited. One
|
|
178
|
+
directory's names are read before any of its entries is unlinked, because
|
|
179
|
+
unlinking from a directory while iterating it is undefined. Progress is partial
|
|
180
|
+
on refusal: entries already unlinked stay unlinked. `force: true` succeeds for
|
|
181
|
+
a path whose ancestors do not exist, exactly as `fs.rm` does.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "The host bundle"
|
|
3
|
+
description: "Why the Host surface is a closed set of five tags, which of them this package composes and which it implements, and why there is no shell service, no HTTP wrapper, and no Windows support."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 1
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A Smithers **host** is the boundary between a durable flow and the machine
|
|
9
|
+
underneath it. The surface is closed: five service tags, no more, and
|
|
10
|
+
[`@smthrs/kernel`](/api/kernel) owns the list.
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
FileSystem Path ChildProcessSpawner Jj HttpClient
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Closing the list is what makes the capability check complete. The kernel
|
|
17
|
+
decorates each of the five with a permission check, so a program that reaches
|
|
18
|
+
the machine has to go through a tag the kernel is watching. A sixth capability
|
|
19
|
+
smuggled in beside them would be authority nobody checked.
|
|
20
|
+
|
|
21
|
+
Three of the five tags are Effect's own, from `effect/FileSystem`,
|
|
22
|
+
`effect/Path`, and `effect/unstable/process/ChildProcessSpawner` and
|
|
23
|
+
`effect/unstable/http/HttpClient`. Smithers provides implementations of them
|
|
24
|
+
rather than wrappers around them, so an ordinary Effect program runs on a
|
|
25
|
+
Smithers host unchanged. `Jj` is the one Smithers tag, and it lives in
|
|
26
|
+
[`@smthrs/jj`](/api/jj).
|
|
27
|
+
|
|
28
|
+
## What this package composes
|
|
29
|
+
|
|
30
|
+
`@effect/platform-node` already ships most of a Node host. `NodeHost.layer` is
|
|
31
|
+
mostly assembly:
|
|
32
|
+
|
|
33
|
+
| Tag | Implementation |
|
|
34
|
+
| --------------------- | --------------------------------------- |
|
|
35
|
+
| `Path` | `NodePath.layer` |
|
|
36
|
+
| `ChildProcessSpawner` | `NodeChildProcessSpawner.layer` |
|
|
37
|
+
| `HttpClient` | `EgressHttpClient.layer(process.env)` |
|
|
38
|
+
| `Jj` | `@smthrs/jj`'s `NodeJj.layer` |
|
|
39
|
+
| `FileSystem` | this package's `AtomicFileSystem.layer` |
|
|
40
|
+
|
|
41
|
+
Only the filesystem slot is this package's own implementation, and
|
|
42
|
+
[it exists because Node cannot express what the kernel needs](./descriptor-relative-filesystem.md).
|
|
43
|
+
|
|
44
|
+
The HTTP slot is Undici either way. `EgressHttpClient.layer` routes it through
|
|
45
|
+
the egress proxy `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY` name, and is exactly
|
|
46
|
+
`NodeHttpClient.layerUndici` when they name none, so an unproxied host is
|
|
47
|
+
unchanged. A bundle spans a whole host, and a host may be a sandbox whose only
|
|
48
|
+
way out is that proxy; a bare Undici `Agent` reads none of those variables and
|
|
49
|
+
dials every origin directly, which such a host drops.
|
|
50
|
+
|
|
51
|
+
`NodeHost` re-exports the pieces it composes, so a program that wants one slot
|
|
52
|
+
rather than the whole bundle needs no second dependency:
|
|
53
|
+
`NodeHost.AtomicFileSystem`, `NodeHost.ProcessReaper`, `NodeHost.NodeCrypto`,
|
|
54
|
+
`NodeHost.NodeFileSystem`, `NodeHost.NodeChildProcessSpawner`,
|
|
55
|
+
`NodeHost.NodeHttpClient`, and `NodeHost.EgressHttpClient`.
|
|
56
|
+
|
|
57
|
+
`NodeCrypto` is there for a different reason from the rest. `Crypto` is not a
|
|
58
|
+
Host service: it carries no host authority the kernel could attenuate, so it is
|
|
59
|
+
not in the closed list. Every durable composition needs one anyway, and a
|
|
60
|
+
program that already depends on this package for its host should not need a
|
|
61
|
+
second dependency for the digest.
|
|
62
|
+
|
|
63
|
+
## The four layers
|
|
64
|
+
|
|
65
|
+
The bundle ships four layers, along two independent axes: where `Jj` is rooted,
|
|
66
|
+
and whether spawned processes are contained.
|
|
67
|
+
|
|
68
|
+
| Layer | `Jj` bound to | Containment |
|
|
69
|
+
| ------------------------------------------- | ----------------------------- | ----------- |
|
|
70
|
+
| `NodeHost.layer` | the process working directory | no |
|
|
71
|
+
| `NodeHost.layerAt(root)` | one absolute repository root | no |
|
|
72
|
+
| `NodeHost.layerContained(options?)` | the process working directory | yes |
|
|
73
|
+
| `NodeHost.layerContainedAt(root, options?)` | one absolute repository root | yes |
|
|
74
|
+
|
|
75
|
+
The rooted pair exists because a durable host outlives any one working
|
|
76
|
+
directory. `@smthrs/flows`' `NodeRuntime` composes `layerContainedAt`, so both
|
|
77
|
+
the repository and the process containment are pinned by the runtime rather
|
|
78
|
+
than inherited from whatever shell started it.
|
|
79
|
+
|
|
80
|
+
The contained pair carries a requirement the plain pair does not: a
|
|
81
|
+
`ProcessLedger`. See [Process containment](./process-containment.md) for what
|
|
82
|
+
it buys and why the ledger is not defaulted.
|
|
83
|
+
|
|
84
|
+
## What the bundle does not provide
|
|
85
|
+
|
|
86
|
+
Each of these is a decision with a consequence, not a gap.
|
|
87
|
+
|
|
88
|
+
**There is no shell service.** Running a command is Effect's `ChildProcess` and
|
|
89
|
+
`ChildProcessSpawner`. That means a wall-clock budget is `Effect.timeout`
|
|
90
|
+
around the effect rather than an option on a command record, and cancellation
|
|
91
|
+
is fiber interruption rather than an `AbortSignal`. A Smithers shell service
|
|
92
|
+
would be a second way to express what Effect already expresses, and the kernel
|
|
93
|
+
would then have two spawn paths to check instead of one.
|
|
94
|
+
|
|
95
|
+
**There is no HTTP service.** An outgoing request is Effect's `HttpClient`, and
|
|
96
|
+
`@effect/platform-node` already ships the Undici-backed implementation. Undici
|
|
97
|
+
follows no redirect unless a redirect interceptor is installed, and this bundle
|
|
98
|
+
installs none, so every hop stays a separate request. That is what lets the
|
|
99
|
+
kernel check `net:get` and `net:post` against the host of every URL a request
|
|
100
|
+
actually reaches, redirect targets included.
|
|
101
|
+
|
|
102
|
+
**There is no Windows support.** `AtomicFileSystem` needs `O_NOFOLLOW`,
|
|
103
|
+
`O_DIRECTORY`, and `dir_fd`, none of which Windows has, so every filesystem
|
|
104
|
+
call fails closed there. `ProcessReaper` still reaches `taskkill /T /F` through
|
|
105
|
+
`systemFor("win32")` rather than retiring every record unsignalled, because a
|
|
106
|
+
reaper that silently discarded records would be worse than one that says what
|
|
107
|
+
it does. Treat it as unsupported best-effort.
|
|
108
|
+
|
|
109
|
+
**There is no path-based fallback.** A host with no usable `smithers-jj-export` helper fails
|
|
110
|
+
every guarded filesystem call with `PermissionDenied`. The adapter never
|
|
111
|
+
degrades to a check-then-path operation, because that is exactly the race it
|
|
112
|
+
exists to close.
|
|
113
|
+
|
|
114
|
+
## Conformance
|
|
115
|
+
|
|
116
|
+
The bundle is not trusted to be correct because it is composed correctly. It
|
|
117
|
+
runs the shared host contract from `@smthrs/kernel/test/contract` twice: once
|
|
118
|
+
with explicit expectations, and once taking every default the suite offers,
|
|
119
|
+
against a loopback HTTP server so the `HttpClient` success path is asserted
|
|
120
|
+
rather than only its refusal.
|
|
121
|
+
|
|
122
|
+
On top of that, the atomic filesystem is compared row by row against
|
|
123
|
+
`@effect/platform-node`'s own adapter (open flags, errno classification, `stat`
|
|
124
|
+
fields, and the glob grammar), and containment is driven over real detached
|
|
125
|
+
process groups.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Process containment"
|
|
3
|
+
description: "How a crashed host's abandoned processes get killed: incarnations, the process ledger, and every guard ProcessReaper checks before it signals a durable record."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 3
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A target's exit does not mean its children exited. A host killed outright
|
|
9
|
+
runs no finalizers either. The contained Node host uses a live POSIX supervisor
|
|
10
|
+
to own each process group independently of the target and to observe host loss
|
|
11
|
+
through a private connection. A process ledger lets a later host reconcile
|
|
12
|
+
records whose cleanup was not confirmed.
|
|
13
|
+
|
|
14
|
+
## Incarnations
|
|
15
|
+
|
|
16
|
+
A **host** has a durable `hostId`. Each time a process starts under that id, it
|
|
17
|
+
is a new **incarnation**, identified by its pid. The ledger records which
|
|
18
|
+
incarnation started each process, so a later incarnation can tell its own live
|
|
19
|
+
children from an abandoned one's.
|
|
20
|
+
|
|
21
|
+
That is the whole shape of the problem: the input to reaping is a durable
|
|
22
|
+
record that outlived the process that wrote it, on a machine where the
|
|
23
|
+
operating system reuses pid numbers.
|
|
24
|
+
|
|
25
|
+
## The live half: the contained spawner
|
|
26
|
+
|
|
27
|
+
`NodeHost.layerContained` uses `ProcessReaper.layerSpawner` to compose
|
|
28
|
+
[`@smthrs/kernel`](/api/kernel)'s `ContainedSpawner` with a native process
|
|
29
|
+
adapter and `ProcessReaper.processLifecycle`:
|
|
30
|
+
|
|
31
|
+
- Preparation creates an owner without executing the target. The kernel
|
|
32
|
+
records its identity and the original command digest before activation.
|
|
33
|
+
- Every pipeline leg has a separate owner and ledger record. On POSIX the
|
|
34
|
+
handle's `pid` is that supervisor's; `exitCode` and `isRunning` describe the
|
|
35
|
+
target. Signals go through `handle.kill`.
|
|
36
|
+
- Cleanup keeps its deadline after the target exits, so a same-group child
|
|
37
|
+
cannot keep inherited output open indefinitely. Only verified cleanup retires
|
|
38
|
+
the record; failed cleanup fails scope close and retains it.
|
|
39
|
+
- The grace period is an upper bound. Once the target has exited and the group
|
|
40
|
+
holds no other live member and no captured escaped descendant, cleanup
|
|
41
|
+
finishes at once.
|
|
42
|
+
- A grouped supervisor starts a guardian in its group before the target: a
|
|
43
|
+
`/bin/sh` that waits on a pipe only the supervisor holds. A supervisor killed
|
|
44
|
+
outright (`SIGKILL`, the OOM killer) runs no handler, so at that pipe's EOF
|
|
45
|
+
the guardian kills its own group with `kill 0`, which names the caller's
|
|
46
|
+
group rather than a number that could be stale. It ignores the catchable
|
|
47
|
+
signals a stop sends the group and dies in the supervisor's final `SIGKILL`.
|
|
48
|
+
A host without `/bin/sh` refuses to start a grouped target. A lost
|
|
49
|
+
supervisor sends no cleanup receipt, so its release stays unverified.
|
|
50
|
+
|
|
51
|
+
Confined children use private directories for `HOME` and XDG config, cache, data, and state.
|
|
52
|
+
|
|
53
|
+
Default commands lead their own group. `detached: false` opts out of group
|
|
54
|
+
cleanup and signals only the native target. Explicit stopping of a still-live
|
|
55
|
+
grouped target additionally attempts a revalidated positive-PID sweep of
|
|
56
|
+
escaped descendants, with ordinary POSIX PID races remaining. Natural exit
|
|
57
|
+
does not promise cleanup of deliberately escaped sessions.
|
|
58
|
+
|
|
59
|
+
An explicit stop owns its deadline until cleanup is acknowledged. A target exit
|
|
60
|
+
received during that stop does not send another request. The helper preserves
|
|
61
|
+
the accepted signal and grace period across target exit and either socket's
|
|
62
|
+
closure, even when its configured default is `SIGKILL`. A still-live target
|
|
63
|
+
receives that policy even after calling `setsid()` to leave the supervisor's
|
|
64
|
+
group. Group membership selects the descendant sweep, not the target's signal
|
|
65
|
+
or grace period. Only an unactivated owner or verified natural completion can
|
|
66
|
+
use the host's immediate cleanup shortcut. After the target exits, the helper
|
|
67
|
+
reads the process table once: an empty group with no captured escaped
|
|
68
|
+
descendant ends the stop at once, since waiting cannot clean anything more.
|
|
69
|
+
A live group member, a captured escaped descendant or an unreadable table keeps
|
|
70
|
+
the full deadline. Requests and lifetime
|
|
71
|
+
status use separate private sockets, so an EPIPE on a late request cannot discard
|
|
72
|
+
a buffered target exit or cleanup receipt. The host drains that receipt and
|
|
73
|
+
verifies the process group before retiring the ledger record.
|
|
74
|
+
|
|
75
|
+
Delivery deadlines and verification retries use the platform clock. Freezing
|
|
76
|
+
the caller's Effect clock does not freeze native process cleanup or turn an
|
|
77
|
+
unverifiable group into an unbounded scope finalizer.
|
|
78
|
+
|
|
79
|
+
`jj` goes through that same spawner rather than around it. `NodeJj.layer`
|
|
80
|
+
spawns its own children, which is right for a host that has no spawner to
|
|
81
|
+
offer; under containment it would mean a `jj` invocation that leads no recorded
|
|
82
|
+
process group, appears in no ledger, and survives the incarnation that started
|
|
83
|
+
it.
|
|
84
|
+
|
|
85
|
+
## The durable half: the sweep
|
|
86
|
+
|
|
87
|
+
`ProcessReaper.reap` runs once while the contained layer is built. It reads the
|
|
88
|
+
records a previous incarnation of the same `hostId` left behind and kills the
|
|
89
|
+
process groups they name. Standing a host up is therefore also what cleans up
|
|
90
|
+
after the incarnation that crashed.
|
|
91
|
+
|
|
92
|
+
The rules are narrow on purpose. A record is signalled only when every one of
|
|
93
|
+
these holds:
|
|
94
|
+
|
|
95
|
+
1. It belongs to a different incarnation of the same `hostId`. The ledger
|
|
96
|
+
filters that before the reaper sees it.
|
|
97
|
+
2. Its owner is gone. A pid that exists but belongs to another user counts as
|
|
98
|
+
alive, so only `ESRCH` reads as dead and every other answer keeps the
|
|
99
|
+
record.
|
|
100
|
+
3. Its numbers name something this platform can signal. On POSIX that is a
|
|
101
|
+
process group the child leads itself, so `pgid` has to be a safe integer
|
|
102
|
+
above 1 and equal to `pid`. On Windows it is a pid, so `pgid` has to be
|
|
103
|
+
absent. The check is made against the durable record rather than trusted
|
|
104
|
+
from it, because `process.kill(-0, "SIGKILL")` would signal this host's own
|
|
105
|
+
process group.
|
|
106
|
+
4. That group is not this process's group, and its pid is not this process's
|
|
107
|
+
pid. The host's real process group is read from the operating system,
|
|
108
|
+
because a stored number can claim anything and the shell that started this
|
|
109
|
+
host shares its group.
|
|
110
|
+
5. The number still names the process the record describes. A record written
|
|
111
|
+
before this machine booted names a pid from a pid space that no longer
|
|
112
|
+
exists; within one boot, the recorded start time has to match the start time
|
|
113
|
+
the operating system reports for that pid.
|
|
114
|
+
|
|
115
|
+
## No evidence never authorizes a kill
|
|
116
|
+
|
|
117
|
+
Two of those guards are questions put to `ps`: this process's own group, and
|
|
118
|
+
when the recorded pid started. Either can go unanswered on a host with no
|
|
119
|
+
usable `ps`.
|
|
120
|
+
|
|
121
|
+
An unanswered guard refuses. A guard that did not run is not a guard that
|
|
122
|
+
passed, so a record whose identity could not be verified is kept rather than
|
|
123
|
+
signalled.
|
|
124
|
+
|
|
125
|
+
The probe is deliberately unhelpful to anyone trying to fool it. It runs
|
|
126
|
+
`/bin/ps` by absolute path, never a `PATH` lookup, under `LC_ALL=C` and `TZ=UTC`
|
|
127
|
+
in a minimal environment, with a five-second `SIGKILL` deadline. Start times
|
|
128
|
+
are parsed explicitly as UTC, independent of the host runtime's timezone.
|
|
129
|
+
Live cleanup on Linux instead reads group membership, start times, and zombie
|
|
130
|
+
state directly from `/proc`, so it works without `procps`. Missing processes
|
|
131
|
+
are skipped; unreadable or malformed data leaves cleanup unverified. Other
|
|
132
|
+
POSIX hosts use `/bin/ps` for live cleanup with the same timezone and parsing.
|
|
133
|
+
The crash-recovery sweep still uses the `ps` probes described here.
|
|
134
|
+
The probe reads the
|
|
135
|
+
`pgid` column as one run of decimal digits and nothing else: `Number.parseInt`
|
|
136
|
+
reads a prefix, so `"12 34"`, `"12abc"`, and `"0x10"` would each become a
|
|
137
|
+
number that is not this host's group, silently pass the own-group comparison,
|
|
138
|
+
and suppress the refusal that exists to say the comparison could not be made.
|
|
139
|
+
|
|
140
|
+
## Refusals, and which ones are final
|
|
141
|
+
|
|
142
|
+
Every refusal also decides whether the record is retired. Retiring says in the
|
|
143
|
+
journal that nothing was signalled, which stops every later incarnation
|
|
144
|
+
re-examining a number the operating system has moved on from. Only a refusal a
|
|
145
|
+
later incarnation cannot answer differently is final.
|
|
146
|
+
|
|
147
|
+
| Refusal | What it means | Retired |
|
|
148
|
+
| --------------------- | ----------------------------------------------------------- | ------- |
|
|
149
|
+
| `owner-alive` | the incarnation that started it is still running | no |
|
|
150
|
+
| `identity-unverified` | this host could not read the pid's start time | no |
|
|
151
|
+
| `own-group-unknown` | this host could not read its own process group | no |
|
|
152
|
+
| `kill-failed` | the signal was refused, so it is tried again | no |
|
|
153
|
+
| `no-group` | it shared its owner's group, so there is no group to signal | yes |
|
|
154
|
+
| `own-group` | it named this host's own group or pid | yes |
|
|
155
|
+
| `invalid-record` | its numbers name nothing this platform may signal | yes |
|
|
156
|
+
| `pre-boot` | it was written before this machine booted | yes |
|
|
157
|
+
| `process-gone` | the pid it names does not exist | yes |
|
|
158
|
+
| `identity-mismatch` | the pid exists but did not start when the record says | yes |
|
|
159
|
+
|
|
160
|
+
A retirement that fails to commit is logged rather than propagated. The process
|
|
161
|
+
is already dead and the record stays inherited, so the next incarnation reads
|
|
162
|
+
it, finds the pid gone, and retires it then; failing the whole sweep would take
|
|
163
|
+
the host down for a bookkeeping write.
|
|
164
|
+
|
|
165
|
+
## The one deliberate leak
|
|
166
|
+
|
|
167
|
+
`identityToleranceMs` is 2000. It covers two gaps at once: `ps` reports
|
|
168
|
+
`lstart` with one-second granularity, and `uptime` places the boot instant to
|
|
169
|
+
the nearest second.
|
|
170
|
+
|
|
171
|
+
The boot tolerance points backwards, so a record written moments after boot is
|
|
172
|
+
read as predating it and refused. That refusal is `pre-boot`, which retires the
|
|
173
|
+
record, so a group spawned inside that two-second window is never reaped by any
|
|
174
|
+
later incarnation either. It is a leak rather than a retry, and it is the
|
|
175
|
+
deliberate price of refusing to kill on an instant the host can only place to
|
|
176
|
+
the nearest second.
|
|
177
|
+
|
|
178
|
+
## Windows
|
|
179
|
+
|
|
180
|
+
Windows has no `lstart` and no process groups, so two guards cannot be answered
|
|
181
|
+
there: the identity check falls back to the boot-time comparison alone, and
|
|
182
|
+
there is no own-group refusal to make. `taskkill /T /F` walks the tree down
|
|
183
|
+
from the recorded pid instead. This is a documented weaker guarantee on an
|
|
184
|
+
unsupported platform, not an oversight.
|
|
185
|
+
|
|
186
|
+
## Next
|
|
187
|
+
|
|
188
|
+
[Contain child processes](../guides/contain-child-processes.md) is the
|
|
189
|
+
task-shaped version of this page: the layers to compose, the ledger to choose,
|
|
190
|
+
and how to read what the sweep decided.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Answer whether a run owner is alive"
|
|
3
|
+
description: "Wire HostLiveness.isAlive as the probe the engine consults before it steals a run, understand why the rule errs toward alive, and know where the sibling implementation disagrees."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
A durable run records the owner that holds it: a `hostId` and a pid. When
|
|
7
|
+
another process wants that run, it asks whether the recorded owner is still
|
|
8
|
+
alive. `HostLiveness.isAlive` is this package's answer.
|
|
9
|
+
|
|
10
|
+
Both ways of being wrong are expensive, and they are not equally expensive. A
|
|
11
|
+
wrong `true` strands a run until an operator intervenes. A wrong `false` runs
|
|
12
|
+
one run twice.
|
|
13
|
+
|
|
14
|
+
## Wire the probe
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { HostLiveness } from "@smthrs/platform-node"
|
|
18
|
+
|
|
19
|
+
const isAlive = HostLiveness.isAlive({ hostId: "engine-1" })
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`isAlive` returns a function from an owner to `Effect<boolean>`. The owner type
|
|
23
|
+
is structural: `{ hostId: string, pid: number }`, which is exactly the shape of
|
|
24
|
+
[`@smthrs/journal`](/api/journal)'s `OwnerId` minus its nonce, so an `OwnerId`
|
|
25
|
+
is accepted without this package depending on the journal to name a type.
|
|
26
|
+
|
|
27
|
+
`@smthrs/flows`' `NodeRuntime` defaults to this function, so a program that
|
|
28
|
+
composes the runtime gets it without asking.
|
|
29
|
+
|
|
30
|
+
## The rule
|
|
31
|
+
|
|
32
|
+
The rule is asymmetric on purpose:
|
|
33
|
+
|
|
34
|
+
- **An owner on a different host is alive.** A pid means nothing across
|
|
35
|
+
machines, and the process table this probe reads is only this machine's.
|
|
36
|
+
- **An owner on this host is dead only when the signal-0 probe reports
|
|
37
|
+
`ESRCH`.** Every other result preserves liveness.
|
|
38
|
+
|
|
39
|
+
`process.kill(pid, 0)` sends no signal. `EPERM` means
|
|
40
|
+
the process exists and belongs to another user, which is still a live owner
|
|
41
|
+
this host must not rob. A throw that is not an object at all carries no code,
|
|
42
|
+
and reading through it rather than off it is what keeps that from crashing the
|
|
43
|
+
probe: a thrown string or `null` is an answer nobody gave, which counts as a
|
|
44
|
+
live owner.
|
|
45
|
+
|
|
46
|
+
## The residual risk
|
|
47
|
+
|
|
48
|
+
Pid reuse. An owner whose pid was recycled by an unrelated program reads as
|
|
49
|
+
alive, and its run waits for an operator.
|
|
50
|
+
|
|
51
|
+
That is the safe direction. Nothing about a recycled pid can tell this process
|
|
52
|
+
whether the run is still being driven, and the cost of guessing wrong the other
|
|
53
|
+
way is two hosts executing one run.
|
|
54
|
+
|
|
55
|
+
## Substitute a better answer where you have one
|
|
56
|
+
|
|
57
|
+
A multi-process deployment with a supervisor or a lease system knows better
|
|
58
|
+
than any pid probe, and should answer from that instead. `isAlive` is one
|
|
59
|
+
implementation of a slot, not a fixed policy.
|
|
60
|
+
|
|
61
|
+
## Know where the sibling disagrees
|
|
62
|
+
|
|
63
|
+
`@smthrs/run-store`'s `Ownership.sameHostPidProbe` fills the same
|
|
64
|
+
`LivenessCheck`. Both probes use the same `ESRCH`-only death rule.
|
|
65
|
+
`EPERM`, `EINVAL`, and unknown signal errors preserve liveness.
|
|
66
|
+
|
|
67
|
+
The signal-error rows describe valid same-host pids:
|
|
68
|
+
|
|
69
|
+
| Input | `HostLiveness.isAlive` | `Ownership.sameHostPidProbe` |
|
|
70
|
+
| ------------------------------------------------ | ---------------------- | ---------------------------- |
|
|
71
|
+
| signal error `ESRCH` | dead | dead |
|
|
72
|
+
| signal error `EPERM`, `EINVAL`, or unknown error | alive | alive |
|
|
73
|
+
| owner on a different `hostId` | alive | defer to expired lease |
|
|
74
|
+
|
|
75
|
+
`sameHostPidProbe` returns `false` for a foreign host without probing its pid.
|
|
76
|
+
The engine consults it after lease expiry, and the store verifies the expired
|
|
77
|
+
lease before allowing a steal. `HostLiveness.isAlive` returns `true` for a
|
|
78
|
+
foreign host, so runs on a permanently dead host wait for an operator.
|
|
79
|
+
|
|
80
|
+
Which answer a deployment gets depends on the entry point it used:
|
|
81
|
+
`@smthrs/flows`' `NodeRuntime` defaults to this function, while
|
|
82
|
+
[`@smthrs/cli`](/api/cli)'s `NodeControl` passes `sameHostPidProbe`.
|
|
83
|
+
|
|
84
|
+
`HostLiveness.isAlive` compares with its configured `hostId` and ignores the
|
|
85
|
+
`context` argument of `Ownership.LivenessCheck`. `sameHostPidProbe` compares
|
|
86
|
+
with `context.claimant.hostId`. Pick the probe for its cross-host policy and
|
|
87
|
+
source of host identity.
|