aere-quantum/control-plane/README.md

8.5 KiB

AERE Quantum Security Control Plane

From "we found the problem" to "this is exactly what changes, in which order, what we can migrate automatically, how the rest is fixed on your server, and how that is proven". Node.js 24 with OpenSSL 3.5, no dependencies. It composes tools that exist beside it: the cryptographic inventory (crypto-inventory/), the TLS readiness scanner (readiness/), the PQ Gateway, KMS and PKI (pq-gateway/, pq-kms/, pq-pki/), the verification layer (verify-layer/) and the AIP-23 envelope builder (proof-kinds/).

Code comments, internal names, test names and file names are in Romanian; command lines, JSON fields, messages and this documentation are in English (the data format was translated on 2026-09-29, version 2 of each format).

1. Plan: control-plane.mjs, plan-migrare.mjs

node control-plane.mjs --code <dir> [--scan scan.json] [--json]

Runs the inventory on a code directory and, optionally, takes a scan of a hostname (the JSON of the readiness scanner), and writes a prioritized plan. Every quantum-vulnerable asset becomes an action:

{ ref, asset, problem, current, target, method: "auto-aere" | "manual" | "blocked", product: "gateway" | "kms" | "pki" | null,
  how, cn?, primitive?, urgency: "CRITICAL" | "HIGH" | "MEDIUM" | "LOW", blocker, location, source }

