hammerkit 1.27.0-next.21 → 1.27.0-next.23

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/build.schema.json CHANGED
@@ -296,6 +296,9 @@
296
296
  "type": "string",
297
297
  "minLength": 1
298
298
  },
299
+ "publish": {
300
+ "type": "boolean"
301
+ },
299
302
  "image": {
300
303
  "type": "string"
301
304
  },
@@ -308,6 +311,56 @@
308
311
  "volumeSize": {
309
312
  "type": "string",
310
313
  "description": "size of its claim on Kubernetes, e.g. 10Gi"
314
+ },
315
+ "capAdd": {
316
+ "type": "array",
317
+ "items": {
318
+ "type": "string",
319
+ "enum": [
320
+ "AUDIT_CONTROL",
321
+ "AUDIT_READ",
322
+ "AUDIT_WRITE",
323
+ "BLOCK_SUSPEND",
324
+ "BPF",
325
+ "CHECKPOINT_RESTORE",
326
+ "CHOWN",
327
+ "DAC_OVERRIDE",
328
+ "DAC_READ_SEARCH",
329
+ "FOWNER",
330
+ "FSETID",
331
+ "IPC_LOCK",
332
+ "IPC_OWNER",
333
+ "KILL",
334
+ "LEASE",
335
+ "LINUX_IMMUTABLE",
336
+ "MAC_ADMIN",
337
+ "MAC_OVERRIDE",
338
+ "MKNOD",
339
+ "NET_ADMIN",
340
+ "NET_BIND_SERVICE",
341
+ "NET_BROADCAST",
342
+ "NET_RAW",
343
+ "PERFMON",
344
+ "SETFCAP",
345
+ "SETGID",
346
+ "SETPCAP",
347
+ "SETUID",
348
+ "SYS_ADMIN",
349
+ "SYS_BOOT",
350
+ "SYS_CHROOT",
351
+ "SYS_MODULE",
352
+ "SYS_NICE",
353
+ "SYS_PACCT",
354
+ "SYS_PTRACE",
355
+ "SYS_RAWIO",
356
+ "SYS_RESOURCE",
357
+ "SYS_TIME",
358
+ "SYS_TTY_CONFIG",
359
+ "SYSLOG",
360
+ "WAKE_ALARM"
361
+ ]
362
+ },
363
+ "description": "Linux capabilities added to the container, e.g. [SYS_ADMIN]"
311
364
  }
312
365
  },
313
366
  "required": [
@@ -379,6 +432,9 @@
379
432
  "runner": {
380
433
  "$ref": "#/definitions/hammerkit/properties/tasks/additionalProperties/anyOf/0/properties/runner"
381
434
  },
435
+ "publish": {
436
+ "$ref": "#/definitions/hammerkit/properties/tasks/additionalProperties/anyOf/0/properties/publish"
437
+ },
382
438
  "platform": {
383
439
  "type": "object",
384
440
  "properties": {
@@ -520,6 +576,9 @@
520
576
  },
521
577
  "args": {
522
578
  "type": "boolean"
579
+ },
580
+ "publish": {
581
+ "type": "boolean"
523
582
  }
524
583
  },
525
584
  "required": [
@@ -576,6 +635,9 @@
576
635
  "number"
577
636
  ]
578
637
  }
638
+ },
639
+ "publish": {
640
+ "type": "boolean"
579
641
  }
580
642
  },
581
643
  "required": [
@@ -606,6 +668,10 @@
606
668
  "with": {
607
669
  "$ref": "#/definitions/hammerkit/properties/envs",
608
670
  "description": "Environment values for the current build task\nhttps://hammerkit.dev/docs/build-file/environment-variables"
671
+ },
672
+ "publish": {
673
+ "type": "boolean",
674
+ "description": "false: a server leaves out what this file declares"
609
675
  }
610
676
  },
611
677
  "required": [
@@ -633,6 +699,10 @@
633
699
  "with": {
634
700
  "$ref": "#/definitions/hammerkit/properties/envs",
635
701
  "description": "Environment values for the current build task\nhttps://hammerkit.dev/docs/build-file/environment-variables"
702
+ },
703
+ "publish": {
704
+ "type": "boolean",
705
+ "description": "false: a server leaves out what this file declares"
636
706
  }
637
707
  },
