kern-sandbox 0.2.38 → 0.2.39

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.
Files changed (2) hide show
  1. package/index.js +23 -4
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -36,7 +36,7 @@ const crypto = require("crypto");
36
36
  const zlib = require("zlib");
37
37
  const { spawn, spawnSync } = require("child_process");
38
38
 
39
- const VERSION = "0.2.38";
39
+ const VERSION = "0.2.39";
40
40
 
41
41
  const DEFAULT_IMAGE = "python:3.12-slim";
42
42
  const WORKSPACE = "/workspace"; // where the persistent workspace is mounted inside every box
@@ -285,9 +285,22 @@ function pycSourceId(image) {
285
285
  const root = path.join(cacheHome(), "kern", "images");
286
286
  const safe = sanitizeRef(image);
287
287
  const h = crypto.createHash("sha256");
288
- for (const suffix of [".image", ".ok"]) {
289
- h.update(fs.readFileSync(path.join(root, safe + suffix)).subarray(0, 4096));
290
- h.update(Buffer.from([0]));
288
+ // THE CONFIG, which changes when ENTRYPOINT/ENV/WORKDIR/USER do.
289
+ h.update(fs.readFileSync(path.join(root, `${safe}.image`)).subarray(0, 4096));
290
+ // THE SENTINEL'S STAMP, NOT ITS BYTES. `.ok` holds the REFERENCE, so its contents are the tag and
291
+ // never move when the tag does: hashing them missed the commonest case there is, a rebuilt rootfs
292
+ // under an unchanged config. Its mtime and length are what kern ITSELF uses to decide an image's
293
+ // content changed, because a re-pull rewrites the sentinel last. Measured: a real re-pull leaves
294
+ // the config byte-identical and moves this.
295
+ const st = fs.statSync(path.join(root, `${safe}.ok`), { bigint: true });
296
+ h.update(`${st.mtimeNs}:${st.size}`);
297
+ // AND THE LAYER MANIFEST FOR A BUILT IMAGE, which names its layers by content. A pulled image has
298
+ // none, and that absence is part of the identity: an image that stops being layered is not the
299
+ // same image.
300
+ try {
301
+ h.update(fs.readFileSync(path.join(root, `${safe}.layers`)).subarray(0, 8192));
302
+ } catch {
303
+ h.update("\0no-layers");
291
304
  }
292
305
  return h.digest("hex").slice(0, 32);
293
306
  } catch {
@@ -4411,5 +4424,11 @@ module.exports = {
4411
4424
  _ociCanonicalRef: ociCanonicalRef,
4412
4425
  _PYC_BUILD_CODE: PYC_BUILD_CODE,
4413
4426
  _pycStartBuild: pycStartBuild,
4427
+ /** Await every build still in flight. FOR TESTS, and it removes a real race rather than masking
4428
+ * one: a test that removes its temp cache home while a background build is still writing into it
4429
+ * fails in `rimraf` with ENOTEMPTY, which is a teardown ordering bug and reads like a product
4430
+ * defect. Measured under the gate, where the machine is busy enough for the build to outlive the
4431
+ * test. */
4432
+ _pycSettle: () => Promise.allSettled([...PYC_BUILDS.values()]),
4414
4433
  version: VERSION,
4415
4434
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kern-sandbox",
3
- "version": "0.2.38",
3
+ "version": "0.2.39",
4
4
  "description": "Your model writes the code. This runs it where it can't touch your machine: a rootless Linux container, no daemon, no VM, no cloud, no account. A kernel boundary, not a microVM: for deliberately hostile code, use one.",
5
5
  "keywords": [
6
6
  "sandbox",