@stacksjs/ts-cloud 0.7.85 → 0.7.86

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 (57) hide show
  1. package/dist/aws/ec2.d.ts +13 -0
  2. package/dist/aws/index.js +1 -1
  3. package/dist/bin/cli.js +510 -510
  4. package/dist/{chunk-qbj74thz.js → chunk-4erbrg3e.js} +2 -2
  5. package/dist/{chunk-703nkybg.js → chunk-7m60qnc8.js} +10 -0
  6. package/dist/{chunk-gx4288t5.js → chunk-ztbn47we.js} +102 -58
  7. package/dist/deploy/index.js +15 -7
  8. package/dist/deploy/server-dns.d.ts +66 -7
  9. package/dist/drivers/index.js +2 -2
  10. package/dist/index.d.ts +2 -1
  11. package/dist/index.js +13 -5
  12. package/dist/ui/access-denied.html +2 -2
  13. package/dist/ui/account/automation.html +3 -3
  14. package/dist/ui/account/security.html +2 -2
  15. package/dist/ui/applications/compose.html +4 -4
  16. package/dist/ui/applications/new.html +2 -2
  17. package/dist/ui/data/backups.html +4 -4
  18. package/dist/ui/data/services.html +4 -4
  19. package/dist/ui/data/volumes.html +4 -4
  20. package/dist/ui/index.html +4 -4
  21. package/dist/ui/operations/alerts.html +3 -3
  22. package/dist/ui/operations/configuration.html +3 -3
  23. package/dist/ui/operations/jobs.html +3 -3
  24. package/dist/ui/operations/maintenance.html +3 -3
  25. package/dist/ui/operations/observability.html +4 -4
  26. package/dist/ui/operations/previews.html +4 -4
  27. package/dist/ui/operations/queue.html +4 -4
  28. package/dist/ui/operations/regions.html +4 -4
  29. package/dist/ui/operations/releases.html +4 -4
  30. package/dist/ui/operations/workloads.html +4 -4
  31. package/dist/ui/security.html +2 -2
  32. package/dist/ui/server/actions.html +4 -4
  33. package/dist/ui/server/activity.html +2 -2
  34. package/dist/ui/server/capacity.html +4 -4
  35. package/dist/ui/server/database.html +4 -4
  36. package/dist/ui/server/deployments.html +4 -4
  37. package/dist/ui/server/firewall.html +4 -4
  38. package/dist/ui/server/fleet.html +4 -4
  39. package/dist/ui/server/logs.html +4 -4
  40. package/dist/ui/server/metrics.html +4 -4
  41. package/dist/ui/server/services.html +2 -2
  42. package/dist/ui/server/sites.html +4 -4
  43. package/dist/ui/server/ssh-keys.html +4 -4
  44. package/dist/ui/server/team.html +4 -4
  45. package/dist/ui/serverless/alarms.html +4 -4
  46. package/dist/ui/serverless/assets.html +2 -2
  47. package/dist/ui/serverless/data.html +4 -4
  48. package/dist/ui/serverless/deployments.html +2 -2
  49. package/dist/ui/serverless/firewall.html +2 -2
  50. package/dist/ui/serverless/functions.html +4 -4
  51. package/dist/ui/serverless/logs.html +4 -4
  52. package/dist/ui/serverless/metrics.html +2 -2
  53. package/dist/ui/serverless/queues.html +4 -4
  54. package/dist/ui/serverless/secrets.html +4 -4
  55. package/dist/ui/serverless/traces.html +4 -4
  56. package/dist/ui/serverless.html +3 -3
  57. package/package.json +3 -3
@@ -45,7 +45,7 @@ import {
45
45
  resolveSiteKind,
46
46
  resolveUiSource,
47
47
  siteInstallBase
48
- } from "./chunk-gx4288t5.js";
48
+ } from "./chunk-ztbn47we.js";
49
49
  import {
50
50
  artifactKey,
51
51
  buildCloudFormationTemplate,
@@ -70,7 +70,7 @@ import {
70
70
  } from "./chunk-1qjyqrc5.js";
71
71
  import {
72
72
  SSMClient
73
- } from "./chunk-703nkybg.js";
73
+ } from "./chunk-7m60qnc8.js";
74
74
  import {
75
75
  CloudFormationClient
76
76
  } from "./chunk-4cjrg98a.js";
@@ -1087,6 +1087,15 @@ class EC2Client {
1087
1087
  return [];
1088
1088
  return Array.isArray(item) ? item : [item];
1089
1089
  }
