openship 0.6.1 → 0.6.6

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 (67) hide show
  1. package/README.md +7 -2
  2. package/dist/{bare-IWS2QHJ3.js → bare-OXJVW3AK.js} +11 -9
  3. package/dist/{chunk-VZ2ZHPS2.js → chunk-2YM3NI2E.js} +3 -9
  4. package/dist/chunk-4I2BM2DT.js +68 -0
  5. package/dist/{chunk-2NZSUXOB.js → chunk-5DMWSTOM.js} +310 -1459
  6. package/dist/{chunk-3FYIXVX5.js → chunk-77QDF6UD.js} +581 -122
  7. package/dist/{chunk-IOT5ZX23.js → chunk-A4GXYE4L.js} +449 -383
  8. package/dist/chunk-CBLD2HS5.js +428 -0
  9. package/dist/chunk-ELGRMZOW.js +25 -0
  10. package/dist/{chunk-TWJCLD4W.js → chunk-FE7ZZQSE.js} +5 -5
  11. package/dist/{chunk-3NZICQHF.js → chunk-HKRQXPTI.js} +31 -8
  12. package/dist/{chunk-IXM5245F.js → chunk-LFE5KQ7Y.js} +553 -538
  13. package/dist/{chunk-VDDMADOF.js → chunk-LH2PLRQO.js} +16 -2
  14. package/dist/{chunk-EMXI4UCU.js → chunk-LUKYPVJ4.js} +29 -10
  15. package/dist/{chunk-UC7YIEHT.js → chunk-MLYSEZQB.js} +352 -47
  16. package/dist/chunk-NK5N5LXN.js +1681 -0
  17. package/dist/{chunk-RRJJGVKE.js → chunk-OBPKYXQL.js} +75 -17
  18. package/dist/{chunk-6ODUBKUB.js → chunk-PCPVTH6M.js} +78 -88
  19. package/dist/chunk-Q7WGTQQ5.js +88 -0
  20. package/dist/{chunk-EW6KB2J7.js → chunk-QVW76WDL.js} +440 -74
  21. package/dist/{chunk-YIPMDGG6.js → chunk-RSR7PFIQ.js} +79 -23
  22. package/dist/{chunk-3LDWWY7U.js → chunk-SM26HDS2.js} +41 -7
  23. package/dist/{chunk-OTLSJYAC.js → chunk-UJ6JZM65.js} +5185 -341
  24. package/dist/chunk-UVNMQM7W.js +444 -0
  25. package/dist/chunk-VTGPEC6B.js +308 -0
  26. package/dist/chunk-WVAML4H6.js +2145 -0
  27. package/dist/{chunk-JIMUYEKN.js → chunk-YIMOCZBZ.js} +1 -1
  28. package/dist/chunk-YMMQRE5J.js +292 -0
  29. package/dist/cloud-75VIT4KV.js +37 -0
  30. package/dist/{detect-ALF4GXRX.js → detect-OIE5V3VZ.js} +3 -3
  31. package/dist/{docker-QECQJ5P4.js → docker-VTR2GK5Y.js} +16 -8
  32. package/dist/{docker-edge-executor-62N64MGS.js → docker-edge-executor-22IBXNYJ.js} +5 -5
  33. package/dist/{edge-import-SAYTRJLO.js → edge-import-WJUB4M7F.js} +27 -23
  34. package/dist/edge-preflight-GBNCPNVU.js +42 -0
  35. package/dist/{ensure-container-edge-CFSCPGZK.js → ensure-container-edge-KPI2CVXJ.js} +9 -8
  36. package/dist/{executor-PT6UEJ5M.js → executor-7LXXZPBR.js} +15 -5
  37. package/dist/host-channel-preflight-KE6OONYK.js +50 -0
  38. package/dist/index.js +3783 -4221
  39. package/dist/{nginx-YOEEFBTE.js → nginx-KUFMLMAC.js} +13 -5
  40. package/dist/node-bootstrap.js +241 -0
  41. package/dist/node-entry.js +52 -1
  42. package/dist/{openresty-lua-WGBO2M3Z.js → openresty-lua-ATTH35BE.js} +8 -2
  43. package/dist/privilege-I4W3ZNZK.js +19 -0
  44. package/dist/server/engine/samples/amavisd/amavisd.conf +25 -0
  45. package/dist/server/engine/samples/fail2ban/jail.d/sshd.local +4 -1
  46. package/dist/server/engine/samples/postfix/master.cf +14 -0
  47. package/dist/server/index.js +77659 -61909
  48. package/dist/server/migrations/0098_instance_product_mode.sql +12 -0
  49. package/dist/server/migrations/0099_mail_webmail_project.sql +24 -0
  50. package/dist/server/migrations/0100_server_ssh_private_key.sql +11 -0
  51. package/dist/server/migrations/0101_mail_resume_step.sql +12 -0
  52. package/dist/server/migrations/0102_project_workload_axes.sql +19 -0
  53. package/dist/server/migrations/0103_dns_credential.sql +41 -0
  54. package/dist/server/migrations/0104_instance_host_control.sql +15 -0
  55. package/dist/server/migrations/0105_mail_inbound_rules.sql +80 -0
  56. package/dist/server/migrations/0106_mcp_call_tracking.sql +23 -0
  57. package/dist/server/migrations/0107_build_minute_metering_indexes.sql +20 -0
  58. package/dist/server/migrations/0108_credential.sql +68 -0
  59. package/dist/server/migrations/meta/_journal.json +77 -0
  60. package/dist/{setup-M2ZDGH3O.js → setup-XGLGUUFW.js} +10 -9
  61. package/dist/ssh-client-JBMA7SN4.js +28 -0
  62. package/dist/up-K4BV7466.js +57 -0
  63. package/package.json +4 -2
  64. package/dist/chunk-IW2UKBVF.js +0 -144
  65. package/dist/chunk-ZCFVHA25.js +0 -89
  66. package/dist/cloud-M2YLWEXO.js +0 -31
  67. /package/dist/{dist-es-JGBPABJI.js → dist-es-AXFRS7PS.js} +0 -0
