OpenPGP Production Incident Recovery Checklist

Scope and confirmed behavior

This checklist addresses a production failure in which the browser OpenPGP loader selects a bundle but cannot fetch it:

  • secure origins select /assets/openpgp.min.js (v6);

  • public HTTP origins select /assets/openpgp.v5.11.3.min.js (v5);

  • the loader is itself emitted with a fingerprinted URL;

  • the bundle URLs are currently raw, runtime JavaScript dependencies.

The HTTP v5 selection is correct. A failure mentioning /assets/openpgp.v5.11.3.min.js is an asset-delivery failure, not a fallback selection failure.

Target environment

  • Public HTTP origin: http://zenmemes.com/

  • Public HTTPS origin: https://zenmemes.com/

  • Required HTTP bundle: http://zenmemes.com/assets/openpgp.v5.11.3.min.js

  • Required HTTPS bundle: https://zenmemes.com/assets/openpgp.min.js

  • Hosting constraint: only the application files and public/.htaccess are controllable; do not require Apache virtual-host access for recovery.

Observed production state

The public HTTP origin does not serve raw assets, not merely the v5 bundle. Fingerprinted requests can be served by the PHP FrontController, but raw requests fall through to a 404. The immediate recovery is the safe raw public asset fallback in the code-fix inventory; restoring a file alone cannot fix this host behavior.

./v3 build-static alone is not a recovery action for this incident: its current output does not contain the raw v5 filename requested by the loader.

Do not clear browser storage, regenerate user keys, force HTTPS, or enable HSTS as incident workarounds. Those actions can strand existing browser-held identities and do not restore the missing bundle.

1. Contain and identify the live asset origin

- [ ] Pause any deployment that publishes a static artifact directory as the
  public asset origin until it has passed the checks below.
- [ ] Record the effective production `DocumentRoot`, CDN/origin mapping, and
  the location from which `/assets/*` is served.
- [ ] Confirm that the deployed application checkout includes both tracked
  source files:

  ```text
  public/assets/openpgp.min.js
  public/assets/openpgp.v5.11.3.min.js
  ```

- [x] The deployed checkout contains `openpgp.v5.11.3.min.js` with the
  expected SHA-256:
  `89ae4b15e830e08096a125003218bdc7b5c39a4075437dc8d6d4a4e3bdc9a550`.

- [ ] Confirm the web server can read both files.
- [ ] Confirm client-visible behavior only: the checked-in `public/.htaccess`
  permits existing `/assets/*` files to bypass PHP. If a requested asset still
  reaches PHP, the application must supply a safe public-asset fallback rather
  than relying on unavailable host configuration access.
- [ ] From an external machine, capture status, headers, and final URL for
  each request. Use `GET`, rather than relying only on `HEAD`:

  ```bash
  curl -sS -D - -o /dev/null http://zenmemes.com/assets/openpgp.v5.11.3.min.js
  curl -sS -D - -o /dev/null https://zenmemes.com/assets/openpgp.min.js
  ```

- [ ] Treat any redirect, HTML/PHP error page, 404, 403, 405, or non-JavaScript
  content type as a failed asset check.
- [x] The required HTTP v5 asset check failed with `404`; the required HTTPS
  v6 asset check returned JavaScript.
- [ ] Compare the response body checksum with the deployed source file. A
  `200` response containing an error document is not a successful recovery.
- [ ] Inspect CDN/reverse-proxy rules for an asset allowlist, deployment-copy
  exclusion, or a static-release origin that omits the raw bundle files.

2. Restore service safely

3. Verify user-visible recovery

4. Fix the release and asset contract

5. Fix the identity prompt failure path

6. Add regression coverage

7. Update operations and observability

Completion criteria