1090
+ parseFirstIpv6Address(item) {
1091
+ for (const eni of this.parseArray(item)) {
1092
+ for (const entry of this.parseArray(eni?.ipv6AddressesSet?.item)) {
1093
+ if (entry?.ipv6Address)
1094
+ return entry.ipv6Address;
1095
+ }
1096
+ }
1097
+ return;
1098
+ }
1090
1099
  parseTags(item) {
1091
1100
  return this.parseArray(item).map((t) => ({
1092
1101
  Key: t.key,
@@ -1110,6 +1119,7 @@ class EC2Client {
1110
1119
  } : undefined,
1111
1120
  PrivateIpAddress: i.privateIpAddress,
1112
1121
  PublicIpAddress: i.ipAddress,
1122
+ Ipv6Address: this.parseFirstIpv6Address(i.networkInterfaceSet?.item),
1113
1123
  SubnetId: i.subnetId,
1114
1124
  VpcId: i.vpcId,
1115
1125
  SecurityGroups: this.parseArray(i.groupSet?.item).map((g) => ({
@@ -12,7 +12,7 @@ import {
12
12
  import {
13
13
  EC2Client,
14
14
  SSMClient
15
- } from "./chunk-703nkybg.js";
15
+ } from "./chunk-7m60qnc8.js";
16
16
  import {
17
17
  CloudFormationClient
18
18
  } from "./chunk-4cjrg98a.js";
@@ -27,6 +27,98 @@ import {
27
27
  import { readFileSync } from "node:fs";
28
28
  import { join } from "node:path";
29
29
 
30
+ // src/deploy/server-dns.ts
31
+ function collectServerDnsDomains(sites = {}) {
32
+ const domains = new Set;
33
+ for (const site of Object.values(sites)) {
34
+ if (!site.domain)
35
+ continue;
36
+ if (site.redirect || site.deploy === "server" || site.start)
37
+ domains.add(site.domain);
38
+ }
39
+ return domains;
40
+ }
41
+ function normalizeName(name) {
42
+ return name.replace(/\.$/, "").toLowerCase();
43
+ }
44
+ function matchesHostname(record, zone, hostname) {
45
+ const recordName = normalizeName(record.name);
46
+ const normalizedZone = normalizeName(zone);
47
+ const normalizedHostname = normalizeName(hostname);
48
+ const relativeName = normalizedHostname === normalizedZone ? "@" : normalizedHostname.endsWith(`.${normalizedZone}`) ? normalizedHostname.slice(0, -(normalizedZone.length + 1)) : normalizedHostname;
49
+ return recordName === normalizedHostname || recordName === relativeName || relativeName === "@" && (recordName === "" || recordName === normalizedZone);
50
+ }
51
+ async function removeStaleServerAddressRecords(provider, zone, hostname, desiredAddress, recordType = "A") {
52
+ const listed = await provider.listRecords(zone);
53
+ if (!listed.success)
54
+ return [`could not list ${recordType} records: ${listed.message || "unknown provider error"}`];
55
+ const matching = listed.records.filter((record) => record.type === recordType && matchesHostname(record, zone, hostname));
56
+ const desiredIndex = matching.findIndex((record) => record.content === desiredAddress);
57
+ if (matching.length <= 1 || desiredIndex === -1)
58
+ return [];
59
+ const warnings = [];
60
+ for (const [index, record] of matching.entries()) {
61
+ if (index === desiredIndex)
62
+ continue;
63
+ const result = await provider.deleteRecord(zone, record);
64
+ if (!result.success)
65
+ warnings.push(`could not remove stale ${record.name} ${recordType} ${record.content}: ${result.message || "unknown provider error"}`);
66
+ }
67
+ return warnings;
68
+ }
69
+ function normalizePublicIpv6(reported) {
70
+ if (!reported)
71
+ return;
72
+ const trimmed = reported.trim();
73
+ if (!trimmed)
74
+ return;
75
+ if (!trimmed.includes("/"))
76
+ return trimmed;
77
+ const [block] = trimmed.split("/");
78
+ if (!block.endsWith("::"))
79
+ return block || undefined;
80
+ return `${block}1`;
81
+ }
82
+ var IPV6_EXCLUDED_HOST_LABELS = new Set(["mail", "smtp", "imap", "mx"]);
83
+ function hostAcceptsIpv6(fqdn) {
84
+ return !IPV6_EXCLUDED_HOST_LABELS.has(fqdn.split(".")[0] ?? "");
85
+ }
86
+ async function reconcileAddressRecords(options) {
87
+ const { provider, zone, fqdn, ipv4, ipv6, ttl = 600 } = options;
88
+ const report = { published: [], warnings: [] };
89
+ const families = [{ type: "A", content: ipv4 }];
90
+ if (ipv6 && hostAcceptsIpv6(fqdn))
91
+ families.push({ type: "AAAA", content: ipv6 });
92
+ for (const { type, content } of families) {
93
+ const result = await provider.upsertRecord(zone, { name: fqdn, type, content, ttl });
94
+ if (result?.success === false) {
95
+ report.warnings.push(`${fqdn} → ${content} failed: ${result.error || result.message || "unknown error"}`);
96
+ continue;
97
+ }
98
+ report.warnings.push(...(await removeStaleServerAddressRecords(provider, zone, fqdn, content, type)).map((warning) => `${fqdn} cleanup: ${warning}`));
99
+ if (await verifyAddressRecord(provider, zone, fqdn, content, type))
100
+ report.published.push({ fqdn, type, content });
101
+ else
102
+ report.warnings.push(`${fqdn} → ${content} reported success at ${provider.name} but no matching ${type} record exists — create it manually: ${type} ${fqdn} → ${content}`);
103
+ }
104
+ return report;
105
+ }
106
+ async function verifyAddressRecord(provider, zone, fqdn, content, recordType) {
107
+ try {
108
+ if (typeof provider?.listRecords !== "function")
109
+ return true;
110
+ const listed = await provider.listRecords(zone);
111
+ if (!listed?.success || !Array.isArray(listed.records))
112
+ return true;
113
+ return listed.records.some((record) => {
114
+ const name = typeof record?.name === "string" ? record.name.replace(/\.$/, "") : "";
115
+ return record?.type === recordType && name === fqdn && record?.content === content;
116
+ });
117
+ } catch {
118
+ return true;
119
+ }
120
+ }
121
+
30
122
  // src/drivers/shared/package-manager.ts
31
123
  var PANTRY_PACKAGES = {
32
124
  php: "php.net",
@@ -1752,6 +1844,7 @@ class AwsDriver {
1752
1844
  return {
1753
1845
  appInstanceId: first.id,
1754
1846
  appPublicIp: first.publicIp,
1847
+ appPublicIpv6: first.publicIpv6,
1755
1848
  sshUser: "ubuntu",
1756
1849
  deployStoragePath: "/var/ts-cloud/staging"
1757
1850
  };
@@ -1834,6 +1927,7 @@ class AwsDriver {
1834
1927
  return {
1835
1928
  appInstanceId: instanceId,
1836
1929
  appPublicIp: running.PublicIpAddress,
1930
+ appPublicIpv6: normalizePublicIpv6(running.Ipv6Address),
1837
1931
  sshUser: "ubuntu",
1838
1932
  deployStoragePath: "/var/ts-cloud/staging"
1839
1933
  };
@@ -1892,6 +1986,7 @@ class AwsDriver {
1892
1986
  return {
1893
1987
  appInstanceId: first?.id,
1894
1988
  appPublicIp: first?.publicIp,
1989
+ appPublicIpv6: first?.publicIpv6,
1895
1990
  sshUser: "ubuntu",
1896
1991
  deployStoragePath: "/var/ts-cloud/staging"
1897
1992
  };
@@ -1961,6 +2056,7 @@ class AwsDriver {
1961
2056
  id: instance.InstanceId,
1962
2057
  name: nameTag,
1963
2058
  publicIp: instance.PublicIpAddress,
2059
+ publicIpv6: normalizePublicIpv6(instance.Ipv6Address),
1964
2060
  privateIp: instance.PrivateIpAddress,
1965
2061
  status: instance.State?.Name
1966
2062
  });
@@ -2045,59 +2141,6 @@ import { execSync } from "node:child_process";
2045
2141
  import { randomUUID } from "node:crypto";
2046
2142
  import { existsSync, readFileSync as readFileSync2 } from "node:fs";
2047
2143
 
2048
- // src/deploy/server-dns.ts
2049
- function collectServerDnsDomains(sites = {}) {
2050
- const domains = new Set;
2051
- for (const site of Object.values(sites)) {
2052
- if (!site.domain)
2053
- continue;
2054
- if (site.redirect || site.deploy === "server" || site.start)
2055
- domains.add(site.domain);
2056
- }
2057
- return domains;
2058
- }
2059
- function normalizeName(name) {
2060
- return name.replace(/\.$/, "").toLowerCase();
2061
- }
2062
- function matchesHostname(record, zone, hostname) {
2063
- const recordName = normalizeName(record.name);
2064
- const normalizedZone = normalizeName(zone);
2065
- const normalizedHostname = normalizeName(hostname);
2066
- const relativeName = normalizedHostname === normalizedZone ? "@" : normalizedHostname.endsWith(`.${normalizedZone}`) ? normalizedHostname.slice(0, -(normalizedZone.length + 1)) : normalizedHostname;
2067
- return recordName === normalizedHostname || recordName === relativeName || relativeName === "@" && (recordName === "" || recordName === normalizedZone);
2068
- }
2069
- async function removeStaleServerAddressRecords(provider, zone, hostname, desiredAddress, recordType = "A") {
2070
- const listed = await provider.listRecords(zone);
2071
- if (!listed.success)
2072
- return [`could not list ${recordType} records: ${listed.message || "unknown provider error"}`];
2073
- const matching = listed.records.filter((record) => record.type === recordType && matchesHostname(record, zone, hostname));
2074
- const desiredIndex = matching.findIndex((record) => record.content === desiredAddress);
2075
- if (matching.length <= 1 || desiredIndex === -1)
2076
- return [];
2077
- const warnings = [];
2078
- for (const [index, record] of matching.entries()) {
2079
- if (index === desiredIndex)
2080
- continue;
2081
- const result = await provider.deleteRecord(zone, record);
2082
- if (!result.success)
2083
- warnings.push(`could not remove stale ${record.name} ${recordType} ${record.content}: ${result.message || "unknown provider error"}`);
2084
- }
2085
- return warnings;
2086
- }
2087
- function hetznerBoxIpv6(reported) {
2088
- if (!reported)
2089
- return;
2090
- const trimmed = reported.trim();
2091
- if (!trimmed)
2092
- return;
2093
- if (!trimmed.includes("/"))
2094
- return trimmed;
2095
- const [block] = trimmed.split("/");
2096
- if (!block.endsWith("::"))
2097
- return block || undefined;
2098
- return `${block}1`;
2099
- }
2100
-
2101
2144
  // src/drivers/shared/fleet.ts
2102
2145
  function resolveFleetTopology(compute = {}) {
2103
2146
  const appServers = Math.max(1, compute.appServers ?? compute.instances ?? 1);
@@ -2666,7 +2709,7 @@ class HetznerDriver {
2666
2709
  serverId: alreadyRunning.id,
2667
2710
  serverName: alreadyRunning.name,
2668
2711
  publicIp: alreadyRunning.public_net.ipv4?.ip,
2669
- publicIpv6: hetznerBoxIpv6(alreadyRunning.public_net.ipv6?.ip),
2712
+ publicIpv6: normalizePublicIpv6(alreadyRunning.public_net.ipv6?.ip),
2670
2713
  deployStoragePath: "/var/ts-cloud/staging",
2671
2714
  sshUser: this.sshUser
2672
2715
  };
@@ -2714,7 +2757,7 @@ class HetznerDriver {
2714
2757
  serverName: running.name,
2715
2758
  firewallId: firewall.id,
2716
2759
  publicIp: running.public_net.ipv4?.ip,
2717
- publicIpv6: hetznerBoxIpv6(running.public_net.ipv6?.ip),
2760
+ publicIpv6: normalizePublicIpv6(running.public_net.ipv6?.ip),
2718
2761
  deployStoragePath: "/var/ts-cloud/staging",
2719
2762
  sshUser: this.sshUser
2720
2763
  };
@@ -3216,6 +3259,7 @@ class HetznerDriver {
3216
3259
  id: String(server.id),
3217
3260
  name: server.name,
3218
3261
  publicIp: server.public_net.ipv4?.ip,
3262
+ publicIpv6: normalizePublicIpv6(server.public_net.ipv6?.ip),
3219
3263
  privateIp: server.private_net?.[0]?.ip,
3220
3264
  status: server.status
3221
3265
  });
@@ -3428,7 +3472,7 @@ ${out}`);
3428
3472
  deployStoragePath: state.deployStoragePath || "/var/ts-cloud/staging",
3429
3473
  appInstanceId: state.serverId ? String(state.serverId) : undefined,
3430
3474
  appPublicIp: server?.public_net.ipv4?.ip || state.publicIp,
3431
- appPublicIpv6: hetznerBoxIpv6(server?.public_net.ipv6?.ip) || state.publicIpv6,
3475
+ appPublicIpv6: normalizePublicIpv6(server?.public_net.ipv6?.ip) || state.publicIpv6,
3432
3476
  sshUser: state.sshUser || this.sshUser,
3433
3477
  servicesPrivateIp: state.servicesPrivateIp
3434
3478
  };
@@ -6254,4 +6298,4 @@ function buildCloudFrontOriginConfig(options) {
6254
6298
  CustomErrorResponses: { Quantity: 0 }
6255
6299
  };
6256
6300
  }
6257
- export { PANTRY_PROJECT_DIR, pgAdminCommand, BACKUP_RUNNER_PATH, buildBackupRestoreScript, siteInstallBase, isPhpSite, resolveSiteDeployTarget, resolveSiteKind, validateDeploymentConfig, shipsARelease, DEFAULT_RPX_CERTS_DIR, normalizeRoutePath, deriveRouteId, buildRpxConfig, buildRpxLbConfig, renderRpxLauncher, RPX_DIR, RPX_LAUNCHER_PATH, RPX_SERVICE_NAME, buildRpxProvisionScript, buildRpxFragmentRefreshScript, buildUbuntuBootstrapScript, AwsDriver, collectServerDnsDomains, removeStaleServerAddressRecords, hetznerBoxIpv6, resolveHetznerLocation, HetznerClient, resolveHetznerApiToken2 as resolveHetznerApiToken, normalizeSshPublicKey, wrapCloudInitUserData, HetznerDriver, LocalBoxDriver, isBoxMode, createCloudDriver, CloudDriverFactory, cloudDrivers, planHetznerServerResize, isHetznerCapacityError, executeHetznerServerResize, buildSshArgs, sshExec, sshExecOrThrow, scpUpload, waitForSsh, waitForCloudInit, collectHetznerResizeManifest, prepareHetznerResize, verifyHetznerResize, resolveHetznerHostOptimizationPlan, buildHetznerHostOptimizationScript, applyHetznerHostOptimization, collectHetznerHostOptimizationReport, verifyHetznerHostOptimization, resizeCheckpointPath, resizeLockPath, readResizeCheckpoint, writeResizeCheckpoint, acquireResizeLock, ensureSshKey, ensureFirewall, ensureServer, serverPublicIpv4, UBUNTU_2404_AMI_PARAM, buildBoxUserData, HetznerBoxProvisioner, AwsBoxProvisioner, createBoxProvisioner, releasePaths, buildRollbackScript, resolveExecStart, buildSiteDeployScript, buildStaticSiteDeployScript, releaseTarballTmpPath, buildAwsArtifactFetch, buildLocalArtifactFetch, buildHostCleanupScript, MANAGEMENT_DASHBOARD_SITE, dashboardCredentialsFile, resolveDashboardAuth, resolveUiSource, ensureManagementDashboard, buildManagementDashboardArtifact, resolveSiteFramework, deploySiteRelease, deployAllComputeSites, reloadRpxGateway, renewRpxCertificates, MANAGED_CACHE_POLICY_OPTIMIZED, MANAGED_CACHE_POLICY_DISABLED, MANAGED_ORIGIN_REQUEST_POLICY_ALL_VIEWER, buildCloudFrontOriginConfig };
6301
+ export { collectServerDnsDomains, removeStaleServerAddressRecords, normalizePublicIpv6, IPV6_EXCLUDED_HOST_LABELS, hostAcceptsIpv6, reconcileAddressRecords, verifyAddressRecord, PANTRY_PROJECT_DIR, pgAdminCommand, BACKUP_RUNNER_PATH, buildBackupRestoreScript, siteInstallBase, isPhpSite, resolveSiteDeployTarget, resolveSiteKind, validateDeploymentConfig, shipsARelease, DEFAULT_RPX_CERTS_DIR, normalizeRoutePath, deriveRouteId, buildRpxConfig, buildRpxLbConfig, renderRpxLauncher, RPX_DIR, RPX_LAUNCHER_PATH, RPX_SERVICE_NAME, buildRpxProvisionScript, buildRpxFragmentRefreshScript, buildUbuntuBootstrapScript, AwsDriver, resolveHetznerLocation, HetznerClient, resolveHetznerApiToken2 as resolveHetznerApiToken, normalizeSshPublicKey, wrapCloudInitUserData, HetznerDriver, LocalBoxDriver, isBoxMode, createCloudDriver, CloudDriverFactory, cloudDrivers, planHetznerServerResize, isHetznerCapacityError, executeHetznerServerResize, buildSshArgs, sshExec, sshExecOrThrow, scpUpload, waitForSsh, waitForCloudInit, collectHetznerResizeManifest, prepareHetznerResize, verifyHetznerResize, resolveHetznerHostOptimizationPlan, buildHetznerHostOptimizationScript, applyHetznerHostOptimization, collectHetznerHostOptimizationReport, verifyHetznerHostOptimization, resizeCheckpointPath, resizeLockPath, readResizeCheckpoint, writeResizeCheckpoint, acquireResizeLock, ensureSshKey, ensureFirewall, ensureServer, serverPublicIpv4, UBUNTU_2404_AMI_PARAM, buildBoxUserData, HetznerBoxProvisioner, AwsBoxProvisioner, createBoxProvisioner, releasePaths, buildRollbackScript, resolveExecStart, buildSiteDeployScript, buildStaticSiteDeployScript, releaseTarballTmpPath, buildAwsArtifactFetch, buildLocalArtifactFetch, buildHostCleanupScript, MANAGEMENT_DASHBOARD_SITE, dashboardCredentialsFile, resolveDashboardAuth, resolveUiSource, ensureManagementDashboard, buildManagementDashboardArtifact, resolveSiteFramework, deploySiteRelease, deployAllComputeSites, reloadRpxGateway, renewRpxCertificates, MANAGED_CACHE_POLICY_OPTIMIZED, MANAGED_CACHE_POLICY_DISABLED, MANAGED_ORIGIN_REQUEST_POLICY_ALL_VIEWER, buildCloudFrontOriginConfig };
@@ -30,7 +30,7 @@ import {
30
30
  synchronizeDashboardUsers,
31
31
  trackDashboardOperation,
32
32
  verifyStaticApiOrigin
33
- } from "../chunk-qbj74thz.js";
33
+ } from "../chunk-4erbrg3e.js";
34
34
  import {
35
35
  deleteStaticSite,
36
36
  deployStaticSite,
@@ -51,13 +51,16 @@ import"../chunk-50jpda9q.js";
51
51
  import"../chunk-he9a874b.js";
52
52
  import"../chunk-gttvakpv.js";
53
53
  import {
54
+ IPV6_EXCLUDED_HOST_LABELS,
54
55
  MANAGEMENT_DASHBOARD_SITE,
55
56
  buildManagementDashboardArtifact,
56
57
  collectServerDnsDomains,
57
58
  dashboardCredentialsFile,
58
59
  ensureManagementDashboard,
59
- hetznerBoxIpv6,
60
+ hostAcceptsIpv6,
60
61
  isPhpSite,
62
+ normalizePublicIpv6,
63
+ reconcileAddressRecords,
61
64
  removeStaleServerAddressRecords,
62
65
  resolveDashboardAuth,
63
66
  resolveSiteDeployTarget,
@@ -65,10 +68,11 @@ import {
65
68
  resolveUiSource,
66
69
  shipsARelease,
67
70
  siteInstallBase,
68
- validateDeploymentConfig
69
- } from "../chunk-gx4288t5.js";
71
+ validateDeploymentConfig,
72
+ verifyAddressRecord
73
+ } from "../chunk-ztbn47we.js";
70
74
  import"../chunk-1qjyqrc5.js";
71
- import"../chunk-703nkybg.js";
75
+ import"../chunk-7m60qnc8.js";
72
76
  import"../chunk-4cjrg98a.js";
73
77
  import"../chunk-hpv68b4a.js";
74
78
  import"../chunk-32e7ya18.js";
@@ -77,6 +81,7 @@ import"../chunk-zqtpg06c.js";
77
81
  import"../chunk-v0bahtg2.js";
78
82
  export {
79
83
  verifyStaticApiOrigin,
84
+ verifyAddressRecord,
80
85
  validateDeploymentConfig,
81
86
  uploadStaticFiles,
82
87
  trackDashboardOperation,
@@ -98,11 +103,13 @@ export {
98
103
  resolveDashboardAction,
99
104
  removeStaleServerAddressRecords,
100
105
  redeployServerlessApp,
106
+ reconcileAddressRecords,
107
+ normalizePublicIpv6,
101
108
  isPhpSite,
102
109
  invalidateCache,
103
110
  initializeDashboardControlPlane,
104
111
  infraEnvFromOutputs,
105
- hetznerBoxIpv6,
112
+ hostAcceptsIpv6,
106
113
  hashContainerContext,
107
114
  generateStaticSiteTemplate,
108
115
  generateExternalDnsStaticSiteTemplate,
@@ -131,5 +138,6 @@ export {
131
138
  buildFunctionEnv,
132
139
  buildAndPushServerlessImage,
133
140
  buildAndPushContainerImage,
134
- MANAGEMENT_DASHBOARD_SITE
141
+ MANAGEMENT_DASHBOARD_SITE,
142
+ IPV6_EXCLUDED_HOST_LABELS
135
143
  };
@@ -19,13 +19,72 @@ export declare function collectServerDnsDomains(sites?: Record<string, ServerSit
19
19
  */
20
20
  export declare function removeStaleServerAddressRecords(provider: DnsProvider, zone: string, hostname: string, desiredAddress: string, recordType?: 'A' | 'AAAA'): Promise<string[]>;
21
21
  /**
22
- * Hetzner hands a cloud server a routed /64 and configures `::1` inside it on
23
- * the interface, but the API reports the block (`2a01:4f8:c014:6186::/64`), not
24
- * the address. Publishing the block verbatim as an AAAA record yields a host
25
- * nothing answers on, so turn it into the address the box actually holds.
22
+ * Turn whatever a provider reports as a box's IPv6 into an address an AAAA
23
+ * record can point at.
26
24
  *
27
- * A plain address (no prefix) is returned unchanged, so this is safe to apply
28
- * to whatever the driver surfaced.
25
+ * Providers disagree on what "the server's IPv6" means. Hetzner hands out a
26
+ * routed /64 and reports the block (`2a01:4f8:c014:6186::/64`) while the
27
+ * interface actually holds `::1` inside it; AWS reports a plain address per
28
+ * network interface. Publishing a block verbatim gives an AAAA record nothing
29
+ * answers on, which is the kind of failure that only shows up for the fraction
30
+ * of visitors whose network prefers IPv6 — so every driver runs its value
31
+ * through here rather than reinventing the narrowing.
32
+ *
33
+ * A plain address passes through unchanged.
34
+ */
35
+ export declare function normalizePublicIpv6(reported: string | undefined | null): string | undefined;
36
+ /**
37
+ * Hostname labels that must stay IPv4-only.
38
+ *
39
+ * A box typically runs its mail server on IPv4 alone, and its IPv6 address
40
+ * usually has no PTR — both of which make an AAAA on a mail host actively
41
+ * harmful: senders try the v6 address first, find nothing listening (or get
42
+ * rejected for the missing reverse record) and defer the message. Web traffic
43
+ * has no such constraint, so the exclusion is per-host rather than per-zone.
44
+ */
45
+ export declare const IPV6_EXCLUDED_HOST_LABELS: ReadonlySet<string>;
46
+ /** Whether `fqdn` should get an AAAA record pointing at the box. */
47
+ export declare function hostAcceptsIpv6(fqdn: string): boolean;
48
+ export interface AddressRecordReport {
49
+ /** Records confirmed present at the provider after the write. */
50
+ published: Array<{
51
+ fqdn: string;
52
+ type: 'A' | 'AAAA';
53
+ content: string;
54
+ }>;
55
+ /** Anything the caller should surface: failed writes, unverified writes, stale-record cleanup problems. */
56
+ warnings: string[];
57
+ }
58
+ interface ReconcileAddressRecordsOptions {
59
+ provider: DnsProvider;
60
+ zone: string;
61
+ fqdn: string;
62
+ ipv4: string;
63
+ /** Omit when the box has no public IPv6; the AAAA pass is then skipped entirely. */
64
+ ipv6?: string;
65
+ ttl?: number;
66
+ }
67
+ /**
68
+ * Point one hostname at a box on every address family the box actually has.
69
+ *
70
+ * Shared by every driver and by the framework's deploy command so the rules
71
+ * live in one place: upsert, then remove the *other* records of that family for
72
+ * the same hostname (a leftover address round-robins traffic to a dead host —
73
+ * and a stale AAAA is the worse of the two, because dual-stack clients prefer
74
+ * IPv6), then verify against the provider rather than trusting the write.
75
+ */
76
+ export declare function reconcileAddressRecords(options: ReconcileAddressRecordsOptions): Promise<AddressRecordReport>;
77
+ /**
78
+ * Best-effort post-write check that the record exists at the provider.
79
+ *
80
+ * Returns true when the provider offers no list API or the listing itself
81
+ * fails: verification must never turn a possibly-good write into a false
82
+ * alarm. It exists to catch phantom successes — an upsert that reported OK
83
+ * while editing the wrong record — at providers that can list their zone.
84
+ *
85
+ * The listing is deliberately untyped. Typed listings map to endpoints like
86
+ * Porkbun's retrieveByNameType, whose subdomain-less form returns apex records
87
+ * only, which made verification blind to every non-apex record.
29
88
  */
30
- export declare function hetznerBoxIpv6(reported: string | undefined | null): string | undefined;
89
+ export declare function verifyAddressRecord(provider: DnsProvider, zone: string, fqdn: string, content: string, recordType: 'A' | 'AAAA'): Promise<boolean>;
31
90
  export {};
@@ -68,9 +68,9 @@ import {
68
68
  waitForSsh,
69
69
  wrapCloudInitUserData,
70
70
  writeResizeCheckpoint
71
- } from "../chunk-gx4288t5.js";
71
+ } from "../chunk-ztbn47we.js";
72
72
  import"../chunk-1qjyqrc5.js";
73
- import"../chunk-703nkybg.js";
73
+ import"../chunk-7m60qnc8.js";
74
74
  import"../chunk-4cjrg98a.js";
75
75
  import"../chunk-wj3s95p9.js";
76
76
  import"../chunk-zqtpg06c.js";
package/dist/index.d.ts CHANGED
@@ -30,7 +30,8 @@ export { keyMatchesFilters, migrateObjectStorage, remapKey } from './object-stor
30
30
  export type { MigrateEndpoint, MigrateError, MigrateOptions, MigratePlanItem, MigrateProgress, MigrateResult, MigrateVerification, } from './object-storage/migrate';
31
31
  export * from './ssl';
32
32
  export { deployStaticSite, deployStaticSiteFull, uploadStaticFiles, invalidateCache, deleteStaticSite, generateStaticSiteTemplate, deployStaticSiteWithExternalDns, deployStaticSiteWithExternalDnsFull, generateExternalDnsStaticSiteTemplate, deploySite, resolveSiteDeployTarget, resolveSiteKind, validateDeploymentConfig, buildAndPushServerlessImage, buildFunctionEnv, deployServerlessApp, infraEnvFromOutputs, redeployServerlessApp, rollbackServerlessApp, runRemoteCommand, setMaintenance, buildManagementDashboardArtifact, dashboardCredentialsFile, ensureManagementDashboard, MANAGEMENT_DASHBOARD_SITE, resolveDashboardAuth, resolveUiSource, } from './deploy';
33
- export { collectServerDnsDomains, hetznerBoxIpv6, removeStaleServerAddressRecords } from './deploy/server-dns';
33
+ export { collectServerDnsDomains, hostAcceptsIpv6, IPV6_EXCLUDED_HOST_LABELS, normalizePublicIpv6, reconcileAddressRecords, removeStaleServerAddressRecords, verifyAddressRecord, } from './deploy/server-dns';
34
+ export type { AddressRecordReport } from './deploy/server-dns';
34
35
  export type { EnsureDashboardLogger, ResolvedDashboardAuth, StaticSiteConfig, DeployResult, UploadOptions, ExternalDnsStaticSiteConfig, ExternalDnsDeployResult, DeploySiteConfig, DeploySiteResult, StaticSiteDnsProvider, SiteDeployKind, DeploymentValidationResult, BuildImageOptions, BuiltImage, CodeSource, DeployServerlessOptions, ResolvedContext, } from './deploy';
35
36
  export { createCloudDriver, CloudDriverFactory, cloudDrivers, AwsDriver, HetznerDriver, HetznerClient, resolveHetznerApiToken, normalizeSshPublicKey, ensureFirewall, ensureServer, ensureSshKey, serverPublicIpv4, sshExec, sshExecOrThrow, scpUpload, waitForSsh, waitForCloudInit, buildSshArgs, generateUbuntuAppCloudInit, wrapCloudInitUserData, buildHostCleanupScript, buildSiteDeployScript, buildStaticSiteDeployScript, resolveExecStart, deployAllComputeSites, deploySiteRelease, } from './drivers';
36
37
  export type { CreateCloudDriverOptions } from './drivers/factory';
package/dist/index.js CHANGED
@@ -283,7 +283,7 @@ import {
283
283
  volumeCapabilities,
284
284
  webhookEndpoint,
285
285
  zeroCapacity
286
- } from "./chunk-qbj74thz.js";
286
+ } from "./chunk-4erbrg3e.js";
287
287
  import {
288
288
  deleteStaticSite,
289
289
  deployStaticSite,
@@ -365,6 +365,7 @@ import {
365
365
  CloudDriverFactory,
366
366
  HetznerClient,
367
367
  HetznerDriver,
368
+ IPV6_EXCLUDED_HOST_LABELS,
368
369
  MANAGEMENT_DASHBOARD_SITE,
369
370
  buildHostCleanupScript,
370
371
  buildManagementDashboardArtifact,
@@ -382,8 +383,10 @@ import {
382
383
  ensureManagementDashboard,
383
384
  ensureServer,
384
385
  ensureSshKey,
385
- hetznerBoxIpv6,
386
+ hostAcceptsIpv6,
387
+ normalizePublicIpv6,
386
388
  normalizeSshPublicKey,
389
+ reconcileAddressRecords,
387
390
  removeStaleServerAddressRecords,
388
391
  resolveDashboardAuth,
389
392
  resolveExecStart,
@@ -396,10 +399,11 @@ import {
396
399
  sshExec,
397
400
  sshExecOrThrow,
398
401
  validateDeploymentConfig,
402
+ verifyAddressRecord,
399
403
  waitForCloudInit,
400
404
  waitForSsh,
401
405
  wrapCloudInitUserData
402
- } from "./chunk-gx4288t5.js";
406
+ } from "./chunk-ztbn47we.js";
403
407
  import {
404
408
  ABTestManager,
405
409
  AI,
@@ -817,7 +821,7 @@ import {
817
821
  import {
818
822
  EC2Client,
819
823
  SSMClient
820
- } from "./chunk-703nkybg.js";
824
+ } from "./chunk-7m60qnc8.js";
821
825
  import {
822
826
  CloudFormationClient
823
827
  } from "./chunk-4cjrg98a.js";
@@ -5125,6 +5129,7 @@ export {
5125
5129
  verifyTotp,
5126
5130
  verifyMediaAccessToken,
5127
5131
  verifyArtifactSignature,
5132
+ verifyAddressRecord,
5128
5133
  validateTemplateSize,
5129
5134
  validateTemplate,
5130
5135
  validateResourceLimits,
@@ -5266,6 +5271,7 @@ export {
5266
5271
  recordDashboardHostPosture,
5267
5272
  reconcileSourceWebhook,
5268
5273
  reconcileJobObservation,
5274
+ reconcileAddressRecords,
5269
5275
  quickHash,
5270
5276
  queueManagementManager,
5271
5277
  publicKeyFingerprint,
@@ -5302,6 +5308,7 @@ export {
5302
5308
  normalizeSourceEvent,
5303
5309
  normalizeScheduleExpression,
5304
5310
  normalizeRuntimeStatus,
5311
+ normalizePublicIpv6,
5305
5312
  nextScheduleRuns,
5306
5313
  networkSecurityManager,
5307
5314
  needsRenewal,
@@ -5346,7 +5353,7 @@ export {
5346
5353
  infraEnvFromOutputs,
5347
5354
  imageScanningManager,
5348
5355
  hotp,
5349
- hetznerBoxIpv6,
5356
+ hostAcceptsIpv6,
5350
5357
  healthCheckManager,
5351
5358
  hashString,
5352
5359
  hashManifest,
@@ -5775,6 +5782,7 @@ export {
5775
5782
  InvalidOperationTransitionError,
5776
5783
  InfrastructureGenerator,
5777
5784
  ImageScanningManager,
5785
+ IPV6_EXCLUDED_HOST_LABELS,
5778
5786
  IAMClient,
5779
5787
  HetznerDriver,
5780
5788
  HetznerClient,