/** * Deployment-time rollout controls. These are deliberately separate from the * domain capability contracts: a capability may have a complete contract and * still remain disabled in a particular release. * * The allowlist is evaluated on the server for every entry point. Unknown * names are rejected instead of becoming an accidental opt-in, and newly * added known capabilities are absent from the baseline allowlist until an * operator explicitly enables them. */ export const RELEASE_CAPABILITIES = [ 'asset_search', 'asset_valuation', 'asset_sync', 'assistant_dispatch', 'session_token', 'subscriptions', 'wallet_balance', 'inventory_export', 'manufacturing_opportunities', ] as const; export type ReleaseCapability = (typeof RELEASE_CAPABILITIES)[number]; /** Existing first-slice behavior remains enabled unless an operator narrows it. */ export const BASELINE_RELEASE_CAPABILITIES: readonly ReleaseCapability[] = [ 'asset_search', 'asset_valuation', 'asset_sync', 'assistant_dispatch', 'session_token', ]; const known = new Set(RELEASE_CAPABILITIES); export class CapabilityConfigurationError extends Error { constructor(message: string) { super(message); this.name = 'CapabilityConfigurationError'; } } /** * Parse APP_CAPABILITY_ALLOWLIST without accepting typos or duplicate policy * entries. An explicitly empty value disables every capability; an omitted * value preserves the existing private asset/sign-in baseline. */ export function parseCapabilityAllowlist(raw: string | undefined): readonly ReleaseCapability[] { if (raw === undefined) return [...BASELINE_RELEASE_CAPABILITIES]; const value = raw.trim(); if (value === '') return []; const result: ReleaseCapability[] = []; const seen = new Set(); for (const token of value.split(',')) { const name = token.trim(); if (!name || !known.has(name)) { throw new CapabilityConfigurationError(`Unknown deployment capability: ${name || ''}`); } if (!seen.has(name)) { seen.add(name); result.push(name as ReleaseCapability); } } return result; } export function capabilityEnabled( capability: ReleaseCapability, allowlist: readonly ReleaseCapability[], ): boolean { return allowlist.includes(capability); } export function capabilityDisabledReason(capability: ReleaseCapability): string { return `The ${capability} capability is disabled by deployment configuration.`; } /** A non-throwing check is useful at HTTP boundaries that should fail closed. */ export function requireCapability( capability: ReleaseCapability, allowlist: readonly ReleaseCapability[], ): void { if (!capabilityEnabled(capability, allowlist)) { throw new CapabilityConfigurationError(capabilityDisabledReason(capability)); } } /** Release identifiers are metadata, not a source of authorization. */ export function validReleaseId(value: string): boolean { return /^(?!latest$)[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value); } /** * Production images must carry a non-placeholder immutable identifier. Local * development and fixture runs intentionally use `local`. */ export function releaseIdIsImmutable(value: string, environment = process.env.NODE_ENV): boolean { if (!validReleaseId(value)) return false; if (environment !== 'production') return true; return value !== 'local' && value !== 'dev' && value !== 'development' && value !== 'unreleased'; }