dsh-mobile 0.4.5 → 0.4.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.
package/lib/index.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import { X509Certificate } from "node:crypto";
1
2
  import z from "@deepseek-ai/schemastery";
2
3
  import { ChildProcessWithoutNullStreams } from "node:child_process";
3
4
  import { Readable } from "node:stream";
@@ -649,6 +650,8 @@ declare class MobileAccessGateway {
649
650
  } | undefined, blockedUpgradeLog?: BlockedUpgradePathLog | undefined, onDiscoveryDegraded?: ((source: 'broadcast' | 'mdns', code: string) => void) | undefined);
650
651
  /** Initialize durable state, validate TLS, and bind the externally reachable listener. */
651
652
  start(): Promise<void>;
653
+ /** Install a newly signed provided leaf for future TLS handshakes without dropping sessions. */
654
+ refreshProvidedTls(): Promise<void>;
652
655
  private startDiscovery;
653
656
  private reportDiscoveryDegraded;
654
657
  private recordBroadcastFailure;
@@ -839,12 +842,51 @@ declare const FRP_CADDY_SNIPPET_PATH = "/etc/caddy/dsh-mobile-dsh.caddy";
839
842
  declare const FRP_CADDY_SNIPPET_MARKER = "# Managed by DSH Mobile - snippet, safe to delete";
840
843
  /** Exact line the main Caddyfile must contain (uncommented) for the site to load. */
841
844
  declare const FRP_CADDY_IMPORT_LINE = "import /etc/caddy/dsh-mobile-dsh.caddy";
842
- /** Build the Caddy site for one public host (without markers or import wiring). */
843
- declare function createCaddySite(publicHost: string, certDir?: string): string;
844
- /** Build the only supported frps config and Caddy snippet from validated user inputs. */
845
- declare function createRestrictedFrpServerTemplate(serverPort: number, token: string, publicOrigin: string): string;
845
+ /**
846
+ * Entry TLS mode for the FRP channel.
847
+ *
848
+ * - `public-ip-cert` (default): the VPS Caddy terminates TLS with a public-CA
849
+ * certificate and reverse-proxies the plaintext vhost to frps.
850
+ * - `self-signed`: no Caddy and no public certificate at all. frps publishes a
851
+ * raw TCP proxy and the DSH gateway terminates TLS itself with a leaf signed
852
+ * by its own pairing CA, which the Android app pins through `ca.cer`.
853
+ */
854
+ type FrpEntryTls = 'public-ip-cert' | 'self-signed';
855
+ /** Optional overrides for the generated Caddy site; every field defaults to the upstream value. */
856
+ interface CaddySiteOptions {
857
+ readonly certDir?: string;
858
+ /**
859
+ * The user's real `vhostHTTPPort`. `attach` mode must never assume the
860
+ * upstream 7080: a wrong port silently disables the plaintext-exposure gate.
861
+ */
862
+ readonly vhostHttpPort?: number;
863
+ /** `self-signed` is a TCP passthrough and never produces a Caddy site. */
864
+ readonly entryTls?: FrpEntryTls;
865
+ }
866
+ /**
867
+ * Build the Caddy site for one public host (without markers or import wiring).
868
+ *
869
+ * The second parameter accepts either a bare certificate directory (the legacy
870
+ * signature) or a full {@link CaddySiteOptions} object, so existing callers keep
871
+ * working unchanged.
872
+ */
873
+ declare function createCaddySite(publicHost: string, options?: CaddySiteOptions | string): string;
874
+ /**
875
+ * Build the only supported frps config and Caddy snippet from validated user inputs.
876
+ *
877
+ * Default `options` reproduce the upstream artefact byte for byte; `attach` mode
878
+ * passes the user's real vhost port, and `self-signed` is rejected because that
879
+ * mode publishes a raw TCP proxy instead of an HTTP vhost.
880
+ */
881
+ declare function createRestrictedFrpServerTemplate(serverPort: number, token: string, publicOrigin: string, options?: CaddySiteOptions): string;
846
882
  //#endregion
847
883
  //#region src/frp-config.d.ts
884
+ /** How the restricted FRP channel is provisioned on the VPS. */
885
+ type FrpMode = 'deploy' | 'attach';
886
+ /** Public entry port used by the self-signed TCP passthrough (never 443 by requirement). */
887
+ declare const FRP_DEFAULT_PUBLIC_PORT = 33080;
888
+ /** Ports owned by other DSH Mobile listeners; the public entry may never reuse them. */
889
+ declare const FRP_RESERVED_PORTS: readonly number[];
848
890
  /** Credentials and endpoints required by the restricted FRP provider. */
