dsh-mobile 0.4.5 → 0.4.7

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