638
708
  "required": [
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dockerTask = exports.buildCreateOptions = void 0;
3
+ exports.dockerTask = exports.getCapabilityHostConfig = exports.buildCreateOptions = void 0;
4
4
  const run_labels_1 = require("../docker/run-labels");
5
5
  const execute_docker_1 = require("./execute-docker");
6
6
  const abort_1 = require("./abort");
@@ -47,10 +47,30 @@ function buildCreateOptions(item, stateKey, network, environment, secrets, resou
47
47
  ...(0, item_network_1.getNetworkHostConfig)(network),
48
48
  AutoRemove: true,
49
49
  ...resources,
50
+ ...getCapabilityHostConfig(item, environment),
50
51
  },
51
52
  };
52
53
  }
53
54
  exports.buildCreateOptions = buildCreateOptions;
55
+ /**
56
+ * The capabilities a task adds (`capAdd`). On a server they are only granted in the
57
+ * sandboxed runtime of `backend.capabilities`, never by the kernel of the docker host.
58
+ */
59
+ function getCapabilityHostConfig(item, environment) {
60
+ const capAdd = item.data.capAdd;
61
+ if (capAdd.length === 0) {
62
+ return {};
63
+ }
64
+ if (!environment.hosted) {
65
+ return { CapAdd: capAdd };
66
+ }
67
+ const runtime = environment.hosted.capabilityRuntime;
68
+ if (!runtime) {
69
+ throw new Error(`${item.name}: adds the capabilities ${capAdd.join(', ')}, which this server grants in no sandboxed runtime`);
70
+ }
71
+ return { CapAdd: capAdd, Runtime: runtime };
72
+ }
73
+ exports.getCapabilityHostConfig = getCapabilityHostConfig;
54
74
  async function dockerTask(docker, item, environment, options) {
55
75
  item.status.write('info', `execute ${item.name} in container`);
56
76
  let network = null;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getContainerSecurity = exports.getRunLabels = exports.getRunLabelValue = exports.RUN_LABEL = exports.getPodPlacement = exports.placeOnPool = exports.getNodeArch = exports.runInToolImage = exports.getLocalTaskPool = void 0;
3
+ exports.getPodRuntimeClass = exports.getContainerSecurity = exports.getRunLabels = exports.getRunLabelValue = exports.RUN_LABEL = exports.getPodPlacement = exports.placeOnPool = exports.getNodeArch = exports.runInToolImage = exports.getLocalTaskPool = void 0;
4
4
  const crypto_1 = require("crypto");
5
5
  /**
6
6
  * The pool a local task runs on: the first that offers its platform. A pool says
@@ -34,6 +34,7 @@ function runInToolImage(task, server, platform) {
34
34
  user: null,
35
35
  mounts: [],
36
36
  volumeSize: null,
37
+ capAdd: [],
37
38
  pool: pool.name,
38
39
  arch: getNodeArch(pool),
39
40
  };
@@ -82,18 +83,41 @@ exports.getRunLabels = getRunLabels;
82
83
  /**
83
84
  * What a container of a run may do on a server, where it shares nodes with the pods of
84
85
  * other callers: no privilege escalation (setuid binaries such as sudo), no raw sockets
85
- * (spoofing on a bridge network) and the runtime's seccomp profile.
86
+ * (spoofing on a bridge network) and the runtime's seccomp profile. The capabilities a
87
+ * task adds (`capAdd`) come on top, in the sandbox of getPodRuntimeClass on a server.
86
88
  */
87
- function getContainerSecurity(kubernetes) {
88
- return kubernetes.server
89
- ? {
90
- securityContext: {
91
- allowPrivilegeEscalation: false,
92
- capabilities: { drop: ['NET_RAW'] },
93
- seccompProfile: { type: 'RuntimeDefault' },
94
- },
95
- }
96
- : {};
89
+ function getContainerSecurity(kubernetes, capAdd = []) {
90
+ const add = capAdd.length > 0 ? { add: [...capAdd] } : {};
91
+ if (!kubernetes.server) {
92
+ return capAdd.length > 0 ? { securityContext: { capabilities: add } } : {};
93
+ }
94
+ const drop = capAdd.includes('NET_RAW') ? {} : { drop: ['NET_RAW'] };
95
+ return {
96
+ securityContext: {
97
+ allowPrivilegeEscalation: false,
98
+ capabilities: { ...drop, ...add },
99
+ seccompProfile: { type: 'RuntimeDefault' },
100
+ },
101
+ };
97
102
  }
98
103
  exports.getContainerSecurity = getContainerSecurity;
104
+ /**
105
+ * The RuntimeClass of the pod of a task that adds capabilities on a server: the sandbox the
106
+ * operator named (`backend.capabilities.runtimeClass`), so the capabilities hold in its own
107
+ * kernel and never in the node's. Elsewhere the cluster is the build file's own.
108
+ */
109
+ function getPodRuntimeClass(task, kubernetes) {
110
+ if (task.capAdd.length === 0 || !kubernetes.server) {
111
+ return {};
112
+ }
113
+ const sandbox = kubernetes.server.capabilities;
114
+ if (!sandbox) {
115
+ throw new Error(`${task.name}: adds the capabilities ${task.capAdd.join(', ')}, which this server grants in no sandboxed runtime`);
116
+ }
117
+ if (sandbox.unavailable) {
118
+ throw new Error(`${task.name}: adds the capabilities ${task.capAdd.join(', ')}, but ${sandbox.unavailable}`);
119
+ }
120
+ return { runtimeClassName: sandbox.runtimeClass };
121
+ }
122
+ exports.getPodRuntimeClass = getPodRuntimeClass;
99
123
  //# sourceMappingURL=job-placement.js.map
@@ -151,7 +151,9 @@ function canUseRunner(task, kubernetes, persistence) {
151
151
  persistence.mounts.some((mount) => (0, path_1.relative)(workspace.root, mount.mount.mountPath) === '') &&
152
152
  task.needs.length === 0 &&
153
153
  task.data.secrets.length === 0 &&
154
- task.data.generates.length === 0);
154
+ task.data.generates.length === 0 &&
155
+ // its pod would need the sandboxed runtime of a Job
156
+ task.data.capAdd.length === 0);
155
157
  }
