AGENTS.md@packages/client/src/lib/gateway · git:20261019.254d85a · 2026-10-19 · sha256 25df720f66d4ffb9
AGENTS.md@packages/client/src/lib/gateway git:20261019.254d85aA
Immutable. This exact content never changes and is served at /api/v1/blob/25df720f66d4ffb9.
# DOX — packages/client/src/lib/gateway
Files in this directory. One row per source file. See change: fold-oversized-agents-directories.
| File | Purpose |
|------|---------|
| `gateway-action.ts` | Pure config algebra for the "add gateway URL" action (D12/D13). Exports `validateGatewayDraft` (scheme drives eligibility: `http://` needs a trusted network and can be neither QR-paired nor OAuth; ≥1 auth mode mandatory), `buildGatewayAddPatch` (ONE patch carrying `publicBaseUrls` + `cors.allowedOrigins` + `auth.redirectBaseUrl` iff oauth + `trustedNetworks` iff trusted-network + the `gateways[]` provenance record — atomicity is structural), `buildGatewayRemovePatch` (reverts only recorded values STILL EQUAL in live config), `computeGatewayStatus` (read-only; trusted networks checked against the EFFECTIVE merge, top-level ∪ `auth.bypassHosts`), `buildGatewayFixPatch` (delta only, never re-runs add), `suggestMeshGatewayDraft(readiness, config)` (Add-editor defaults from first connected provider's non-empty `magicdns` URL not already a gateway/publicBaseUrl → `{url, authModes:["trusted-network"], cidr: exact mesh host}`; never the whole tailnet range — D12), plus the D9 offer layer: `buildGatewayModeOffer`/`isUnregisteredGatewayUrl` and `retainAvailableModes`/`everyModeAvailable` — the offer is recomputed each readiness tick but a checked box is not, so the availability rule lives here, not only in JSX. No I/O. See change: config-override-oauth-redirect-base. |
| `gateway-api.ts` | Client fetch helpers for the Gateway surfaces. Exports `getBlockEvents`, `runEnrollStep`, `getConfig`,… → see `gateway-api.ts.AGENTS.md` |
| `gateway-config-ops.ts` | Pure config-mutation helpers for the Gateway UI. Exports `isSecureBaseUrl`, `appendPublicBaseUrl`… → see `gateway-config-ops.ts.AGENTS.md` `suggestTrustEntries` now reads the SHARED well-known-range table; the module re-exports the reachability predicate for client callers. See change: warn-unreachable-trusted-networks. |
| `gateway-connection.ts` | Pure Connect/Disconnect toggle state. `gatewayToggle(statusDetail, dirty)` → `{action, connected, disabledReason}`: disconnect iff `connectedProviders` non-empty (older server: zrok `active` fallback); unsaved edits block Connect. `safeApprovalUrl(url)` → href only for https `login.tailscale.com` (server-relayed CLI output; blocks other hosts/`javascript:`). No I/O. `gatewayToggle` also returns `rows` (planned providers) + `partial`. `remoteAccessIndicator(tunnel, bindHost|null, waiting)` → toolbar `{tone: failed|partial|internet|network|local, title}`; `failed` needs the gated `providers`; unknown bind host never reads "this computer only". See change: add-remote-access-wizard. |
| `gateway-endpoints.ts` | Two-QR transport split + endpoints fetch. Exports `isPairingEligible` (scheme-authoritative, tls tag… → see `gateway-endpoints.ts.AGENTS.md` |
| `gateway-providers.ts` | Client provider matrix metadata. Exports `GatewayProviderId`, `GATEWAY_PROVIDERS` (zrok/ngrok public;… → see `gateway-providers.ts.AGENTS.md` |
| `gateway-setup.ts` | Per-provider setup-step model (D3 taxonomy). Exports `SetupStepKind`… → see `gateway-setup.ts.AGENTS.md` |
| `primary-switch.ts` | Pure D10 layer: `canMakePrimary` (connected AND not already primary — a disconnected promotion mints a redirect URI nobody can reach), `primarySwitchConsequence` (names the redirect-URI re-mint and the sign-in breakage, not just "are you sure"), `buildPrimarySwitchPatch` (writes `tunnel.provider` ONLY — a deep merge must not blank `tunnel.<id>.mode`). See change: add-zrok-custom-reserved-name. |
| `readiness-poll.ts` | Pure poll state machine for the readiness board. `shouldTick`/`onOpen`/`onClose`/`onTickStart`/`onTickResult`/`onTickError`/`secondsSinceCheck`, plus `READINESS_LABEL`/`nextAction`/`readinessSeverity`. Invariant: `onOpen` performs exactly ONE immediate tick, then one every 5s while open; `onClose` stops polling AND clears `inFlight` (otherwise the next open's first tick is suppressed forever); a result landing after close is DISCARDED; a tick starting while one is in flight is suppressed. Every state carries text, never colour alone. See change: add-zrok-custom-reserved-name. |
| `reserved-name.ts` | Pure state for Gateway Setup step 3. `reservedNameStepState` → idle/typing-valid/invalid/taken/write-failed/reserved/replace-confirm; `localValidationError` (states a FIX), `needsReplaceConfirm`, `reservedNameUrl`. `RESERVED_NAME_RE` MIRRORS the server's and is pinned against a verbatim copy — a mirror that disagrees is worse than none. Client check is a latency affordance, never the authority. See change: add-zrok-custom-reserved-name. |
| `tls-api.ts` | Built-in HTTPS client helpers: `getTlsStatus()` (`/api/health` → `tls`, `null` when absent), `getStoredTlsConfig()` (strict `GET /api/config` read of the `tls` block: any non-success read THROWS, never `{}`) and `issueTlsNow(sourceId, name?)` (`POST /api/tls/issue`; 401/403 → refusal message, else server reason). See change: add-tailscale-cert-tls. Adds `putAcmeSource` / `deleteAcmeSource` / `putAcmeCredentials` (`/api/tls/acme/:id[/credentials]`), `getStoredAcmeSources`, `AcmeSourceEntry` / `StoredAcmeEntry` / `AcmeCredentials`. See change: add-acme-dns01-certs. |
| `tls-view.ts` | Pure view model of the Built-in HTTPS section: `deriveTlsPill` (first match wins: Checking… / Off / Stopped / Setup needed / On / On, renewal failing / Failed / Getting certificate…), `deriveCertRow` (pill + facts + action; unknown running stage → Working…), `planApply` (unchanged / invalid port / minimal PUT patch), `describeApplyError` (400 → plain words per field), `isHeld`/`daysLeft`/`retryMinutes`. See change: add-tailscale-cert-tls. `deriveCertRow` takes an optional `isRequestingStage`. See change: add-acme-dns01-certs. |
| `use-tls-status.ts` | `useTlsStatus()` — reads `/api/health.tls` on mount and every `TLS_STATUS_POLL_MS` (5 s), one read in flight (a tick skips), `refresh(true)` after the section's own writes queues one immediate re-read. `status` null until the first read. See change: add-tailscale-cert-tls. |
| `acme-view.ts` | Pure helpers of the Domain certificates group/editor: `ACME_STAGES`/`isAcmeStage`, `inRenewalWindow` (≤ 1/3 of 90 d), `challengeHost`, `checkedAtOf`/`checkedAgo`, `isRejectedCredentials`, `isCaInvalid`, `gatesFor`, `deriveAcmeGroupPill`, `normalizeDomain`, `idProblem`, `fieldOfServerMessage`. See change: add-acme-dns01-certs. |
| `acme-credential-errors.ts` | Tiny `useSyncExternalStore` store of per-source credentials-write error messages (never a secret) so a group shows the error of a save made in the Add editor. `setAcmeCredentialError`, `clearAcmeCredentialError`, `useAcmeCredentialError`. See change: add-acme-dns01-certs. |