@studiometa/trafic-agent 0.1.36 → 0.1.38

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/README.md CHANGED
@@ -157,12 +157,114 @@ The agent:
157
157
 
158
158
  ## Endpoints
159
159
 
160
+ All internal endpoints are prefixed with `__` so they cannot collide with a
161
+ project's own paths. Everything else is treated as a request for a project.
162
+
160
163
  | Endpoint | Description |
161
164
  |----------|-------------|
162
- | `GET /auth` | Forward auth for Traefik |
163
- | `GET /errors` | Error handler for stopped projects |
164
- | `GET /status` | Health check |
165
- | `GET /projects` | List all projects (JSON) |
165
+ | `GET /__auth__` | Forward auth for Traefik. `200` allows, `401` prompts for basic auth |
166
+ | `GET /__status__?project=<name>` | Project status as JSON, for the waiting page to poll |
167
+ | `GET /__health__` | Agent health and version |
168
+ | anything else | Waiting page for a known project, error page otherwise |
169
+
170
+ ## TLS
171
+
172
+ Traefik obtains the certificates, and `setup --email` is what turns that on:
173
+
174
+ ```bash
175
+ trafic setup --host server.example.com --tld previews.example.com \
176
+ --email admin@example.com
177
+ ```
178
+
179
+ With an email, DDEV sets `use_letsencrypt=true` and Traefik requests a
180
+ certificate per project hostname on first use — previews included, since each
181
+ one gets its own router. Without an email, projects are served with a locally
182
+ trusted mkcert certificate, which is fine behind a proxy that terminates TLS
183
+ itself but shows a browser warning if used directly.
184
+
185
+ Two limits worth knowing before you rely on it:
186
+
187
+ - Let's Encrypt allows **50 new certificates per registered domain per week**.
188
+ Each preview hostname is a new certificate, so environments that churn fast
189
+ can hit the ceiling. Renewals do not count against it.
190
+ - Turning Let's Encrypt off does not discard certificates already issued.
191
+ Traefik keeps them in `acme.json` in the `ddev-global-cache` volume and
192
+ serves them again after a restart, so `configureDdev` deletes that storage
193
+ when Let's Encrypt is disabled.
194
+
195
+ Wildcard certificates would avoid the per-hostname limit but need a DNS-01
196
+ challenge, which DDEV does not do. If you churn tens of previews a week, put a
197
+ proxy in front that can (Caddy or Traefik with a DNS provider) and disable
198
+ Let's Encrypt in DDEV so the two do not both try.
199
+
200
+ ## Network exposure
201
+
202
+ **`ufw default deny incoming` does not cover the ports Docker publishes.**
203
+ Docker adds its rules to `nat/PREROUTING` and the `DOCKER` chain of `FORWARD`,
204
+ both of which are evaluated before UFW's chains. So the ports ddev-router
205
+ publishes are reachable from the internet whatever UFW says about them, and
206
+ `ufw status` will not tell you otherwise.
207
+
208
+ `setup` opens 22, 80 and 443, and 9876 for the agent from the Docker bridge
209
+ only. Those rules are accurate. What they do **not** do is close the ports
210
+ ddev-router publishes for DDEV's tools:
211
+
212
+ | Port | Service |
213
+ |------|---------|
214
+ | 8025, 8026 | Mailpit |
215
+ | 8142, 8143 | xhgui |
216
+
217
+ Both layers now apply:
218
+
219
+ 1. **`DOCKER-USER` rules**, installed by `setup`. That is the chain Docker
220
+ jumps to first in `FORWARD` and never flushes, so it is the only host-level
221
+ hook that can filter a published port. The rules drop traffic to the tool
222
+ ports unless it comes from Docker's own networks. They match on the
223
+ *original* destination port (`-m conntrack --ctorigdstport`), because by
224
+ `FORWARD` the destination has already been rewritten to the container.
225
+
226
+ They live in `/usr/local/sbin/trafic-docker-firewall`, reapplied at boot and
227
+ after any Docker restart by `trafic-docker-firewall.service` — Docker
228
+ recreates `DOCKER-USER` empty and nothing else would restore them.
229
+ **They will not appear in `ufw status`.** Inspect them with
230
+ `iptables -S DOCKER-USER`.
231
+
232
+ 2. **Forward auth**, attached to every entry point ddev-router publishes, so a
233
+ request that does reach a tool port still needs credentials. An earlier
234
+ version attached it only to 80 and 443, which left xhgui answering from the
235
+ internet with no authentication at all.
236
+
237
+ Access from the host itself is unaffected: a connection to `127.0.0.1:8026`
238
+ goes through Docker's userland proxy rather than `FORWARD`, so an SSH tunnel
239
+ still works:
240
+
241
+ ```bash
242
+ ssh -L 8026:127.0.0.1:8026 ddev@server.example.com
243
+ # then open https://localhost:8026
244
+ ```
245
+
246
+ ### A provider firewall is not a substitute
247
+
248
+ Recommended as an extra layer, but do not rely on it alone. Measured on an OVH
249
+ dedicated server with the Edge Firewall enabled and a correct rule denying
250
+ these ports: a connection from **another host inside OVH** still completed in
251
+ 113ms, while a port with nothing listening was dropped and timed out at 12s.
252
+ The deny applies to traffic crossing the provider's edge; traffic that never
253
+ crosses it is not filtered.
254
+
255
+ Anyone able to rent a VM from the same provider is inside that blind spot,
256
+ which for a large host is a very low bar. That is why the `DOCKER-USER` rules
257
+ above exist: they apply to every packet regardless of origin.
258
+
259
+ On OVH the setting is the Network Firewall in the control panel. Two things to
260
+ know: it is *stateless*, so `permit tcp established` is required or return
261
+ traffic for outbound connections is dropped, and it is IPv4-only. `setup` does
262
+ not configure it — it can neither create nor verify it.
263
+
264
+ Binding ddev-router to loopback only (`router-bind-all-interfaces=false`)
265
+ closes everything at once, but then nothing serves the public and you need a
266
+ host proxy in front. That is a reasonable setup; it is just a different one
267
+ from what `setup` builds.
166
268
 
