mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 18:37:24 +00:00
* relay: edge export skip on sustained outage, evacuation drain sweep, account allow scoped by region - Leaving hosts skip the edge export once it has been unavailable for 120 s (ORCA_RELAY_EDGE_EXPORT_SKIP_AFTER_MS), keeping Postgres devices and fencing best effort, instead of 503ing until the edge is back. - Directors drain edge hosts that left the cohort (POST /__admin/drain, 20 per jittered 60 s tick) so connected desktops re-ask and move off. - Account-allowlisted hosts must also be in the edge regions. - Tests for the invite-socket edge branch. * relay: export skip needs continuous failures; evacuation sweep runs soon after start, then every 10 min Failures more than 30 s apart restart the unavailable clock, so one old failure cannot skip an export. The sweep reads the edge rows (cell_id has no index): first pass 45-75 s after start (a cohort change is a new revision), every 45-75 s while hosts are queued, else every 7.5-12.5 min.
504 lines
21 KiB
TypeScript
504 lines
21 KiB
TypeScript
import { z } from 'zod'
|
|
import { isRelayCellConnectionHardCap, RELAY_CELL_CONNECTION_HARD_CAP, RELAY_MAX_CELL_CONNECTION_UNOBSERVED_BOUND, relayCellAdmissionBounds, type RelayCellConnectionHardCap } from '@orca-cloud/relay-contract/admission-budgets'
|
|
import { RELAY_DEFAULT_REGION, RelayRegionSchema, type RelayRegion } from '@orca-cloud/relay-contract/relay-regions'
|
|
import {
|
|
RELAY_MAX_READINESS_GRACE_MS,
|
|
RELAY_READINESS_JWKS_GRACE_MS,
|
|
RELAY_READINESS_SQL_GRACE_MS
|
|
} from './process-lifecycle/relay-readiness.js'
|
|
import type { EdgeRelayConfig } from './edge-relay.js'
|
|
|
|
export const RELAY_MAX_CELL_CAPACITY_REQUESTS = 100_000
|
|
export const RELAY_DATABASE_POOL_MAX = 10
|
|
export const RELAY_DIRECTOR_DATABASE_POOL_MAX = 3
|
|
export const RELAY_PUBLIC_RESOLVE_CONCURRENCY = 1
|
|
export const RELAY_PUBLIC_RESOLVE_WAIT_MS = 5_000
|
|
|
|
|
|
const RelayCellConnectionHardCapSchema = z.custom<RelayCellConnectionHardCap>(
|
|
isRelayCellConnectionHardCap
|
|
)
|
|
|
|
const EnvironmentBooleanSchema = z
|
|
.enum(['true', 'false'])
|
|
.default('true')
|
|
.transform((value) => value === 'true')
|
|
|
|
const OptionalServiceAccountSchema = z.preprocess(
|
|
(value) => (value === '' ? undefined : value),
|
|
z.string().email().optional()
|
|
)
|
|
|
|
// Cloud Run unset values arrive as empty strings, which coerce to 0 (fail-first readiness) instead of the grace default.
|
|
const readinessGraceSchema = (defaultMs: number) =>
|
|
z.preprocess(
|
|
(value) => (value === '' ? undefined : value),
|
|
z.coerce.number().int().min(0).max(RELAY_MAX_READINESS_GRACE_MS).default(defaultMs)
|
|
)
|
|
|
|
const EnvSchema = z.object({
|
|
PORT: z.coerce.number().int().positive().default(8080),
|
|
ORCA_RELAY_PUBLIC_URL: z.string().url(),
|
|
ORCA_RELAY_CELL_URL: z.string().url(),
|
|
ORCA_RELAY_AUTH_ISSUER: z.string().url(),
|
|
ORCA_RELAY_AUTH_AUDIENCE: z.literal('orca-relay').default('orca-relay'),
|
|
ORCA_RELAY_JWKS_URL: z.string().url(),
|
|
ORCA_RELAY_ASSIGNMENT_SIGNING_KEY: z.string().min(32),
|
|
ORCA_RELAY_ROLE: z.enum(['combined', 'director', 'cell']).default('combined'),
|
|
ORCA_RELAY_CELL_ID: z.string().min(1).max(128).default('combined'),
|
|
ORCA_RELAY_REGION: RelayRegionSchema.default(RELAY_DEFAULT_REGION),
|
|
ORCA_RELAY_CELL_CAPACITY: z.coerce
|
|
.number()
|
|
.int()
|
|
.positive()
|
|
.max(RELAY_MAX_CELL_CAPACITY_REQUESTS)
|
|
.default(900),
|
|
ORCA_RELAY_CELL_CONNECTION_HARD_CAP: z.coerce
|
|
.number()
|
|
.int()
|
|
.pipe(RelayCellConnectionHardCapSchema)
|
|
.optional(),
|
|
ORCA_RELAY_CELL_CONNECTION_UNOBSERVED_BOUND: z.coerce
|
|
.number()
|
|
.int()
|
|
.nonnegative()
|
|
.max(RELAY_MAX_CELL_CONNECTION_UNOBSERVED_BOUND)
|
|
.optional(),
|
|
ORCA_RELAY_CELLS_JSON: z.string().default('[]'),
|
|
ORCA_RELAY_ADMIN_AUDIENCE: z.string().url(),
|
|
ORCA_RELAY_DEPLOY_SERVICE_ACCOUNT: z.string().email(),
|
|
ORCA_RELAY_CAPACITY_SERVICE_ACCOUNT: OptionalServiceAccountSchema,
|
|
ORCA_RELAY_ASIA_PROOF_SERVICE_ACCOUNT: OptionalServiceAccountSchema,
|
|
ORCA_RELAY_MONITOR_SERVICE_ACCOUNT: OptionalServiceAccountSchema,
|
|
ORCA_RELAY_FENCE_SERVICE_ACCOUNT: OptionalServiceAccountSchema,
|
|
ORCA_RELAY_FENCE_BROKER_SERVICE_ACCOUNT: OptionalServiceAccountSchema,
|
|
ORCA_RELAY_REHOME_DIRECTOR_SERVICE_ACCOUNT: OptionalServiceAccountSchema,
|
|
ORCA_RELAY_REHOME_AUDIENCE: z.preprocess(
|
|
(value) => (value === '' ? undefined : value),
|
|
z.string().url().optional()
|
|
),
|
|
ORCA_RELAY_RUNTIME_SERVICE_ACCOUNT: z.string().email().optional(),
|
|
ORCA_RELAY_DIRECTOR_URL: z.string().url().optional(),
|
|
ORCA_RELAY_HEARTBEAT_AUDIENCE: z.string().url().optional(),
|
|
ORCA_RELAY_IMAGE_DIGEST: z
|
|
.string()
|
|
.regex(/^sha256:[a-f0-9]{64}$/)
|
|
.optional(),
|
|
ORCA_RELAY_ADMIN_JWKS_URL: z.string().url().default('https://www.googleapis.com/oauth2/v3/certs'),
|
|
ORCA_RELAY_DATABASE_POOL_MAX: z.coerce.number().int().positive().max(100).optional(),
|
|
ORCA_RELAY_READINESS_JWKS_GRACE_MS: readinessGraceSchema(RELAY_READINESS_JWKS_GRACE_MS),
|
|
ORCA_RELAY_READINESS_SQL_GRACE_MS: readinessGraceSchema(RELAY_READINESS_SQL_GRACE_MS),
|
|
ORCA_RELAY_PUBLIC_ASSIGNMENTS_ENABLED: EnvironmentBooleanSchema,
|
|
ORCA_RELAY_REGIONAL_PLACEMENT_ENABLED: EnvironmentBooleanSchema,
|
|
ORCA_RELAY_REGION_CORRECTION_COHORT_PERCENT: z.coerce.number().int().min(0).max(100).default(0),
|
|
// Step 5 on directors: `off` (default) is today's path; `dry-run` asks the picked cell to
|
|
// check a booking without making it, and logs the answer; `on` places on switched-on cells.
|
|
ORCA_RELAY_RESERVE_PLACEMENT: z.enum(['off', 'dry-run', 'on']).default('off'),
|
|
// Step 1 C: this share of hosts (by hash) on a reserve cell outside their region is booked
|
|
// in it on reconnect; 0 (default) is off.
|
|
ORCA_RELAY_RESERVE_REHOME_PERCENT: z.coerce.number().int().min(0).max(100).default(0),
|
|
// Step 3 shadow directory: unset or empty polls no cell; `all` or a comma list of cell ids.
|
|
ORCA_RELAY_SHADOW_SEAT_FEED_CELLS: z
|
|
.string()
|
|
.default('')
|
|
.transform((value) =>
|
|
value.trim() === 'all'
|
|
? ('all' as const)
|
|
: value
|
|
.split(',')
|
|
.map((cellId) => cellId.trim())
|
|
.filter((cellId) => cellId.length > 0)
|
|
),
|
|
ORCA_RELAY_PUBLIC_ASSIGNMENT_CONCURRENCY: z.coerce.number().int().positive().max(100).default(2),
|
|
ORCA_RELAY_PUBLIC_STICKY_CONCURRENCY: z.coerce.number().int().positive().max(100).default(1),
|
|
ORCA_RELAY_PUBLIC_STICKY_QUEUE_MAX: z.coerce.number().int().positive().max(4_096).default(64),
|
|
ORCA_RELAY_PUBLIC_STICKY_WAIT_MS: z.coerce.number().int().positive().max(30_000).default(2_000),
|
|
ORCA_RELAY_PUBLIC_STICKY_RETRY_AFTER_SECONDS: z.coerce
|
|
.number()
|
|
.int()
|
|
.positive()
|
|
.max(60)
|
|
.default(2),
|
|
// Borrowed from placement concurrency, which always keeps at least one permit.
|
|
ORCA_RELAY_DRAIN_RETURN_CONCURRENCY: z.coerce.number().int().positive().max(4).default(1),
|
|
ORCA_RELAY_DRAIN_RETURN_QUEUE_MAX: z.coerce.number().int().positive().max(64).default(4),
|
|
ORCA_RELAY_DRAIN_RETURN_WAIT_MS: z.coerce.number().int().positive().max(10_000).default(3_000),
|
|
// The desktop parser caps a Retry-After at 5 minutes; a larger value is truncated there.
|
|
ORCA_RELAY_DRAIN_RETURN_MAX_RETRY_AFTER_SECONDS: z.coerce
|
|
.number()
|
|
.int()
|
|
.min(2)
|
|
.max(300)
|
|
.default(300),
|
|
ORCA_RELAY_PUBLIC_ASSIGNMENT_QUEUE_MAX: z.coerce
|
|
.number()
|
|
.int()
|
|
.positive()
|
|
.max(4_096)
|
|
.default(128),
|
|
ORCA_RELAY_PUBLIC_ASSIGNMENT_WAIT_MS: z.coerce
|
|
.number()
|
|
.int()
|
|
.positive()
|
|
.max(30_000)
|
|
.default(4_000),
|
|
ORCA_RELAY_PUBLIC_ASSIGNMENT_RETRY_AFTER_SECONDS: z.coerce
|
|
.number()
|
|
.int()
|
|
.positive()
|
|
.max(300)
|
|
.default(5),
|
|
// Relay edge (director only). Unset URL: no edge cell, no override, today's behaviour.
|
|
ORCA_RELAY_EDGE_CELL_URL: z.preprocess(
|
|
(value) => (value === '' ? undefined : value),
|
|
z.string().url().optional()
|
|
),
|
|
ORCA_RELAY_EDGE_ROLLOUT_PERCENT: z.coerce.number().int().min(0).max(100).default(0),
|
|
ORCA_RELAY_EDGE_ALLOW_HOSTS: z.string().default(''),
|
|
ORCA_RELAY_EDGE_DENY_HOSTS: z.string().default(''),
|
|
// Comma-separated account ids (relay token `sub`); every host of these accounts joins.
|
|
ORCA_RELAY_EDGE_ALLOW_USERS: z.string().default(''),
|
|
ORCA_RELAY_EDGE_REGIONS: z.string().default(RELAY_DEFAULT_REGION),
|
|
// Set: the edge keeps host data in its objects, so moves push and export it (signed with this
|
|
// key, never the assignment lease key). Never unset while rows are on the edge.
|
|
ORCA_RELAY_EDGE_ADMIN_SIGNING_KEY: z.preprocess(
|
|
(value) => (value === '' ? undefined : value),
|
|
z.string().min(32).optional()
|
|
),
|
|
// Unset: EDGE_EXPORT_SKIP_AFTER_MS. How long the edge's export must keep failing before leaving
|
|
// hosts are placed without it (0: on the first failure).
|
|
ORCA_RELAY_EDGE_EXPORT_SKIP_AFTER_MS: z.preprocess(
|
|
(value) => (value === '' ? undefined : value),
|
|
z.coerce.number().int().min(0).optional()
|
|
),
|
|
DATABASE_URL: z.string().optional(),
|
|
ORCA_RELAY_DATA_DIR: z.string().default('./data/relay')
|
|
})
|
|
|
|
const RelayCellConfigSchema = z
|
|
.object({
|
|
id: z.string().min(1).max(128),
|
|
url: z.string().url(),
|
|
capacityRequests: z.number().int().positive().max(RELAY_MAX_CELL_CAPACITY_REQUESTS),
|
|
region: RelayRegionSchema.default(RELAY_DEFAULT_REGION),
|
|
initiallyEnabled: z.boolean().optional(),
|
|
connectionHardCap: RelayCellConnectionHardCapSchema.optional(),
|
|
connectionUnobservedBound: z
|
|
.number()
|
|
.int()
|
|
.nonnegative()
|
|
.max(RELAY_MAX_CELL_CONNECTION_UNOBSERVED_BOUND)
|
|
.optional()
|
|
})
|
|
.strict()
|
|
.superRefine((value, context) => {
|
|
if (
|
|
(value.connectionHardCap === undefined) !==
|
|
(value.connectionUnobservedBound === undefined)
|
|
) {
|
|
context.addIssue({
|
|
code: 'custom',
|
|
message: 'connection hard cap and unobserved bound must be configured together'
|
|
})
|
|
} else if (
|
|
value.connectionHardCap !== undefined &&
|
|
value.connectionUnobservedBound! >
|
|
relayCellAdmissionBounds(value.connectionHardCap).maxUnobservedBound
|
|
) {
|
|
context.addIssue({
|
|
code: 'custom',
|
|
path: ['connectionUnobservedBound'],
|
|
message: 'connection unobserved bound must leave ordinary admission capacity'
|
|
})
|
|
}
|
|
})
|
|
|
|
export type RelayCellConfig = Omit<z.infer<typeof RelayCellConfigSchema>, 'region'> & {
|
|
region?: RelayRegion
|
|
}
|
|
|
|
export type RelayConfig = {
|
|
port: number
|
|
publicUrl: string
|
|
cellUrl: string
|
|
authIssuer: string
|
|
authAudience: 'orca-relay'
|
|
jwksUrl: string
|
|
assignmentSigningKey: Uint8Array
|
|
role: 'combined' | 'director' | 'cell'
|
|
cellId: string
|
|
region?: RelayRegion
|
|
cells: RelayCellConfig[]
|
|
adminAudience: string
|
|
deployServiceAccount: string
|
|
capacityServiceAccount?: string
|
|
asiaProofServiceAccount?: string
|
|
monitorServiceAccount?: string
|
|
fenceServiceAccount?: string
|
|
fenceBrokerServiceAccount?: string
|
|
rehomeDirectorServiceAccount?: string
|
|
rehomeAudience?: string
|
|
shadowSeatFeedCells?: 'all' | string[]
|
|
reservePlacement?: 'off' | 'dry-run' | 'on'
|
|
reserveRehomePercent?: number
|
|
runtimeServiceAccount: string
|
|
directorUrl?: string
|
|
heartbeatAudience?: string
|
|
imageDigest?: string
|
|
connectionHardCap?: RelayCellConnectionHardCap
|
|
connectionUnobservedBound?: number
|
|
adminJwksUrl: string
|
|
databasePoolMax: number
|
|
readinessJwksGraceMs?: number
|
|
readinessSqlGraceMs?: number
|
|
publicAssignmentsEnabled: boolean
|
|
regionalPlacementEnabled?: boolean
|
|
regionCorrectionCohortPercent?: number
|
|
publicAssignmentConcurrency: number
|
|
publicAssignmentQueueMax: number
|
|
publicAssignmentWaitMs: number
|
|
publicResolveConcurrency: number
|
|
publicResolveWaitMs: number
|
|
publicAssignmentRetryAfterSeconds: number
|
|
publicStickyConcurrency?: number
|
|
publicStickyQueueMax?: number
|
|
publicStickyWaitMs?: number
|
|
publicStickyRetryAfterSeconds?: number
|
|
drainReturnConcurrency?: number
|
|
drainReturnQueueMax?: number
|
|
drainReturnWaitMs?: number
|
|
drainReturnMaxRetryAfterSeconds?: number
|
|
databaseUrl?: string
|
|
dataDir: string
|
|
edge?: EdgeRelayConfig
|
|
}
|
|
|
|
function canonicalOrigin(value: string, name: string): string {
|
|
const url = new URL(value)
|
|
if (url.origin !== value || url.pathname !== '/') throw new Error(`${name} must be an origin`)
|
|
const loopback = ['127.0.0.1', 'localhost', '::1', '[::1]'].includes(url.hostname)
|
|
if (url.protocol !== 'https:' && !(loopback && url.protocol === 'http:')) {
|
|
throw new Error(`${name} must use HTTPS outside loopback development`)
|
|
}
|
|
return value
|
|
}
|
|
|
|
export function loadRelayConfig(env: NodeJS.ProcessEnv = process.env): RelayConfig {
|
|
const parsed = EnvSchema.parse(env)
|
|
const adminServiceAccounts = [
|
|
parsed.ORCA_RELAY_DEPLOY_SERVICE_ACCOUNT,
|
|
parsed.ORCA_RELAY_CAPACITY_SERVICE_ACCOUNT,
|
|
parsed.ORCA_RELAY_ASIA_PROOF_SERVICE_ACCOUNT,
|
|
parsed.ORCA_RELAY_MONITOR_SERVICE_ACCOUNT,
|
|
parsed.ORCA_RELAY_FENCE_SERVICE_ACCOUNT,
|
|
parsed.ORCA_RELAY_FENCE_BROKER_SERVICE_ACCOUNT,
|
|
parsed.ORCA_RELAY_REHOME_DIRECTOR_SERVICE_ACCOUNT
|
|
].filter((value): value is string => value !== undefined)
|
|
if (new Set(adminServiceAccounts).size !== adminServiceAccounts.length) {
|
|
throw new Error('relay admin service accounts must be distinct')
|
|
}
|
|
if (
|
|
(parsed.ORCA_RELAY_REHOME_DIRECTOR_SERVICE_ACCOUNT === undefined) !==
|
|
(parsed.ORCA_RELAY_REHOME_AUDIENCE === undefined)
|
|
) {
|
|
throw new Error('relay rehome identity and audience must be configured together')
|
|
}
|
|
if (
|
|
parsed.ORCA_RELAY_REHOME_DIRECTOR_SERVICE_ACCOUNT &&
|
|
parsed.ORCA_RELAY_REHOME_DIRECTOR_SERVICE_ACCOUNT ===
|
|
(parsed.ORCA_RELAY_RUNTIME_SERVICE_ACCOUNT ?? parsed.ORCA_RELAY_DEPLOY_SERVICE_ACCOUNT)
|
|
) {
|
|
throw new Error('relay rehome director identity must differ from the cell runtime identity')
|
|
}
|
|
if (
|
|
parsed.ORCA_RELAY_REHOME_AUDIENCE &&
|
|
new URL(parsed.ORCA_RELAY_REHOME_AUDIENCE).pathname !== '/v1/admin/host-drain'
|
|
) {
|
|
throw new Error('relay rehome audience must target the host drain route')
|
|
}
|
|
const directorUrl = parsed.ORCA_RELAY_DIRECTOR_URL
|
|
? canonicalOrigin(parsed.ORCA_RELAY_DIRECTOR_URL, 'ORCA_RELAY_DIRECTOR_URL')
|
|
: undefined
|
|
const publicUrl = canonicalOrigin(parsed.ORCA_RELAY_PUBLIC_URL, 'ORCA_RELAY_PUBLIC_URL')
|
|
const configuredCells = z
|
|
.array(RelayCellConfigSchema)
|
|
.max(128)
|
|
.parse(JSON.parse(parsed.ORCA_RELAY_CELLS_JSON) as unknown)
|
|
.map((cell) => ({ ...cell, url: canonicalOrigin(cell.url, `cell ${cell.id}`) }))
|
|
const ownCell = {
|
|
id: parsed.ORCA_RELAY_CELL_ID,
|
|
region: parsed.ORCA_RELAY_REGION,
|
|
url: canonicalOrigin(parsed.ORCA_RELAY_CELL_URL, 'ORCA_RELAY_CELL_URL'),
|
|
capacityRequests: parsed.ORCA_RELAY_CELL_CAPACITY,
|
|
connectionHardCap: parsed.ORCA_RELAY_CELL_CONNECTION_HARD_CAP,
|
|
connectionUnobservedBound: parsed.ORCA_RELAY_CELL_CONNECTION_UNOBSERVED_BOUND,
|
|
initiallyEnabled: true
|
|
}
|
|
if (
|
|
(ownCell.connectionHardCap === undefined) !==
|
|
(ownCell.connectionUnobservedBound === undefined)
|
|
) {
|
|
throw new Error('connection hard cap and unobserved bound must be configured together')
|
|
}
|
|
if (
|
|
ownCell.connectionHardCap !== undefined &&
|
|
ownCell.connectionUnobservedBound! >
|
|
relayCellAdmissionBounds(ownCell.connectionHardCap).maxUnobservedBound
|
|
) {
|
|
throw new Error('connection unobserved bound must leave ordinary admission capacity')
|
|
}
|
|
const cells = parsed.ORCA_RELAY_ROLE === 'director' ? configuredCells : [ownCell]
|
|
if (cells.length === 0) throw new Error('director requires at least one configured cell')
|
|
if (new Set(cells.map(({ id }) => id)).size !== cells.length) {
|
|
throw new Error('relay cell ids must be unique')
|
|
}
|
|
const databasePoolMax =
|
|
parsed.ORCA_RELAY_DATABASE_POOL_MAX ??
|
|
(parsed.ORCA_RELAY_ROLE === 'director'
|
|
? RELAY_DIRECTOR_DATABASE_POOL_MAX
|
|
: RELAY_DATABASE_POOL_MAX)
|
|
if (
|
|
parsed.ORCA_RELAY_ROLE !== 'cell' &&
|
|
parsed.ORCA_RELAY_PUBLIC_ASSIGNMENT_CONCURRENCY >= databasePoolMax
|
|
) {
|
|
throw new Error('public relay admission must leave database pool headroom')
|
|
}
|
|
if (
|
|
parsed.ORCA_RELAY_ROLE !== 'cell' &&
|
|
parsed.ORCA_RELAY_PUBLIC_ASSIGNMENT_CONCURRENCY + parsed.ORCA_RELAY_PUBLIC_STICKY_CONCURRENCY >
|
|
databasePoolMax
|
|
) {
|
|
throw new Error('sticky and placement admission together must fit the database pool')
|
|
}
|
|
const edge = parseEdgeConfig(parsed, publicUrl)
|
|
if (edge && cells.some((cell) => cell.id === 'edge' || cell.url === edge.cellUrl)) {
|
|
throw new Error('the relay edge cell id and URL must not be a configured cell')
|
|
}
|
|
return {
|
|
port: parsed.PORT,
|
|
publicUrl,
|
|
cellUrl: ownCell.url,
|
|
authIssuer: canonicalOrigin(parsed.ORCA_RELAY_AUTH_ISSUER, 'ORCA_RELAY_AUTH_ISSUER'),
|
|
authAudience: parsed.ORCA_RELAY_AUTH_AUDIENCE,
|
|
jwksUrl: parsed.ORCA_RELAY_JWKS_URL,
|
|
assignmentSigningKey: new TextEncoder().encode(parsed.ORCA_RELAY_ASSIGNMENT_SIGNING_KEY),
|
|
role: parsed.ORCA_RELAY_ROLE,
|
|
cellId: ownCell.id,
|
|
region: ownCell.region,
|
|
cells,
|
|
adminAudience: parsed.ORCA_RELAY_ADMIN_AUDIENCE,
|
|
deployServiceAccount: parsed.ORCA_RELAY_DEPLOY_SERVICE_ACCOUNT,
|
|
capacityServiceAccount: parsed.ORCA_RELAY_CAPACITY_SERVICE_ACCOUNT,
|
|
asiaProofServiceAccount: parsed.ORCA_RELAY_ASIA_PROOF_SERVICE_ACCOUNT,
|
|
monitorServiceAccount: parsed.ORCA_RELAY_MONITOR_SERVICE_ACCOUNT,
|
|
fenceServiceAccount: parsed.ORCA_RELAY_FENCE_SERVICE_ACCOUNT,
|
|
fenceBrokerServiceAccount: parsed.ORCA_RELAY_FENCE_BROKER_SERVICE_ACCOUNT,
|
|
rehomeDirectorServiceAccount: parsed.ORCA_RELAY_REHOME_DIRECTOR_SERVICE_ACCOUNT,
|
|
rehomeAudience: parsed.ORCA_RELAY_REHOME_AUDIENCE,
|
|
shadowSeatFeedCells: parsed.ORCA_RELAY_SHADOW_SEAT_FEED_CELLS,
|
|
reservePlacement: parsed.ORCA_RELAY_RESERVE_PLACEMENT,
|
|
reserveRehomePercent: parsed.ORCA_RELAY_RESERVE_REHOME_PERCENT,
|
|
runtimeServiceAccount:
|
|
parsed.ORCA_RELAY_RUNTIME_SERVICE_ACCOUNT ?? parsed.ORCA_RELAY_DEPLOY_SERVICE_ACCOUNT,
|
|
directorUrl,
|
|
heartbeatAudience:
|
|
parsed.ORCA_RELAY_HEARTBEAT_AUDIENCE ??
|
|
(directorUrl || parsed.ORCA_RELAY_ROLE === 'director'
|
|
? new URL('/v1/admin/cell-heartbeat', directorUrl ?? publicUrl).toString()
|
|
: undefined),
|
|
imageDigest: parsed.ORCA_RELAY_IMAGE_DIGEST,
|
|
connectionHardCap: ownCell.connectionHardCap,
|
|
connectionUnobservedBound: ownCell.connectionUnobservedBound,
|
|
adminJwksUrl: parsed.ORCA_RELAY_ADMIN_JWKS_URL,
|
|
databasePoolMax,
|
|
readinessJwksGraceMs: parsed.ORCA_RELAY_READINESS_JWKS_GRACE_MS,
|
|
readinessSqlGraceMs: parsed.ORCA_RELAY_READINESS_SQL_GRACE_MS,
|
|
publicAssignmentsEnabled: parsed.ORCA_RELAY_PUBLIC_ASSIGNMENTS_ENABLED,
|
|
regionalPlacementEnabled: parsed.ORCA_RELAY_REGIONAL_PLACEMENT_ENABLED,
|
|
regionCorrectionCohortPercent: parsed.ORCA_RELAY_REGION_CORRECTION_COHORT_PERCENT,
|
|
publicAssignmentConcurrency: parsed.ORCA_RELAY_PUBLIC_ASSIGNMENT_CONCURRENCY,
|
|
publicAssignmentQueueMax: parsed.ORCA_RELAY_PUBLIC_ASSIGNMENT_QUEUE_MAX,
|
|
publicAssignmentWaitMs: parsed.ORCA_RELAY_PUBLIC_ASSIGNMENT_WAIT_MS,
|
|
publicResolveConcurrency: RELAY_PUBLIC_RESOLVE_CONCURRENCY,
|
|
publicResolveWaitMs: RELAY_PUBLIC_RESOLVE_WAIT_MS,
|
|
publicAssignmentRetryAfterSeconds: parsed.ORCA_RELAY_PUBLIC_ASSIGNMENT_RETRY_AFTER_SECONDS,
|
|
publicStickyConcurrency: parsed.ORCA_RELAY_PUBLIC_STICKY_CONCURRENCY,
|
|
publicStickyQueueMax: parsed.ORCA_RELAY_PUBLIC_STICKY_QUEUE_MAX,
|
|
publicStickyWaitMs: parsed.ORCA_RELAY_PUBLIC_STICKY_WAIT_MS,
|
|
publicStickyRetryAfterSeconds: parsed.ORCA_RELAY_PUBLIC_STICKY_RETRY_AFTER_SECONDS,
|
|
drainReturnConcurrency: parsed.ORCA_RELAY_DRAIN_RETURN_CONCURRENCY,
|
|
drainReturnQueueMax: parsed.ORCA_RELAY_DRAIN_RETURN_QUEUE_MAX,
|
|
drainReturnWaitMs: parsed.ORCA_RELAY_DRAIN_RETURN_WAIT_MS,
|
|
drainReturnMaxRetryAfterSeconds: parsed.ORCA_RELAY_DRAIN_RETURN_MAX_RETRY_AFTER_SECONDS,
|
|
databaseUrl: parsed.DATABASE_URL,
|
|
dataDir: parsed.ORCA_RELAY_DATA_DIR,
|
|
...(edge ? { edge } : {})
|
|
}
|
|
}
|
|
|
|
const RELAY_HOST_ID_PATTERN = /^[A-Za-z0-9_-]{16}$/
|
|
|
|
function hostIdList(value: string, name: string): Set<string> {
|
|
const ids = value
|
|
.split(',')
|
|
.map((id) => id.trim())
|
|
.filter((id) => id.length > 0)
|
|
for (const id of ids) {
|
|
if (!RELAY_HOST_ID_PATTERN.test(id)) {
|
|
throw new Error(`${name} has an invalid relay host id`)
|
|
}
|
|
}
|
|
return new Set(ids)
|
|
}
|
|
|
|
function userIdList(value: string): Set<string> {
|
|
const ids = value
|
|
.split(',')
|
|
.map((id) => id.trim())
|
|
.filter((id) => id.length > 0)
|
|
for (const id of ids) {
|
|
if (id.length > 128 || !/^[A-Za-z0-9_.:|-]+$/.test(id)) {
|
|
throw new Error('ORCA_RELAY_EDGE_ALLOW_USERS has an invalid account id')
|
|
}
|
|
}
|
|
return new Set(ids)
|
|
}
|
|
|
|
function parseEdgeConfig(
|
|
parsed: z.infer<typeof EnvSchema>,
|
|
publicUrl: string
|
|
): EdgeRelayConfig | undefined {
|
|
if (parsed.ORCA_RELAY_ROLE !== 'director' || !parsed.ORCA_RELAY_EDGE_CELL_URL) {
|
|
return undefined
|
|
}
|
|
const cellUrl = canonicalOrigin(parsed.ORCA_RELAY_EDGE_CELL_URL, 'ORCA_RELAY_EDGE_CELL_URL')
|
|
if (cellUrl === publicUrl) {
|
|
throw new Error('ORCA_RELAY_EDGE_CELL_URL must differ from the director')
|
|
}
|
|
const regions = parsed.ORCA_RELAY_EDGE_REGIONS.split(',')
|
|
.map((region) => region.trim())
|
|
.filter((region) => region.length > 0)
|
|
.map((region) => RelayRegionSchema.parse(region))
|
|
const adminKey = parsed.ORCA_RELAY_EDGE_ADMIN_SIGNING_KEY
|
|
// The deployed edge keeps host data in its objects: without the key the director could not
|
|
// push it, and hosts would land on an edge that holds none of their devices.
|
|
if (adminKey === undefined) {
|
|
throw new Error('ORCA_RELAY_EDGE_CELL_URL needs ORCA_RELAY_EDGE_ADMIN_SIGNING_KEY')
|
|
}
|
|
if (adminKey === parsed.ORCA_RELAY_ASSIGNMENT_SIGNING_KEY) {
|
|
throw new Error('ORCA_RELAY_EDGE_ADMIN_SIGNING_KEY must differ from the assignment signing key')
|
|
}
|
|
return {
|
|
cellUrl,
|
|
adminSigningKey: new TextEncoder().encode(adminKey),
|
|
rolloutPercent: parsed.ORCA_RELAY_EDGE_ROLLOUT_PERCENT,
|
|
allowHosts: hostIdList(parsed.ORCA_RELAY_EDGE_ALLOW_HOSTS, 'ORCA_RELAY_EDGE_ALLOW_HOSTS'),
|
|
allowUsers: userIdList(parsed.ORCA_RELAY_EDGE_ALLOW_USERS),
|
|
denyHosts: hostIdList(parsed.ORCA_RELAY_EDGE_DENY_HOSTS, 'ORCA_RELAY_EDGE_DENY_HOSTS'),
|
|
regions: new Set(regions),
|
|
...(parsed.ORCA_RELAY_EDGE_EXPORT_SKIP_AFTER_MS === undefined
|
|
? {}
|
|
: { exportSkipAfterMs: parsed.ORCA_RELAY_EDGE_EXPORT_SKIP_AFTER_MS })
|
|
}
|
|
}
|