Dennis Yu

BlitzMetrics Access Standard

How we create, store, use, rotate, and remove access to every site and system we run. Version 1.0.

Version 1.0
Effective 2026-08-02
Owner web@blitzmetrics.com
Review Quarterly
Status Active

1. Why this exists

On 2026-08-02 an audit of our own fleet credential cache — 159 sites — found this:

Measure Found What it means
Admin passwords stored in plaintext in one file on one laptop 159 of 159 One lost laptop is a total fleet compromise
Distinct passwords across 159 sites 88 ~71 sites share a password with another site
Shortest password in the cache 7 chars Trivially brute-forced
Sites reachable with exactly two shared logins 153 of 159 Two secrets open 96% of what we run
Accounts belonging to an identifiable person 5 of 159 Audit logs cannot name anyone
Application passwords the April 11 rollout actually created 1 of 112 111 silent failures; nobody retried for four months
Age of the cache everyone works from 117 days Stale enough to lock out its own owner

None of that is a person’s mistake. It is the absence of a written standard. Every one of those numbers is the predictable output of a process that was never specified, so this document specifies it. The companion audit script measures every number in the table above and fails the build when any of them regress.

The load-bearing sentence

If a control in this standard is not measured by the audit script, it is a preference, not a standard. Section 13 is therefore the most important section in this document, and every requirement below carries a control ID that maps to a check in that script.

2. Scope and definitions

This standard applies to every system BlitzMetrics or its affiliated brands operate or administer, on any platform, including but not limited to: WordPress sites on the EC2 fleet, WordPress sites on WP Engine, BlitzAdmin, GitLab, GitHub, AWS, Cloudflare, DNS registrars, Google Workspace, and any third-party SaaS that holds client data.

Term Definition as used here
Property One administrable system with its own login boundary. One WordPress site is one property.
Human credential A credential a person authenticates with. Always tied to one named individual.
Machine credential A credential a program authenticates with: application password, API token, deploy key, SFTP account.
Vault The designated secret store. For BlitzMetrics this is AWS Secrets Manager in us-east-1. Nothing else is a vault — not a spreadsheet, not a password-protected file, not a Basecamp message, not a mailbox.
Admin of record The one named person accountable for access on a property. Not a team address.
Break-glass Pre-provisioned emergency access, used only when normal access fails, and always noisy.

3. The six principles

  1. Identity is personal. Every human action on every system traces to one human being.
  2. The vault is the only copy. A secret exists in exactly one place that is designed to hold secrets.
  3. Secrets are never transmitted. We send links to secrets, never secrets.
  4. Removal is one action. Offboarding one person must not require touching 159 systems.
  5. Emergency access is loud. Break-glass is always available and always alarms.
  6. Compliance is measured, not asserted. Anything not checked by a scheduled job will drift.

4. Identity: named accounts only

AC-1 Every human must authenticate with an account bearing their own name. Shared human logins are prohibited on all properties.

AC-2 The usernames admin, administrator, root, wpadmin, access, webmaster, test, and user must not exist as interactive accounts on any property.

AC-3 Role-address accounts (access@blitzmetrics.com, operations@blitzmetrics.com, web@blitzmetrics.com) must not be used as login identities on any property. They remain valid as mailing lists and as notification destinations.

AC-4 Access must be granted at the lowest WordPress role that permits the work. Editor is the default for content contributors. Administrator requires the admin of record’s approval, recorded in the access map.

4.1 Username convention

firstname.lastname, lowercase, ASCII, matching the person’s Google Workspace identity. Contractors take the same form with a .ext suffix: felix.fagbuyi.ext. The suffix makes contractor access greppable at audit time, which is exactly the population most likely to be missed at offboarding.

4.2 Account email

Each account’s email must be that person’s own company address. An account whose email is a shared inbox is a shared account wearing a name, because anyone on that list can trigger a password reset and take it over. This is control AC-5.

5. Secrets: the vault is the only copy

SE-1 Every credential must live in AWS Secrets Manager. The plaintext fleet credential cache must be deleted, not archived.

SE-2 Secrets must not appear in email bodies, chat messages, Basecamp posts, Google Docs, spreadsheets, tickets, commit messages, CI logs, screenshots, or support requests.

SE-3 Generated secrets must be at least 32 characters, drawn from a cryptographically secure RNG. Minimum for any human-set password is 16 characters, and it must be generated, not chosen.

SE-4 No secret may be reused across two properties. Uniqueness is verified by comparing salted hashes; the audit never handles plaintext.

SE-5 Any secret that has ever been transmitted in cleartext is burned and must be rotated, regardless of whether interception is suspected.

SE-6 The output of a rotation must not be a document. If a rotation produces a spreadsheet, the rotation was performed incorrectly and must be redone.

5.1 Rotation cadence

Credential class Max age Trigger for immediate rotation
WordPress administrator 180 days Offboarding, suspected compromise, any cleartext exposure
Application password / API token 365 days Offboarding of the owning human, integration retirement
Database, SFTP, infrastructure 90 days Any readable backup file, any host-level compromise
Break-glass 90 days Every use, without exception

