alasio 7.0.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/cli/src/api-server.js +18 -0
  2. package/dist/cli/src/api-server.js.map +1 -0
  3. package/dist/cli/src/branches.js +123 -46
  4. package/dist/cli/src/branches.js.map +1 -1
  5. package/dist/cli/src/cluster/docker.js +25 -10
  6. package/dist/cli/src/cluster/docker.js.map +1 -1
  7. package/dist/cli/src/cluster/host.js +33 -7
  8. package/dist/cli/src/cluster/host.js.map +1 -1
  9. package/dist/cli/src/cluster/k3s.js +51 -12
  10. package/dist/cli/src/cluster/k3s.js.map +1 -1
  11. package/dist/cli/src/cluster/node.js +115 -33
  12. package/dist/cli/src/cluster/node.js.map +1 -1
  13. package/dist/cli/src/cluster/stargz.js +55 -0
  14. package/dist/cli/src/cluster/stargz.js.map +1 -0
  15. package/dist/cli/src/commands/branch.js +5 -4
  16. package/dist/cli/src/commands/branch.js.map +1 -1
  17. package/dist/cli/src/commands/common.js +9 -3
  18. package/dist/cli/src/commands/common.js.map +1 -1
  19. package/dist/cli/src/commands/folder.js +14 -0
  20. package/dist/cli/src/commands/folder.js.map +1 -0
  21. package/dist/cli/src/commands/init.js +54 -4
  22. package/dist/cli/src/commands/init.js.map +1 -1
  23. package/dist/cli/src/commands/lake.js +40 -6
  24. package/dist/cli/src/commands/lake.js.map +1 -1
  25. package/dist/cli/src/commands/restore-filer-store.js +40 -8
  26. package/dist/cli/src/commands/restore-filer-store.js.map +1 -1
  27. package/dist/cli/src/commands/status.js +216 -20
  28. package/dist/cli/src/commands/status.js.map +1 -1
  29. package/dist/cli/src/commands/uninstall.js +8 -9
  30. package/dist/cli/src/commands/uninstall.js.map +1 -1
  31. package/dist/cli/src/commands/up.js +11 -3
  32. package/dist/cli/src/commands/up.js.map +1 -1
  33. package/dist/cli/src/commands/upgrade.js +1 -1
  34. package/dist/cli/src/commands/upgrade.js.map +1 -1
  35. package/dist/cli/src/commands.js +2 -1
  36. package/dist/cli/src/commands.js.map +1 -1
  37. package/dist/cli/src/config.js +24 -9
  38. package/dist/cli/src/config.js.map +1 -1
  39. package/dist/cli/src/cpu-baseline.js +8 -4
  40. package/dist/cli/src/cpu-baseline.js.map +1 -1
  41. package/dist/cli/src/folders.js +67 -0
  42. package/dist/cli/src/folders.js.map +1 -0
  43. package/dist/cli/src/gateway-calls.js +58 -0
  44. package/dist/cli/src/gateway-calls.js.map +1 -0
  45. package/dist/cli/src/install.js +119 -17
  46. package/dist/cli/src/install.js.map +1 -1
  47. package/dist/cli/src/kept-releases.js +27 -20
  48. package/dist/cli/src/kept-releases.js.map +1 -1
  49. package/dist/cli/src/kube/api.js +11 -5
  50. package/dist/cli/src/kube/api.js.map +1 -1
  51. package/dist/cli/src/kube/apply.js +16 -73
  52. package/dist/cli/src/kube/apply.js.map +1 -1
  53. package/dist/cli/src/kube/rollout.js +84 -26
  54. package/dist/cli/src/kube/rollout.js.map +1 -1
  55. package/dist/cli/src/lake.js +124 -0
  56. package/dist/cli/src/lake.js.map +1 -0
  57. package/dist/cli/src/manifests/admission.js +381 -223
  58. package/dist/cli/src/manifests/admission.js.map +1 -1
  59. package/dist/cli/src/manifests/alasio.js +79 -362
  60. package/dist/cli/src/manifests/alasio.js.map +1 -1
  61. package/dist/cli/src/manifests/branch.js +10 -26
  62. package/dist/cli/src/manifests/branch.js.map +1 -1
  63. package/dist/cli/src/manifests/collector.js +250 -135
  64. package/dist/cli/src/manifests/collector.js.map +1 -1
  65. package/dist/cli/src/manifests/common.js +32 -2
  66. package/dist/cli/src/manifests/common.js.map +1 -1
  67. package/dist/cli/src/manifests/config.js +256 -92
  68. package/dist/cli/src/manifests/config.js.map +1 -1
  69. package/dist/cli/src/manifests/disruption.js +10 -0
  70. package/dist/cli/src/manifests/disruption.js.map +1 -1
  71. package/dist/cli/src/manifests/folders.js +128 -0
  72. package/dist/cli/src/manifests/folders.js.map +1 -0
  73. package/dist/cli/src/manifests/grafana.js +4 -2
  74. package/dist/cli/src/manifests/grafana.js.map +1 -1
  75. package/dist/cli/src/manifests/index.js +15 -8
  76. package/dist/cli/src/manifests/index.js.map +1 -1
  77. package/dist/cli/src/manifests/keda.js +2 -1
  78. package/dist/cli/src/manifests/keda.js.map +1 -1
  79. package/dist/cli/src/manifests/lake.js +350 -73
  80. package/dist/cli/src/manifests/lake.js.map +1 -1
  81. package/dist/cli/src/manifests/log.js +316 -0
  82. package/dist/cli/src/manifests/log.js.map +1 -0
  83. package/dist/cli/src/manifests/neon.js +128 -49
  84. package/dist/cli/src/manifests/neon.js.map +1 -1
  85. package/dist/cli/src/manifests/network-policies.js +263 -100
  86. package/dist/cli/src/manifests/network-policies.js.map +1 -1
  87. package/dist/cli/src/manifests/node-agent.js +24 -8
  88. package/dist/cli/src/manifests/node-agent.js.map +1 -1
  89. package/dist/cli/src/manifests/object-store.js +4 -2
  90. package/dist/cli/src/manifests/object-store.js.map +1 -1
  91. package/dist/cli/src/manifests/sandbox-gateway.js +663 -0
  92. package/dist/cli/src/manifests/sandbox-gateway.js.map +1 -0
  93. package/dist/cli/src/manifests/scalers.js +16 -5
  94. package/dist/cli/src/manifests/scalers.js.map +1 -1
  95. package/dist/cli/src/manifests/sizing.js +16 -8
  96. package/dist/cli/src/manifests/sizing.js.map +1 -1
  97. package/dist/cli/src/manifests/valkey.js +11 -5
  98. package/dist/cli/src/manifests/valkey.js.map +1 -1
  99. package/dist/cli/src/manifests/vpa.js +3 -1
  100. package/dist/cli/src/manifests/vpa.js.map +1 -1
  101. package/dist/cli/src/manifests/webhook.js +102 -0
  102. package/dist/cli/src/manifests/webhook.js.map +1 -0
  103. package/dist/cli/src/manifests/workspace-storage.js +174 -89
  104. package/dist/cli/src/manifests/workspace-storage.js.map +1 -1
  105. package/dist/cli/src/neon-storage.js +155 -0
  106. package/dist/cli/src/neon-storage.js.map +1 -0
  107. package/dist/cli/src/release.js +9 -6
  108. package/dist/cli/src/release.js.map +1 -1
  109. package/dist/cli/src/secrets.js +45 -4
  110. package/dist/cli/src/secrets.js.map +1 -1
  111. package/dist/cli/src/target.js +1 -1
  112. package/dist/cli/src/target.js.map +1 -1
  113. package/dist/cli/src/telegram.js +56 -10
  114. package/dist/cli/src/telegram.js.map +1 -1
  115. package/dist/cli/src/webhook.js +182 -0
  116. package/dist/cli/src/webhook.js.map +1 -0
  117. package/dist/cluster/node/pins.json +12 -2
  118. package/dist/neon/control/branches.js +126 -21
  119. package/dist/neon/control/branches.js.map +1 -1
  120. package/dist/neon/control/endpoints.js +9 -0
  121. package/dist/neon/control/endpoints.js.map +1 -1
  122. package/dist/neon/control/jwt.js +16 -3
  123. package/dist/neon/control/jwt.js.map +1 -1
  124. package/dist/neon/control/secrets.js +51 -7
  125. package/dist/neon/control/secrets.js.map +1 -1
  126. package/dist/src/binder/names.js +33 -0
  127. package/dist/src/binder/names.js.map +1 -0
  128. package/dist/src/binder/tokens.js +58 -0
  129. package/dist/src/binder/tokens.js.map +1 -0
  130. package/dist/src/branch/names.js +12 -2
  131. package/dist/src/branch/names.js.map +1 -1
  132. package/dist/src/events/names.js +7 -0
  133. package/dist/src/events/names.js.map +1 -0
  134. package/dist/src/gateway/names.js +201 -0
  135. package/dist/src/gateway/names.js.map +1 -0
  136. package/dist/src/harness/names.js +49 -0
  137. package/dist/src/harness/names.js.map +1 -0
  138. package/dist/src/kube/sandbox-shape.js +19 -69
  139. package/dist/src/kube/sandbox-shape.js.map +1 -1
  140. package/dist/src/node-agent/names.js +118 -2
  141. package/dist/src/node-agent/names.js.map +1 -1
  142. package/dist/src/sandbox/names.js +12 -20
  143. package/dist/src/sandbox/names.js.map +1 -1
  144. package/dist/src/sandbox/otlp-resource.js +173 -0
  145. package/dist/src/sandbox/otlp-resource.js.map +1 -0
  146. package/dist/src/sandbox/telemetry.js +43 -0
  147. package/dist/src/sandbox/telemetry.js.map +1 -0
  148. package/dist/src/telegram/deliveries.js +40 -0
  149. package/dist/src/telegram/deliveries.js.map +1 -0
  150. package/dist/src/telemetry/config.js +131 -0
  151. package/dist/src/telemetry/config.js.map +1 -0
  152. package/dist/src/telemetry/forward.js +154 -0
  153. package/dist/src/telemetry/forward.js.map +1 -0
  154. package/package.json +1 -1
  155. package/dist/cli/src/manifests/agent-sandbox.js +0 -92
  156. package/dist/cli/src/manifests/agent-sandbox.js.map +0 -1
  157. package/dist/cli/src/manifests/juicefs-csi.js +0 -319
  158. package/dist/cli/src/manifests/juicefs-csi.js.map +0 -1
  159. package/dist/cli/src/manifests/sandboxes.agents.x-k8s.io.json +0 -6295
  160. package/dist/src/kube/config.js +0 -143
  161. package/dist/src/kube/config.js.map +0 -1