849
891
  interface FrpSettings {
850
892
  readonly version: 1;
@@ -852,6 +894,17 @@ interface FrpSettings {
852
894
  readonly serverPort: number;
853
895
  readonly token: string;
854
896
  readonly publicOrigin: string;
897
+ /**
898
+ * `deploy` (default) installs the plugin's own frps on the VPS.
899
+ * `attach` reuses an frps that already runs there and never touches it.
900
+ */
901
+ readonly mode?: FrpMode;
902
+ /** `public-ip-cert` (default) or the gateway-terminated `self-signed` passthrough. */
903
+ readonly entryTls?: FrpEntryTls;
904
+ /** The real `vhostHTTPPort` of the user's frps; defaults to the upstream 7080. */
905
+ readonly vhostHttpPort?: number;
906
+ /** Public entry port of the self-signed TCP proxy; defaults to 33080. */
907
+ readonly publicPort?: number;
855
908
  }
856
909
  /** Safe FRP configuration fields returned to the desktop UI. */
857
910
  interface FrpConfigurationStatus {
@@ -862,6 +915,12 @@ interface FrpConfigurationStatus {
862
915
  readonly vhostHttpPort: number;
863
916
  readonly storagePath: string;
864
917
  readonly errorCode?: string;
918
+ /** Present only when the saved configuration leaves the upstream `deploy` default. */
919
+ readonly mode?: FrpMode;
920
+ /** Present only when the saved configuration uses the self-signed entry. */
921
+ readonly entryTls?: FrpEntryTls;
922
+ /** Present only when a non-default public entry port is saved. */
923
+ readonly publicPort?: number;
865
924
  }
866
925
  /** Validate the FRP server hostname or IP address. */
867
926
  declare function validateFrpServerAddress(value: unknown): string;
@@ -871,7 +930,34 @@ declare function validateFrpServerPort(value: unknown): number;
871
930
  declare function validateFrpToken(value: unknown): string;
872
931
  /** Validate the public HTTPS origin used by Caddy and Android pairing. */
873
932
  declare function validateFrpPublicOrigin(value: unknown): string;
874
- /** Parse FRP settings at the loopback request and filesystem boundaries. */
933
+ /** Validate the provisioning mode. */
934
+ declare function validateFrpMode(value: unknown): FrpMode;
935
+ /** Validate the entry TLS mode. */
936
+ declare function validateFrpEntryTls(value: unknown): FrpEntryTls;
937
+ /** Validate the loopback vhost port of the target frps. */
938
+ declare function validateFrpVhostHttpPort(value: unknown): number;
939
+ /** Validate the public entry port of the self-signed TCP proxy. */
940
+ declare function validateFrpPublicPort(value: unknown): number;
941
+ /** Effective provisioning mode; an absent field keeps the upstream behaviour. */
942
+ declare function resolveFrpMode(settings: FrpSettings): FrpMode;
943
+ /** Effective entry TLS mode; an absent field keeps the upstream behaviour. */
944
+ declare function resolveFrpEntryTls(settings: FrpSettings): FrpEntryTls;
945
+ /** Effective loopback vhost port; an absent field keeps the upstream behaviour. */
946
+ declare function resolveFrpVhostHttpPort(settings: FrpSettings): number;
947
+ /** Effective public entry port of the self-signed passthrough. */
948
+ declare function resolveFrpPublicPort(settings: FrpSettings): number;
949
+ /** True when the gateway terminates TLS itself behind a raw frps TCP proxy. */
950
+ declare function isFrpSelfSignedIngress(settings: FrpSettings): boolean;
951
+ /**
952
+ * Parse FRP settings at the loopback request and filesystem boundaries.
953
+ *
954
+ * Optional keys are kept only when supplied, so a legacy `settings.json` still
955
+ * round-trips byte for byte and every default stays the upstream behaviour.
956
+ * Two cross-field rules are enforced here because neither can be recovered later:
957
+ * the self-signed entry is a TCP passthrough and therefore attach-only, and
958
+ * attach mode with a public-CA entry must state the user's real vhost port —
959
+ * silently assuming 7080 would disable the plaintext-exposure gate.
960
+ */
875
961
  declare function parseFrpSettings(value: unknown): FrpSettings;
876
962
  /**
877
963
  * Merge a partial VPS request body with the saved configuration so a blank
@@ -885,7 +971,13 @@ declare function mergeSavedFrpTarget(partial: Readonly<Record<string, unknown>>,
885
971
  readonly serverAddress: string;
886
972
  readonly serverPort: number;
887
973
  };
888
- /** Build the single-purpose frpc configuration for the current loopback gateway. */
974
+ /**
975
+ * Build the single-purpose frpc configuration for the current loopback gateway.
976
+ *
977
+ * The default (public-CA) entry is an HTTP vhost behind Caddy and is emitted byte
978
+ * for byte as before. The self-signed entry cannot use a vhost at all: frps only
979
+ * forwards raw TCP, and the gateway terminates TLS on that connection.
980
+ */
889
981
  declare function createFrpcToml(settings: FrpSettings, localPort: number): string;
890
982
  /** Build the matching restricted frps and Caddy templates for one VPS. */
891
983
  declare function createFrpServerTemplate(settings: FrpSettings): string;
@@ -899,7 +991,13 @@ declare class FrpConfigStore {
899
991
  constructor(stateDirectory: string);
900
992
  /** Load private settings while rejecting links, oversized files, and unknown fields. */
901
993
  initialize(): Promise<void>;
902
- /** Return configuration metadata without exposing the FRP token. */
994
+ /**
995
+ * Return configuration metadata without exposing the FRP token.
996
+ *
997
+ * The optional keys appear only when the saved configuration departs from the
998
+ * upstream defaults, so the pre-existing field set is unchanged for every
999
+ * legacy configuration.
1000
+ */
903
1001
  status(): FrpConfigurationStatus;
904
1002
  /** Return private settings only to the provider lifecycle. */
905
1003
  settings(): FrpSettings | undefined;
@@ -911,6 +1009,226 @@ declare class FrpConfigStore {
911
1009
  purge(): Promise<FrpConfigurationStatus>;
912
1010
  }
913
1011
  //#endregion
1012
+ //#region src/frp-attach.d.ts
1013
+ /** Default proof path used to confirm the public entry answered from this computer. */
1014
+ declare const FRP_ATTACH_DISCOVERY_PATH = "/mobile-access/discovery";
1015
+ /** Declaration printed above the VPS half of an attach template. */
1016
+ declare const FRP_ATTACH_VPS_DECLARATION: string;
1017
+ /** Declaration printed above the local half of an attach template. */
1018
+ declare const FRP_ATTACH_LOCAL_DECLARATION = "# 本机只写入 frpc.toml 并启动 frpc:不安装、不改动本机 frps,也不改动 DSH 自身配置。";
1019
+ /** Declaration for the self-signed passthrough, which keeps its CA on this computer. */
1020
+ declare const FRP_ATTACH_SELF_SIGNED_DECLARATION: string;
1021
+ /** Optional inputs for attach artefacts; every field falls back to the validated settings. */
1022
+ interface FrpAttachOptions {
1023
+ readonly vhostHttpPort?: number;
1024
+ readonly publicPort?: number;
1025
+ readonly certDir?: string;
1026
+ /** Absolute path of the plugin-written frpc.toml, used for the `frpc verify` self-check. */
1027
+ readonly configFile?: string;
1028
+ /** Loopback port the gateway will listen on; only a placeholder inside the copied runbook. */
1029
+ readonly localPort?: number;
1030
+ /**
1031
+ * Explicit reveal of the shared token inside previews and clipboard copies.
1032
+ *
1033
+ * Defaults to `false`: every artefact the panel produces on its own stays
1034
+ * masked, because the copied text lands in the system clipboard. Only the
1035
+ * panel's dedicated "copy the frpc.toml with its token" action sets this, and
1036
+ * the result never reaches a stored state, a log, or localStorage.
1037
+ */
1038
+ readonly revealToken?: boolean;
1039
+ }
1040
+ /** The two separated halves of the attach artefact, plus their combined text. */
1041
+ interface FrpAttachTemplate {
1042
+ readonly mode: 'attach';
1043
+ readonly vps: string;
1044
+ readonly local: string;
1045
+ readonly text: string;
1046
+ }
1047
+ /**
1048
+ * Reject every attach request that could not be carried out safely before any
1049
+ * artefact is produced. Attach never installs frps, so it must know the user's
1050
+ * real vhost port (unless the self-signed passthrough removes the vhost entirely).
1051
+ */
1052
+ declare function validateAttachSettings(settings: FrpSettings, options?: FrpAttachOptions): void;
1053
+ /** Shared VPS-side inputs of both the copied runbook and the panel plan. */
1054
+ interface FrpAttachVpsParts {
1055
+ readonly publicHost: string;
1056
+ readonly selfSigned: boolean;
1057
+ readonly vhostHttpPort?: number;
1058
+ readonly publicPort?: number;
1059
+ readonly snippet: string;
1060
+ readonly certGuide: readonly string[];
1061
+ }
1062
+ /**
1063
+ * Derive every VPS-side value once so the copied runbook and the panel plan can
1064
+ * never drift apart.
1065
+ */
1066
+ declare function frpAttachVpsParts(settings: FrpSettings, options?: FrpAttachOptions): FrpAttachVpsParts;
1067
+ /**
1068
+ * Build the attach artefact for an existing frps.
1069
+ *
1070
+ * The result is deliberately split in two: the VPS half contains only what the
1071
+ * user's own server needs (Caddy snippet + import + certificate guidance, or a
1072
+ * single firewall rule for the self-signed passthrough), and the local half
1073
+ * contains the generated frpc.toml plus its `frpc verify` self-check. Neither
1074
+ * half installs, rewrites, or restarts the user's frps.
1075
+ */
1076
+ declare function createFrpAttachTemplateParts(settings: FrpSettings, options?: FrpAttachOptions): FrpAttachTemplate;
1077
+ /** Combined attach artefact, ready for the clipboard. */
1078
+ declare function createFrpAttachTemplate(settings: FrpSettings, options?: FrpAttachOptions): string;
1079
+ //#endregion
1080
+ //#region src/frp-attach-plan.d.ts
1081
+ /** Stable step identifiers; the panel maps each one to a translated title. */
1082
+ type FrpAttachStepId = 'write-snippet' | 'add-import' | 'issue-ip-cert' | 'enable-cert-timer' | 'verify-https' | 'open-public-port' | 'verify-frps' | 'verify-entry';
1083
+ /** One actionable VPS-side step; `title` is a message key, `label` the copied text. */
1084
+ interface FrpAttachPlanStep {
1085
+ readonly id: FrpAttachStepId;
1086
+ readonly title: string;
1087
+ readonly label: string;
1088
+ readonly commands: readonly string[];
1089
+ readonly optional: boolean;
1090
+ readonly verifyHint: string;
1091
+ }
1092
+ /** The local half of the plan: the exact frpc.toml plus its self-check command. */
1093
+ interface FrpAttachPlanLocal {
1094
+ /** The frpc.toml as previewed: masked unless the caller explicitly revealed the token. */
1095
+ readonly frpcToml: string;
1096
+ /** True when `frpcToml` carries the fixed placeholder instead of the shared token. */
1097
+ readonly tokenMasked: boolean;
1098
+ readonly verifyCommand: string;
1099
+ readonly configFile: string;
1100
+ readonly declaration: string;
1101
+ }
1102
+ /** Zero-SSH plan for attaching to an frps the user already runs. */
1103
+ interface FrpAttachPlan {
1104
+ readonly mode: 'attach';
1105
+ readonly entryTls: FrpEntryTls;
1106
+ readonly vhostHttpPort?: number;
1107
+ readonly publicPort?: number;
1108
+ readonly local: FrpAttachPlanLocal;
1109
+ readonly vps: readonly FrpAttachPlanStep[];
1110
+ readonly warnings: readonly string[];
1111
+ }
1112
+ /**
1113
+ * Build the copy-only attach plan. It performs no network access, no SSH, and no
1114
+ * filesystem write: the caller renders or copies the steps and the local frpc
1115
+ * configuration is written by the ordinary provider lifecycle.
1116
+ */
1117
+ declare function createFrpAttachPlan(settings: FrpSettings, options?: FrpAttachOptions): FrpAttachPlan;
1118
+ //#endregion
1119
+ //#region src/cert-renewal.d.ts
1120
+ /** Days left below which the entry certificate is reported as expiring. */
1121
+ declare const CERT_EXPIRING_DAYS = 2;
1122
+ /** Read-only lifetime verdict for one certificate; never mutates or renews anything. */
1123
+ interface CertRenewalStatus {
1124
+ readonly state: 'ok' | 'expiring' | 'expired' | 'unknown';
1125
+ /** Unix milliseconds of `notAfter`. */
1126
+ readonly notAfter?: number;
1127
+ readonly daysRemaining?: number;
1128
+ readonly subject?: string;
1129
+ readonly issuer?: string;
1130
+ readonly errorCode?: string;
1131
+ }
1132
+ /** Evaluate one `notAfter` timestamp against the current clock. */
1133
+ declare function evaluateCertificateLifetime(notAfter: number, now?: number): CertRenewalStatus;
1134
+ /**
1135
+ * Read one PEM certificate from disk and report only its lifetime.
1136
+ *
1137
+ * A missing or unreadable file yields `unknown` with the stable
1138
+ * `frp_attach_cert_unknown` code so the panel can explain how to re-issue it.
1139
+ */
1140
+ declare function readCertificateRenewal(file: string, now?: number): Promise<CertRenewalStatus>;
1141
+ /**
1142
+ * Ask a live TLS endpoint for its leaf certificate and report its lifetime.
1143
+ *
1144
+ * Verification is intentionally skipped: this is a read-only lifetime probe for
1145
+ * an endpoint whose trust anchor is already pinned elsewhere, and a self-signed
1146
+ * leaf is the expected answer for the passthrough entry.
1147
+ */
1148
+ declare function probeOriginCertificate(host: string, port: number, timeoutMs?: number): Promise<CertRenewalStatus>;
1149
+ //#endregion
1150
+ //#region src/frp-ingress.d.ts
1151
+ /** Private files owned by the self-signed FRP ingress. */
1152
+ interface FrpIngressPaths {
1153
+ readonly directory: string;
1154
+ readonly caCertFile: string;
1155
+ readonly caKeyFile: string;
1156
+ readonly certFile: string;
1157
+ readonly keyFile: string;
1158
+ readonly statusFile: string;
1159
+ }
1160
+ /** Signed ingress material plus the fingerprint the app must pin. */
1161
+ interface FrpIngressCertificate {
1162
+ readonly paths: FrpIngressPaths;
1163
+ readonly ca: X509Certificate;
1164
+ readonly leaf: X509Certificate;
1165
+ readonly caFingerprint: string;
1166
+ readonly status: CertRenewalStatus;
1167
+ }
1168
+ /** Everything the panel needs for the on-demand self-check. */
1169
+ interface FrpIngressSelfCheck {
1170
+ readonly mode: 'deploy' | 'attach';
1171
+ readonly entryTls: 'public-ip-cert' | 'self-signed';
1172
+ readonly vhostHttpPort: number;
1173
+ readonly publicPort: number;
1174
+ readonly publicOrigin: string;
1175
+ /** Reachable HTTPS entry; includes the TCP proxy port in self-signed mode. */
1176
+ readonly entryOrigin: string;
1177
+ readonly serverAddress: string;
1178
+ readonly serverPort: number;
1179
+ readonly caFingerprint?: string;
1180
+ readonly certificate?: CertRenewalStatus;
1181
+ readonly caCertificate?: CertRenewalStatus;
1182
+ readonly inbound: {
1183
+ readonly listenHost: '127.0.0.1';
1184
+ readonly allowedCidrs: readonly string[];
1185
+ };
1186
+ }
1187
+ /** Private ingress directory, always a sibling of the remote device file. */
1188
+ declare function frpIngressPaths(stateFile: string): FrpIngressPaths;
1189
+ /**
1190
+ * Materialize the CA and leaf the gateway terminates TLS with.
1191
+ *
1192
+ * The CA remains stable for paired phones; an expired CA must be re-paired, not
1193
+ * replaced silently. The leaf is re-signed when expiring or no longer valid for
1194
+ * the public IPv4, and the running gateway reloads it after a renewal check.
1195
+ */
1196
+ declare function ensureFrpIngressCertificate(settings: FrpSettings, stateFile: string, now?: number, expectedCaFingerprint?: string): Promise<FrpIngressCertificate>;
1197
+ /**
1198
+ * Build the read-only self-check payload shown in the desktop panel.
1199
+ *
1200
+ * No secret ever leaves this function: the CA is reported by fingerprint only,
1201
+ * and the token is never read.
1202
+ */
1203
+ declare function frpIngressSelfCheck(settings: FrpSettings, stateFile: string): Promise<FrpIngressSelfCheck>;
1204
+ //#endregion
1205
+ //#region src/managed-setup.d.ts
1206
+ /** One host a server leaf must be valid for: an IP literal, a DNS name, or both. */
1207
+ interface ServerCertificateTarget {
1208
+ readonly commonName: string;
1209
+ readonly ipAddresses?: readonly string[];
1210
+ readonly dnsNames?: readonly string[];
1211
+ }
1212
+ /** Where a freshly signed server leaf is installed. */
1213
+ interface ServerCertificateFiles {
1214
+ readonly certFile: string;
1215
+ readonly keyFile: string;
1216
+ }
1217
+ /** CA material a leaf is signed with. */
1218
+ interface ServerCertificateAuthority {
1219
+ readonly caCertFile: string;
1220
+ readonly caKeyFile: string;
1221
+ }
1222
+ /**
1223
+ * Sign one server leaf with an existing self-signed CA and install it privately.
1224
+ *
1225
+ * Both the LAN listener and the self-signed FRP ingress use this one path, so a
1226
+ * certificate can never be produced by two divergent code paths. IP entries are
1227
+ * required for console addresses (`type: 7`), DNS entries (`type: 2`) cover named
1228
+ * hosts; the Android client accepts either through its pinned CA.
1229
+ */
1230
+ declare function issueServerCertificate(authority: ServerCertificateAuthority, target: ServerCertificateTarget, output: ServerCertificateFiles, lifetimeDays?: number): Promise<void>;
1231
+ //#endregion
914
1232
  //#region src/remote.d.ts
915
1233
  /** Remote transports supported by the desktop plugin and Android client. */
916
1234
  type RemoteProvider = 'tailscale' | 'cpolar' | 'cloudflared' | 'frp' | 'origin';
@@ -963,18 +1281,59 @@ interface FrpStatus {
963
1281
  readonly origin?: string;
964
1282
  readonly errorCode?: string;
965
1283
  }
1284
+ /**
1285
+ * Explicit target of the start-up discovery self-check.
1286
+ *
1287
+ * The target is derived from the effective entry, never assumed: the public-CA
1288
+ * entry answers on the saved origin (443 behind Caddy, publicly trusted chain),
1289
+ * while the self-signed entry is a raw TCP passthrough on `publicPort` whose
1290
+ * leaf chains to the plugin's own ingress CA.
1291
+ */
1292
+ interface FrpDiscoveryProbeTarget {
1293
+ /** Absolute HTTPS origin to dial, including the public entry port. */
1294
+ readonly origin: string;
1295
+ /**
1296
+ * PEM bundle that anchors the entry leaf in place of the system trust store.
1297
+ * Absent keeps the default chain, which is correct for a publicly trusted
1298
+ * certificate; it is never a way to skip verification.
1299
+ */
1300
+ readonly trustAnchorPem?: string;
1301
+ }
966
1302
  /** Inputs for one FRP client process and authenticated DSH gateway. */
967
1303
  interface FrpControllerOptions {
968
1304
  readonly store: MobileAccessControlStore;
969
1305
  readonly executable: string;
970
1306
  readonly config: FrpConfigStore;
1307
+ /**
1308
+ * Plugin-wide installation identity (the LAN pairing CA fingerprint).
1309
+ *
1310
+ * Retained for backward compatibility and constructor validation only. It no
1311
+ * longer takes part in the start-up self-check: that check compares against the
1312
+ * identity of the gateway this controller created and advertises, because the
1313
+ * self-signed FRP ingress gateway is pinned to the ingress CA fingerprint by
1314
+ * design, and that value deliberately differs from this one.
1315
+ */
971
1316
  readonly instanceId: string;
972
- readonly createGateway: (origin: string) => Promise<MobileAccessGateway>;
1317
+ readonly createGateway: (origin: string, settings: FrpSettings) => Promise<MobileAccessGateway>;
973
1318
  readonly onStatus?: (status: FrpStatus) => void;
974
1319
  readonly verifyConfig?: (executable: string, configFile: string) => Promise<void>;
975
1320
  readonly launchClient?: (executable: string, configFile: string) => ChildProcessWithoutNullStreams;
976
1321
  readonly probeVhostExposure?: (serverAddress: string, port: number) => Promise<boolean>;
977
- readonly probeDiscovery?: (origin: string, expectedInstanceId: string, signal: AbortSignal) => Promise<boolean>;
1322
+ readonly probeDiscovery?: (target: FrpDiscoveryProbeTarget, expectedInstanceId: string, signal: AbortSignal) => Promise<boolean>;
1323
+ /**
1324
+ * Resolve the CA the self-check must pin for the self-signed entry.
1325
+ *
1326
+ * Only the composing plugin knows where the ingress material lives, so the
1327
+ * trust anchor is injected instead of guessed here. Consulted solely for the
1328
+ * self-signed passthrough; the public-CA entry always keeps the system trust
1329
+ * store. A configured resolver that cannot provide the CA fails startup with
1330
+ * `frp_ingress_ca_invalid`; the system trust store must not mask that failure.
1331
+ */
1332
+ readonly resolveDiscoveryTrustAnchor?: (settings: FrpSettings) => Promise<string | undefined>;
1333
+ /** Re-issue an expiring leaf and reload the live TLS listener without changing its CA. */
1334
+ readonly maintainIngressCertificate?: (settings: FrpSettings, gateway: MobileAccessGateway) => Promise<void>;
1335
+ /** Test seam for the maintenance timer; the product uses a twelve-hour interval. */
1336
+ readonly ingressCertificateCheckMs?: number;
978
1337
  readonly startTimeoutMs?: number;
979
1338
  readonly retryIntervalMs?: number;
980
1339
  }
@@ -990,6 +1349,7 @@ declare class FrpController implements RemoteProviderController {
990
1349
  private latest;
991
1350
  private queue;
992
1351
  private startupAbort;
1352
+ private ingressCertificateTimer;
993
1353
  constructor(options: FrpControllerOptions);
994
1354
  /** Restore the remembered FRP switch without changing LAN or other providers. */
995
1355
  initialize(): Promise<void>;
@@ -1008,7 +1368,21 @@ declare class FrpController implements RemoteProviderController {
1008
1368
  private enqueue;
1009
1369
  private publish;
1010
1370
  private start;
1371
+ /**
1372
+ * Derive the self-check target and trust anchor for the effective entry.
1373
+ *
1374
+ * The public-CA entry is reached on the saved origin itself: Caddy terminates
1375
+ * TLS on 443 with a publicly trusted certificate, so the probe must keep the
1376
+ * system trust store and the origin URL untouched. The self-signed entry is a
1377
+ * raw TCP passthrough on `publicPort` whose leaf chains to the ingress CA the
1378
+ * app pins from `pairingCaFile`; probing `publicOrigin` there would knock on
1379
+ * 443 — where nothing listens — with a chain the system cannot verify. Both
1380
+ * facts used to be implicit, and together they kept the channel out of `ready`
1381
+ * until `frp_start_timeout`.
1382
+ */
1383
+ private discoveryCheck;
1011
1384
  private waitForDiscovery;
1385
+ private scheduleIngressCertificateCheck;
1012
1386
  private failGeneration;
1013
1387
  private stop;
1014
1388
  private stopProcessAndGateway;
@@ -1440,5 +1814,5 @@ declare function originGatewayConfig(template: ResolvedGatewayConfig, settings:
1440
1814
  /** Mount the resident control route and its optional authenticated LAN gateway. */
1441
1815
  declare function apply(ctx: Context, config: PluginConfig): Promise<void>;
1442
1816
  //#endregion
1443
- export { AUTH_PREFIX, AccessController, type AccessControllerOptions, AccessError, type AuthoritySpec, type BlockedUpgradePathEntry, BlockedUpgradePathLog, BoundedRateLimiter, CLOUDFLARED_COMPONENT_RELEASE, CSRF_COOKIE, CSRF_HEADER, CloudflaredComponentManager, type CloudflaredComponentStatus, CloudflaredController, type CloudflaredControllerOptions, type CloudflaredState, type CloudflaredStatus, type CloudflaredTunnelMode, type CloudflaredTunnelSettings, type CloudflaredTunnelStatus, CloudflaredTunnelStore, Config, DEFAULT_ORIGIN_LISTEN_PORT, FRP_VHOST_HTTP_PORT as DEFAULT_VHOST_HTTP_PORT, FRP_VHOST_HTTP_PORT, DEVICE_COOKIE, type DeviceProbeResult, type DeviceSnapshot, type DeviceStore, type DeviceSummary, type DisabledTlsConfig, EXTENSION_LIMITS, FRP_CADDY_IMPORT_LINE, FRP_CADDY_SNIPPET_MARKER, FRP_CADDY_SNIPPET_PATH, FRP_COMPONENT_RELEASES, FrpComponentManager, type FrpComponentStatus, FrpConfigStore, type FrpConfigurationStatus, FrpController, type FrpControllerOptions, type FrpSettings, type FrpState, type FrpStatus, JsonDeviceStore, JsonMobileAccessControlStore, JsonRemoteProviderStore, LOCAL_ADMIN_PREFIX, type LocalExtensionManifest, MAX_BLOCKED_UPGRADE_PATHS, MAX_EXTRA_WEBSOCKET_PATHS, MAX_WEBSOCKET_PATH_LENGTH, MemoryDeviceStore, type MobileAccessControlState, type MobileAccessControlStore, MobileAccessGateway, MobileAccessGatewayController, type MobileAccessService as MobileAccessRegistry, MobileAccessService, type MobileAccessRuntime, type MobileActionContext, type MobileExtensionClientEntry, type MobileExtensionDefinition, MobileExtensionError, type MobileExtensionManifest, type MobileExtensionStatus, type MobileHostAction, type MobileHostRoute, type MobileRouteRequest, type MobileRouteResponse, OriginConfigStore, type OriginConfigurationStatus, OriginController, type OriginControllerOptions, type OriginSettings, type OriginState, type OriginStatus, type PairingResult, type ParsedCidr, type PluginConfig, type ProvidedTlsConfig, REMOTE_PROVIDERS, type RemoteProvider, type RemoteProviderController, type RemoteProviderState, type RemoteProviderStatus, type RenewalResult, RequestTrustPolicy, type ResolvedGatewayConfig, SESSION_COOKIE, type SessionAuthorization, type SessionEndReason, type StoredDevice, TASK_EVENT_DEBOUNCE_MS, type TaskCompletionEvent, type TaskEventContext, TaskEventHub, type TaskEventSession, type TaskEventSink, type TaskEventWatcherOptions, type TaskTurnEvent, type TlsConfig, WS_PATHS, WebSocketPathStore, addressAllowed, apply, assertExtensionId, configuredRemoteProvider, createCaddySite, createFrpServerTemplate, createFrpcToml, createMobileAccessService, createRestrictedFrpServerTemplate, inject, isCloudflaredRegistration, isGloballyRoutableIpv4, isLoopbackAddress, mergeSavedCloudflaredTunnelSettings, mergeSavedFrpSettings, mergeSavedFrpTarget, name, normalizeWebSocketPaths, originGatewayConfig, parseAuthority, parseCidr, parseCloudflaredOrigin, parseCloudflaredTunnelSettings, parseControlFile, parseDeviceSnapshot, parseExtensionManifest, parseFrpSettings, parseGatewayConfig, parseMobileAccessControlState, parseOriginSettings, parseRemoteProviderState, resolveAuthority, rewriteMobileIndex, validateCloudflaredTunnelHostname, validateCloudflaredTunnelPort, validateCloudflaredTunnelToken, validateFrpPublicOrigin, validateFrpServerAddress, validateFrpServerPort, validateFrpToken, validateOriginAllowedCidrs, validateOriginListenHost, validateOriginListenPort, validateOriginPublicOrigin, validateWebSocketPath, watchTaskCompletions };
1817
+ export { AUTH_PREFIX, AccessController, type AccessControllerOptions, AccessError, type AuthoritySpec, type BlockedUpgradePathEntry, BlockedUpgradePathLog, BoundedRateLimiter, CERT_EXPIRING_DAYS, CLOUDFLARED_COMPONENT_RELEASE, CSRF_COOKIE, CSRF_HEADER, type CaddySiteOptions, type CertRenewalStatus, CloudflaredComponentManager, type CloudflaredComponentStatus, CloudflaredController, type CloudflaredControllerOptions, type CloudflaredState, type CloudflaredStatus, type CloudflaredTunnelMode, type CloudflaredTunnelSettings, type CloudflaredTunnelStatus, CloudflaredTunnelStore, Config, DEFAULT_ORIGIN_LISTEN_PORT, FRP_VHOST_HTTP_PORT as DEFAULT_VHOST_HTTP_PORT, FRP_VHOST_HTTP_PORT, DEVICE_COOKIE, type DeviceProbeResult, type DeviceSnapshot, type DeviceStore, type DeviceSummary, type DisabledTlsConfig, EXTENSION_LIMITS, FRP_ATTACH_DISCOVERY_PATH, FRP_ATTACH_LOCAL_DECLARATION, FRP_ATTACH_SELF_SIGNED_DECLARATION, FRP_ATTACH_VPS_DECLARATION, FRP_CADDY_IMPORT_LINE, FRP_CADDY_SNIPPET_MARKER, FRP_CADDY_SNIPPET_PATH, FRP_COMPONENT_RELEASES, FRP_DEFAULT_PUBLIC_PORT, FRP_RESERVED_PORTS, type FrpAttachOptions, type FrpAttachPlan, type FrpAttachPlanLocal, type FrpAttachPlanStep, type FrpAttachStepId, type FrpAttachTemplate, type FrpAttachVpsParts, FrpComponentManager, type FrpComponentStatus, FrpConfigStore, type FrpConfigurationStatus, FrpController, type FrpControllerOptions, type FrpEntryTls, type FrpIngressCertificate, type FrpIngressPaths, type FrpIngressSelfCheck, type FrpSettings, type FrpState, type FrpStatus, JsonDeviceStore, JsonMobileAccessControlStore, JsonRemoteProviderStore, LOCAL_ADMIN_PREFIX, type LocalExtensionManifest, MAX_BLOCKED_UPGRADE_PATHS, MAX_EXTRA_WEBSOCKET_PATHS, MAX_WEBSOCKET_PATH_LENGTH, MemoryDeviceStore, type MobileAccessControlState, type MobileAccessControlStore, MobileAccessGateway, MobileAccessGatewayController, type MobileAccessService as MobileAccessRegistry, MobileAccessService, type MobileAccessRuntime, type MobileActionContext, type MobileExtensionClientEntry, type MobileExtensionDefinition, MobileExtensionError, type MobileExtensionManifest, type MobileExtensionStatus, type MobileHostAction, type MobileHostRoute, type MobileRouteRequest, type MobileRouteResponse, OriginConfigStore, type OriginConfigurationStatus, OriginController, type OriginControllerOptions, type OriginSettings, type OriginState, type OriginStatus, type PairingResult, type ParsedCidr, type PluginConfig, type ProvidedTlsConfig, REMOTE_PROVIDERS, type RemoteProvider, type RemoteProviderController, type RemoteProviderState, type RemoteProviderStatus, type RenewalResult, RequestTrustPolicy, type ResolvedGatewayConfig, SESSION_COOKIE, type ServerCertificateAuthority, type ServerCertificateFiles, type ServerCertificateTarget, type SessionAuthorization, type SessionEndReason, type StoredDevice, TASK_EVENT_DEBOUNCE_MS, type TaskCompletionEvent, type TaskEventContext, TaskEventHub, type TaskEventSession, type TaskEventSink, type TaskEventWatcherOptions, type TaskTurnEvent, type TlsConfig, WS_PATHS, WebSocketPathStore, addressAllowed, apply, assertExtensionId, configuredRemoteProvider, createCaddySite, createFrpAttachPlan, createFrpAttachTemplate, createFrpAttachTemplateParts, createFrpServerTemplate, createFrpcToml, createMobileAccessService, createRestrictedFrpServerTemplate, ensureFrpIngressCertificate, evaluateCertificateLifetime, frpAttachVpsParts, frpIngressPaths, frpIngressSelfCheck, inject, isCloudflaredRegistration, isFrpSelfSignedIngress, isGloballyRoutableIpv4, isLoopbackAddress, issueServerCertificate, mergeSavedCloudflaredTunnelSettings, mergeSavedFrpSettings, mergeSavedFrpTarget, name, normalizeWebSocketPaths, originGatewayConfig, parseAuthority, parseCidr, parseCloudflaredOrigin, parseCloudflaredTunnelSettings, parseControlFile, parseDeviceSnapshot, parseExtensionManifest, parseFrpSettings, parseGatewayConfig, parseMobileAccessControlState, parseOriginSettings, parseRemoteProviderState, probeOriginCertificate, readCertificateRenewal, resolveAuthority, resolveFrpEntryTls, resolveFrpMode, resolveFrpPublicPort, resolveFrpVhostHttpPort, rewriteMobileIndex, validateAttachSettings, validateCloudflaredTunnelHostname, validateCloudflaredTunnelPort, validateCloudflaredTunnelToken, validateFrpEntryTls, validateFrpMode, validateFrpPublicOrigin, validateFrpPublicPort, validateFrpServerAddress, validateFrpServerPort, validateFrpToken, validateFrpVhostHttpPort, validateOriginAllowedCidrs, validateOriginListenHost, validateOriginListenPort, validateOriginPublicOrigin, validateWebSocketPath, watchTaskCompletions };
1444
1818
  //# sourceMappingURL=index.d.mts.map