5.2 Historical exposure

Provisioning mail sent to operations@blitzmetrics.com contains live working credentials for every site provisioned to date. Those messages are a credential store that we do not control the readership of. Under SE-5 the credentials are burned; under SE-2 the messages must be purged after rotation completes, not before — purging first destroys the only record of which properties need rotating.

6. Provisioning: what the generator must do

Everything above fails within a week if the machine that creates sites keeps creating them the old way. The site generator is the highest-leverage control surface we own: it touches every property at birth.

PR-1 The generator must not create a user named admin or any name in AC-2.

PR-2 The generator must create the initial administrator with a per-site name derived from the property, never a fleet-wide constant.

PR-3 The initial password must come from a CSPRNG at ≥32 characters and must be written directly to Secrets Manager. It must not be returned to the caller, logged, or printed.

PR-4 The provisioning notification email must contain the property URL, the login URL, the username, and a link to the vault entry. It must not contain the password.

PR-5 Provisioning must fail closed. If the vault write fails, the site is not marked provisioned and the run exits non-zero.

PR-6 The generator must register the new property in the access map in the same transaction that creates it.

The accompanying patch document implements PR-1 through PR-6 hunk by hunk with before/after and a verification checklist.

7. Multi-factor authentication

MF-1 Every account with administrator privileges on any property must have TOTP two-factor enabled. Wordfence Login Security provides this at no cost on the WordPress fleet.

MF-2 SMS must not be used as a second factor.

MF-3 Recovery codes must be stored in the vault under the owning human’s entry, never printed or emailed.

MF-4 An administrator account without 2FA must be demoted to Editor automatically after 14 days.

MF-4 is deliberately harsh. A deadline with no consequence is a wish. Demotion is reversible in thirty seconds by the person who caused it, which makes it the cheapest possible enforcement action.

8. Machine credentials and application passwords

MC-1 Automation must authenticate with an application password or API token, never with a human’s interactive password.

MC-2 Every machine credential must name its purpose and its owning human in the vault entry.

MC-3 A bulk credential rollout must verify each result and report per-property success or failure. A rollout is complete at 100%, not at “the code ran.”

MC-4 Any rollout reporting fewer than 100% successes must exit non-zero and must not be marked done.

Root cause, April 11

The application-password rollout created 1 of 112 credentials and reported success, because the code path that mattered was never checked — only the code path that ran. For four months we believed we had application passwords. MC-3 and MC-4 exist so that a rollout cannot lie again: verification is a separate read-back against the property, not an inference from the absence of an exception.

9. The access map

The single artifact that would have prevented the 2026-08-02 lockout. One row per property, machine-readable, generated from the vault rather than maintained by hand.

Field Example Why
domain blitzmetrics.com Key
platform wpengine Two platforms, two conventions. This field is the fix.
login_url https://my.wpengine.com → WP Admin SSO Removes guessing, which is what produced the lockout
admin_of_record dennis.yu One accountable human
vault_path blitz/prod/blitzmetrics.com/wp-admin/dennis.yu Where the secret is, never what it is
break_glass WP Engine portal SSO Documented before it is needed
waf wordfence, egress allowlisted Explains lockout behaviour in advance
mfa totp Auditable
last_rotated 2026-08-02 Drives §5.1

AM-1 The access map must be generated from the vault and the fleet, never hand-maintained.

AM-2 The access map must not contain any secret value.

AM-3 A property missing from the access map must be treated as an unmanaged property and remediated within 48 hours.

10. Break-glass access

Every property needs a documented way in that does not depend on the thing that just broke. On WP Engine that is the portal’s WP Admin SSO button, which signs in without submitting a password. On the EC2 fleet it is SSM Session Manager plus WP-CLI, which needs no WordPress password at all.

BG-1 Every property must have a documented break-glass path in the access map.

BG-2 Break-glass use must emit an alert to web@blitzmetrics.com at the time of use.

BG-3 Any credential used for break-glass must be rotated within 24 hours of use.

BG-4 Break-glass must be tested quarterly on a random sample of five properties. An untested emergency path is not an emergency path.

11. Offboarding

OB-1 Departure must disable all access within 24 hours.

OB-2 Offboarding must be one action against identity, not a per-property sweep.

OB-3 Removal must be verified by read-back. A tool that reports success without verifying has not removed anyone.

OB-4 Every machine credential owned by the departing human must be rotated or retired.

OB-5 A quarterly audit must compare fleet accounts against the current roster and flag every orphan.

OB-3 is not theoretical. BlitzAdmin’s remove-user function silently fails, which means no departure has ever actually been processed. Until that is fixed, offboarding is a manual per-property task and must be tracked as one.

12. WAF and lockout policy

WF-1 Wordfence’s “immediately lock out invalid usernames” must remain enabled fleet-wide. It is load-bearing while the entry vector of the July incident is unidentified.

WF-2 Our egress IP ranges must be allowlisted on every property so that correct behaviour cannot lock out the team.

WF-3 Disabling a WAF for troubleshooting must be re-enabled and verified within one hour, and the verification must be recorded.