@@ -1,22 +1,24 @@
1
- import { COMPUTE_PORT } from "../../../neon/control/branches.js";
1
+ import { COMPUTE_CTL_PORT, COMPUTE_PORT } from "../../../neon/control/branches.js";
2
2
  import { BRANCH_FORK_PORT } from "../../../src/branch/names.js";
3
+ import { POOL_LABEL } from "../../../src/binder/names.js";
4
+ import { NET_MODE_LABEL } from "../../../src/kube/sandbox-shape.js";
3
5
  import { NODE_AGENT_PORT } from "../../../src/node-agent/names.js";
4
6
  import { forksForBranches, sessionsNamespace } from "./alasio.js";
5
- import { BRANCH_LABEL, COLLECTOR_PORT, collectorRuns, componentName, given, grafanaRuns, labels, MAIN_ENVIRONMENT, NAMESPACE, nodeAgentRuns, RELEASE, selectorLabels, } from "./common.js";
7
+ import { GATEWAY_COMPONENT, SCRAPER_COMPONENT } from "./collector.js";
8
+ import { BRANCH_LABEL, COLLECTOR_PORT, collectorRuns, componentName, given, grafanaRuns, labels, lakeRuns, MAIN_ENVIRONMENT, NAMESPACE, nodeAgentRuns, RELEASE, selectorLabels, } from "./common.js";
6
9
  import { replicated } from "./disruption.js";
7
10
  import { GOSSIP_PORT } from "./grafana.js";
8
- import { LAKE_QUERY_PORT } from "./lake.js";
11
+ import { INTAKE_COMPONENT, LAKE_QUERY_PORT, LOADER_COMPONENT, QUERY_COMPONENT, RECONCILE_COMPONENT } from "./lake.js";
12
+ import { LOG, LOG_COMPONENT, LOG_CONTROLLER_PORT, LOG_METRICS_PORT, LOG_PORT } from "./log.js";
13
+ import { WEBHOOK, WEBHOOK_COMPONENT, WEBHOOK_HEALTH_PORT } from "./webhook.js";
9
14
  import { BACKUP_COMPONENT, COMPONENTS, FILER, FILER_STORE, GATEWAY, LIFECYCLE_COMPONENT, MASTER, METRICS_PORT, RESTORE_COMPONENTS, RESTORE_SOURCE, VOLUME_SERVER, } from "./object-store.js";
