@claxedo/workspace-relay 0.8.0 → 0.10.0

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/dist/main.d.ts DELETED
@@ -1,255 +0,0 @@
1
- import { type WorkspaceRelayBunDrainController } from "./bun";
2
- import { type WorkspaceRelayDirectory } from "./directory";
3
- import { type RelayHostPublicKey, type RevocationLookup, type WorkspaceRelayTarget } from "./server";
4
- import { type RelayKey } from "./auth";
5
- export { createCachedRevocationClient, createCachedTargetClient } from "./server";
6
- /**
7
- * PostHog is the relay's error sink — one vendor carries error tracking for
8
- * every runtime behind one distinct_id space.
9
- *
10
- * Sending requires two independent opt-ins: `CLAXEDO_TELEMETRY_MODE=on` AND
11
- * `CLAXEDO_POSTHOG_KEY` (`POSTHOG_KEY` accepted as a fallback — the same
12
- * unprefixed name claxedo-server's posthog.ts reads). Any other combination
13
- * constructs no `PostHog` client: zero SDK overhead, zero network. Host
14
- * defaults to `https://us.i.posthog.com` (the ingest host, not the legacy
15
- * `app.posthog.com` default some SDKs ship). Release is the git SHA from
16
- * `CLAXEDO_RELEASE` (`GIT_SHA` accepted as alias); captures are tagged
17
- * unit=relay + deployment_mode (absent → "local").
18
- *
19
- * `posthog-node` runs fine on Bun (`Bun.serve`), unlike the Cloudflare
20
- * Worker, which keeps it off its import graph entirely (see worker.ts).
21
- */
22
- export type RelayObservabilityEnv = {
23
- /** Only `on` permits sending, matched case-insensitively after trimming. */
24
- CLAXEDO_TELEMETRY_MODE?: string | undefined;
25
- CLAXEDO_POSTHOG_KEY?: string | undefined;
26
- POSTHOG_KEY?: string | undefined;
27
- CLAXEDO_POSTHOG_HOST?: string | undefined;
28
- CLAXEDO_RELEASE?: string | undefined;
29
- GIT_SHA?: string | undefined;
30
- CLAXEDO_DEPLOYMENT_MODE?: string | undefined;
31
- /** Accept process.env verbatim (extra keys are ignored). */
32
- [key: string]: string | undefined;
33
- };
34
- export type RelayTelemetryOptions = {
35
- key: string;
36
- host: string;
37
- release?: string;
38
- tags: {
39
- unit: "relay";
40
- deployment_mode: string;
41
- };
42
- };
43
- /**
44
- * Pure env → PostHog options resolver. `undefined` = do NOT construct a client
45
- * (no client, no network), which is the answer unless BOTH opt-ins are
46
- * present. The mode is read before the key so that a deployment which has not
47
- * said `on` stays silent no matter which keys reach its environment.
48
- * Exported for tests.
49
- */
50
- export declare function relayTelemetryOptions(env: RelayObservabilityEnv): RelayTelemetryOptions | undefined;
51
- export declare function initRelayObservability(env?: RelayObservabilityEnv): {
52
- enabled: boolean;
53
- };
54
- /**
55
- * Capture-and-flush used by the fatal handlers and the startup catch. With no
56
- * initialized client (key absent) this is a documented no-op. Never throws —
57
- * observability must not preempt the exit path.
58
- *
59
- * `distinctId: "system"` mirrors the ops-plane convention: process-fatal
60
- * events carry no user identity. `client.flush()` has no built-in timeout,
61
- * so it races a 2s timer; the flush promise's
62
- * rejection is swallowed even when the timer wins the race, so a slow network
63
- * failure that resolves after the timeout never surfaces as a second
64
- * unhandledRejection mid-shutdown.
65
- */
66
- export declare function reportFatal(error: unknown): Promise<void>;
67
- /**
68
- * Production fail-closed gate for the relay → claxedo-server resolver channel.
69
- *
70
- * If `CLAXEDO_RELAY_RESOLVER_TOKEN` is missing/empty in production the relay
71
- * would otherwise call the resolver unauthenticated. Refuse to boot.
72
- *
73
- * Allowed in dev/test for ergonomics — the resolver itself falls back to a
74
- * loopback-only check when no token is configured.
75
- */
76
- export type ValidateProductionEnvInput = {
77
- NODE_ENV?: string | undefined;
78
- CLAXEDO_RELAY_RESOLVER_TOKEN?: string | undefined;
79
- };
80
- export type ValidateProductionEnvResult = {
81
- ok: true;
82
- } | {
83
- ok: false;
84
- exitCode: 2;
85
- message: string;
86
- };
87
- export declare function validateProductionEnv(env: ValidateProductionEnvInput): ValidateProductionEnvResult;
88
- /**
89
- * Resolve the verification key (or keyset resolver) for Runtime Access Tokens.
90
- *
91
- * Precedence: `CLAXEDO_CONTROL_PLANE_JWKS_URL` > `CLAXEDO_RUNTIME_ACCESS_TOKEN_PUBLIC_KEY_PEM`.
92
- * The PEM env var is kept as a fallback so JWKS rollouts can ship gradually.
93
- *
94
- * Exported for tests.
95
- */
96
- export type LoadRuntimeAccessKeyEnv = {
97
- CLAXEDO_CONTROL_PLANE_JWKS_URL?: string | undefined;
98
- CLAXEDO_RUNTIME_ACCESS_TOKEN_PUBLIC_KEY_PEM?: string | undefined;
99
- };
100
- export declare function loadRuntimeAccessKeyOrJwks(env: LoadRuntimeAccessKeyEnv): Promise<RelayKey>;
101
- /**
102
- * Resolve the RHT key material — both the private key used to sign RHTs and
103
- * the public key(s) we publish at `/.well-known/jwks.json`.
104
- *
105
- * Precedence for the public counterpart:
106
- * 1. `CLAXEDO_RELAY_HOST_PUBLIC_KEY_PEM` (explicit override; useful when
107
- * the private key is held in an HSM-style import that strips public bits).
108
- * 2. Derived from the private key via `exportJWK` (works because we now
109
- * load the private key with `extractable: true`).
110
- *
111
- * `kid` is the explicit `CLAXEDO_RELAY_HOST_KID` env var if set, otherwise
112
- * SHA-256(public-key-x).slice(0, 16). Mint and JWKS use the same derivation
113
- * so they always agree.
114
- *
115
- * `next` is optional and is included in JWKS only — never used to mint. It
116
- * exists to support rolling rotation: publish the next key, drain the
117
- * current one, swap signing key, retire the old.
118
- *
119
- * Exported for tests.
120
- */
121
- export type LoadRelayHostKeyMaterialEnv = {
122
- /**
123
- * Gates the production fail-closed check: with no signing-key PEM in
124
- * `production` the loader exits 2 instead of generating an ephemeral key.
125
- * The check lives here rather than in `validateProductionEnv` because the
126
- * loader already inspects the signing-key env var.
127
- */
128
- NODE_ENV?: string | undefined;
129
- CLAXEDO_RELAY_HOST_SIGNING_KEY_PEM?: string | undefined;
130
- CLAXEDO_RELAY_HOST_PUBLIC_KEY_PEM?: string | undefined;
131
- CLAXEDO_RELAY_HOST_NEXT_PUBLIC_KEY_PEM?: string | undefined;
132
- CLAXEDO_RELAY_HOST_KID?: string | undefined;
133
- CLAXEDO_RELAY_HOST_NEXT_KID?: string | undefined;
134
- };
135
- export type RelayHostKeyMaterial = {
136
- privateKey: CryptoKey;
137
- current: RelayHostPublicKey;
138
- next?: RelayHostPublicKey;
139
- };
140
- export declare function loadRelayHostKeyMaterial(env: LoadRelayHostKeyMaterialEnv): Promise<RelayHostKeyMaterial>;
141
- /**
142
- * Parse `CLAXEDO_RELAY_AUDIT_ACCEPT_SAMPLE_RATE` into a number in [0, 1].
143
- * Falls back to a NODE_ENV-gated default (production: 0.1, dev/test: 1.0) if
144
- * unset, empty, or out of range. Exported for tests.
145
- */
146
- export type ParseAuditAcceptSampleRateEnv = {
147
- CLAXEDO_RELAY_AUDIT_ACCEPT_SAMPLE_RATE?: string | undefined;
148
- NODE_ENV?: string | undefined;
149
- };
150
- export declare function parseAuditAcceptSampleRate(env: ParseAuditAcceptSampleRateEnv): number;
151
- /**
152
- * Parse the `CLAXEDO_RELAY_METRICS_TOKEN` env var. Returns `undefined`
153
- * for missing/empty/whitespace-only values so the relay can fall back to its
154
- * loopback-only `/metrics` access policy.
155
- */
156
- export type ParseMetricsTokenEnv = {
157
- CLAXEDO_RELAY_METRICS_TOKEN?: string | undefined;
158
- };
159
- export declare function parseMetricsToken(env: ParseMetricsTokenEnv): string | undefined;
160
- export type RuntimeAccessTokenCacheEnv = {
161
- CLAXEDO_RELAY_RUNTIME_ACCESS_TOKEN_CACHE_TTL_MS?: string | undefined;
162
- };
163
- export declare function runtimeAccessTokenCacheTtlMsFromEnv(env: RuntimeAccessTokenCacheEnv): number;
164
- export type DirectHttpConcurrencyEnv = {
165
- CLAXEDO_RELAY_DIRECT_HTTP_CONCURRENCY?: string | undefined;
166
- };
167
- export declare function directHttpConcurrencyFromEnv(env: DirectHttpConcurrencyEnv): number | undefined;
168
- export type ResolverClientCacheOptions = {
169
- targetCacheTtlMs?: number;
170
- revocationCacheTtlMs?: number;
171
- /**
172
- * Absolute URL of the control plane's host-generation lookup. Unset derives
173
- * `<resolver base>/host-generation`, the route every resolver base serves
174
- * next to `/target` and `/revocation`.
175
- */
176
- hostGenerationUrl?: string;
177
- hostGenerationCacheTtlMs?: number;
178
- };
179
- export type ResolverClientCacheEnv = {
180
- CLAXEDO_RELAY_TARGET_CACHE_TTL_MS?: string | undefined;
181
- CLAXEDO_RELAY_REVOCATION_CACHE_TTL_MS?: string | undefined;
182
- CLAXEDO_RELAY_HOST_GENERATION_URL?: string | undefined;
183
- CLAXEDO_RELAY_HOST_GENERATION_CACHE_TTL_MS?: string | undefined;
184
- };
185
- export declare function resolverClientCacheOptionsFromEnv(env: ResolverClientCacheEnv): ResolverClientCacheOptions;
186
- export declare function createResolverClient(baseUrl: string, token: string | undefined, options?: ResolverClientCacheOptions): {
187
- target: (workspaceId: string, hostId: string) => Promise<WorkspaceRelayTarget | undefined>;
188
- revocation: RevocationLookup;
189
- hostGeneration: import("./server").HostGenerationLookup;
190
- };
191
- /**
192
- * Graceful SIGTERM drain wiring.
193
- *
194
- * On `SIGTERM` (or `SIGINT` for local dev) the relay must:
195
- * 1. Flip the drain flag so `/health` reports 503 and Fly removes the
196
- * machine from routing, and so new HTTP requests + tunnel registrations
197
- * fast-path to 503 before doing any auth or upstream work.
198
- * 2. Wait up to `drainTimeoutMs` (default 30 s, override via
199
- * `CLAXEDO_RELAY_DRAIN_TIMEOUT_MS`) for the in-flight tunnel HTTP
200
- * response map to empty across every connected host tunnel.
201
- * 3. Release the directory's TTL sweep timer via `directory.dispose()` so
202
- * the process can exit cleanly without a dangling `setInterval`.
203
- * 4. Exit with code 0.
204
- *
205
- * Exposed for tests so the trigger logic can be exercised without actually
206
- * raising signals or stopping the process. Pass `register: false` in tests
207
- * to skip the real `process.on(...)` registration.
208
- */
209
- export type ShutdownDrainHandlerOptions = {
210
- drain: WorkspaceRelayBunDrainController;
211
- directory: Pick<WorkspaceRelayDirectory, "dispose">;
212
- drainTimeoutMs: number;
213
- /** Defaults to `process.exit`. Override for tests. */
214
- exit?: (code?: number) => void;
215
- /** Defaults to a no-op log. */
216
- log?: (message: string) => void;
217
- /** When true (default), register `SIGTERM` and `SIGINT` handlers on `process`. */
218
- register?: boolean;
219
- /** Signals to listen for. Defaults to `["SIGTERM", "SIGINT"]`. */
220
- signals?: NodeJS.Signals[];
221
- /** Defaults to a no-op (we let Bun.serve get GC'd as part of process exit). */
222
- stopServer?: () => Promise<void> | void;
223
- /**
224
- * Upper bound on `stopServer`. A wedged socket close must not outlast the
225
- * platform's kill timeout, or a graceful exit becomes a SIGKILL that skips
226
- * `directory.dispose()`. Defaults to 5 s; tests override it to stay fast.
227
- */
228
- stopTimeoutMs?: number;
229
- };
230
- export type ShutdownDrainHandle = {
231
- trigger(): Promise<void>;
232
- isShuttingDown(): boolean;
233
- };
234
- export declare function installShutdownDrainHandler(options: ShutdownDrainHandlerOptions): ShutdownDrainHandle;
235
- export type FatalProcessHandlerOptions = {
236
- drain: WorkspaceRelayBunDrainController;
237
- directory: Pick<WorkspaceRelayDirectory, "dispose">;
238
- exit?: (code?: number) => void;
239
- log?: (message: string) => void;
240
- register?: boolean;
241
- /**
242
- * Error-tracker hook, invoked (and awaited) before teardown so a fatal
243
- * crash reaches PostHog before the process exits. Failures are logged and
244
- * never block the exit path. Defaults to none (tests, key-absent runs).
245
- */
246
- report?: (error: unknown, source: "uncaughtException" | "unhandledRejection") => void | Promise<void>;
247
- stopServer?: () => Promise<void> | void;
248
- /** Upper bound on `stopServer`; see `ShutdownDrainHandlerOptions.stopTimeoutMs`. */
249
- stopTimeoutMs?: number;
250
- };
251
- export type FatalProcessHandle = {
252
- trigger(error: unknown, source: "uncaughtException" | "unhandledRejection"): Promise<void>;
253
- isFatal(): boolean;
254
- };
255
- export declare function installFatalProcessHandlers(options: FatalProcessHandlerOptions): FatalProcessHandle;