release: verify production portfolio deployment
This commit is contained in:
100
docs/deployment/release-checklist.md
Normal file
100
docs/deployment/release-checklist.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Portfolio release checklist
|
||||
|
||||
This checklist separates deterministic local evidence from changes that require Luca's explicit production authorization. A green local build is not permission to connect to a server, change DNS, deploy, or submit anything to a search engine.
|
||||
|
||||
## Release inputs blocked on Luca
|
||||
|
||||
Do not create `config/site.production.json` and do not start production work until Luca supplies and approves all of the following:
|
||||
|
||||
- [ ] Canonical root HTTPS URL.
|
||||
- [ ] Immutable production build ID.
|
||||
- [ ] Target server and fixed release root/path.
|
||||
- [ ] Deployment credentials through an external secret mechanism.
|
||||
- [ ] Exact release activation approval after reviewing the signed digest and predecessor.
|
||||
- [ ] Search Console account/property access and separate approval for submission.
|
||||
|
||||
`config/site.production.json` remains ignored. When authorized, it may contain exactly `environment`, `canonicalUrl`, and `buildId`; it must never contain credentials or private server paths.
|
||||
|
||||
## Local, credentialless evidence
|
||||
|
||||
Run collection in a disposable, credentialless sandbox whose network access is limited to the configured package registry. The explicit network flag is an operator acknowledgement; it is intentionally not part of the normal verification command.
|
||||
|
||||
```text
|
||||
node scripts/supply-chain-check.mjs collect \
|
||||
--project . \
|
||||
--evidence /external/write-once/release-evidence/supply-chain \
|
||||
--triage APPROVED_COMMITTED_MEDIUM_TRIAGE.json \
|
||||
--allow-registry-network yes \
|
||||
--today YYYY-MM-DD
|
||||
```
|
||||
|
||||
The collector runs `npm ci --ignore-scripts`, production and full development audits, the pinned license checker, and the pinned CycloneDX generator. It strips the environment to a minimal credential-free set. The verifier fails when package metadata or the lockfile changes, evidence digests do not match, a license is not allowlisted, an SBOM is malformed, or a Critical/High finding exists. Every Medium finding must have one current, exact, committed triage entry; stale extras also fail.
|
||||
|
||||
- [ ] `npm run verify:supply-chain` passes against the selected immutable evidence directory.
|
||||
- [ ] `npm audit` evidence covers production dependencies and the complete development tree.
|
||||
- [ ] Critical findings: 0. High findings: 0.
|
||||
- [ ] Every Medium finding has a current committed owner, rationale, and review date.
|
||||
- [ ] CycloneDX SBOM is bound by the evidence manifest and packaged with the release.
|
||||
- [ ] License inventory contains no unknown, copyleft, or unapproved expression.
|
||||
- [ ] `npm run build:preview` passes.
|
||||
- [ ] Complete production build, unit/browser/accessibility/performance gates pass using the approved production config.
|
||||
- [ ] Privacy reconciliation and signed-release admission pass against external trust state.
|
||||
- [ ] Deterministic release package extracts and verifies in a temporary root.
|
||||
- [ ] Local Nginx config check, security headers, MIME, compression, and byte range checks pass.
|
||||
- [ ] No source maps, original/private assets, private product names, credentials, scanner payloads, or private server paths appear in the release closure.
|
||||
|
||||
Record for Luca's release checkpoint:
|
||||
|
||||
- Signed manifest digest:
|
||||
- Immutable release/build ID:
|
||||
- Package SHA-256 and byte size:
|
||||
- Approval/provenance root:
|
||||
- Supply-chain evidence digest:
|
||||
- Privacy evidence digest:
|
||||
- Manifest expiry:
|
||||
- Expected current predecessor digest and fence:
|
||||
- Planned canonical host:
|
||||
- Planned target release root (share privately, not in Git):
|
||||
- Exact prepare and activation commands:
|
||||
- Exact signed rollback target/command:
|
||||
- Local verification summary:
|
||||
|
||||
## Explicit remote authorization checkpoint
|
||||
|
||||
- [ ] Luca reviewed the values above and explicitly authorized this exact signed release.
|
||||
- [ ] The authorization has not expired and still names the current predecessor/fence.
|
||||
|
||||
Without both checks, stop. Do not connect to the server.
|
||||
|
||||
## Authorized staging and activation
|
||||
|
||||
- [ ] Transfer only the immutable verified package to the approved target.
|
||||
- [ ] Run server-side preparation; confirm it changes no `current` pointer.
|
||||
- [ ] Verify the readiness receipt digest, expiry, predecessor, and expected fence.
|
||||
- [ ] Activate with the fenced atomic pointer switch.
|
||||
- [ ] Preserve the previous immutable release and receipt for rollback.
|
||||
- [ ] Run the public smoke command with the approved canonical URL, alternate hosts, and signed manifest digest:
|
||||
|
||||
```text
|
||||
npm run smoke:production -- \
|
||||
--canonical-url https://APPROVED-HOST/ \
|
||||
--manifest-digest LOWERCASE_SHA256 \
|
||||
--alternate-url http://APPROVED-ALTERNATE/
|
||||
```
|
||||
|
||||
Add `--video-path /assets/APPROVED-HASHED.webm` when the deployed public closure contains video. A deployed response must expose the signed digest through `X-Portfolio-Manifest-Digest`. The smoke gate checks the canonical 200 response, one-hop 308 redirects, security/cache/compression/MIME/range behavior, canonical/OG/JSON-LD/robots/sitemap agreement, substantive server-rendered content without JavaScript, and absence of source maps/private product identifiers.
|
||||
|
||||
- [ ] Public smoke report digest equals the locally approved signed manifest digest.
|
||||
- [ ] If any activation or public smoke check fails, stop traffic changes and follow the signed rollback procedure.
|
||||
|
||||
## Post-launch SEO (separate external action)
|
||||
|
||||
- [ ] Confirm the canonical page and sitemap are publicly indexable and alternate hosts still redirect in one hop.
|
||||
- [ ] After Luca authorizes Search Console access, add/verify the exact canonical property.
|
||||
- [ ] Submit the exact canonical sitemap and request indexing for the canonical home page.
|
||||
- [ ] Record the property, sitemap URL, submission timestamp, operator, and result outside secrets.
|
||||
- [ ] Monitor branded-name indexing and crawl issues. Do not promise a ranking position; SEO gates establish correctness and discoverability, not ranking guarantees.
|
||||
|
||||
## Completion record
|
||||
|
||||
Phase 4 is remotely complete only when the deployed digest equals the locally approved signed digest, public smoke checks pass, the previous release remains recoverable, the Search Console action is recorded, and no private source, private identifier, or credential entered the public closure.
|
||||
55
docs/deployment/rollback-record-template.md
Normal file
55
docs/deployment/rollback-record-template.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Portfolio rollback record
|
||||
|
||||
Create one record per rollback attempt. Keep credentials, secret key material, private scanner evidence, and private server paths out of this document and Git. A rollback uses a newly signed rollback manifest; an old release manifest is never replayed.
|
||||
|
||||
## Authorization
|
||||
|
||||
- Incident/change reference:
|
||||
- Authorized by:
|
||||
- Authorization timestamp (UTC):
|
||||
- Operator:
|
||||
- Reason and observed failure:
|
||||
|
||||
## Fenced release state
|
||||
|
||||
- Canonical public URL:
|
||||
- Current signed manifest digest:
|
||||
- Current immutable release ID:
|
||||
- Current fence:
|
||||
- Approved rollback target artifact digest:
|
||||
- Newly signed rollback manifest digest:
|
||||
- Rollback manifest expiry:
|
||||
- Expected predecessor digest:
|
||||
- Expected fence:
|
||||
- External approval/ledger revision:
|
||||
|
||||
## Verification before pointer change
|
||||
|
||||
- [ ] Rollback target is an existing immutable release directory.
|
||||
- [ ] Target artifact hashes match the newly signed rollback manifest.
|
||||
- [ ] Signature, scope, issuer epoch, approval/ledger state, expiry, predecessor, and fence verify.
|
||||
- [ ] Readiness and internal smoke checks pass.
|
||||
- [ ] A concurrent activation has not changed the current digest or fence.
|
||||
|
||||
## Outcome
|
||||
|
||||
- Activation start timestamp (UTC):
|
||||
- Activation finish timestamp (UTC):
|
||||
- Result: succeeded / failed without pointer change / failed after pointer change
|
||||
- New current signed manifest digest:
|
||||
- New fence:
|
||||
- Atomic activation receipt digest:
|
||||
- Public smoke report digest:
|
||||
- Public smoke result:
|
||||
- Previous/current release still recoverable:
|
||||
- Additional containment or follow-up:
|
||||
|
||||
## Post-rollback checks
|
||||
|
||||
- [ ] Canonical HTTPS response is 200 and reports the expected manifest digest.
|
||||
- [ ] Alternate hosts redirect to the canonical URL in one permanent hop.
|
||||
- [ ] Security, cache, compression, MIME, and range headers pass.
|
||||
- [ ] Canonical/OG/JSON-LD/robots/sitemap agree.
|
||||
- [ ] JavaScript-disabled content remains substantive and accessible.
|
||||
- [ ] No source maps, private identifiers, or credentials are public.
|
||||
- [ ] Incident and Search Console/crawl implications are recorded if applicable.
|
||||
Reference in New Issue
Block a user