10
15
  import { PROXY_PORT } from "./neon.js";
11
- import { CONTROLLER_POD_LABELS, JOB_POD_SELECTOR, MOUNT_POD_LABELS, NODE_POD_LABELS } from "./juicefs-csi.js";
12
16
  import { VALKEY_PORT } from "./valkey.js";
17
+ import { FOLDER_PORT, foldersRun } from "./folders.js";
13
18
  import { JUICEFS_ADMIN } from "./workspace-storage.js";
14
- /** The label of every session's pod, which alasio gives it. */
15
- const SESSION = { "alasio.dev/workload": "session" };
19
+ import { GATEWAY as SANDBOX_GATEWAY_NAME, GATEWAY_COMPONENT as SANDBOX_GATEWAY_COMPONENT, GATEWAY_PORT as SANDBOX_GATEWAY_PORT, POOL_POD_PORT, RECEIVER, RECEIVER_COMPONENT, sandboxServiceRuns, } from "./sandbox-gateway.js";
16
20
  /** The namespace `name`, as a peer's selector. */
17
21
  const namespaceNamed = (name) => ({ matchLabels: { "kubernetes.io/metadata.name": name } });
18
- /** The alasio pod of `environment`, as a peer. */
19
- const alasioOf = ({ namespace }) => ({ namespaceSelector: namespaceNamed(namespace), podSelector: { matchLabels: selectorLabels("alasio") } });
20
22
  /** Every branch environment's namespace, as a peer's selector, by the label each has. */
21
23
  const BRANCH_NAMESPACES = { matchExpressions: [{ key: BRANCH_LABEL, operator: "Exists" }] };
22
24
  /** The labels of the data stack's pods, which its policy admits each other by. */
@@ -33,62 +35,47 @@ function policy(name, namespace, component, spec) {
33
35
  return { apiVersion: "networking.k8s.io/v1", kind: "NetworkPolicy", metadata: { name, namespace, labels: labels(component) }, spec };
34
36
  }
35
37
  /**
36
- * Sessions: nothing in or out but bayma from their alasio, its telemetry receiver (the
37
- * one egress of a session without internet), and, for a session with internet, the
38
- * public internet, none of the cluster's or its network's private addresses. A branch
39
- * environment's sessions are its alasio's alone, as main's are main's.
38
+ * Sessions: nothing in or out of their namespace's pods but what the sandbox service's
39
+ * pools' policies admit (poolPolicies): a pod of no pool reaches nothing, and nothing it.
40
40
  */
41
41
  function sessionPolicies(config, environment) {
42
- const { blockedCidrs } = config.sessions;
43
- const namespace = sessionsNamespace(config, environment);
44
- const alasio = alasioOf(environment);
45
- return [
46
- policy("default-deny", namespace, "session", { podSelector: {}, policyTypes: ["Ingress", "Egress"] }),
47
- policy("bayma-from-alasio", namespace, "session", {
48
- podSelector: { matchLabels: SESSION },
49
- policyTypes: ["Ingress"],
50
- ingress: [{ from: [alasio], ports: [{ protocol: "TCP", port: 7290 }] }],
51
- }),
52
- policy("telemetry-to-alasio", namespace, "session", {
53
- podSelector: { matchLabels: SESSION },
54
- policyTypes: ["Egress"],
55
- egress: [{ to: [alasio], ports: [{ protocol: "TCP", port: config.telemetry.receiverPort }] }],
56
- }),
57
- policy("full-internet", namespace, "session", {
58
- podSelector: { matchLabels: { ...SESSION, "alasio.dev/net-mode": "full" } },
59
- policyTypes: ["Egress"],
60
- egress: [{ to: [{ ipBlock: { cidr: "0.0.0.0/0", except: [...blockedCidrs] } }] }],
61
- }),
62
- ];
42
+ return [policy("default-deny", sessionsNamespace(config, environment), "session", { podSelector: {}, policyTypes: ["Ingress", "Egress"] })];
63
43
  }
64
44
  /** The object store's parts, of the stack, to each of which partPolicies and gatewayPolicy admit what talks to it alone. */
65
45
  const PARTS = [COMPONENTS.gateway, COMPONENTS.master, COMPONENTS.volumeServer, COMPONENTS.filer];
66
46
  /** A component of main's, as a peer from any namespace. */
67
47
  const ofMain = (component) => ({ namespaceSelector: namespaceNamed(NAMESPACE), podSelector: { matchLabels: selectorLabels(component) } });
48
+ /** The computes' components: a primary's, and its read replica's. */
49
+ const COMPUTES = ["neon-compute", "neon-compute-ro"];
50
+ /** The stack's pods that what talks to each of them alone reaches: the object store's parts, and the log. */
51
+ const APART = [...PARTS, LOG_COMPONENT];
68
52
  /**
69
- * Neon's services, the lake and their jobs reach each other, but the compute, which
70
- * takes its clients' sessions from main's Neon proxy alone, which every client connects
71
- * through, neon-control's, which wakes it and counts its sessions, and the collector's
72
- * scrapes of compute_ctl's metrics. In a branch environment, its lake and compute
73
- * likewise, but for the scrapes, as the collector scrapes main's compute alone. The object
74
- * store's parts, of the stack too, are reached as partPolicies and gatewayPolicy say alone.
53
+ * Neon's services, the lake and their jobs reach each other, but the computes, primary
54
+ * and read replica, which take their clients' sessions from main's Neon proxy alone,
55
+ * which every client connects through, and neon-control's, which wakes them and counts
56
+ * their sessions, and on compute_ctl's port neon-control's, which gives them their specs
57
+ * anew, and the collector's scraper's scrapes of its metrics. In a branch environment, its
58
+ * lake and computes likewise, but for the scrapes, as the scraper scrapes main's computes
59
+ * alone. The object store's parts and the log, of the stack too, are reached as
60
+ * partPolicies, gatewayPolicy and logPolicy say alone.
75
61
  */
