pqcota

module
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 30, 2026 License: Apache-2.0

README

pqcota

ci license go

v0.9.0 — Discovery · Inventory · Provisioning run end to end on Linux, and the demo applies generated artifacts to real nodes and rolls them back. Actions carry which snapshot state they came from, and the generator resolves that evidence against the real history and records it. Windows nodes run through the same discovery path (CNG and JCA) — generating their migration artifacts is not there yet → roadmap

A PQC migration management platform (OSS, Apache-2.0). It handles the PQC migration of legacy crypto runtimes (OpenSSL · Java JCE/JCA) across three stages: Discovery → Inventory → Provisioning.

Name — pqcota (pronounced P-cota) = PQC (post-quantum cryptography) + Orchestra (-ota). This software is a player in the orchestra, not the maestro. The maestro who decides what to migrate and when is the user wielding the tool; pqcota plays its own part (observe · normalize · generate) precisely.

platform structure diagram

demo video (2 min 55 s) — from observation through applying and rolling back the generated artifacts: ML-KEM goes 0 → 14 → 0 on a real node.

Read on → roadmap · contributing · compatibility policy


What it looks like

Observations are tied to nodes and apps, and the group actually negotiated on the wire is shown alongside.

──────── ① discovered assets (per node) ────────
  pay-app
    • JCA provider chain: SUN,SunRsaSign,…,BC   [EVIDENCE_STRENGTH_CONFIRMED]
        ↑ this BC appears nowhere in java.security (grep: 0 hits).
          The app registered it at runtime — invisible to static scans.
    • OpenSSL  libcrypto.so.3 3.5.5 (OpenSSL)   [EVIDENCE_STRENGTH_CONFIRMED]
  pay-db
    • OpenSSL  libcrypto.so.1.1 1.1.1f (OpenSSL) [EVIDENCE_STRENGTH_CONFIRMED]

──────── ② observed edges + quantum-resistance grade ────────
  🟢 web-gw  → pay-app   TLS  X25519MLKEM768 [fips-standard]
  🟢 web-gw  → pay-app   SSH  sntrup761x25519-sha512@openssh.com [experimental]
  🔴 web-gw  → pay-db    TLS  x25519
  🔴 web-gw  → pay-db    SSH  curve25519-sha256

  grade totals: 🟢 PQC 2 · 🔴 classical 2 · ⚪ unknown 0

The same observation is also rendered as a topology.

🔴 is not a verdict of "vulnerable" — it is the observation that a classical algorithm was negotiated. What to change, and when, is the user's call. Full expected output lives in demo/expected-output.

What it does — three stages

Stage What it does Output
① Discovery Observes which cryptography is in use on running systems — loaded libraries, JVM provider chains, algorithms negotiated in the handshake per-node observations (canonical CBOM)
② Inventory Ties each observation to the node and the apps it belongs to, and accumulates them — machine metadata, diffs between snapshots a central, append-only inventory
③ Provisioning Generates the PQC migration artifacts from a finalized plan — config fragments, apply/rollback Ansible playbooks (L1/L2/L3), rollback basis playbooks + before records

What it does not do — declaration (CMDB) reconciliation, review-and-sign-off governance and fleet orchestration are not in these repositories. The contracts (contracts/) hold their place, and no judgment engine is built — once the tool decides for you, the line that "🔴 is an observation, not a verdict" collapses. What each stage promises, and where it stops, is written in its own README and in the contracts.

Try it — demo

With just Docker, run the whole scope at once — access prep → discovery → inventory → provisioning (generate, apply, roll back), against nodes it stands up as containers. The demo builds all five repositories, so clone them side by side first (Build).

./demo/scripts/up.sh && ./demo/scripts/demo.sh   # tear down: ./demo/scripts/down.sh

Pick where to start.

  • The whole scope at once, with Docker → the two lines above. Setup, expected output, and how to point it at your own hosts are in demo/
  • One command at a time, minimal setup → examples/. Go is all you need — no Postgres, no target nodes
  • Working on the repo → CONTRIBUTING, and the stage READMEs above

