@decocms/sandbox-controller 0.16.2 → 0.16.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/dist/index.d.ts CHANGED
@@ -3730,6 +3730,15 @@ interface FreestyleSandboxProviderOptions {
3730
3730
  image?: string;
3731
3731
  /** The org-fs sidecar image. Default: the chart's `orgFs.image`. */
3732
3732
  orgFsSidecarImage?: string;
3733
+ /**
3734
+ * What the daemon signs the bot's commits with, as the sandbox-env chart's
3735
+ * `commitSigning` does for pods. Unset leaves commits unsigned. The key goes
3736
+ * into the base snapshot, so code in any of these sandboxes can read it.
3737
+ */
3738
+ commitSigning?: {
3739
+ email: string;
3740
+ privateKey: string;
3741
+ };
3733
3742
  /** Pause a VM after this long without network activity. Default 5 min. */
3734
3743
  idleTimeoutSeconds?: number;
3735
3744
  /**
@@ -3760,6 +3769,7 @@ declare class FreestyleSandboxProvider implements SandboxProvider {
3760
3769
  private readonly image;
3761
3770
  private readonly sidecarImage;
3762
3771
  private readonly allowUserNamespaces;
3772
+ private readonly commitSigning;
3763
3773
  private readonly idleTimeoutSeconds;
3764
3774
  private readonly idlePauseMs;
3765
3775
  private readonly autoDeleteSeconds;
package/dist/index.js CHANGED
@@ -4521,6 +4521,7 @@ function tenantPoolReader(store, cluster, { refreshMs = DEFAULT_TENANT_POOL_REFR
4521
4521
 
4522
4522
  // ../sandbox/server/provider/freestyle.ts
4523
4523
  import { createHash as createHash3, createHmac, randomBytes as randomBytes2 } from "crypto";
4524
+ import { dirname } from "path";
4524
4525
 
4525
4526
  // ../../node_modules/.bun/freestyle@0.2.16/node_modules/freestyle/dist/errors.js
4526
4527
  var FreestyleApiError = class _FreestyleApiError extends Error {
@@ -6804,7 +6805,7 @@ var Freestyle = class {
6804
6805
  // ../sandbox/package.json
6805
6806
  var package_default = {
6806
6807
  name: "@decocms/sandbox",
6807
- version: "1.96.2",
6808
+ version: "1.96.4",
6808
6809
  private: true,
6809
6810
  type: "module",
6810
6811
  description: "Sandbox runtime for isolated Studio tool execution",
@@ -6888,6 +6889,8 @@ var DEFAULT_SIDECAR_IMAGE = "ghcr.io/decocms/studio/orgfs-sidecar:0.2.0";
6888
6889
  var HOST_ORG_DIR = "/srv/sandbox/org";
6889
6890
  var HOST_CTL_DIR = "/srv/sandbox/orgfs-ctl";
6890
6891
  var CTL_DIR = "/run/orgfs";
6892
+ var HOST_SIGNING_KEY = "/srv/sandbox/git-signing-key";
6893
+ var SIGNING_KEY_FILE = "/run/git-signing/key";
6891
6894
  var HOST_SETUP = [
6892
6895
  `mkdir -p ${HOST_ORG_DIR} ${HOST_CTL_DIR}`,
6893
6896
  `grep -q ' ${HOST_ORG_DIR} ' /etc/fstab || printf '%s\\n' '${HOST_ORG_DIR} ${HOST_ORG_DIR} none bind,rshared 0 0' 'tmpfs ${HOST_CTL_DIR} tmpfs uid=1000,gid=1000,mode=0755,size=1m 0 0' >> /etc/fstab`,
@@ -6924,6 +6927,7 @@ var FreestyleSandboxProvider = class {
6924
6927
  image;
6925
6928
  sidecarImage;
6926
6929
  allowUserNamespaces;
6930
+ commitSigning;
6927
6931
  idleTimeoutSeconds;
6928
6932
  idlePauseMs;
6929
6933
  autoDeleteSeconds;
@@ -6965,6 +6969,7 @@ var FreestyleSandboxProvider = class {
6965
6969
  this.image = opts.image ?? `ghcr.io/decocms/studio/studio-sandbox-go:${package_default.version}`;
6966
6970
  this.sidecarImage = opts.orgFsSidecarImage ?? DEFAULT_SIDECAR_IMAGE;
6967
6971
  this.allowUserNamespaces = opts.allowUserNamespaces ?? false;
6972
+ this.commitSigning = opts.commitSigning;
6968
6973
  this.idleTimeoutSeconds = opts.idleTimeoutSeconds ?? 5 * 60;
6969
6974
  this.idlePauseMs = opts.idlePauseMs ?? 10 * 6e4;
6970
6975
  this.autoDeleteSeconds = opts.autoDeleteSeconds ?? 24 * 60 * 60;
@@ -7031,9 +7036,13 @@ var FreestyleSandboxProvider = class {
7031
7036
  baseSnapshot() {
7032
7037
  const { image, sidecarImage } = this;
7033
7038
  const namespaceMode = this.allowUserNamespaces ? "\nuser-namespaces" : "";
7039
+ const signing = this.commitSigning ? `
7040
+ signing
7041
+ ${this.commitSigning.email}
7042
+ ${this.commitSigning.privateKey}` : "";
7034
7043
  const slug = `studio-sandbox-${createHash3("sha256").update(`${image}
7035
7044
  ${sidecarImage}
7036
- running${namespaceMode}`).digest("hex").slice(0, 16)}`;
7045
+ running${namespaceMode}${signing}`).digest("hex").slice(0, 16)}`;
7037
7046
  if (this.knownSnapshots.has(slug)) return Promise.resolve(slug);
7038
7047
  return this.snapshotInflight.run(slug, async () => {
7039
7048
  if (await this.snapshotExists(slug)) {
@@ -7086,9 +7095,21 @@ running${namespaceMode}`).digest("hex").slice(0, 16)}`;
7086
7095
  APP_ROOT: WORKDIR,
7087
7096
  PROXY_PORT: String(DAEMON_PORT),
7088
7097
  ORGFS_SIDECAR_CONFIG_PATH: `${CTL_DIR}/config.json`,
7089
- ORGFS_SIDECAR_STATUS_PATH: `${CTL_DIR}/status.json`
7098
+ ORGFS_SIDECAR_STATUS_PATH: `${CTL_DIR}/status.json`,
7099
+ ...this.commitSigning && {
7100
+ GIT_SIGNING_KEY_FILE: SIGNING_KEY_FILE,
7101
+ GIT_SIGNING_KEY_EMAIL: this.commitSigning.email
7102
+ }
7090
7103
  };
7091
7104
  const envFlags = Object.keys(env).map((k) => `-e ${shellQuote(k)}`).join(" ");
7105
+ if (this.commitSigning) {
7106
+ await this.run(
7107
+ vm,
7108
+ `printf '%s' "$GIT_SIGNING_KEY" | sudo sh -c 'mkdir -p ${dirname(HOST_SIGNING_KEY)} && umask 377 && cat > ${HOST_SIGNING_KEY} && chown 1000:1000 ${HOST_SIGNING_KEY}'`,
7109
+ { env: { GIT_SIGNING_KEY: this.commitSigning.privateKey } }
7110
+ );
7111
+ }
7112
+ const signingKeyMount = this.commitSigning ? ` -v ${HOST_SIGNING_KEY}:${SIGNING_KEY_FILE}:ro` : "";
7092
7113
  await this.run(
7093
7114
  vm,
7094
7115
  `sudo sh -c ${shellQuote(HOST_SETUP)} && docker run -d --name ${SIDECAR} --restart=always --privileged --device /dev/fuse -e APP_ROOT=${WORKDIR} -e ORGFS_FEED_IDLE_MS=60000 -v ${HOST_ORG_DIR}:${WORKDIR}/org:rshared -v ${HOST_CTL_DIR}:${CTL_DIR} ${shellQuote(this.sidecarImage)}`,
@@ -7097,7 +7118,7 @@ running${namespaceMode}`).digest("hex").slice(0, 16)}`;
7097
7118
  await this.run(
7098
7119
  vm,
7099
7120
  // Codex's read-only sandbox uses bubblewrap inside this per-user VM.
7100
- `docker run -d --name ${CONTAINER} --restart=always${this.allowUserNamespaces ? " --security-opt seccomp=unconfined --security-opt apparmor=unconfined" : ""} -p ${DAEMON_PORT}:${DAEMON_PORT} -v ${HOST_ORG_DIR}:${WORKDIR}/org:rslave -v ${HOST_CTL_DIR}:${CTL_DIR} ${envFlags} ${shellQuote(this.image)}`,
7121
+ `docker run -d --name ${CONTAINER} --restart=always${this.allowUserNamespaces ? " --security-opt seccomp=unconfined --security-opt apparmor=unconfined" : ""} -p ${DAEMON_PORT}:${DAEMON_PORT} -v ${HOST_ORG_DIR}:${WORKDIR}/org:rslave -v ${HOST_CTL_DIR}:${CTL_DIR}${signingKeyMount} ${envFlags} ${shellQuote(this.image)}`,
7101
7122
  { env, timeoutMs: IMAGE_PULL_TIMEOUT_MS }
7102
7123
  );
7103
7124
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/sandbox-controller",
3
- "version": "0.16.2",
3
+ "version": "0.16.4",
4
4
  "description": "Runs Studio agent sandboxes on Kubernetes, and serves them to a remote Studio.",
5
5
  "repository": {
6
6
  "type": "git",