import { type Result } from "@fjall/generator"; import type { TaskDefinition } from "@aws-sdk/client-ecs"; import type { EcsService } from "../../services/infrastructure/EcsService.js"; import type { DeployableService } from "../../services/infrastructure/EcsServiceResolver.js"; import type { SsmParameterMetadata, SsmParameterMetadataService } from "../../services/infrastructure/SsmParameterMetadataService.js"; export interface StaleParameter { name: string; lastModifiedDate: string; version?: number; } /** * A service's drift verdict. `unknown` is an answer, not a missing one: the * live task definition could not be read, or SSM described none of the * parameters it injects, so nothing can be said about them. It is a case of * its own precisely so a renderer cannot spell it as `current` — an operator * who is told a service is up to date after a rotation stops looking. */ export type SecretsDriftVerdict = "stale" | "current" | "unknown"; export interface ServiceSecretsDrift { serviceName: string; serviceArn: string; /** The cluster this service runs in — one app's services may span clusters. */ clusterArn: string; clusterName: string; taskDefinitionArn?: string; taskDefRegisteredAt?: string; /** SSM parameter names the live task definition injects (`secrets[].valueFrom`). */ consumedParameters: string[]; verdict: SecretsDriftVerdict; /** * Why the verdict is `unknown` — stripped and masked by whichever producer * composed it, so a renderer prints it as-is. Absent for every other * verdict. */ unknownReason?: string; staleParameters: StaleParameter[]; } export interface ApplicationSecretsDrift { services: ServiceSecretsDrift[]; anyStale: boolean; /** At least one service whose drift could not be determined at all. */ anyUnknown: boolean; /** Latest known Version per consumed parameter name, across all services. */ parameterVersions: Record; } /** * Normalise an ECS `secrets[].valueFrom` reference to an SSM parameter name. * Returns undefined for non-SSM references (Secrets Manager ARNs) — those are * resolved by ECS from a different store and are out of drift scope. */ export declare function ssmParameterNameFromValueFrom(valueFrom: string): string | undefined; /** Collect the unique SSM parameter names a task definition injects. */ export declare function extractConsumedSsmParameters(taskDefinition: TaskDefinition): string[]; export interface StaleParameterScan { stale: StaleParameter[]; /** * Consumed names SSM returned no usable metadata for — absent from the * describe result, or present without a `LastModifiedDate`. There is * nothing to compare them against, so they are reported rather than * skipped: a name that silently fell out of the describe (an IAM policy * that does not cover it, a parameter deleted after the task launched) is * indistinguishable from one that is up to date, and reading it as up to * date is the answer an operator acts on after a rotation. */ undescribed: string[]; } /** * Pure staleness comparator: a parameter is stale for a service when it was * modified AFTER the service's live task definition was registered — running * tasks resolved the old value at launch and nothing has rolled them since. * Self-heals on any roll (fresh registeredAt) and catches out-of-band writes * (webapp, console, another machine) because the inputs are AWS ground truth. * * Returns the names it could NOT judge alongside the stale ones. A missing * comparison is not a clean one. */ export declare function deriveStaleParameters(taskDefRegisteredAt: Date | undefined, consumedNames: string[], parameters: readonly SsmParameterMetadata[]): StaleParameterScan; export interface SecretsDriftServices { ecsService: Pick; ssmService: Pick; } /** * Compute per-service secrets drift for an application's deployable ECS * services, which may span clusters. Fails when live state cannot be read * at all, when the caller's signal aborts part-way — on either leg, ECS or * SSM — or when ECS does not hold a service the Compute stack exports. * * `current` is only ever said about a service every one of whose injected * parameters was compared. A service whose own task definition cannot be * read, or one SSM described none of, reports `unknown`; a service with no * consumed SSM parameters is genuinely `current`, because there is nothing * that could have drifted. */ export declare function computeSecretsDrift(services: SecretsDriftServices, targets: readonly DeployableService[], abortSignal?: AbortSignal): Promise>;