76
62
  function neonPolicies({ namespace }) {
77
63
  return [
78
64
  policy(componentName("neon"), namespace, "neon", {
79
- podSelector: { matchLabels: STACK, matchExpressions: [{ key: "app.kubernetes.io/component", operator: "NotIn", values: [...PARTS, "neon-compute"] }] },
65
+ podSelector: { matchLabels: STACK, matchExpressions: [{ key: "app.kubernetes.io/component", operator: "NotIn", values: [...APART, ...COMPUTES] }] },
80
66
  policyTypes: ["Ingress"],
81
67
  ingress: [{ from: [{ podSelector: { matchLabels: STACK } }] }],
82
68
  }),
83
- policy(componentName("neon-compute"), namespace, "neon", {
84
- podSelector: { matchLabels: selectorLabels("neon-compute") },
69
+ ...COMPUTES.map((component) => policy(componentName(component), namespace, "neon", {
70
+ podSelector: { matchLabels: selectorLabels(component) },
85
71
  policyTypes: ["Ingress"],
86
72
  ingress: [
87
73
  { from: [ofMain("neon-proxy"), ofMain("neon-control")], ports: [{ protocol: "TCP", port: COMPUTE_PORT }] },
88
- // The collector scrapes main's compute_ctl alone (collector.ts).
89
- ...(namespace === NAMESPACE ? [{ from: [ofMain("collector")], ports: [{ protocol: "TCP", port: 3080 }] }] : []),
74
+ { from: [ofMain("neon-control")], ports: [{ protocol: "TCP", port: COMPUTE_CTL_PORT }] },
75
+ // The collector's scraper scrapes main's computes' compute_ctl alone (collector.ts).
76
+ ...(namespace === NAMESPACE ? [{ from: [ofMain(SCRAPER_COMPONENT)], ports: [{ protocol: "TCP", port: COMPUTE_CTL_PORT }] }] : []),
90
77
  ],
91
- }),
78
+ })),
92
79
  ];
93
80
  }
94
81
  /**
@@ -127,30 +114,23 @@ function branchStoragePolicy(config) {
127
114
  });
128
115
  }
129
116
  /**
130
- * Workspace storage's metadata and data are reached by JuiceFS's own pods alone (its
131
- * controller, its node service, the mount pods it runs and the Jobs that delete volumes'
132
- * directories, in the driver's namespace) and alasio's JuiceFS admin pods: Valkey takes no
133
- * other connection but the stack's collector's, which reads its metrics, and the bundled
134
- * object store takes theirs on its S3 port beside the stack's own, and Valkey's, whose pod
135
- * formats the file system. The driver's Jobs are told by no label of their own, so any
136
- * Job's pod in its namespace is admitted, which the credentials of each still guard.
117
+ * Workspace storage's metadata and data are reached by JuiceFS's clients alone, alasio's
118
+ * JuiceFS admin pods, the nodes' mount among them: Valkey takes, beside theirs, the node
119
+ * agents', which set workspaces' quotas on it, and no other connection but the collector's
120
+ * scraper's, which reads its metrics; and the bundled object store takes theirs on its S3
121
+ * port beside the stack's own, and Valkey's, whose pod formats the file system.
137
122
  */