Requirements

To build

  • Go 1.26.4+
  • buf (+protoc-gen-go, protoc-gen-go-grpc) — only when you change a proto (in pqcota-common). The generated gen/ is committed, so a plain build does not need it
  • JDK 11+ — optional, only to build the JVM attach sidecar; without it that step is skipped

To run

  • Multiple nodes — Ansible on the controller, SSH access to the targets
  • A single node — nothing to install; run the binary on that node directly (pqcota-netcap needs CAP_NET_RAW) → pqcota-discovery cmd

Build

pqcota is five repositories: this one (demo, examples, release bundles, contributing guide) and four modules. Clone them side by side, because go.mod reads the four from ../ until they are tagged:

git clone https://github.com/randyinthedev-hash/pqcota
git clone https://github.com/randyinthedev-hash/pqcota-common        # contracts, generated code, shared logic
git clone https://github.com/randyinthedev-hash/pqcota-inventory     # the inventory stage
git clone https://github.com/randyinthedev-hash/pqcota-discovery     # collectors, their commands, the reference playbook
git clone https://github.com/randyinthedev-hash/pqcota-provisioning  # the provisioning stage
cd pqcota

pqcota consists of one central controller node and the target nodes it reaches over Ansible/SSH. You build on the controller — both the CLIs you run there and the collectors you ship to the target nodes are produced here.

It builds straight from a clone. The Go code generated from the contracts is committed in pqcota-common, so you need no code-generation tooling of your own; consumers can use the contract types with go get alone. It is regenerated only when a proto changes (see the end of this section).

① The CLIs you run on the controller — ingest and query observations, generate playbooks.

D=github.com/randyinthedev-hash
go build -o bin/ $D/pqcota-common/cmd/... $D/pqcota-discovery/cmd/... $D/pqcota-inventory/cmd/... $D/pqcota-provisioning/cmd/...

② The collectors that go on the target nodes — built statically for the node's OS and arch. Which collector runs on which OS is in the command reference.

D=github.com/randyinthedev-hash/pqcota-discovery/cmd

# Linux nodes
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o dist/linux-amd64/ $D/pqcota-nodescan $D/pqcota-netcap $D/pqcota-jvmscan

# Windows nodes
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o dist/windows-amd64/ $D/pqcota-cngscan $D/pqcota-jvmscan

make build-jar                  # only if you have JVM nodes: attach sidecar → build/collector.jar

CGO_ENABLED=0 (static linking — distro/libc agnostic) is fixed; what you change is GOOS and GOARCH. For the accepted values see the Go documentation.

Linux nodes need kernel 3.2 or newer. That is the floor the Go toolchain sets, and this repo asks for nothing newer. CentOS 7 (3.10) and Debian 8 (3.16) are above it; RHEL 6 (2.6.32) is below. What individual features additionally require is in the supported scope.

Privileges and environment variables for running the collectors on a node → pqcota-discovery cmd.

If you changed a proto, regenerate the contract code. This is the procedure for someone working on the contracts, and it runs in pqcota-common. make tools installs the generator plugins (protoc-gen-go, -grpc) and make generate does the conversion. Put the resulting gen/ in the same commit as the proto you changed.

cd ../pqcota-common
make tools && make generate     # contracts/proto → gen/

make tools puts the plugins in $(go env GOPATH)/bin. If that directory isn't on your PATH, make generate fails with "plugin not found" — which looks like a failed install but really means it just isn't visible. Both targets call that case out, but adding it to your shell profile saves you from hitting it every time: export PATH="$PATH:$(go env GOPATH)/bin".

Contributing to the repos (tests, gates, contract changes) → CONTRIBUTING.

Stack

  • Go — every collector and CLI; CGO_ENABLED=0 static single binaries
  • Java — only the JVM attach sidecar (that observation is possible only from inside the JVM)
  • Protobuf/gRPC — the contracts that join the stages (contracts/)
  • Postgres — only when accumulating and querying many nodes over time; not used for single-node observation

Supported scope