167
269
  ## License
168
270
 
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { d as validateConfig, i as runPendingMigrations, l as loadConfig, n as setup, r as listMigrations, s as startIdleScheduler, t as audit } from "./setup-ClYjldGS.js";
3
- import { E as closeDb, b as success, g as isRoot, h as info, p as exec, r as startServer, x as warn, y as step } from "./server-4J8XoTP3.js";
2
+ import { g as validateConfig, i as runPendingMigrations, m as loadConfig, n as setup, r as listMigrations, s as startIdleScheduler, t as audit } from "./setup-BFU-DlX3.js";
3
+ import { E as exec, M as step, N as success, O as info, P as warn, h as closeDb, k as isRoot, n as startServer } from "./server-CvSztHW2.js";
4
4
  import { readFileSync } from "node:fs";
5
5
  import { execSync, spawnSync } from "node:child_process";
6
6
  import { parseArgs } from "node:util";
@@ -108,7 +108,7 @@ function runUpgrade(dryRun = false, reExecArgs) {
108
108
  }
109
109
  const alreadyReExeced = process.env["TRAFIC_UPGRADE_REEXEC"] === "1";
110
110
  step("Check for updates");
111
- const current = "0.1.36";
111
+ const current = "0.1.38";
112
112
  info(`Current version: ${current}`);
113
113
  const latest = fetchLatestVersion();
114
114
  if (!latest) warn("Could not reach npm registry — skipping version check");
@@ -191,7 +191,7 @@ function printHelp() {
191
191
  console.log(HELP.trim());
192
192
  }
193
193
  function printVersion() {
194
- console.log("0.1.36");
194
+ console.log("0.1.38");
195
195
  }
