import type { TDnsRecordType } from './dns.js'; /** * Type of platform acting as a gateway client against a gateway (dcrouter). */ export type TGatewayClientType = 'onebox' | 'cloudly' | 'custom'; /** * Canonical managed route purposes understood by gateways. */ export type TManagedRouteKind = 'letsencrypt-http01-forward'; /** * DNS handling requested alongside a gateway route synchronization. * * - 'reconcile' claims or replaces exact-host manual A, AAAA, and CNAME * records, including already-correct records, with gateway-managed state. * - 'observe' reports DNS state without mutating it. * - 'skip' bypasses DNS inspection and mutation and is the default when omitted. */ export type TGatewayRouteDnsMode = 'reconcile' | 'observe' | 'skip'; /** * Detailed DNS outcome reported by a gateway route synchronization. */ export type TGatewayRouteDnsStatus = | 'not-applicable' | 'skipped' | 'managed-created' | 'managed-updated' | 'managed-overwrote-manual' | 'managed-unchanged' | 'observed-satisfied' | 'managed-deleted' | 'managed-orphan-deleted' | 'record-missing' | 'target-invalid' | 'zone-unmanaged' | 'zone-unavailable' | 'record-conflict' | 'mutation-failed' | 'compensation-failed'; /** * DNS record evidence attached to a gateway route synchronization result. */ export interface IGatewayRouteDnsRecord { id?: string; type: 'A' | 'AAAA' | 'CNAME'; name: string; value: string; ttl: number; proxied?: boolean; managed: boolean; } /** * DNS reconciliation or observation result for a gateway route. */ export interface IGatewayRouteDnsResult { success: boolean; retryable: boolean; mode: TGatewayRouteDnsMode; status: TGatewayRouteDnsStatus; hostname?: string; targetIp?: string; /** Desired gateway-managed address record type. CNAME appears only in record evidence. */ recordType?: 'A' | 'AAAA'; zoneId?: string; zoneName?: string; nameservers?: string[]; records?: IGatewayRouteDnsRecord[]; /** Prior manual records replaced by this reconciliation, preserved as evidence. */ overwrittenRecords?: IGatewayRouteDnsRecord[]; /** Time at which the gateway checked the DNS state, as Unix epoch milliseconds. */ checkedAt: number; /** Time at which authoritative DNS verified the desired state, as Unix epoch milliseconds. */ authoritativeVerifiedAt?: number; message?: string; } /** * Identity attached to gateway requests. Gateways run their own user/auth * domain (e.g. dcrouter operator accounts), so role/type stay open strings; * platform identities (IIdentity) are structurally assignable to this shape. */ export interface IGatewayIdentity { jwt: string; userId: string; name: string; expiresAt: number; role?: string; type?: string; } /** * Role resolved for a gateway API token or identity. */ export type TGatewayTokenRole = 'admin' | 'gatewayClient' | 'operator'; /** * Feature surface a gateway exposes to connected gateway clients. */ export interface IGatewayCapabilities { routes: { read: boolean; write: boolean; idempotentSync: boolean; }; domains: { read: boolean; write: boolean; }; certificates: { read: boolean; export: boolean; forceRenew: boolean; }; email: { domains: boolean; inbound: boolean; outbound: boolean; }; remoteIngress: { enabled: boolean; }; dns: { authoritative: boolean; providerManaged: boolean; }; http3: { enabled: boolean; }; /** Absent means that this gateway does not advertise Web Push support. */ webPush?: { bindings: boolean; delivery: boolean; cancellation: boolean; vapidRotation: boolean; }; } /** * Capability flags granted to a gateway client token. */ export interface IGatewayTokenCapabilities { readDomains?: boolean; readDnsRecords?: boolean; readRoutes?: boolean; syncRoutes?: boolean; syncDnsRecords?: boolean; readMail?: boolean; manageMail?: boolean; readCertificates?: boolean; requestCertificates?: boolean; readWebPush?: boolean; manageWebPush?: boolean; } /** * A host/ports combination a gateway client is allowed to route traffic to. */ export interface IGatewayRouteTargetAllowance { host: string; ports: number[]; /** Allows every port for this host only; it never broadens another host allowance. */ allowAnyPort?: boolean; } /** Public gateway-client policy and lifecycle state. */ export interface IGatewayClient { id: string; type: TGatewayClientType; name: string; description?: string; /** `*` matches any hostname under a managed zone; `*.zone` matches exactly one label. */ hostnamePatterns: string[]; allowedRouteTargets: IGatewayRouteTargetAllowance[]; capabilities: IGatewayTokenCapabilities; enabled: boolean; tokenCount?: number; /** Generation of the durable live policy document. */ policyGeneration: number; createdAt: number; updatedAt: number; createdBy: string; } /** Complete desired state used for idempotent gateway-client provisioning. */ export interface IGatewayClientProvisioningSpec { id: string; type: TGatewayClientType; name: string; description?: string; /** `*` matches any hostname under a managed zone; `*.zone` matches exactly one label. */ hostnamePatterns: string[]; allowedRouteTargets: IGatewayRouteTargetAllowance[]; capabilities: IGatewayTokenCapabilities; enabled?: boolean; } /** * One-time credential material returned after durable token creation. * * Provisioning creates a candidate only. Existing client-bound credentials * remain active until the candidate authenticates the explicit finalization * request, preventing a lost provisioning response from locking out the * gateway client. */ export interface IGatewayClientCredential { tokenId: string; tokenValue: string; issuedAt: number; state: 'candidate'; finalizationRequired: true; } export type TGatewayClientProvisioningAction = 'created' | 'updated' | 'unchanged'; /** * Resolved auth context for a gateway client token, as reported by the gateway. * Scopes are gateway-defined strings; the closed scope list lives in the gateway. */ interface IGatewayClientContextBase { scopes: string[]; /** `*` matches any hostname under a managed zone; `*.zone` matches exactly one label. */ hostnamePatterns: string[]; allowedRouteTargets: IGatewayRouteTargetAllowance[]; capabilities: IGatewayTokenCapabilities; } /** Server-authoritative lifecycle of an authenticated gateway-client credential. */ export type TGatewayCredentialState = 'candidate' | 'active' | 'manual'; /** * Resolved authorization context. A gateway-client role always proves the * concrete credential, bound client, and current live policy generation. * Admin/operator contexts remain unbound for backward compatibility. */ export type IGatewayClientContext = IGatewayClientContextBase & ( | { role: 'gatewayClient'; credentialId: string; /** Present on lifecycle-aware gateways. Candidates are validation/finalization-only. */ credentialState?: TGatewayCredentialState; gatewayClient: { type: TGatewayClientType; id: string; policyGeneration: number; }; } | { role: 'admin' | 'operator'; credentialId?: never; gatewayClient?: never; } ); /** * Where a gateway-managed domain is sourced from. * * - 'dcrouter' → the gateway itself is the authoritative DNS server. * - 'provider' → the domain is managed through an external DNS provider. */ export type TGatewayDomainSource = 'dcrouter' | 'provider'; /** * What a gateway client may do with a gateway-exposed domain. */ export interface IGatewayDomainCapabilities { canCreateSubdomains: boolean; canManageDnsRecords: boolean; canIssueCertificates: boolean; canHostEmail: boolean; } /** * A domain a gateway exposes to gateway clients for app hosting. */ export interface IGatewayDomain { id: string; /** Fully qualified domain name (e.g. 'example.com'). */ name: string; source: TGatewayDomainSource; /** Gateway-internal DNS provider id — only set when source === 'provider'. */ providerId?: string; /** True when the gateway is the authoritative DNS server for this domain. */ authoritative: boolean; nameservers?: string[]; /** Provider's internal zone identifier — only set when source === 'provider'. */ externalZoneId?: string; /** Last time records were synced from the provider — only set when source === 'provider'. */ lastSyncedAt?: number; description?: string; createdAt: number; updatedAt: number; createdBy: string; capabilities: IGatewayDomainCapabilities; /** Number of gateway-client services currently using this domain. */ serviceCount?: number; /** Gateway UI path for managing this domain. */ managePath?: string; } /** * DNS record view for gateway-client app hostnames. * Records with status 'missing' describe expected-but-absent records and * use type 'MISSING' placeholders where applicable. */ export interface IGatewayDnsRecord { id: string; /** Id of the parent gateway domain. */ domainId: string; domainName?: string; /** Fully qualified record name (e.g. 'www.example.com'). */ name: string; type: TDnsRecordType | 'MISSING'; value: string; /** TTL in seconds. */ ttl: number; /** Provider-specific: whether the record is proxied (e.g. Cloudflare). */ proxied?: boolean; source: 'local' | 'synced'; /** Provider's internal record id — only set for provider records. */ providerRecordId?: string; createdAt: number; updatedAt: number; createdBy: string; status: 'active' | 'missing'; gatewayClientType: TGatewayClientType; gatewayClientId: string; /** App identifier within the owning gateway client platform. */ appId: string; hostname: string; routeId?: string; serviceName?: string; /** Gateway UI path for managing this record. */ managePath?: string; } /** * Ownership of a gateway-client-synced route: which platform, which platform * instance, and which app/hostname the route belongs to. */ export interface IGatewayClientOwnership { gatewayClientType?: TGatewayClientType; gatewayClientId?: string; /** App identifier within the owning gateway client platform. */ appId: string; /** Hostname ownership for domain routes. Preserved for existing route keys. */ hostname?: string; /** * Stable route key. Without hostname it identifies a non-domain route such as * a public-port mapping. Together with hostname it distinguishes multiple * managed routes for the same hostname. */ routeRef?: string; } /** * A single forward target of a gateway route. */ export interface IGatewayRouteTarget { host: string; port: number; } /** * Match condition for a gateway route. */ export interface IGatewayRouteMatch { ports: number[]; domains?: string[]; transport?: 'tcp' | 'udp' | 'all'; path?: string; } /** * TLS handling for a gateway route. 'terminate' with certificate 'auto' * requests gateway-managed ACME certificates. */ export interface IGatewayRouteTls { mode: 'terminate' | 'passthrough' | 'terminate-and-reencrypt'; certificate?: 'auto'; } /** * Forward action of a gateway route. */ export interface IGatewayRouteAction { type: 'forward'; targets: IGatewayRouteTarget[]; tls?: IGatewayRouteTls; websocket?: { enabled: boolean; }; } /** * Platform-neutral desired state for one gateway route. * * This is deliberately narrower than the gateway's internal route config: * control planes express route intent with this shape and the gateway maps * it onto its own proxy configuration. */ export interface IGatewayRouteConfig { name: string; match: IGatewayRouteMatch; action: IGatewayRouteAction; /** Higher values are matched first by the gateway. */ priority?: number; /** Declares a canonical gateway-managed route purpose. */ managedRouteKind?: TManagedRouteKind; /** * Trusted ingress paths that may reach this route without public edge ingress. * * Omission is accepted only for compatibility with clients predating this * contract. New and updated managed routes should set both values explicitly. */ ingress?: { /** * Accept connections made directly to a listener on the gateway hub. * This must not be enabled by default for newly-created routes. */ directHub: boolean; /** Accept authenticated SmartVPN connections carrying trusted VPN metadata. */ smartVpn: boolean; }; remoteIngress?: { enabled: boolean; edgeFilter?: string[]; }; } /** * Gateway-client-owned route view used for reconciliation by clients. */ export interface IGatewayClientRoute { id: string; name: string; enabled: boolean; ownership: IGatewayClientOwnership; route: IGatewayRouteConfig; sourceProfileRef?: string; /** Persisted DNS handling mode for this managed route. */ dnsMode?: TGatewayRouteDnsMode; /** Explicit provider proxy intent for the managed address record. */ dnsProxied?: boolean; managePath?: string; } /** * Result of an idempotent gateway route sync. */ export interface IGatewayRouteSyncResult { success: boolean; action?: 'created' | 'updated' | 'deleted' | 'unchanged'; routeId?: string; dns?: IGatewayRouteDnsResult; message?: string; }