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/CHANGELOG.md +23 -0
- package/CONTRIBUTORS.md +11 -3
- package/README.en.md +38 -11
- package/README.md +38 -11
- package/SECURITY.md +7 -5
- package/docs/ATTACH_EXISTING_FRPS.en.md +44 -0
- package/docs/ATTACH_EXISTING_FRPS.md +98 -0
- package/docs/README.md +3 -0
- package/docs/SELF_HOSTED_FRP.en.md +3 -1
- package/docs/SELF_HOSTED_FRP.md +7 -1
- package/lib/cli.js +52 -15
- package/lib/cli.js.map +1 -1
- package/lib/client.js +1298 -156
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +390 -12
- package/lib/index.mjs +2268 -581
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-compat.js.map +1 -1
- package/lib/mobile-layout.js +75 -20
- package/lib/mobile-layout.js.map +1 -1
- package/package.json +18 -14
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
|
-
/**
|
|
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
|
-
/**
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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?: (
|
|
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
|