# QuietDeploy Check a deployment plan against a network-signature budget before it runs. QuietDeploy takes the steps of a rollout (what each one contacts, how many bytes it moves, for how long, with how many retries) and checks them against a total-volume budget, a peak-rate ceiling and a destination allowlist. It is pure, with no network and no clock. It sends nothing. ## What it checks and why A deployment is observable. On a constrained or monitored link, the volume, the destinations and the timing of what a rollout pulls are a signal in themselves. The failures tend to be the same three. 1. The pull nobody counted. A build that "just pulls a base image" can move hundreds of megabytes to a third-party registry. It does not show up in the plan because nobody wrote it as a step; it is a side effect of one. A step with `bytes: null` is NOT ASSESSED, never zero. 2. Egress outside the declared set. An allowlist exists because a deploy that reaches an unexpected host is either a supply-chain problem or a leak. That check runs first and stays critical however small the transfer is. Volume is irrelevant to it. 3. A burst that fits the total and not any window. Ten targets pulling 200 MB each inside a minute is a different signature from the same traffic over an hour, and a total-only budget passes both. Peak rate is checked on its own and multiplied by `concurrency`. Retries are cost. `worstCaseBytes()` counts every attempt, because a step budgeted at one attempt is budgeted at its best case. A plan that fits on the first try and not with retries fails. When any step is unmeasured, the total is reported as a lower bound, not a total. A partial sum presented as a total is how a budget check reassures without checking. The total still fails if the known bytes alone exceed the budget, since unmeasured steps cannot rescue a plan that is already over. ## Usage ```ts import { checkPlan, formatPlan, type DeployStep, type SignatureBudget } from '@extant2000/quiet-deploy' const steps: DeployStep[] = [ { name: 'pull-base-image', destination: 'registry.example.com', bytes: 200_000_000, durationSeconds: 60, retries: 2, where: 'ci.yml' }, { name: 'fetch-cli', destination: 'downloads.example.net', bytes: 5_000_000, durationSeconds: 10, where: 'scripts/setup.sh' }, { name: 'push-app-image', destination: 'registry.example.com', bytes: null, where: 'ci.yml' }, ] const budget: SignatureBudget = { maxTotalBytes: 2e9, maxBytesPerSecond: 5e6, allowedDestinations: ['registry.example.com'], concurrency: 4, } const report = checkPlan(steps, budget) for (const f of report.findings) console.log(`${f.determination.padEnd(13)} ${f.id}: ${f.summary}`) // The full evidence-record report for a terminal or CI log console.log(formatPlan(steps, budget)) ``` `checkPlan()` returns a `CapabilityReport` from `@extant2000/evidence-record`. `worstCaseBytes()`, `peakRate()` and `formatBytes()` are exported as well. ## Sample output Produced by running the example above (the loop over findings): ``` not-assessed total: 405.0 MB of 2.0 GB accounted for, with 1 step(s) unmeasured fail pull-base-image: pull-base-image peaks at 13.3 MB/s fail fetch-cli: fetch-cli contacts downloads.example.net, which is not in the declared set not-assessed push-app-image: push-app-image moves an unknown volume to registry.example.com ``` The 405 MB total counts both attempts of the base-image pull. The pull fails the rate check only because four run at once. ## Install The package is published to the GitLab package registry, so point the `@extant2000` scope there first: ``` npm config set @extant2000:registry https://gitlab.com/api/v4/packages/npm/ npm install @extant2000/quiet-deploy ``` ## License MIT. Copyright (c) 2026 Extant 2000 LLC. See [LICENSE](LICENSE).