156
158
  exports.canUseRunner = canUseRunner;
157
159
  // What the runner is made of: the same key for the same pod
@@ -92,10 +92,12 @@ function parseTask(cwd, task, environment, context) {
92
92
  return {
93
93
  ...baseWorkNode,
94
94
  type: 'container-task',
95
- user: (0, get_container_user_1.getContainerUser)(),
95
+ // a capability holds for root only: a task that adds some runs as its image's user
96
+ user: task.schema.capAdd?.length ? null : (0, get_container_user_1.getContainerUser)(),
96
97
  image: (0, template_value_1.templateValue)(task.schema.image, envs),
97
98
  mounts: (0, parse_work_mounts_1.parseWorkMounts)(cwd, task.schema, envs),
98
99
  volumeSize: task.schema.volumeSize ?? null,
100
+ capAdd: task.schema.capAdd ?? [],
99
101
  };
100
102
  }
101
103
  else {
@@ -130,7 +130,8 @@ exports.describeFailedJob = describeFailedJob;
130
130
  * The containers of the Job of a command: the network gate first, nothing runs before the
131
131
  * item's policy holds, then the init containers that read its secrets, then the command. The
132
132
  * proxy's token (`network.secretEnv`) reaches the init containers through `secretSpec` only:
133
- * the command's container gets the proxy variables of a limited network without it.
133
+ * the command's container gets the proxy variables of a limited network without it. A task that
134
+ * adds capabilities gets them in the sandbox's RuntimeClass only.
134
135
  */
135
136
  function getTaskJobContainers(options) {
136
137
  const { task, cmd, envs, secrets, network, secretSpec, kubernetes } = options;
@@ -155,9 +156,10 @@ function getTaskJobContainers(options) {
155
156
  name: options.container,
156
157
  volumeMounts: [...options.mounts, ...secrets.mounts, ...secretSpec.mounts],
157
158
  ...(0, container_resources_1.getKubernetesResources)(task.data.resources),
158
- ...(0, job_placement_1.getContainerSecurity)(kubernetes),
159
+ ...(0, job_placement_1.getContainerSecurity)(kubernetes, task.data.capAdd),
159
160
  },
160
161
  ],
162
+ ...(0, job_placement_1.getPodRuntimeClass)({ name: task.name, capAdd: task.data.capAdd }, kubernetes),
161
163
  };
162
164
  }
163
165
  exports.getTaskJobContainers = getTaskJobContainers;
@@ -5,6 +5,7 @@ const path_1 = require("path");
5
5
  const zod_1 = require("zod");
6
6
  const yaml_1 = require("yaml");
7
7
  const get_cache_directory_1 = require("../optimizer/get-cache-directory");
8
+ const build_file_secret_provider_schema_1 = require("../schema/build-file-secret-provider-schema");
8
9
  // Names with a meaning of their own in `run.on` / `--on`