Observation (Discovery)
What is observed Target Why
OpenSSL assets · communication edges Linux (amd64·arm64) depends on /proc, ELF, AF_PACKET
JVM provider chains Java 8+ · Linux (full) · Windows (with a JDK) the path that attaches without a JDK is Linux-only. On Windows the machine needs a JDK to reach runtime registrations; without one only java.security is read
Windows CNG providers and algorithms Windows (amd64·arm64) it asks bcrypt.dll directly for the registered providers — no WMI, no PowerShell
Inventory
What Target Why
the ingest and query CLIs anywhere — Linux, macOS, Windows they touch only files and a database, no OS primitives
the store Postgres (append-only) only when you want history and changes. For a single look, pqcota-discover-view needs no store
accepted input a collector's CollectionResult · CycloneDX 1.6/1.7 CBOM · a declaration written by a person what a machine saw and what a person wrote down never share a lane
Migration (Provisioning)

What is generated depends on the remediation kind in the plan.

Runtime Situation What is generated
OpenSSL 3.5+ (native PQC) a config fragment only — the legacy runtime is untouched
3.0–3.4 provider module staging + a config fragment referencing it. You supply the module
1.1.1 and older nothing — a fork replacement is required, so it is marked as a manual step
JCA (Java) JDK 24+ (native PQC) a java.security fragment only. It keeps a classical group alongside — released JDKs still do not negotiate the hybrid TLS group (measured up to JDK 25)
JDK 8+ (provider injection) provider JAR staging + a java.security registration fragment. Staging the JAR alone does not load it, so activation is a separate step
older (EOL) nothing — a JDK upgrade is required, so it is marked as a manual step

Application goes through Ansible playbooks. The generated playbook assumes POSIX paths and modules (ansible.builtin.copy, /opt/pqcota), so the target nodes are Linux — Ansible itself also drives Windows, but this output does not yet.

Windows (CNG) is observed (pqcota-cngscan). Generating its migration artifacts needs the substrate generalization first, so it stays on the roadmap.


Status · version

Every release attaches per-architecture static binaries and SHA256SUMS to the releases. The bundles are split by where they go: the node bundles (pqcota-linux-*, pqcota-windows-*) are carried onto the hosts you observe, and the controller bundle (pqcota-ctl-linux-*) sits on the one central machine. The latter carries every ingest, query, approval and generation CLI. Verify what you download with sha256sum -c SHA256SUMS; signed releases are on the roadmap. Per-version goals and results are in the release notes.

License

Directories

Path Synopsis
demo
topology/topogen command
Command topogen — demo/topology/topology.yaml → 데모 산출물 생성기.
Command topogen — demo/topology/topology.yaml → 데모 산출물 생성기.
workloads/pqc-echo command
Command pqc-echo — 데모용 PQC TLS 트래픽 생성기(🟢 엣지).
Command pqc-echo — 데모용 PQC TLS 트래픽 생성기(🟢 엣지).
test
crossstage
Package crossstage — 여러 단계의 패키지를 함께 import하는 통합 테스트만 두는 자리.
Package crossstage — 여러 단계의 패키지를 함께 import하는 통합 테스트만 두는 자리.
tools
checkcollectors command
Command checkcollectors — **노드에 올릴 collector 목록이 두 곳에서 어긋나지 않게** 막는다.
Command checkcollectors — **노드에 올릴 collector 목록이 두 곳에서 어긋나지 않게** 막는다.
checkdeps command
Command checkdeps — **단계 사이의 import 방향**을 막는다.
Command checkdeps — **단계 사이의 import 방향**을 막는다.
checkdocs command
Command checkdocs — 문서 게이트.
Command checkdocs — 문서 게이트.
checkgates command
Command checkgates — **규칙은 있는데 제품이 부르지 않는 게이트**를 막는다.
Command checkgates — **규칙은 있는데 제품이 부르지 않는 게이트**를 막는다.
checkprose command
Command checkprose — 문서와 도구 출력의 **한국어 문체**를 본다.
Command checkprose — 문서와 도구 출력의 **한국어 문체**를 본다.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL