Skip to content

Rolling it out across a company

One engineer can run pqdoctor scan. A company needs the loop around it: find every endpoint, scan inside and out, route each finding to the team that owns it, track it over time, and gate CI so it doesn’t regress.

Terminal window
pqdoctor inventory ct:example.com zone:/etc/bind/db.example.com kubernetes:ingress.json terraform:tf.json --out hosts.txt
Source From
ct:DOMAIN certificate transparency (crt.sh, falling back to Cert Spotter): every name a public certificate covered
zone:FILE a BIND zone file
cloudflare the Cloudflare API (CLOUDFLARE_API_TOKEN, read-only)
kubernetes:FILE kubectl get ingress -A -o json — the namespace becomes an owner hint
terraform:FILE terraform show -json
aws:FILE aws elbv2 describe-load-balancers / describe-listeners

Infrastructure sources also give findings before any scan: an AWS listener on a security policy without post-quantum key exchange is reported straight from the Terraform or AWS JSON, with the policy to switch to.

2. Owners and tickets — route each finding

Section titled “2. Owners and tickets — route each finding”

owners.toml maps host patterns to teams, first match wins:

[[team]]
name = "payments"
hosts = ["pay.example.com", "*.pay.example.com"]
github = "acme/payments"
labels = ["team:payments"]
[default]
team = "security"
github = "acme/pq-tracking"
Terminal window
pqdoctor tickets results.jsonl --owners owners.toml # what it would file, by team
GITHUB_TOKEN=… pqdoctor tickets results.jsonl --owners owners.toml --apply
pqdoctor tickets results.jsonl --owners owners.toml --jira-csv pq.csv

One issue per host, with the verdict, the class, the library or provider, and the next step with its command. Re-runs update the same issue (a hidden marker finds it), and when the host turns post-quantum the issue gets a comment and closes itself.

agent.toml
name = "dc-east"
state_dir = "/var/lib/pqdoctor"
inventory = ["zone:/etc/bind/db.corp.example", "kubernetes:/var/lib/pqdoctor/ingress.json"]
hosts = ["internal-api.corp.example:8443"]
owners = "/etc/pqdoctor/owners.toml"
interval_minutes = 1440
[fleet]
url = "https://fleet.example.com" # token from PQDOCTOR_FLEET_TOKEN
[tickets]
apply = true # GITHUB_TOKEN from the environment
Terminal window
pqdoctor agent agent.toml --once # one cycle (cron, a Kubernetes CronJob, a scheduled task)
pqdoctor agent agent.toml # loops every interval_minutes

Each cycle: inventory → scan with the fingerprint → what changed since last time → push to the fleet (with the leverage plan and infrastructure findings) → tickets.

Every run lands in one place: the fleet, a Worker and a D1 database on your own Cloudflare account. It shows the share post-quantum overall and by team, the trend, regressions and fixes, every host’s history, the latest plan, infrastructure findings and certificate readiness, with CSV exports. From the next release, Flarebox deploys it as a reviewed change and shows it in its Fleet page; its Tokens tab makes the ingest tokens below (stored hashed, optionally limited to a team).

Terminal window
pqdoctor push results.jsonl --to https://fleet.example.com --token pqf_… --owners owners.toml --with-plan

Scan with --auth (the agent and the Action do by default) and the fleet also tracks, per host, whether it serves an ML-DSA certificate and whether its chain would still fit the server’s first flight once it does. See Certificates: the second axis. pqdoctor <host> shows the same projection for one server, with three cases (an ML-DSA leaf under a classical CA, the whole chain on ML-DSA-44, on ML-DSA-65) and what to trim first: a root sent in the chain, a long cross-signed path.

- uses: Kalyan-Deva/pqdoctor-action@v1
with:
hosts-file: pq/hosts.txt
fail-on: regression # only when something that was post-quantum stops being
upload-sarif: true # findings in GitHub code scanning
fleet-url: https://fleet.example.com
fleet-token: ${{ secrets.PQDOCTOR_FLEET_TOKEN }}

It installs the prebuilt pqdoctor and verifies it against the release’s checksums before running it, scans gently, writes SARIF and a job summary, compares with the last run (kept in the Actions cache), and can comment on pull requests.