9
10
  exports.RESERVED_SERVER_NAMES = ['local', 'auto'];
10
11
  const serverNamePattern = /^[a-z0-9][a-z0-9._-]*$/i;
@@ -43,6 +44,10 @@ exports.hostConfigSchema = zod_1.z
43
44
  servers: zod_1.z.record(exports.hostServerSchema).default({}),
44
45
  tokenCommands: zod_1.z.record(tokenCommandSchema).optional(),
45
46
  run: zod_1.z.object({ on: zod_1.z.string().optional() }).passthrough().default({}),
47
+ // how this machine reads secrets, beside what the build files declare (see
48
+ // collectSecretCatalog); a server reads only its own config
49
+ secretProviders: build_file_secret_provider_schema_1.buildFileSecretProvidersSchema.optional(),
50
+ secretAccounts: build_file_secret_provider_schema_1.buildFileSecretAccountsSchema.optional(),
46
51
  })
47
52
  .passthrough();
48
53
  // Names like "constructor" must not find inherited members.
@@ -17,6 +17,22 @@ const units_1 = require("../utils/units");
17
17
  const service_tools_1 = require("../harness/service-tools");
18
18
  const path_1 = require("path");
19
19
  const check_keys_1 = require("../optimizer/check-keys");
20
+ function describeOutcome(planned) {
21
+ switch (planned.outcome) {
22
+ case 'published':
23
+ return 'publishing it would publish it';
24
+ case 'already published':
25
+ return planned.yanked ? 'it is published already, and yanked' : 'it is published already';
26
+ case 'proposed':
27
+ return planned.missing?.length
28
+ ? `publishing it would propose it, it names accounts you do not use: ${planned.missing
29
+ .map(server_client_1.printable)
30
+ .join(', ')}`
31
+ : 'publishing it would propose it, somebody who may publish approves it';
32
+ default:
33
+ return 'the server accepts it, publishing it needs builds: propose or publish';
34
+ }
35
+ }
20
36
  const RUN_ID = /^run_[A-Za-z0-9_-]{16}$/;
21
37
  // how a check passed, one line
22
38
  function describePassed(passed) {
@@ -106,6 +122,7 @@ function describePlan(plan) {
106
122
  ...(entry.mounts.length > 0 ? [` mounts: ${entry.mounts.map(server_client_1.printable).join(', ')}`] : []),
107
123
  ...(entry.ports.length > 0 ? [` ports: ${entry.ports.map(server_client_1.printable).join(', ')}`] : []),
108
124
  ...(entry.generates.length > 0 ? [` generates: ${entry.generates.map(server_client_1.printable).join(', ')}`] : []),
125
+ ...(entry.capAdd?.length ? [` capAdd: ${entry.capAdd.map(server_client_1.printable).join(', ')}`] : []),
109
126
  ]);
110
127
  }
111
128
  exports.describePlan = describePlan;