196
196
  async function runStart(values) {
197
197
  const config = loadConfig(values.config);
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export type { AgentConfig, AuthConfig, AuthRule, AuthRequest, AuthResult, DdevPr
2
2
  export { loadConfig, validateConfig, parseDuration } from "./utils/config.js";
3
3
  export { checkAuth, matchIp, matchHostname, parseBasicAuth, parseBearerToken, } from "./utils/auth.js";
4
4
  export { loadProjectList, buildHostnameIndex, getProjectFromHostname, watchProjectList, getProjectInfo, startProject, stopProject, listProjects, } from "./utils/ddev.js";
5
+ export { buildDockerFirewallScript, buildDockerFirewallUnit, configureDockerFirewall, DOCKER_FIREWALL_SCRIPT, DOCKER_FIREWALL_UNIT, } from "./setup/docker-firewall.js";
5
6
  export { initDb, getDb, closeDb, updateProjectAccess, getProject, setProjectStatus, getIdleProjects, logAccess, getAccessLogs, cleanOldLogs, } from "./utils/db.js";
6
7
  export { loadProjectConfig, shouldNeverStop, getIdleTimeoutMs, } from "./utils/project-config.js";
7
8
  export type { ProjectConfig } from "./utils/project-config.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,WAAW,EACX,UAAU,EACV,QAAQ,EACR,WAAW,EACX,UAAU,EACV,WAAW,EACX,aAAa,EACb,SAAS,GACV,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAG9E,OAAO,EACL,SAAS,EACT,OAAO,EACP,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,WAAW,EACX,YAAY,GACb,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,MAAM,EACN,KAAK,EACL,OAAO,EACP,mBAAmB,EACnB,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,aAAa,EACb,YAAY,GACb,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG/D,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG5E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,WAAW,EACX,UAAU,EACV,QAAQ,EACR,WAAW,EACX,UAAU,EACV,WAAW,EACX,aAAa,EACb,SAAS,GACV,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAG9E,OAAO,EACL,SAAS,EACT,OAAO,EACP,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,WAAW,EACX,YAAY,GACb,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,MAAM,EACN,KAAK,EACL,OAAO,EACP,mBAAmB,EACnB,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,aAAa,EACb,YAAY,GACb,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG/D,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG5E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { a as printAuditResults, c as stopIdleProjects, d as validateConfig, l as loadConfig, n as setup, o as runAudit, s as startIdleScheduler, t as audit, u as parseDuration } from "./setup-ClYjldGS.js";
2
- import { A as getProject, B as startProject, C as loadProjectConfig, D as getAccessLogs, E as closeDb, F as buildHostnameIndex, G as matchIp, H as watchProjectList, I as getProjectFromHostname, K as parseBasicAuth, L as getProjectInfo, M as logAccess, N as setProjectStatus, O as getDb, P as updateProjectAccess, R as listProjects, S as getIdleTimeoutMs, T as cleanOldLogs, U as checkAuth, V as stopProject, W as matchHostname, j as initDb, k as getIdleProjects, q as parseBearerToken, r as startServer, w as shouldNeverStop, z as loadProjectList } from "./server-4J8XoTP3.js";
3
- export { audit, buildHostnameIndex, checkAuth, cleanOldLogs, closeDb, getAccessLogs, getDb, getIdleProjects, getIdleTimeoutMs, getProject, getProjectFromHostname, getProjectInfo, initDb, listProjects, loadConfig, loadProjectConfig, loadProjectList, logAccess, matchHostname, matchIp, parseBasicAuth, parseBearerToken, parseDuration, printAuditResults, runAudit, setProjectStatus, setup, shouldNeverStop, startIdleScheduler, startProject, startServer, stopIdleProjects, stopProject, updateProjectAccess, validateConfig, watchProjectList };
1
+ import { a as printAuditResults, c as stopIdleProjects, d as buildDockerFirewallScript, f as buildDockerFirewallUnit, g as validateConfig, h as parseDuration, l as DOCKER_FIREWALL_SCRIPT, m as loadConfig, n as setup, o as runAudit, p as configureDockerFirewall, s as startIdleScheduler, t as audit, u as DOCKER_FIREWALL_UNIT } from "./setup-BFU-DlX3.js";
2
+ import { B as startProject, C as updateProjectAccess, F as buildHostnameIndex, G as matchIp, H as watchProjectList, I as getProjectFromHostname, K as parseBasicAuth, L as getProjectInfo, R as listProjects, S as setProjectStatus, U as checkAuth, V as stopProject, W as matchHostname, _ as getDb, b as initDb, d as getIdleTimeoutMs, f as loadProjectConfig, g as getAccessLogs, h as closeDb, m as cleanOldLogs, n as startServer, p as shouldNeverStop, q as parseBearerToken, v as getIdleProjects, x as logAccess, y as getProject, z as loadProjectList } from "./server-CvSztHW2.js";
3
+ export { DOCKER_FIREWALL_SCRIPT, DOCKER_FIREWALL_UNIT, audit, buildDockerFirewallScript, buildDockerFirewallUnit, buildHostnameIndex, checkAuth, cleanOldLogs, closeDb, configureDockerFirewall, getAccessLogs, getDb, getIdleProjects, getIdleTimeoutMs, getProject, getProjectFromHostname, getProjectInfo, initDb, listProjects, loadConfig, loadProjectConfig, loadProjectList, logAccess, matchHostname, matchIp, parseBasicAuth, parseBearerToken, parseDuration, printAuditResults, runAudit, setProjectStatus, setup, shouldNeverStop, startIdleScheduler, startProject, startServer, stopIdleProjects, stopProject, updateProjectAccess, validateConfig, watchProjectList };
@@ -394,6 +394,131 @@ function listProjects() {
394
394
  }
395
395
  }
396
396
  //#endregion
397
+ //#region src/setup/steps.ts
398
+ var stepNumber = 0;
399
+ var dryRun = false;
400
+ /**
401
+ * Set dry-run mode
402
+ */
403
+ function setDryRun(value) {
404
+ dryRun = value;
405
+ }
406
+ /**
407
+ * Reset step counter
408
+ */
409
+ function resetSteps() {
410
+ stepNumber = 0;
411
+ }
412
+ /**
413
+ * Log a step
414
+ */
415
+ function step(message) {
416
+ stepNumber++;
417
+ console.log(`\n\x1b[36m[${stepNumber}]\x1b[0m ${message}`);
418
+ }
419
+ /**
420
+ * Log success
421
+ */
422
+ function success(message) {
423
+ console.log(` \x1b[32m✓\x1b[0m ${message}`);
424
+ }
425
+ /**
426
+ * Log warning
427
+ */
428
+ function warn(message) {
429
+ console.log(` \x1b[33m⚠\x1b[0m ${message}`);
430
+ }
431
+ /**
432
+ * Log error
433
+ */
434
+ function error(message) {
435
+ console.log(` \x1b[31m✗\x1b[0m ${message}`);
436
+ }
437
+ /**
438
+ * Log info
439
+ */
440
+ function info(message) {
441
+ console.log(` ${message}`);
442
+ }
443
+ /**
444
+ * Execute a command (or show in dry-run mode)
445
+ */
446
+ function exec(command, options) {
447
+ if (dryRun) {
448
+ if (!options?.silent) console.log(` \x1b[90m$ ${command}\x1b[0m`);
449
+ return "";
450
+ }
451
+ try {
452
+ return execSync(command, {
453
+ encoding: "utf-8",
454
+ stdio: options?.silent ? "pipe" : "inherit"
455
+ });
456
+ } catch (err) {
457
+ throw new Error(`Command failed: ${command}\n${err.stderr ?? ""}`);
458
+ }
459
+ }
460
+ /**
461
+ * Execute a command and return output (silent)
462
+ */
463
+ function execSilent(command) {
464
+ if (dryRun) return "";
465
+ try {
466
+ return execSync(command, {
467
+ encoding: "utf-8",
468
+ stdio: "pipe"
469
+ }).trim();
470
+ } catch {
471
+ return "";
472
+ }
473
+ }
474
+ /**
475
+ * Check if a command exists
476
+ */
477
+ function commandExists(command) {
478
+ try {
479
+ execSync(`which ${command}`, { stdio: "pipe" });
480
+ return true;
481
+ } catch {
482
+ return false;
483
+ }
484
+ }
485
+ /**
486
+ * Check if running as root
487
+ */
488
+ function isRoot() {
489
+ return process.getuid?.() === 0;
490
+ }
491
+ /**
492
+ * Write a file (or show in dry-run mode)
493
+ */
494
+ function writeFile(path, content) {
495
+ if (dryRun) {
496
+ console.log(` \x1b[90mWrite ${path}:\x1b[0m`);
497
+ const lines = content.split("\n").slice(0, 5);
498
+ for (const line of lines) console.log(` \x1b[90m ${line}\x1b[0m`);
499
+ if (content.split("\n").length > 5) console.log(` \x1b[90m ...\x1b[0m`);
500
+ return;
501
+ }
502
+ writeFileSync(path, content);
503
+ }
504
+ //#endregion
505
+ //#region src/setup/io.ts
506
+ /**
507
+ * The real implementation, used when no io is injected.
508
+ *
509
+ * `writeFile` comes from steps.js rather than `node:fs` so writes are
510
+ * skipped in dry-run mode. Reads stay direct: they change nothing.
511
+ */
512
+ var nodeIo = {
513
+ exec,
514
+ execSilent,
515
+ commandExists,
516
+ writeFile,
517
+ fileExists: existsSync,
518
+ readFile: (path) => readFileSync(path, "utf-8"),
519
+ env: process.env
520
+ };
521
+ //#endregion
397
522
  //#region src/utils/db.ts
398
523
  var db = null;
399
524
  /**
@@ -574,131 +699,6 @@ function getIdleTimeoutMs(config) {
574
699
  return ms > 0 ? ms : void 0;
575
700
  }
576
701
  //#endregion
577
- //#region src/setup/steps.ts
578
- var stepNumber = 0;
579
- var dryRun = false;
580
- /**
581
- * Set dry-run mode
582
- */
583
- function setDryRun(value) {
584
- dryRun = value;
585
- }
586
- /**
587
- * Reset step counter
588
- */
589
- function resetSteps() {
590
- stepNumber = 0;
591
- }
592
- /**
593
- * Log a step
594
- */
595
- function step(message) {
596
- stepNumber++;
597
- console.log(`\n\x1b[36m[${stepNumber}]\x1b[0m ${message}`);
598
- }
599
- /**
600
- * Log success
601
- */
602
- function success(message) {
603
- console.log(` \x1b[32m✓\x1b[0m ${message}`);
604
- }
605
- /**
606
- * Log warning
607
- */
608
- function warn(message) {
609
- console.log(` \x1b[33m⚠\x1b[0m ${message}`);
610
- }
611
- /**
612
- * Log error
613
- */
614
- function error(message) {
615
- console.log(` \x1b[31m✗\x1b[0m ${message}`);
616
- }
617
- /**
618
- * Log info
619
- */
620
- function info(message) {
621
- console.log(` ${message}`);
622
- }
623
- /**
624
- * Execute a command (or show in dry-run mode)
625
- */
626
- function exec(command, options) {
627
- if (dryRun) {
628
- if (!options?.silent) console.log(` \x1b[90m$ ${command}\x1b[0m`);
629
- return "";
630
- }
631
- try {
632
- return execSync(command, {
633
- encoding: "utf-8",
634
- stdio: options?.silent ? "pipe" : "inherit"
635
- });
636
- } catch (err) {
637
- throw new Error(`Command failed: ${command}\n${err.stderr ?? ""}`);
638
- }
639
- }
640
- /**
641
- * Execute a command and return output (silent)
642
- */
643
- function execSilent(command) {
644
- if (dryRun) return "";
645
- try {
646
- return execSync(command, {
647
- encoding: "utf-8",
648
- stdio: "pipe"
649
- }).trim();
650
- } catch {
651
- return "";
652
- }
653
- }
654
- /**
655
- * Check if a command exists
656
- */
657
- function commandExists(command) {
658
- try {
659
- execSync(`which ${command}`, { stdio: "pipe" });
660
- return true;
661
- } catch {
662
- return false;
663
- }
664
- }
665
- /**
666
- * Check if running as root
667
- */
668
- function isRoot() {
669
- return process.getuid?.() === 0;
670
- }
671
- /**
672
- * Write a file (or show in dry-run mode)
673
- */
674
- function writeFile(path, content) {
675
- if (dryRun) {
676
- console.log(` \x1b[90mWrite ${path}:\x1b[0m`);
677
- const lines = content.split("\n").slice(0, 5);
678
- for (const line of lines) console.log(` \x1b[90m ${line}\x1b[0m`);
679
- if (content.split("\n").length > 5) console.log(` \x1b[90m ...\x1b[0m`);
680
- return;
681
- }
682
- writeFileSync(path, content);
683
- }
684
- //#endregion
685
- //#region src/setup/io.ts
686
- /**
687
- * The real implementation, used when no io is injected.
688
- *
689
- * `writeFile` comes from steps.js rather than `node:fs` so writes are
690
- * skipped in dry-run mode. Reads stay direct: they change nothing.
691
- */
692
- var nodeIo = {
693
- exec,
694
- execSilent,
695
- commandExists,
696
- writeFile,
697
- fileExists: existsSync,
698
- readFile: (path) => readFileSync(path, "utf-8"),
699
- env: process.env
700
- };
701
- //#endregion
702
702
  //#region src/setup/ddev.ts
703
703
  /**
704
704
  * Install system dependencies required by Trafic
@@ -1110,31 +1110,6 @@ async function handleErrors(req, res) {
1110
1110
  });
1111
1111
  }
1112
1112
  /**
1113
- * Decide whether Caddy may obtain a certificate for a hostname.
1114
- *
1115
- * Caddy's on-demand TLS asks before issuing for a name it has not seen.
1116
- * Without a check, anyone pointing a DNS record at this server would make it
1117
- * request a certificate, and Let's Encrypt quota is per registered domain —
1118
- * a handful of strangers could exhaust it for every preview.
1119
- */
1120
- function allowTlsFor(domain, index) {
1121
- return index.has(domain.toLowerCase());
1122
- }
1123
- /**
1124
- * Handle Caddy's on-demand TLS ask. 2xx allows issuance, anything else denies.
1125
- */
1126
- function handleTlsAsk(req, res) {
1127
- const domain = new URL(req.url ?? "/", `http://${req.headers.host}`).searchParams.get("domain") ?? "";
1128
- if (allowTlsFor(domain, hostnameIndex)) {
1129
- res.writeHead(200);
1130
- res.end("ok");
1131
- return;
1132
- }
1133
- console.log(`TLS ask denied for unknown hostname: ${domain || "(none)"}`);
1134
- res.writeHead(404, { "Content-Type": "text/plain" });
1135
- res.end("Unknown project");
1136
- }
1137
- /**
1138
1113
  * Handle status polling requests
1139
1114
  */
1140
1115
  async function handleStatus(req, res) {
@@ -1178,7 +1153,6 @@ async function handleRequest(req, res) {
1178
1153
  try {
1179
1154
  if (path === "/__auth__" || path.startsWith("/__auth__/")) handleAuth(req, res);
1180
1155
  else if (path === "/__status__" || path.startsWith("/__status__/")) await handleStatus(req, res);
1181
- else if (path === "/__tls__") handleTlsAsk(req, res);
1182
1156
  else if (path === "/__health__") {
1183
1157
  res.writeHead(200, { "Content-Type": "application/json" });
1184
1158
  res.end(JSON.stringify({
@@ -1235,6 +1209,6 @@ function startServer(agentConfig) {
1235
1209
  });
1236
1210
  }
1237
1211
  //#endregion
1238
- export { getProject as A, startProject as B, loadProjectConfig as C, getAccessLogs as D, closeDb as E, buildHostnameIndex as F, matchIp as G, watchProjectList as H, getProjectFromHostname as I, parseBasicAuth as K, getProjectInfo as L, logAccess as M, setProjectStatus as N, getDb as O, updateProjectAccess as P, listProjects as R, getIdleTimeoutMs as S, cleanOldLogs as T, checkAuth as U, stopProject as V, matchHostname as W, resetSteps as _, configureDdev as a, success as b, installDdev as c, nodeIo as d, error as f, isRoot as g, info as h, addDdevAptRepo as i, initDb as j, getIdleProjects as k, installSystemDeps as l, execSilent as m, routePath as n, configureTraefik as o, exec as p, parseBearerToken as q, startServer as r, getDockerGatewayIp as s, allowTlsFor as t, purgeTraefikAcmeStorage as u, setDryRun as v, shouldNeverStop as w, warn as x, step as y, loadProjectList as z };
1212
+ export { resetSteps as A, startProject as B, updateProjectAccess as C, execSilent as D, exec as E, buildHostnameIndex as F, matchIp as G, watchProjectList as H, getProjectFromHostname as I, parseBasicAuth as K, getProjectInfo as L, step as M, success as N, info as O, warn as P, listProjects as R, setProjectStatus as S, error as T, checkAuth as U, stopProject as V, matchHostname as W, getDb as _, configureTraefik as a, initDb as b, installSystemDeps as c, getIdleTimeoutMs as d, loadProjectConfig as f, getAccessLogs as g, closeDb as h, configureDdev as i, setDryRun as j, isRoot as k, purgeTraefikAcmeStorage as l, cleanOldLogs as m, startServer as n, getDockerGatewayIp as o, shouldNeverStop as p, parseBearerToken as q, addDdevAptRepo as r, installDdev as s, routePath as t, readToolPorts as u, getIdleProjects as v, nodeIo as w, logAccess as x, getProject as y, loadProjectList as z };
1239
1213
 
1240
- //# sourceMappingURL=server-4J8XoTP3.js.map
1214
+ //# sourceMappingURL=server-CvSztHW2.js.map