@@ -5,25 +5,33 @@ const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
6
  MANAGED_STATIC_BASE,
7
7
  NginxProvider,
8
+ VHOST_GENERATION,
8
9
  acmeKeyArgs,
9
10
  assertValidChallengeToken,
11
+ isAcmePortBindFailure,
12
+ readVhostGeneration,
10
13
  renderGeoEntries,
11
14
  renderProxyOptions,
12
15
  renderProxyTlsOptions,
13
16
  summarizeCertbotFailure
14
- } from "./chunk-EW6KB2J7.js";
17
+ } from "./chunk-QVW76WDL.js";
18
+ import "./chunk-RSR7PFIQ.js";
15
19
  import "./chunk-I3DPMTSF.js";
16
- import "./chunk-VZ2ZHPS2.js";
17
- import "./chunk-3LDWWY7U.js";
18
- import "./chunk-YIPMDGG6.js";
20
+ import "./chunk-YMMQRE5J.js";
21
+ import "./chunk-UVNMQM7W.js";
19
22
  import "./chunk-ZFJXPCQP.js";
20
- import "./chunk-OTLSJYAC.js";
23
+ import "./chunk-2YM3NI2E.js";
24
+ import "./chunk-SM26HDS2.js";
25
+ import "./chunk-UJ6JZM65.js";
21
26
  import "./chunk-PXKCZB2I.js";