Harvest-now-decrypt-later applies to key exchange and encrypted data, not to signatures (a signature is public; it is not harvested), so a classical key exchange on a public endpoint is the only CRITICAL case, and a classical signature is a future forgery risk. The plan is honest about blockers: a public WebPKI certificate cannot be reissued post-quantum today (no public CA issues one), so it is blocked with the reason written, not auto. The scan findings are classified by a closed table on the ids the scanner actually emits (the test derives them from the scanner's source and fails on an id without a classification).

2. Execution: executa-migrare.mjs

node executa-migrare.mjs --plan plan.json --out <dir> [--consent ref1,ref2|all] [--execute]
  [--gw-cert c.pem --gw-key k.pem --gw-upstream http://h:p [--gw-mode hybrid-only|hybrid-preferred] [--gw-listen 127.0.0.1:8443] [--gw-keep]]
  [--kms-url http://127.0.0.1:8420 --kms-token-file f] [--pki-dir ca --pki-ca issuing --pki-roots ca/root.crt]   (AERE_PKI_PASSPHRASE)

Nothing runs without consent per action, and the default is a dry run (--execute starts the work). Each auto-aere action is done through the matching product and judged by a measurement afterwards, not by an exit code:

product what it does the proof afterwards rollback
gateway starts the PQ Gateway in front of the service and writes its configuration for your service manager a client offering only X25519MLKEM768 completes a TLS 1.3 handshake; a second, classical-only client says whether classical clients are still accepted (hybrid-preferred) or refused (hybrid-only) the gateway is stopped
kms creates the hybrid key (X25519 + ML-KEM-768) or rotates it if it exists; the name comes only from the ref (mig-...) latest_version and the fingerprint read back from the KMS older versions stay decryptable; nothing is deleted
pki issues an ML-DSA-65 certificate from your PQ CA for a cn checked to be a host name (no wildcard) verifyChain up to the root files written in part are removed

Every action is a record in a hash chain sha256(seq | prev | record) in execution.json (records[], each { seq, prev, record, hash }; verdicts OK, FAILED, DRY-RUN, SKIPPED-no-consent). The console and anyone else can check it again; a changed or removed record is caught. It does not change your DNS, ports or firewall (moving traffic to the gateway stays with you), does not issue public WebPKI certificates, and does not delete keys.

3. Remediation: remediere.mjs

node remediere.mjs recipe --plan plan.json --profile nginx|apache|haproxy|node|go [--json]
node remediere.mjs verify --plan plan.json --scan after.json [--applied-at 2026-09-28T10:00:00Z] [--out dir]

The actions no AERE product can do (the manual ones: TLS 1.3 missing, TLS 1.2 accepted, the hybrid group not preferred, HSTS, the certificate) live in your server's configuration, which we do not touch. The recipe gives, for your server software, the exact fragment, where it goes, the measurable precondition (the OpenSSL or Go version and the command that shows it) and the configuration check to run before reloading; fragments are generated from one structured form (groups, minimum version, HSTS), and certificate problems get operational steps. The proof: after you apply it, a new scan judges each action RESOLVED (the defect is gone AND the required property is measured), UNRESOLVED, or UNMEASURED (the scan failed, is of another host, or is not from after the time you applied the change: --applied-at, otherwise the plan's generatedAt; with neither, everything is UNMEASURED). A domain in the plan that is not a host name gets no recipe, so no command to copy is built with it. What is measured by us is written on every recipe (measured): the node profile end to end; for nginx, Apache and HAProxy only that OpenSSL 3.5 accepts the group list (the directive comes from their documentation); Go from the 1.24 release notes, not measured.

4. Compliance report: raport-conformitate.mjs

import { complianceReport, verifyReport } from './raport-conformitate.mjs';
const { report, envelope, evidenceHash } = complianceReport({ plan, organization, date: '2026-09-28', risk: 'high', cnsa: true, cnsaCategories });

For every action: which regimes apply (NIST IR 8547 initial public draft; the EU coordinated roadmap of 2025-06-23; optionally CNSA 2.0), each deadline and the days left on the report's date, and whether the asset is exposed today (key exchange: yes; signature: no; a key of unknown use: not known). Each regime carries its source, the source's date and the date it was read; CNSA 2.0 is read from secondary sources, and says so. An action the report cannot classify is counted, and then the report cannot say that there is no vulnerable algorithm. The report is bound to an AIP-23 compliance envelope whose evidenceHash is the digest of the canonical report. It is not a certification and not a legal opinion, and it covers only what was inventoried and scanned.

5. Console: consola.mjs, consola-web/

node consola.mjs --bundle bundle.json [--plan plan.json] [--execution execution.json] [--json]

One state of a deployment: the audit chain of the verification layer recomputed from its entries, the integrity of every AIP-23 envelope recomputed, the plan folded in, and the execution chain checked again. The console does not trust what a bundle says about itself (chainOk: true over a modified log is reported as a suspect self-report). consola-web/index.html renders that output; it computes nothing in the browser, and a bundle pasted there directly is shown as UNCHECKED.

Tests

bash probele-b1.sh                  # all seven below, each VERDE / ROSU / STRICAT (in the development repository only)
node proba-plan-migrare.mjs         # 30: real certificates and keys (openssl) through the real inventory; the scanner's ids derived from its source
node proba-control-plane.mjs        # 9: the command line end to end, an all-post-quantum tree gives an empty plan
node proba-executa-migrare.mjs      # 30: on real products started locally (KMS, PQ CA, gateway), with the attacks of the review
node proba-remediere.mjs            # 33: real TLS servers on 127.0.0.1 scanned by the readiness scanner, before and after the recipe
node control-negativ-remediere.mjs  # 7 of 7 guards broken in a copy, each turns the test red
node proba-raport-conformitate.mjs  # 27: on the real outputs of the inventory and the scanner; the envelope through the AIP-23 verifier
node control-negativ-raport-conformitate.mjs   # 3 plantings, each turns the named check red
node proba-consola.mjs              # 8: a good bundle and four negative controls
node proba-consola-web.mjs          # 26: the viewer in a real Chromium, phone (390 px) and desktop (1440 px); needs playwright-core and a Chromium
node control-negativ-consola-web.mjs   # 8 defects planted in a copy of the page, each turns its named check red

The report test runs the envelope through the AIP-23 reference verifier (AERE_VERIFY_PROOF=<verify-proof.mjs from aere-node>). No third party has reviewed any of this.