WF-4 User enumeration and XML-RPC must remain disabled fleet-wide.

13. Enforcement: the audit runs, or the standard is fiction

Every control above maps to an automated check in fleet_access_audit.py, which ships alongside this standard.

  • It runs on a schedule, not on good intentions. Daily at 09:00 UTC.
  • It exits non-zero on any violation, so it can gate CI and page on failure.
  • It never prints, logs, or returns a secret value. Reuse is detected by comparing salted SHA-256 digests; the salt is per-run and discarded.
  • It emits JSON for dashboards and plain text for humans.
  • Its thresholds live in one config block at the top of the file, so tightening the standard is a one-line change.
# daily, gates on any violation
0 9 * * *  fleet_access_audit.py --source vault --format text --fail-on any

# CI gate: generator changes must not regress provisioning controls
fleet_access_audit.py --source fixtures/ci --checks PR --fail-on any

EN-1 The audit must run at least daily and its result must be visible to the whole team.

EN-2 A failing audit must produce a ticket with an owner within one business day.

EN-3 The audit must not emit secret material in any output mode. This is verified by the script’s own self-test.

EN-4 Adding a control to this standard must include adding its check to the audit in the same change.

14. Exceptions

Some third-party systems cannot meet these controls. An exception is legitimate; an undocumented exception is not.

  • Filed in writing to web@blitzmetrics.com with the control ID, the reason, the compensating control, and an expiry date.
  • Maximum term 90 days. Renewal requires a fresh request, not silence.
  • Recorded in the access map so the audit reports it as an exception rather than a failure.
  • An expired exception becomes a failure automatically. EX-1

15. Adoption schedule

Due Action Owner Controls
Aug 3 Confirm Wordfence fully re-enabled on blitzmetrics.com; allowlist egress IPs web@ WF-2, WF-3
Aug 8 Apply the site generator patch Josh Healey PR-1 – PR-6
Aug 8 Audit for accounts of departed people, starting with felix.fagbuyi Ops OB-5
Aug 12 Rotate all 159 admin passwords into the vault; delete the plaintext cache web@ SE-1, SE-3, SE-4
Aug 12 Purge provisioning mail history in operations@ (after rotation) Ops SE-2, SE-5
Aug 15 Re-run the application-password rollout to 159 of 159 web@ MC-3, MC-4
Aug 22 Named accounts for every team member; delete admin and access@ everywhere web@, Ops AC-1 – AC-5
Aug 22 TOTP 2FA on every administrator web@ MF-1 – MF-4
Aug 22 Fix BlitzAdmin remove-user; verify by read-back web@ OB-2, OB-3
Aug 22 Access map generated from the vault, published internally Leo AM-1 – AM-3
Aug 29 Audit scheduled daily, result visible to the team web@ EN-1 – EN-4

Appendix A — Control reference

Baseline column is the 2026-08-02 measurement. Every control is machine-checked.

ID Control Baseline Target
AC-1 No shared human logins 153 shared 0
AC-2 No forbidden usernames 154 present 0
AC-3 No role-address logins 82 present 0
AC-4 Least-privilege roles unmeasured 100% justified
AC-5 Account email is a personal address 154 shared 0
SE-1 All secrets in the vault 0 of 159 159 of 159
SE-2 No secrets in cleartext channels 159 exposed 0
SE-3 Length ≥ 32 chars, CSPRNG min 7 159 of 159
SE-4 No password reuse 71 reused 0
SE-5 Exposed secrets rotated 0 rotated 159 of 159
SE-6 Rotation output is not a document cache exists no cache
PR-1..6 Generator provisions safely fails all passes all
MF-1 2FA on every administrator 0 of 159 159 of 159
MC-3/4 App-password rollout verified 1 of 112 159 of 159
AM-1..3 Access map complete and generated absent 159 of 159
BG-1 Break-glass documented absent 159 of 159
OB-3 Removal verified by read-back silently fails verified
OB-5 No orphaned accounts ≥1 known 0
WF-1/2 WAF on, egress allowlisted not allowlisted 159 of 159
EN-1 Audit runs daily never daily

Appendix B — Secret naming convention

blitz/{env}/{domain}/{system}/{principal}

blitz/prod/blitzmetrics.com/wp-admin/dennis.yu
blitz/prod/philmershon.com/wp-app-password/content-factory-bot
blitz/prod/philmershon.com/db/wp_user
blitz/prod/fleet/sftp/blitzsftp
blitz/prod/blitzmetrics.com/break-glass/wpengine-sso

Required tags on every secret: owner (a named human), purpose, rotates (ISO date), platform. The audit reads tags only — it never calls GetSecretValue, so the auditor role needs no read access to secret material at all. That is deliberate: the thing that checks the locks should not be able to open them.

Appendix C — Revision history

Version Date Author Change
1.0 2026-08-02 Dennis Yu Initial standard, written in response to the fleet credential audit of the same date.
BlitzMetrics Access Standard v1.0 · Effective 2026-08-02 · Owner web@blitzmetrics.com
Published because we build in public. Enforced by fleet_access_audit.py, which ships with this document.
Scroll to Top