22
27
  export {
23
28
  MANAGED_STATIC_BASE,
24
29
  NginxProvider,
30
+ VHOST_GENERATION,
25
31
  acmeKeyArgs,
26
32
  assertValidChallengeToken,
33
+ isAcmePortBindFailure,
34
+ readVhostGeneration,
27
35
  renderGeoEntries,
28
36
  renderProxyOptions,
29
37
  renderProxyTlsOptions,
@@ -0,0 +1,241 @@
1
+ // src/node-bootstrap.ts
2
+ import { spawnSync as spawnSync2 } from "child_process";
3
+ import { existsSync as existsSync2 } from "fs";
4
+ import { join as join4 } from "path";
5
+ import { createInterface } from "readline";
6
+
7
+ // src/lib/node-runtime.ts
8
+ import { execFileSync, spawnSync } from "child_process";
9
+ import { chmodSync, existsSync, lstatSync, mkdirSync as mkdirSync2, rmSync, symlinkSync, writeFileSync } from "fs";
10
+ import { join as join3 } from "path";
11
+
12
+ // src/lib/cache.ts
13
+ import { createHash } from "crypto";
14
+ import { createReadStream, createWriteStream, mkdirSync } from "fs";
15
+ import { dirname, join as join2 } from "path";
16
+
17
+ // src/lib/paths.ts
18
+ import { homedir } from "os";
19
+ import { join, resolve } from "path";
20
+ var DEFAULT_HOME = join(homedir(), ".openship");
21
+ var override = process.env.OPENSHIP_HOME?.trim();
22
+ var OS_DIR = override ? resolve(override) : DEFAULT_HOME;
23
+ var DATA_DIR = join(OS_DIR, "data");
24
+ var LOG_DIR = join(OS_DIR, "logs");
25
+ var AUTH_SECRET_FILE = join(OS_DIR, "auth-secret");
26
+ var INTERNAL_TOKEN_FILE = join(OS_DIR, "internal-token");
27
+
28
+ // src/lib/cache.ts
29
+ var CACHE_DIR = join2(OS_DIR, "cache");
30
+ var RELEASES_DIR = join2(CACHE_DIR, "releases");
31
+ async function downloadToFile(url, dest, onProgress) {
32
+ const res = await fetch(url, { headers: { "User-Agent": "openship-cli" } });
33
+ if (!res.ok || !res.body) throw new Error(`HTTP ${res.status} for ${url}`);
34
+ mkdirSync(dirname(dest), { recursive: true });
35
+ const total = Number(res.headers.get("content-length")) || 0;
36
+ const hash = createHash("sha256");
37
+ const file = createWriteStream(dest);
38
+ const reader = res.body.getReader();
39
+ let received = 0;
40
+ try {
41
+ for (; ; ) {
42
+ const { done, value } = await reader.read();
43
+ if (done) break;
44
+ hash.update(value);
45
+ if (!file.write(Buffer.from(value))) {
46
+ await new Promise((r) => file.once("drain", r));
47
+ }
48
+ received += value.length;
49
+ onProgress?.(received, total);
50
+ }
51
+ } finally {
52
+ file.end();
53
+ }
54
+ await new Promise((resolve2, reject) => {
55
+ file.on("finish", () => resolve2());
56
+ file.on("error", reject);
57
+ });
58
+ return { sha256: hash.digest("hex"), size: received };
59
+ }
60
+
61
+ // src/lib/node-runtime.ts
62
+ var NODE_MAJOR = 22;
63
+ var NODE_DIST = "https://nodejs.org/dist";
64
+ var RUNTIME_DIR = join3(OS_DIR, "runtime");
65
+ var RUNTIME_CURRENT = join3(RUNTIME_DIR, "current");
66
+ var BIN_DIR = join3(OS_DIR, "bin");
67
+ var LAUNCHER_PATH = join3(BIN_DIR, "openship");
68
+ var LAUNCHER_SH = `#!/bin/sh
69
+ # Openship CLI launcher \u2014 runs the tarball-installed CLI under Node.
70
+ # Managed by scripts/install.sh and \`openship update\`; manual edits are overwritten.
71
+ SELF="$0"
72
+ while [ -L "$SELF" ]; do
73
+ _t="$(readlink "$SELF")"
74
+ case "$_t" in /*) SELF="$_t" ;; *) SELF="$(dirname "$SELF")/$_t" ;; esac
75
+ done
76
+ BIN_DIR="$(cd "$(dirname "$SELF")" && pwd)"
77
+ HOME_DIR="$(dirname "$BIN_DIR")"
78
+ CLI="$HOME_DIR/cli/current/dist/index.js"
79
+ VENDORED_NODE="$HOME_DIR/runtime/current/bin/node"
80
+ # Prefer a system node >= ${NODE_MAJOR}; fall back to the vendored runtime.
81
+ if command -v node >/dev/null 2>&1 && node -e 'process.exit(+process.versions.node.split(".")[0]>=${NODE_MAJOR}?0:1)' 2>/dev/null; then
82
+ exec node "$CLI" "$@"
83
+ elif [ -x "$VENDORED_NODE" ]; then
84
+ exec "$VENDORED_NODE" "$CLI" "$@"
85
+ else
86
+ echo "openship: no Node >= ${NODE_MAJOR} found (system or vendored). Reinstall: curl -fsSL https://get.openship.io | sh" >&2
87
+ exit 127
88
+ fi
89
+ `;
90
+ function nodeDistOs() {
91
+ if (process.platform === "darwin") return "darwin";
92
+ if (process.platform === "linux") return "linux";
93
+ throw new Error(`Unsupported platform for vendored Node: ${process.platform}`);
94
+ }
95
+ function nodeDistArch() {
96
+ if (process.arch === "x64") return "x64";
97
+ if (process.arch === "arm64") return "arm64";
98
+ throw new Error(`Unsupported arch for vendored Node: ${process.arch}`);
99
+ }
100
+ function majorOf(bin) {
101
+ try {
102
+ const out = execFileSync(bin, ["-e", "process.stdout.write(process.versions.node)"], {
103
+ encoding: "utf8",
104
+ stdio: ["ignore", "pipe", "ignore"]
105
+ });
106
+ const m = Number(out.trim().split(".")[0]);
107
+ return Number.isInteger(m) ? m : null;
108
+ } catch {
109
+ return null;
110
+ }
111
+ }
112
+ function repoint(link, target) {
113
+ try {
114
+ if (lstatSync(link)) rmSync(link, { recursive: true, force: true });
115
+ } catch {
116
+ }
117
+ symlinkSync(target, link);
118
+ }
119
+ async function resolveLatestNode() {
120
+ const res = await fetch(`${NODE_DIST}/index.json`, {
121
+ headers: { "User-Agent": "openship-cli" },
122
+ signal: AbortSignal.timeout(15e3)
123
+ });
124
+ if (!res.ok) throw new Error(`Node dist index lookup failed: HTTP ${res.status}`);
125
+ const rows = await res.json();
126
+ const prefix = `v${NODE_MAJOR}.`;
127
+ const row = rows.find((r) => r.version.startsWith(prefix) && r.lts) ?? rows.find((r) => r.version.startsWith(prefix));
128
+ if (!row) throw new Error(`No Node ${NODE_MAJOR}.x release found on nodejs.org`);
129
+ return row.version;
130
+ }
131
+ async function fetchVendoredNode(onLog) {
132
+ const ver = await resolveLatestNode();
133
+ const os = nodeDistOs();
134
+ const arch = nodeDistArch();
135
+ const name = `node-${ver}-${os}-${arch}.tar.gz`;
136
+ mkdirSync2(RUNTIME_DIR, { recursive: true });
137
+ const tarball = join3(RUNTIME_DIR, name);
138
+ onLog?.(`Fetching Node ${ver} (${os}-${arch}) from nodejs.org\u2026`);
139
+ const { sha256 } = await downloadToFile(`${NODE_DIST}/${ver}/${name}`, tarball);
140
+ const shaRes = await fetch(`${NODE_DIST}/${ver}/SHASUMS256.txt`, {
141
+ headers: { "User-Agent": "openship-cli" },
142
+ signal: AbortSignal.timeout(15e3)
143
+ });
144
+ if (!shaRes.ok) {
145
+ rmSync(tarball, { force: true });
146
+ throw new Error(`Node checksum list fetch failed: HTTP ${shaRes.status}`);
147
+ }
148
+ const expected = (await shaRes.text()).split("\n").map((l) => l.trim()).find((l) => l.endsWith(` ${name}`) || l.endsWith(` ${name}`))?.split(/\s+/)[0]?.toLowerCase();
149
+ if (!expected) {
150
+ rmSync(tarball, { force: true });
151
+ throw new Error(`No checksum for ${name} in SHASUMS256.txt`);
152
+ }
153
+ if (expected !== sha256) {
154
+ rmSync(tarball, { force: true });
155
+ throw new Error(`Node download checksum mismatch (expected ${expected}, got ${sha256}).`);
156
+ }
157
+ const unpacked = join3(RUNTIME_DIR, `node-${ver}-${os}-${arch}`);
158
+ rmSync(unpacked, { recursive: true, force: true });
159
+ const untar = spawnSync("tar", ["-xzf", tarball, "-C", RUNTIME_DIR], { stdio: "inherit" });
160
+ if (untar.status !== 0) throw new Error("Failed to extract the Node runtime (is `tar` available?).");
161
+ rmSync(tarball, { force: true });
162
+ const nodeBin = join3(unpacked, "bin", "node");
163
+ if (!existsSync(nodeBin)) throw new Error(`Node unpacked but ${nodeBin} is missing (unexpected layout).`);
164
+ repoint(RUNTIME_CURRENT, unpacked);
165
+ return nodeBin;
166
+ }
167
+ async function ensureNodeRuntime(onLog) {
168
+ if ((majorOf("node") ?? 0) >= NODE_MAJOR) return { node: "node", source: "system" };
169
+ const existing = join3(RUNTIME_CURRENT, "bin", "node");
170
+ if (existsSync(existing) && (majorOf(existing) ?? 0) >= NODE_MAJOR) {
171
+ return { node: existing, source: "vendored" };
172
+ }
173
+ return { node: await fetchVendoredNode(onLog), source: "vendored" };
174
+ }
175
+
176
+ // src/node-bootstrap.ts
177
+ var NODE_REEXEC_ENV = "OPENSHIP_NODE_REEXEC";
178
+ var VENDOR_ENV = "OPENSHIP_VENDOR_NODE";
179
+ var vendoredNode = () => join4(OS_DIR, "runtime", "current", "bin", "node");
180
+ function majorOf2(bin) {
181
+ const out = spawnSync2(bin, ["-e", "process.stdout.write(process.versions.node)"], {
182
+ encoding: "utf8"
183
+ });
184
+ if (out.status !== 0) return 0;
185
+ return Number.parseInt(out.stdout.trim().split(".")[0] ?? "", 10) || 0;
186
+ }
187
+ function reexec(nodeBin, cliPath) {
188
+ const res = spawnSync2(nodeBin, [cliPath, ...process.argv.slice(2)], {
189
+ stdio: "inherit",
190
+ env: { ...process.env, [NODE_REEXEC_ENV]: "1" }
191
+ });
192
+ return { handled: true, status: res.status ?? 1 };
193
+ }
194
+ function isAffirmative(answer) {
195
+ return !/^n/i.test(answer.trim());
196
+ }
197
+ function ask(question) {
198
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
199
+ return new Promise((resolve2) => {
200
+ rl.question(question, (answer) => {
201
+ rl.close();
202
+ resolve2(isAffirmative(answer));
203
+ });
204
+ });
205
+ }
206
+ async function recoverOldNode(opts) {
207
+ const existing = vendoredNode();
208
+ if (existsSync2(existing) && majorOf2(existing) >= NODE_MAJOR) {
209
+ return reexec(existing, opts.cliPath);
210
+ }
211
+ if (!opts.offerDownload) return { handled: false };
212
+ if (process.platform === "win32") return { handled: false };
213
+ const forced = process.env[VENDOR_ENV]?.trim();
214
+ if (forced === "0") return { handled: false };
215
+ if (forced !== "1") {
216
+ if (!process.stdin.isTTY) return { handled: false };
217
+ const yes = await ask(
218
+ `
219
+ Openship can download its own Node ${NODE_MAJOR} (~50 MB) into ${OS_DIR}/runtime
220
+ and use it for Openship only \u2014 your system Node is left exactly as it is.
221
+
222
+ Download it now? [Y/n] `
223
+ );
224
+ if (!yes) return { handled: false };
225
+ }
226
+ let nodeBin;
227
+ try {
228
+ nodeBin = (await ensureNodeRuntime(opts.log)).node;
229
+ } catch (e) {
230
+ opts.log(`Could not set up Node ${NODE_MAJOR}: ${e.message}`);
231
+ return { handled: false };
232
+ }
233
+ if (nodeBin === "node") return { handled: false };
234
+ opts.log(`Using Node ${NODE_MAJOR} from ${OS_DIR}/runtime \u2014 future runs pick it up automatically.`);
235
+ return reexec(nodeBin, opts.cliPath);
236
+ }
237
+ export {
238
+ NODE_REEXEC_ENV,
239
+ isAffirmative,
240
+ recoverOldNode
241
+ };
@@ -1,4 +1,55 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/node-entry.ts
4
- import "./index.js";
4
+ import { fileURLToPath } from "url";
5
+ var HARD_FLOOR = 20;
6
+ var SUPPORTED = 22;
7
+ var INSTALLER = "curl -fsSL https://get.openship.io | sh";
8
+ var CLI_PATH = fileURLToPath(new URL("./index.js", import.meta.url));
9
+ var log = (msg) => process.stderr.write(`${msg}
10
+ `);
11
+ var runningBun = typeof process.versions.bun === "string";
12
+ var major = Number.parseInt(process.versions.node?.split(".")[0] ?? "", 10);
13
+ var reexeced = process.env.OPENSHIP_NODE_REEXEC === "1";
14
+ var tooOld = !runningBun && !reexeced && Number.isFinite(major) && major < SUPPORTED;
15
+ if (tooOld) {
16
+ try {
17
+ const { recoverOldNode } = await import("./node-bootstrap.js");
18
+ const outcome = await recoverOldNode({
19
+ cliPath: CLI_PATH,
20
+ // 20 and 21 DO run: reuse a Node already on disk, but don't prompt to download
21
+ // one on every invocation to fix a setup that already works.
22
+ offerDownload: major < HARD_FLOOR,
23
+ log
24
+ });
25
+ if (outcome.handled) process.exit(outcome.status ?? 0);
26
+ } catch {
27
+ }
28
+ if (major < HARD_FLOOR) {
29
+ process.stderr.write(
30
+ `
31
+ Openship needs Node ${SUPPORTED} or newer \u2014 this is Node v${process.versions.node}.
32
+
33
+ Install Node ${SUPPORTED}+ and re-run, or use the official installer, which
34
+ brings its own Node and doesn't touch the one you have:
35
+
36
+ ${INSTALLER}
37
+
38
+ Non-interactive (CI, a service)? Set OPENSHIP_VENDOR_NODE=1 and Openship
39
+ fetches its own Node ${SUPPORTED} without asking.
40
+
41
+ If you upgrade Node with a version manager (nvm, fnm, volta), global packages
42
+ live per Node version \u2014 reinstall the CLI afterwards or the command will go
43
+ missing: npm i -g openship
44
+
45
+ `
46
+ );
47
+ process.exit(1);
48
+ }
49
+ process.stderr.write(
50
+ `Warning: Node v${process.versions.node} is below Openship's supported floor (Node ${SUPPORTED}+). It may work; it isn't tested. Upgrade, or install with:
51
+ ${INSTALLER}
52
+ `
53
+ );
54
+ }
55
+ await import("./index.js");
@@ -10,6 +10,7 @@ import {
10
10
  EDGE_CHALLENGE_LOCATION,
11
11
  EDGE_CHALLENGE_ROOT,
12
12
  EDGE_CHALLENGE_URL_PREFIX,
13
+ EDGE_CLIENT_MAX_BODY_SIZE,
13
14
  EDGE_CONTAINER_MOUNTS,
14
15
  EDGE_DEFAULT_CERT_CN,
15
16
  EDGE_DEFAULT_CERT_DIRNAME,
@@ -18,6 +19,7 @@ import {
18
19
  EDGE_HTTPS_REJECT_BLOCK,
19
20
  EDGE_LUA_PACKAGE_PATH,
20
21
  EDGE_MGMT_SERVER_BLOCK,
22
+ EDGE_SAME_PATH_MOUNTS,
21
23
  EDGE_SERVER_NAMES_HASH_BUCKET_SIZE,
22
24
  EDGE_SHARED_DICTS,
23
25
  GEOIP_DB_PATH,
@@ -38,9 +40,11 @@ import {
38
40
  ensureOpenRestyConfig,
39
41
  luaSourceAvailable,
40
42
  parseNginxVersion
41
- } from "./chunk-YIPMDGG6.js";
43
+ } from "./chunk-RSR7PFIQ.js";
44
+ import "./chunk-YMMQRE5J.js";
45
+ import "./chunk-UVNMQM7W.js";
42
46
  import "./chunk-ZFJXPCQP.js";
43
- import "./chunk-OTLSJYAC.js";
47
+ import "./chunk-UJ6JZM65.js";
44
48
  import "./chunk-PXKCZB2I.js";
45
49
  export {
46
50
  ACME_CHALLENGE_LOCATION,
@@ -50,6 +54,7 @@ export {
50
54
  EDGE_CHALLENGE_LOCATION,
51
55
  EDGE_CHALLENGE_ROOT,
52
56
  EDGE_CHALLENGE_URL_PREFIX,
57
+ EDGE_CLIENT_MAX_BODY_SIZE,
53
58
  EDGE_CONTAINER_MOUNTS,
54
59
  EDGE_DEFAULT_CERT_CN,
55
60
  EDGE_DEFAULT_CERT_DIRNAME,
@@ -58,6 +63,7 @@ export {
58
63
  EDGE_HTTPS_REJECT_BLOCK,
59
64
  EDGE_LUA_PACKAGE_PATH,
60
65
  EDGE_MGMT_SERVER_BLOCK,
66
+ EDGE_SAME_PATH_MOUNTS,
61
67
  EDGE_SERVER_NAMES_HASH_BUCKET_SIZE,
62
68
  EDGE_SHARED_DICTS,
63
69
  GEOIP_DB_PATH,
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env sh
2
+ ":" //# ; exec "$(command -v node || command -v bun)" "$0" "$@"
3
+ import { createRequire as __ospCreateRequire } from "node:module";
4
+ const require = __ospCreateRequire(import.meta.url);
5
+ import {
6
+ privilegedExecutor,
7
+ rootChecked,
8
+ rootOrDegrade
9
+ } from "./chunk-4I2BM2DT.js";
10
+ import "./chunk-YMMQRE5J.js";
11
+ import "./chunk-UVNMQM7W.js";
12
+ import "./chunk-ZFJXPCQP.js";
13
+ import "./chunk-UJ6JZM65.js";
14
+ import "./chunk-PXKCZB2I.js";
15
+ export {
16
+ privilegedExecutor,
17
+ rootChecked,
18
+ rootOrDegrade
19
+ };
@@ -477,6 +477,31 @@ $bad_header_quarantine_to = 'bad-header-quarantine';
477
477
  #$undecipherable_subject_tag = '***UNCHECKED*** '; # undef disables it
478
478
  $undecipherable_subject_tag = undef;
479
479
 
480
+ # ─── What happens when NO virus scanner can be reached (GH-565) ──────────────
481
+ #
482
+ # With the settings above and @av_scanners_backup = () earlier in this file, a
483
+ # message that no scanner could examine is DELIVERED, with no subject tag and no
484
+ # admin notification. That is amavis' default and it is a deliberate choice here:
485
+ # clamd's signature load is memory-hungry and its socket is the most fragile part
486
+ # of the stack, so failing OPEN keeps mail flowing on small VPSes.
487
+ #
488
+ # It is a real trade-off, so know which side you are on. Note that amavis still
489
+ # adds its `X-Virus-Scanned` header on this path — the header records that amavis
490
+ # processed the message, NOT that a scanner passed it, and it must not be read as
491
+ # proof of a clean scan. The Emails → Health tab is the honest signal: it reports
492
+ # ClamAV as Crashed/Failed and says mail is being delivered without scanning.
493
+ #
494
+ # To fail CLOSED instead — mail queues (4xx tempfail) until a scanner is back,
495
+ # rather than arriving unscanned — uncomment ONE of these:
496
+ #
497
+ # $virus_scanners_failure_is_fatal = 1; # scanner failure => tempfail
498
+ # $final_unchecked_destiny = D_TEMPFAIL; # same, via the destiny table
499
+ #
500
+ # Before you do: while clamd is down this defers EVERY inbound message, and a
501
+ # queue that outlives the sender's retry window loses mail. Pair it with the
502
+ # subject tag above if you would rather label than defer.
503
+ # ─────────────────────────────────────────────────────────────────────────────
504
+
480
505
  # Hope to fix 'nested MAIL command' issue on high load server.
481
506
  $smtp_connection_cache_enable = 0;
482
507
 
@@ -1,7 +1,10 @@
1
1
  [sshd]
2
2
  backend = polling
3
3
  journalmatch=
4
- enabled = true
4
+ # openship-mail runs no sshd (SSH is on the host, not in the mail container), so
5
+ # there is no /var/log/auth.log for this jail to tail — leaving it enabled makes
6
+ # fail2ban crash-loop on a missing logpath. Disabled for the containerized engine.
7
+ enabled = false
5
8
  filter = sshd
6
9
  logpath = PH_SSHD_LOGFILE tail
7
10
  action = PH_FAIL2BAN_ACTION[name=sshd, port="PH_SSHD_PORT", protocol=tcp]
@@ -24,12 +24,26 @@ mlmmj unix - n n - - pipe
24
24
  flags=ORhu user=PH_SYS_USER_MLMMJ:PH_SYS_GROUP_MLMMJ argv=PH_CMD_MLMMJ_AMIME_RECEIVE -L PH_MLMMJ_SPOOL_DIR/${nexthop}
25
25
 
26
26
  # Amavisd integration.
27
+ #
28
+ # smtp_tls_security_level=none is REQUIRED, not an optimisation (GH-392). This
29
+ # is an smtp CLIENT service, so it inherits the global smtp_tls_security_level
30
+ # — and amavisd's :10024 listener does not offer STARTTLS. With the global set
31
+ # to `encrypt` every inbound message defers with "TLS is required, but was not
32
+ # offered": a complete INBOUND outage, from a setting that only looks like it
33
+ # concerns outbound delivery. Saving an outbound relay with scope "all" sets
34
+ # exactly that (apps/api/src/modules/mail/admin/outbound-relay.service.ts), and
35
+ # so does an operator hardening TLS by hand.
36
+ #
37
+ # TLS here would be meaningless anyway: the hop is loopback to a content filter
38
+ # on the same host. Pinning it at the transport keeps the guarantee local to the
39
+ # thing that needs it, instead of depending on every writer of the global.
27
40
  smtp-amavis unix - - n - PH_AMAVISD_MAX_SERVERS smtp
28
41
  -o syslog_name=postfix/amavis
29
42
  -o local_header_rewrite_clients=
30
43
  -o smtp_data_done_timeout=1200
31
44
  -o smtp_send_xforward_command=yes
32
45
  -o smtp_dns_support_level=disabled
46
+ -o smtp_tls_security_level=none
33
47
  -o max_use=20
34
48
 
35
49
  # smtp port used by Amavisd to re-inject scanned email back to Postfix