@@ -136,8 +153,24 @@ function registerWorkCommands(remote, context) {
136
153
  .command('publish [server]')
137
154
  .description('publish the build file and the files it includes to a server, runs refer to it by its hash')
138
155
  .addOption(new commander_1.Option('--annotation <key=value>', 'say more about where it comes from, or leave one out with key=; path, repo, commit, machine and user are filled in').argParser(collectOption))
156
+ .addOption(new commander_1.Option('--dry-run', "check it against the server's policy and say what publishing would do, without publishing; builds: run is enough"))
139
157
  .action(guarded('Remote publish was not successful', async (name, options) => {
140
158
  const bundle = await (0, collect_bundle_1.collectBundle)(buildFile, environment);
159
+ if (options.dryRun) {
160
+ const { name: serverName, server, token } = await connect(name);
161
+ const answer = await (0, server_client_1.callServer)(server, token, 'POST', '/v1/definitions/plan', bundle).catch((e) => {
162
+ throw e instanceof server_client_1.ServerError && e.status === 404
163
+ ? new Error(`${serverName} can't check a build file without publishing it, it needs hammerkit 1.27`)
164
+ : e;
165
+ });
166
+ const planned = answer.body;
167
+ const files = Object.keys(bundle.files).length;
168
+ environment.stdout.write(`checked with the policy of ${serverName}: ${(0, server_client_1.printable)(planned.hash)} (${files} ${files === 1 ? 'file' : 'files'}), nothing was published\n`);
169
+ const names = [...(planned.tasks ?? []), ...(planned.services ?? [])];
170
+ environment.stdout.write(`tasks and services: ${names.map(server_client_1.printable).join(', ') || 'none'}\n`);
171
+ environment.stdout.write(`${describeOutcome(planned)}\n`);
172
+ return;
173
+ }
141
174
  const annotations = (0, annotations_1.applyAnnotations)(await (0, annotations_1.collectAnnotations)(buildFile, Object.keys(bundle.files)), options.annotation ?? []);
142
175
  const { name: serverName, server, token } = await connect(name);
143
176
  const answer = await (0, server_client_1.callServer)(server, token, 'POST', '/v1/definitions', {
@@ -49,5 +49,8 @@ exports.buildFileBaseTaskSchema = (0, zod_1.object)({
49
49
  // Name of the server (see `hammerkit remote`) the task has to run on; defaults
50
50
  // to the build file's `runner`. Never a local fallback when it is unknown.
51
51
  runner: runner_schema_1.runnerSchema.optional(),
52
+ // false: a server leaves it out of a published version, for a task only this machine
53
+ // runs (a dev watcher, a task that mounts the docker socket)
54
+ publish: (0, zod_1.boolean)().optional(),
52
55
  });
53
56
  //# sourceMappingURL=build-file-base-task-schema.js.map
@@ -47,6 +47,8 @@ exports.buildFileContainerServiceSchema = (0, zod_1.object)({
47
47
  network: network_schema_1.networkSchema.optional(),
48
48
  // whether its `cmd` takes the arguments after `--` of `up`: false refuses them
49
49
  args: (0, zod_1.boolean)().optional(),
50
+ // false: a server leaves it out of a published version
51
+ publish: (0, zod_1.boolean)().optional(),
50
52
  })
51
53
  .strict()
52
54
  .describe('container service');
@@ -4,12 +4,16 @@ exports.buildFileContainerTaskSchema = void 0;
4
4
  const build_file_base_task_schema_1 = require("./build-file-base-task-schema");
5
5
  const zod_1 = require("zod");
6
6
  const size_schema_1 = require("./size-schema");
7
+ const capabilities_schema_1 = require("./capabilities-schema");
7
8
  exports.buildFileContainerTaskSchema = build_file_base_task_schema_1.buildFileBaseTaskSchema
8
9
  .extend({
9
10
  image: (0, zod_1.string)(),
10
11
  mounts: (0, zod_1.array)((0, zod_1.string)()).optional(),
11
12
  // on Kubernetes, the size of the claim that holds its sources, mounts and outputs
12
13
  volumeSize: size_schema_1.sizeSchema.describe('size of its claim on Kubernetes, e.g. 10Gi').optional(),
14
+ // Linux capabilities added to the container, such as SYS_ADMIN for a daemonless image
15
+ // build. A server allows them only in a sandboxed runtime (`backend.capabilities`).
16
+ capAdd: capabilities_schema_1.capabilitiesSchema.describe('Linux capabilities added to the container, e.g. [SYS_ADMIN]').optional(),
13
17
  })
14
18
  .strict()
15
19
  .describe('container task');
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isGitSource = exports.buildFileIncludeSchema = exports.buildFileLocalSourceSchema = exports.buildFileGitSourceSchema = void 0;
3
+ exports.isPublished = exports.isGitSource = exports.buildFileIncludeSchema = exports.buildFileLocalSourceSchema = exports.buildFileGitSourceSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const envs_schema_1 = require("./envs-schema");
6
6
  // A repo URL or ref starting with `-` would be read by git as an option
@@ -21,17 +21,25 @@ exports.buildFileGitSourceSchema = (0, zod_1.object)({
21
21
  path: (0, zod_1.string)().optional().describe('Build file or directory within the repository'),
22
22
  // overrides for the top-level `envs` the included file declares as its inputs
23
23
  with: envs_schema_1.envsSchema.optional(),
24
+ publish: (0, zod_1.boolean)().optional().describe('false: a server leaves out what this file declares'),
24
25
  }).strict();
25
- // A local path with values for the inputs of the file it names, like `with` of a git
26
- // source. A value may pass on an `envs` value of the including file as `${NAME}`.
26
+ // A local path, with values for the inputs of the file it names like `with` of a git
27
+ // source (a value may pass on an `envs` value of the including file as `${NAME}`), and
28
+ // whether a server publishes what it declares.
27
29
  exports.buildFileLocalSourceSchema = (0, zod_1.object)({
28
30
  path: (0, zod_1.string)().min(1).describe('Build file or directory, relative to the including file'),
29
31
  with: envs_schema_1.envsSchema.optional(),
32
+ publish: (0, zod_1.boolean)().optional().describe('false: a server leaves out what this file declares'),
30
33
  }).strict();
31
- // A string is a local path; an object is a local path with inputs, or a git source (ADR-0004).
34
+ // A string is a local path; an object is a local path with options, or a git source (ADR-0004).
32
35
  exports.buildFileIncludeSchema = (0, zod_1.union)([(0, zod_1.string)(), exports.buildFileLocalSourceSchema, exports.buildFileGitSourceSchema]);
33
36
  function isGitSource(include) {
34
37
  return typeof include !== 'string' && 'git' in include;
35
38
  }
36
39
  exports.isGitSource = isGitSource;
40
+ // `publish: false`: what the file declares is left out of a version a server publishes
41
+ function isPublished(include) {
42
+ return typeof include === 'string' || include.publish !== false;
43
+ }
44
+ exports.isPublished = isPublished;
37
45
  //# sourceMappingURL=build-file-include-schema.js.map
@@ -13,5 +13,7 @@ exports.buildFileKubernetesServiceSchema = (0, zod_1.object)({
13
13
  selector: build_file_kubernetes_service_selector_schema_1.buildFileKubernetesServiceSelectorSchema,
14
14
  labels: labels_schema_1.labelsSchema.optional(),
15
15
  ports: (0, zod_1.array)((0, zod_1.union)([(0, zod_1.string)(), (0, zod_1.number)()])),
16
+ // false: a server leaves it out of a published version
17
+ publish: (0, zod_1.boolean)().optional(),
16
18
  }).strict();
17
19
  //# sourceMappingURL=build-file-kubernetes-service-schema.js.map
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.capabilitiesSchema = exports.LINUX_CAPABILITIES = void 0;
4
+ const zod_1 = require("zod");
5
+ // The Linux capabilities, without the CAP_ prefix (capabilities(7))
6
+ exports.LINUX_CAPABILITIES = [
7
+ 'AUDIT_CONTROL',
8
+ 'AUDIT_READ',
9
+ 'AUDIT_WRITE',
10
+ 'BLOCK_SUSPEND',
11
+ 'BPF',
12
+ 'CHECKPOINT_RESTORE',
13
+ 'CHOWN',
14
+ 'DAC_OVERRIDE',
15
+ 'DAC_READ_SEARCH',
16
+ 'FOWNER',
17
+ 'FSETID',
18
+ 'IPC_LOCK',
19
+ 'IPC_OWNER',
20
+ 'KILL',
21
+ 'LEASE',
22
+ 'LINUX_IMMUTABLE',
23
+ 'MAC_ADMIN',
24
+ 'MAC_OVERRIDE',
25
+ 'MKNOD',
26
+ 'NET_ADMIN',
27
+ 'NET_BIND_SERVICE',
28
+ 'NET_BROADCAST',
29
+ 'NET_RAW',
30
+ 'PERFMON',
31
+ 'SETFCAP',
32
+ 'SETGID',
33
+ 'SETPCAP',
34
+ 'SETUID',
35
+ 'SYS_ADMIN',
36
+ 'SYS_BOOT',
37
+ 'SYS_CHROOT',
38
+ 'SYS_MODULE',
39
+ 'SYS_NICE',
40
+ 'SYS_PACCT',
41
+ 'SYS_PTRACE',
42
+ 'SYS_RAWIO',
43
+ 'SYS_RESOURCE',
44
+ 'SYS_TIME',
45
+ 'SYS_TTY_CONFIG',
46
+ 'SYSLOG',
47
+ 'WAKE_ALARM',
48
+ ];
49
+ // Capabilities a container gets on top of its runtime's default set, by name such as
50
+ // SYS_ADMIN. `ALL` is not one: a task names what it needs.
51
+ exports.capabilitiesSchema = (0, zod_1.array)((0, zod_1.enum)(exports.LINUX_CAPABILITIES, {
52
+ errorMap: (issue, ctx) => issue.code === 'invalid_enum_value'
53
+ ? { message: `unknown capability ${JSON.stringify(issue.received)}, expected a name such as SYS_ADMIN` }
54
+ : { message: ctx.defaultError },
55
+ })).refine((names) => new Set(names).size === names.length, 'a capability is listed twice');
56
+ //# sourceMappingURL=capabilities-schema.js.map