138
123
  function workspaceStoragePolicies(config) {
139
- const driverNamespace = { matchLabels: { "kubernetes.io/metadata.name": config.workspaceStorage.csi.namespace } };
140
- const juicefs = [
141
- { namespaceSelector: driverNamespace, podSelector: { matchLabels: MOUNT_POD_LABELS } },
142
- { namespaceSelector: driverNamespace, podSelector: { matchLabels: CONTROLLER_POD_LABELS } },
143
- { namespaceSelector: driverNamespace, podSelector: { matchLabels: NODE_POD_LABELS } },
144
- { namespaceSelector: driverNamespace, podSelector: JOB_POD_SELECTOR },
145
- { namespaceSelector: { matchLabels: { "kubernetes.io/metadata.name": NAMESPACE } }, podSelector: { matchLabels: JUICEFS_ADMIN } },
146
- ];
124
+ const juicefs = [{ podSelector: { matchLabels: JUICEFS_ADMIN } }];
125
+ // The node agents, which set workspaces' quotas on the metadata engine, and reach no object store of it.
126
+ const quotas = nodeAgentRuns(config) ? [NODE_AGENT] : [];
147
127
  return [
148
128
  policy(componentName("valkey"), NAMESPACE, "valkey", {
149
129
  podSelector: { matchLabels: selectorLabels("valkey") },
150
130
  policyTypes: ["Ingress"],
151
131
  ingress: [
152
- { from: juicefs, ports: [{ protocol: "TCP", port: VALKEY_PORT }] },
153
- { from: [{ podSelector: { matchLabels: selectorLabels("collector") } }], ports: [{ protocol: "TCP", port: VALKEY_PORT }] },
132
+ { from: [...juicefs, ...quotas], ports: [{ protocol: "TCP", port: VALKEY_PORT }] },
133
+ { from: [{ podSelector: { matchLabels: selectorLabels(SCRAPER_COMPONENT) } }], ports: [{ protocol: "TCP", port: VALKEY_PORT }] },
154
134
  ],
155
135
  }),
156
136
  ...(config.objectStore.bundled.enabled
@@ -165,7 +145,7 @@ function workspaceStoragePolicies(config) {
165
145
  /**
166
146
  * The bundled object store's parts behind its gateways, each reached by what talks to it
167
147
  * alone, on its HTTP and gRPC ports (SeaweedFS's gRPC port is the HTTP port's plus 10000),
168
- * and by the collector on its metrics port:
148
+ * and by the collector's scraper on its metrics port:
169
149
  *
170
150
  * - the masters, by each other, in Raft, and by every part that asks them where volumes
171
151
  * are, or registers: the volume servers, the filers, the gateways (whose sidecar makes
@@ -176,17 +156,25 @@ function workspaceStoragePolicies(config) {
176
156
  * - the filers, by each other, which follow each other's changes, the gateways, and the
177
157
  * lifecycle pass, which replays their log.
178
158
  *
179
- * The stack's other pods, which reach the store through its gateways, reach none of them.
159
+ * And each environment's lake's reconciliation, main's and branch environments', reaches the
160
+ * filers, masters and volume servers on their HTTP ports alone, which it asks whether a file
161
+ * the store fails a read of has its data still (neon/lake/src/reconcile.ts's gone). The
162
+ * stack's other pods, which reach the store through its gateways, reach none of them.
180
163
  */
181
164
  function partPolicies() {
182
165
  const of = (component) => ({ podSelector: { matchLabels: selectorLabels(component) } });
183
166
  const ports = (http) => [http, http + 10_000].map((port) => ({ protocol: "TCP", port }));
167
+ const reconciliations = [
168
+ ofMain(RECONCILE_COMPONENT),
169
+ { namespaceSelector: BRANCH_NAMESPACES, podSelector: { matchLabels: selectorLabels(RECONCILE_COMPONENT) } },
170
+ ];
184
171
  const part = (name, component, http, from) => policy(name, NAMESPACE, component, {
185
172
  podSelector: { matchLabels: selectorLabels(component) },
186
173
  policyTypes: ["Ingress"],
187
174
  ingress: [
188
175
  { from: from.map(of), ports: ports(http) },
189
- { from: [of("collector")], ports: [{ protocol: "TCP", port: METRICS_PORT }] },
176
+ { from: reconciliations, ports: [{ protocol: "TCP", port: http }] },
177
+ { from: [of(SCRAPER_COMPONENT)], ports: [{ protocol: "TCP", port: METRICS_PORT }] },
190
178
  ],
191
179
  });
192
180
  const { gateway, master, volumeServer, filer } = COMPONENTS;
@@ -200,7 +188,7 @@ function partPolicies() {
200
188
  * The bundled object store's gateways take the stack's pods on their S3 port, the
201
189
  * store's endpoint (and branch environments' and workspace storage's, as their own
202
190
  * policies say); the lifecycle pass alone on their gRPC port, through which it deletes;
203
- * and the collector alone on their metrics port.
191
+ * and the collector's scraper alone on their metrics port.
204
192
  */
205
193
  function gatewayPolicy() {
206
194
  return policy(GATEWAY, NAMESPACE, COMPONENTS.gateway, {
@@ -209,13 +197,13 @@ function gatewayPolicy() {
209
197
  ingress: [
210
198
  { from: [{ podSelector: { matchLabels: STACK } }], ports: [{ protocol: "TCP", port: 8333 }] },
211
199
  { from: [{ podSelector: { matchLabels: selectorLabels(LIFECYCLE_COMPONENT) } }], ports: [{ protocol: "TCP", port: 18_333 }] },
212
- { from: [{ podSelector: { matchLabels: selectorLabels("collector") } }], ports: [{ protocol: "TCP", port: METRICS_PORT }] },
200
+ { from: [{ podSelector: { matchLabels: selectorLabels(SCRAPER_COMPONENT) } }], ports: [{ protocol: "TCP", port: METRICS_PORT }] },
213
201
  ],
214
202
  });
215
203
  }
216
204
  /**
217
205
  * The bundled object store's filer store takes no connection but its filers', the
218
- * collector's, which reads its metrics, and its backups': it is not of the data stack,
206
+ * collector's scraper's, which reads its metrics, and its backups': it is not of the data stack,
219
207
  * whose pods reach each other. As `alasio restore-filer-store` puts a backup back, the
220
208
  * Valkey that serves it is reached by the Job that loads it alone.
221
209
  */
@@ -227,26 +215,95 @@ function filerStorePolicies() {
227
215
  ingress: [{ from: from.map(of), ports: [{ protocol: "TCP", port: VALKEY_PORT }] }],
228
216
  });
229
217
  return [
230
- valkey(FILER_STORE, COMPONENTS.filerStore, [COMPONENTS.filer, "collector", BACKUP_COMPONENT]),
218
+ valkey(FILER_STORE, COMPONENTS.filerStore, [COMPONENTS.filer, SCRAPER_COMPONENT, BACKUP_COMPONENT]),
231
219
  valkey(RESTORE_SOURCE, RESTORE_COMPONENTS.source, [RESTORE_COMPONENTS.restore]),
232
220
  ];
233
221
  }
234
222
  /**
235
- * The telemetry collector takes OTLP from alasio, the node agents and folder workspaces'
236
- * bayma, beside the stack's own pods (neonPolicies), and from branch environments'
237
- * alasio, compute and lake, and from nothing else: sessions' telemetry reaches it through
238
- * their alasio's receiver alone.
223
+ * KEDA's operator, which reads what it scales on: alasio's, or, where the cluster's own KEDA
224
+ * scales alasio (autoscaling.keda.enabled false), the operator its release manifest
225
+ * labels, in any namespace.
226
+ */
227
+ const kedaOperator = (config) => config.autoscaling.keda.enabled
228
+ ? { podSelector: { matchLabels: selectorLabels("keda-operator") } }
229
+ : { namespaceSelector: {}, podSelector: { matchLabels: { "app.kubernetes.io/name": "keda-operator" } } };
230
+ /**
231
+ * The log takes its clients on their port from the collector's gateway, the webhook intake
232
+ * and the sandbox gateway, which write to it, and the lake's intake, KEDA's operator (kedaOperator), and
233
+ * each environment's alasio and lake loader, main's and branch environments', which read
234
+ * it, alone; its controllers' port, and its clients' as its brokers reach each other,
235
+ * from its own nodes; and its metrics port from the collector's scraper.
236
+ */
237
+ function logPolicy(config) {
238
+ const of = (component) => ({ podSelector: { matchLabels: selectorLabels(component) } });
239
+ const operator = kedaOperator(config);
240
+ return policy(LOG, NAMESPACE, LOG_COMPONENT, {
241
+ podSelector: { matchLabels: selectorLabels(LOG_COMPONENT) },
242
+ policyTypes: ["Ingress"],
243
+ ingress: [
244
+ { from: [of(LOG_COMPONENT)], ports: [LOG_PORT, LOG_CONTROLLER_PORT].map((port) => ({ protocol: "TCP", port })) },
245
+ {
246
+ from: [
247
+ of(GATEWAY_COMPONENT),
248
+ of(WEBHOOK_COMPONENT),
249
+ ...(sandboxServiceRuns(config) ? [of(SANDBOX_GATEWAY_COMPONENT)] : []),
250
+ of(INTAKE_COMPONENT),
251
+ operator,
252
+ of("alasio"),
253
+ of(LOADER_COMPONENT),
254
+ { namespaceSelector: BRANCH_NAMESPACES, podSelector: { matchLabels: selectorLabels("alasio") } },
255
+ { namespaceSelector: BRANCH_NAMESPACES, podSelector: { matchLabels: selectorLabels(LOADER_COMPONENT) } },
256
+ ],
257
+ ports: [{ protocol: "TCP", port: LOG_PORT }],
258
+ },
259
+ { from: [of(SCRAPER_COMPONENT)], ports: [{ protocol: "TCP", port: LOG_METRICS_PORT }] },
260
+ ],
261
+ });
262
+ }
263
+ /**
264
+ * The webhook intake takes its port from webhook.sources alone, Telegram's ranges, where it
265
+ * is on this machine's public address, and from anywhere else: a LoadBalancer Service takes
266
+ * those alone, and the cluster in Docker is reached from this machine. On the machine, the
267
+ * policy is what guards the port: what is forwarded to a pod, the policy's controller
268
+ * accepts ahead of the machine's firewall, which never sees it (cli/src/cluster/node.ts);
269
+ * the machine itself is admitted to its own pods all the same. It takes its health's port
270
+ * from the collector's scraper; it reaches the cluster's DNS and the log's brokers alone,
271
+ * so nothing it is sent can make it reach anything else.
272
+ */
273
+ function webhookPolicy(config) {
274
+ const sources = config.webhook.exposure === "hostAddress" ? config.webhook.sources : ["0.0.0.0/0"];
275
+ return policy(WEBHOOK, NAMESPACE, WEBHOOK_COMPONENT, {
276
+ podSelector: { matchLabels: selectorLabels(WEBHOOK_COMPONENT) },
277
+ policyTypes: ["Ingress", "Egress"],
278
+ ingress: [
279
+ { from: sources.map((cidr) => ({ ipBlock: { cidr } })), ports: [{ protocol: "TCP", port: config.webhook.port }] },
280
+ { from: [{ podSelector: { matchLabels: selectorLabels(SCRAPER_COMPONENT) } }], ports: [{ protocol: "TCP", port: WEBHOOK_HEALTH_PORT }] },
281
+ ],
282
+ egress: [
283
+ {
284
+ to: [{ namespaceSelector: { matchLabels: { "kubernetes.io/metadata.name": "kube-system" } }, podSelector: { matchLabels: { "k8s-app": "kube-dns" } } }],
285
+ ports: [{ protocol: "UDP", port: 53 }, { protocol: "TCP", port: 53 }],
286
+ },
287
+ { to: [{ podSelector: { matchLabels: selectorLabels(LOG_COMPONENT) } }], ports: [{ protocol: "TCP", port: LOG_PORT }] },
288
+ ],
289
+ });
290
+ }
291
+ /**
292
+ * The telemetry collector's gateway takes OTLP from alasio, the node agents, and the sandbox
293
+ * service's gateway and receiver, beside the stack's own pods (neonPolicies), its scraper
294
+ * among them, and from branch environments' alasio, compute and lake, and from nothing else:
295
+ * sessions' and folder instances' telemetry reaches it through a receiver alone, their
296
+ * alasio's or the sandbox service's.
239
297
  */
240
298
  function collectorPolicy(config) {
241
- const { host } = config;
242
- return policy(componentName("collector"), NAMESPACE, "collector", {
243
- podSelector: { matchLabels: selectorLabels("collector") },
299
+ return policy(componentName("collector"), NAMESPACE, GATEWAY_COMPONENT, {
300
+ podSelector: { matchLabels: selectorLabels(GATEWAY_COMPONENT) },
244
301
  policyTypes: ["Ingress"],
245
302
  ingress: [{
246
303
  from: [
247
304
  { podSelector: { matchLabels: selectorLabels("alasio") } },
248
305
  ...(nodeAgentRuns(config) ? [NODE_AGENT] : []),
249
- ...(host.enabled ? [{ namespaceSelector: namespaceNamed(host.namespace) }] : []),
306
+ ...(sandboxServiceRuns(config) ? [SANDBOX_GATEWAY, SANDBOX_RECEIVER] : []),
250
307
  { namespaceSelector: BRANCH_NAMESPACES, podSelector: { matchLabels: selectorLabels("alasio") } },
251
308
  { namespaceSelector: BRANCH_NAMESPACES, podSelector: { matchLabels: STACK } },
252
309
  ],
@@ -279,20 +336,30 @@ function grafanaPolicies({ sessions, grafana: { scaling } }) {
279
336
  ...(gossip.length > 0 ? [{ to: [grafana], ports: gossip }] : []),
280
337
  CLUSTER_DNS,
281
338
  { to: [{ podSelector: { matchLabels: selectorLabels("neon-proxy") } }], ports: [{ protocol: "TCP", port: PROXY_PORT }] },
282
- { to: [{ podSelector: { matchLabels: selectorLabels("lake") } }], ports: [{ protocol: "TCP", port: LAKE_QUERY_PORT }] },
339
+ { to: [{ podSelector: { matchLabels: selectorLabels(QUERY_COMPONENT) } }], ports: [{ protocol: "TCP", port: LAKE_QUERY_PORT }] },
283
340
  // The addresses sessions with internet may not reach, the cluster's and its network's, are Grafana's too.
284
341
  { to: [{ ipBlock: { cidr: "0.0.0.0/0", except: [...sessions.blockedCidrs] } }], ports: [{ protocol: "TCP", port: 443 }] },
285
342
  ],
286
343
  }),
287
344
  admitted("neon-proxy", PROXY_PORT),
288
- admitted("lake", LAKE_QUERY_PORT),
345
+ admitted(QUERY_COMPONENT, LAKE_QUERY_PORT),
289
346
  ];
290
347
  }
348
+ /** The lake's readers take KEDA's operator on their query port, which asks any of them how many queries the pool has not answered (their /pool). */
349
+ function lakeQueryPolicy(config) {
350
+ return policy(componentName(`${QUERY_COMPONENT}-from-autoscaler`), NAMESPACE, QUERY_COMPONENT, {
351
+ podSelector: { matchLabels: selectorLabels(QUERY_COMPONENT) },
352
+ policyTypes: ["Ingress"],
353
+ ingress: [{ from: [kedaOperator(config)], ports: [{ protocol: "TCP", port: LAKE_QUERY_PORT }] }],
354
+ });
355
+ }
291
356
  /**
292
- * The node agents take alasio's calls, on their API's port, and nothing else; they reach
293
- * the cluster's DNS, the object store's S3 port, the bundled one's or the operator's at
294
- * any address, and the collector where it runs, and nothing else. The bundled object
295
- * store admits them.
357
+ * The node agents take the sandbox service's gateway's calls alone, which attaches workspaces
358
+ * and saves and restores sandboxes through them, on their API's port, and nothing else, the
359
+ * gateway alone holding the token they take (./node-agent.ts); they reach the cluster's
360
+ * DNS, the object store's S3 port, the bundled one's or the operator's at any address,
361
+ * workspace storage's metadata engine, which they set workspaces' quotas on, and the collector
362
+ * where it runs, and nothing else. The bundled object store, and the metadata engine, admit them.
296
363
  */
297
364
  function nodeAgentPolicies(config) {
298
365
  const { bundled, external } = config.objectStore;
@@ -305,10 +372,11 @@ function nodeAgentPolicies(config) {
305
372
  policy(componentName("node-agent"), NAMESPACE, "node-agent", {
306
373
  ...NODE_AGENT,
307
374
  policyTypes: ["Ingress", "Egress"],
308
- ingress: [{ from: [{ podSelector: { matchLabels: selectorLabels("alasio") } }], ports: [{ protocol: "TCP", port: NODE_AGENT_PORT }] }],
375
+ ingress: sandboxServiceRuns(config) ? [{ from: [SANDBOX_GATEWAY], ports: [{ protocol: "TCP", port: NODE_AGENT_PORT }] }] : [],
309
376
  egress: [
310
377
  CLUSTER_DNS,
311
378
  objectStore,
379
+ ...(config.workspaceStorage.enabled ? [{ to: [{ podSelector: { matchLabels: selectorLabels("valkey") } }], ports: [{ protocol: "TCP", port: VALKEY_PORT }] }] : []),
312
380
  ...(collectorRuns(config) ? [{ to: [{ podSelector: { matchLabels: selectorLabels("collector") } }], ports: [{ protocol: "TCP", port: COLLECTOR_PORT }] }] : []),
313
381
  ],
314
382
  }),
@@ -321,19 +389,103 @@ function nodeAgentPolicies(config) {
321
389
  : []),
322
390
  ];
323
391
  }
392
+ /** The pods of the sandbox service's pools (./sandbox-gateway.ts), free or bound, as a selector: by the label each has of its pool. */
393
+ const POOLED = { matchExpressions: [{ key: POOL_LABEL, operator: "Exists" }] };
394
+ /** The sandbox service's gateway's pods, as a peer. */
395
+ const SANDBOX_GATEWAY = { podSelector: { matchLabels: selectorLabels(SANDBOX_GATEWAY_COMPONENT) } };
396
+ /** The sandbox service's receiver's pods, as a peer. */
397
+ const SANDBOX_RECEIVER = { podSelector: { matchLabels: selectorLabels(RECEIVER_COMPONENT) } };
398
+ /**
399
+ * The cluster's API server, as an egress rule: its addresses on its ports, as `alasio up`
400
+ * read them (../api-server.ts); or, where it could not, anywhere, which up warns of.
401
+ */
402
+ function apiServerRule(apiServer) {
403
+ if (!apiServer)
404
+ return {};
405
+ return { to: apiServer.addresses.map((address) => ({ ipBlock: { cidr: `${address}/32` } })), ports: apiServer.ports.map((port) => ({ protocol: "TCP", port })) };
406
+ }
407
+ /**
408
+ * The sandbox service's pools' pods (./sandbox-gateway.ts) of `environment`, free or bound:
409
+ * they take main's gateway alone, on bayma's port, and reach main's receiver alone, on its
410
+ * port, at the address their binding gives them, and a pod with internet the public internet
411
+ * beside, none of the cluster's or its network's private addresses, as a session does.
412
+ */
413
+ function poolPolicies(config, environment) {
414
+ const namespace = sessionsNamespace(config, environment);
415
+ return [
416
+ policy(componentName("sandboxes"), namespace, "session", {
417
+ podSelector: POOLED,
418
+ policyTypes: ["Ingress", "Egress"],
419
+ ingress: [{ from: [{ namespaceSelector: namespaceNamed(NAMESPACE), ...SANDBOX_GATEWAY }], ports: [{ protocol: "TCP", port: POOL_POD_PORT }] }],
420
+ egress: [{ to: [{ namespaceSelector: namespaceNamed(NAMESPACE), ...SANDBOX_RECEIVER }], ports: [{ protocol: "TCP", port: config.telemetry.receiverPort }] }],
421
+ }),
422
+ policy(componentName("sandboxes-full-internet"), namespace, "session", {
423
+ podSelector: { ...POOLED, matchLabels: { [NET_MODE_LABEL]: "full" } },
424
+ policyTypes: ["Egress"],
425
+ egress: [{ to: [{ ipBlock: { cidr: "0.0.0.0/0", except: [...config.sessions.blockedCidrs] } }] }],
426
+ }),
427
+ ];
428
+ }
324
429
  /**
325
- * alasio takes no connections but its sessions' telemetry, and, main, where it forks
326
- * sessions for branch environments, their alasio's asking it to (src/branch/fork.ts).
430
+ * The sandbox service's gateway and receiver, which take what sandboxes send, and so reach
431
+ * what they need of the cluster alone:
432
+ *
433
+ * - the gateway takes main's alasio and each branch environment's, which bind through it, its own replicas, which pass each
434
+ * other what is bound on the pods each holds, and KEDA's operator (kedaOperator), which asks
435
+ * it how many binds wait for a pool, on its port alone; it reaches the cluster's DNS, the
436
+ * pools' pods of every environment on bayma's port, the node agents, its replicas, the
437
+ * log, which it writes each environment's events to, the collector where it runs, and the
438
+ * API server;
439
+ * - the receiver takes the pools' pods alone, of main's sessions and of branch environments',
440
+ * on its port; it reaches the cluster's DNS, the collector where it runs, and the API server.
441
+ *
442
+ * The API server is reached at its addresses, as `apiServer` says (apiServerRule).
443
+ */
444
+ function sandboxServicePolicies(config, apiServer) {
445
+ const port = config.telemetry.receiverPort;
446
+ const pools = [
447
+ { namespaceSelector: namespaceNamed(sessionsNamespace(config, MAIN_ENVIRONMENT)), podSelector: POOLED },
448
+ { namespaceSelector: BRANCH_NAMESPACES, podSelector: POOLED },
449
+ ];
450
+ // Folder conversations' instances, which the gateway binds and the receiver takes the telemetry of as of the pools' pods.
451
+ const folders = foldersRun(config) ? [{ namespaceSelector: namespaceNamed(config.host.namespace), podSelector: POOLED }] : [];
452
+ const collector = collectorRuns(config)
453
+ ? [{ to: [{ podSelector: { matchLabels: selectorLabels(GATEWAY_COMPONENT) } }], ports: [{ protocol: "TCP", port: COLLECTOR_PORT }] }]
454
+ : [];
455
+ return [
456
+ policy(SANDBOX_GATEWAY_NAME, NAMESPACE, SANDBOX_GATEWAY_COMPONENT, {
457
+ ...SANDBOX_GATEWAY,
458
+ policyTypes: ["Ingress", "Egress"],
459
+ ingress: [{
460
+ from: [{ podSelector: { matchLabels: selectorLabels("alasio") } }, { namespaceSelector: BRANCH_NAMESPACES, podSelector: { matchLabels: selectorLabels("alasio") } }, SANDBOX_GATEWAY, kedaOperator(config)],
461
+ ports: [{ protocol: "TCP", port: SANDBOX_GATEWAY_PORT }],
462
+ }],
463
+ egress: [
464
+ CLUSTER_DNS,
465
+ { to: [...pools, ...folders], ports: [{ protocol: "TCP", port: POOL_POD_PORT }] },
466
+ { to: [NODE_AGENT], ports: [{ protocol: "TCP", port: NODE_AGENT_PORT }] },
467
+ { to: [SANDBOX_GATEWAY], ports: [{ protocol: "TCP", port: SANDBOX_GATEWAY_PORT }] },
468
+ // The log, its events written to it.
469
+ { to: [{ podSelector: { matchLabels: selectorLabels(LOG_COMPONENT) } }], ports: [{ protocol: "TCP", port: LOG_PORT }] },
470
+ ...collector,
471
+ apiServerRule(apiServer),
472
+ ],
473
+ }),
474
+ policy(RECEIVER, NAMESPACE, RECEIVER_COMPONENT, {
475
+ ...SANDBOX_RECEIVER,
476
+ policyTypes: ["Ingress", "Egress"],
477
+ ingress: [{ from: [...pools, ...folders], ports: [{ protocol: "TCP", port }] }],
478
+ egress: [CLUSTER_DNS, ...collector, apiServerRule(apiServer)],
479
+ }),
480
+ ];
481
+ }
482
+ /**
483
+ * alasio takes no connections but, main, where it forks sessions for branch environments,
484
+ * their alasio's asking it to (src/branch/fork.ts).
327
485
  */
328
486
  function alasioPolicy(config, environment) {
329
487
  const forks = environment.branch === null && forksForBranches(config);
330
488
  const ingress = [
331
- ...(config.sessions.enabled
332
- ? [{
333
- from: [{ namespaceSelector: namespaceNamed(sessionsNamespace(config, environment)), podSelector: { matchLabels: SESSION } }],
334
- ports: [{ protocol: "TCP", port: config.telemetry.receiverPort }],
335
- }]
336
- : []),
337
489
  ...(forks
338
490
  ? [{ from: [{ namespaceSelector: BRANCH_NAMESPACES, podSelector: { matchLabels: selectorLabels("alasio") } }], ports: [{ protocol: "TCP", port: BRANCH_FORK_PORT }] }]
339
491
  : []),
@@ -346,33 +498,44 @@ function alasioPolicy(config, environment) {
346
498
  }
347
499
  /**
348
500
  * The NetworkPolicies of the alasio of `environment`, unless they are turned off: main's,
349
- * and those that admit branch environments to what of main's they share; or a branch
501
+ * and those that admit branch environments to what of main's they share, what reaches its
502
+ * API server reaching it where `apiServer` says (../api-server.ts); or a branch
350
503
  * environment's own.
351
504
  */
352
- export function networkPolicyObjects(config, environment = MAIN_ENVIRONMENT) {
505
+ export function networkPolicyObjects(config, environment = MAIN_ENVIRONMENT, apiServer = null) {
353
506
  if (!config.networkPolicies.enabled)
354
507
  return [];
355
508
  const { sessions, host } = config;
356
509
  if (environment.branch !== null) {
357
- return [...(sessions.enabled ? sessionPolicies(config, environment) : []), alasioPolicy(config, environment), ...neonPolicies(environment)];
510
+ return [
511
+ ...(sessions.enabled ? sessionPolicies(config, environment) : []),
512
+ ...(sandboxServiceRuns(config) ? poolPolicies(config, environment) : []),
513
+ alasioPolicy(config, environment),
514
+ ...neonPolicies(environment),
515
+ ];
358
516
  }
359
517
  return [
360
518
  ...(sessions.enabled ? sessionPolicies(config, environment) : []),
361
- // Folder workspaces' bayma is reached by alasio alone; what it reaches is the machine's.
362
- ...(host.enabled
363
- ? [policy("bayma-from-alasio", host.namespace, "folder-bayma", {
519
+ // Folder conversations' instances are reached by the sandbox gateway alone, which binds them; what they reach is the machine's.
520
+ ...(foldersRun(config)
521
+ ? [policy("folders-from-gateway", host.namespace, "folder-bayma", {
522
+ // Every pod of the namespace, which none but a folder instance's may be.
364
523
  podSelector: {},
365
524
  policyTypes: ["Ingress"],
366
- ingress: [{ from: [alasioOf(environment)], ports: [{ protocol: "TCP", port: 7290 }] }],
525
+ ingress: [{ from: [{ namespaceSelector: namespaceNamed(NAMESPACE), ...SANDBOX_GATEWAY }], ports: [{ protocol: "TCP", port: FOLDER_PORT }] }],
367
526
  })]
368
527
  : []),
369
528
  alasioPolicy(config, environment),
370
529
  ...(config.neon.enabled ? [...neonPolicies(environment), proxyPolicy(), branchStoragePolicy(config)] : []),
371
- ...(config.neon.enabled && config.objectStore.bundled.enabled ? [gatewayPolicy(), ...partPolicies(), ...filerStorePolicies()] : []),
530
+ ...(config.objectStore.bundled.enabled ? [gatewayPolicy(), ...partPolicies(), ...filerStorePolicies()] : []),
531
+ logPolicy(config),
532
+ webhookPolicy(config),
372
533
  ...(collectorRuns(config) ? [collectorPolicy(config)] : []),
373
534
  ...(grafanaRuns(config) ? grafanaPolicies(config) : []),
535
+ ...(lakeRuns(config) ? [lakeQueryPolicy(config)] : []),
374
536
  ...(config.workspaceStorage.enabled ? workspaceStoragePolicies(config) : []),
375
537
  ...(nodeAgentRuns(config) ? nodeAgentPolicies(config) : []),
538
+ ...(sandboxServiceRuns(config) ? [...poolPolicies(config, environment), ...sandboxServicePolicies(config, apiServer)] : []),
376
539
  ];
377
540
  }
378
541
  //# sourceMappingURL=network-policies.js.map