Skip to content

doc, bench: operator guide, release note and handshake benchmark for hybrid v2 transport - #240

Merged
djh58 merged 6 commits into
feature/pq-transportfrom
djh58/pq184-8-docs
Oct 22, 2026
Merged

djh58 merged 6 commits into
feature/pq-transportfrom
djh58/pq184-8-docs

Conversation

@djh58

@djh58 djh58 commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

Summary

Part 8 of #184 (#215): the operator-facing docs for hybrid post-quantum v2 transport, checks that keep them in step with the code, and a handshake benchmark. Stacked on #239 (part 7).

  • doc/user/network/pq-transport.md, the operator guide, listed next to Tor and I2P. It covers:
    • a three-step, copy-paste regtest quick start with a cleanup step;
    • a two-line BIP324 primer;
    • switching the feature on and off, including numeric booleans and the startup warnings;
    • Verify: the startup line, getpqtransportinfo, getpeerinfo transport_pq/transport_pq_status, -netinfo 1's 2p and getnetworkinfo connections_pq;
    • fallback and load shedding in plain words;
    • what it protects and what it doesn't;
    • where the log is;
    • a troubleshooting table keyed by log text and counter name;
    • upgrading and rolling back, with -mlkemportable as the escape hatch.
  • doc/release-notes-240.md: what the feature protects and doesn't, the default (off in this release), and how to switch it on and off.
    • The spec, the guide and the release note link to each other.
    • bitcoin-core-differences.md, rpc-delta-reference.md and fuzzing.md are updated; the fuzzing guide now lists the hybrid v2 transport fuzz targets.
  • test/functional/feature_pq_quickstart.py runs the guide's quick start as written, swapping in only the framework's data directory and ports.
    • Each step must print what the guide shows.
    • It also checks the guide's qbit.conf line, the command-line override and the startup warning texts.
    • It always cleans up.
  • test/lint/lint-pq-doc-tokens.py checks every interface name the PQ docs mark with <!--pq:KIND-->: options, RPC methods and fields, values and log strings.
    • Options are checked against -help/-help-debug output, everything else against the sources.
    • A malformed marker fails, with its file and line.
    • Every run tests the check itself, with a renamed option, a renamed RPC field and a changed log string.
    • It runs in the lint job, in the public-docs-lint job for docs-only PRs, and against real binaries in feature_pq_quickstart.py.
  • src/bench/v2_transport.cpp: one complete in-memory V2Transport handshake per iteration, legacy v2 and hybrid, each with native ML-KEM code and with portable code forced. Each benchmark is named with its bytes on the wire.

H1's flood tooling is not in this PR; #215 stays open for it.

Measured on a KVM guest (AMD Ryzen 9 9955HX), with both sides of a handshake on one thread:

Handshake Bytes Native (x86_64-avx2) Portable
legacy v2 200 110.7 µs 111.6 µs
hybrid 3,384 192.0 µs 290.4 µs

Legacy v2 costs the same with either backend selected, so no capability check is repeated per handshake.

Testing

  • Ran focused unit or functional tests for the changed area:
    • feature_pq_quickstart.py passes with no flag, --v2transport and --v2pqtransport, and 12 of 12 repeated runs.
    • rpc_pqtransport.py and p2p_v2_pq.py pass.
    • bench_qbit -filter='V2_TRANSPORT_HANDSHAKE_.*' ran timed and with -sanity-check -min-time=0, under a 4 GiB memory cap: max RSS 16 MB.
    • Rollback claims were checked with the v1.0.0 release binary.
  • Ran lint or formatting checks relevant to this change:
    • test/lint/lint-pq-doc-tokens.py, with and without binaries, including its self-test. Injected doc typos (a renamed field, value and option, and a malformed marker) are each reported with file and line.
    • check-doc.py, every test/lint/lint-*.py, ruff with the lint runner's rules, and mlc.
    • clang-tidy 21 on the benchmark.
    • actionlint 1.7.12 and the ci/checks suites (123 tests).
  • Not run. Reason: GitHub CI, macOS, Windows and other architectures. The macOS and Windows log paths and the qbit-qt menu paths in the guide come from the sources.

Target Branch

Risk / Review Notes

  • Consensus, script, crypto, wallet, P2P, release, CI, or security-sensitive behavior changed.

Notes:

  • No node behavior changes. The only C++ is the benchmark.
  • CI: public-docs-lint also runs the doc-token check.
  • Please review the guide's security claims ("What It Protects and What It Doesn't") against the spec's security notes, and the troubleshooting table. Both are judgment.
  • The default is off in this stack. R1 (release: hybrid v2 transport release gate and default-on (#184 part 9) #222) rewords the release note, the guide and bitcoin-core-differences.md when it flips.

Part of #215 · Part of #184

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@djh58

djh58 commented Oct 21, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-22T19:35:23.947048Z eb5b7dc Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 7d0d2de90a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@djh58
djh58 force-pushed the djh58/pq184-8-docs branch from 7d0d2de to 61f4420 Compare October 21, 2026 21:11
@djh58

djh58 commented Oct 21, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 61f4420b39

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@djh58
djh58 force-pushed the djh58/pq184-8-docs branch from 61f4420 to 814dc84 Compare October 22, 2026 03:02
@djh58

djh58 commented Oct 22, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 814dc84eda

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@djh58 djh58 added the pq-transport Hybrid post-quantum v2 transport (#184) label Oct 22, 2026
@djh58
djh58 added this pull request to stack #232 October 22, 2026 14:17
@djh58
djh58 force-pushed the djh58/pq184-8-docs branch 3 times, most recently from 814dc84 to d56502a Compare October 22, 2026 17:15
@djh58

djh58 commented Oct 22, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d56502a818

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@djh58

djh58 commented Oct 22, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: 026521efd9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Base automatically changed from djh58/pq184-7-e2e to feature/pq-transport October 22, 2026 18:57
djh58 and others added 6 commits October 22, 2026 14:58
…ve and portable

Time one complete in-memory handshake between a V2Transport initiator
and responder per iteration: fresh keys, production entropy and ML-KEM
code, no garbage, and done only when both sides report v2 with the same
session id and, for hybrid, the peer's verified key confirmation.

Legacy v2 and hybrid each run on the detected ML-KEM-1024 backend and
with portable C forced (mlkem::ForcePortableForTesting, as the ML-KEM
benchmarks do). Each benchmark is named after the transport, the bytes
on the wire per handshake (measured, and checked on every iteration) and
the backend that runs; legacy v2 runs no ML-KEM code, so its two runs
show that the backend selection costs it nothing.

Part of #184 (part 8, #215, H1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
doc/user/network/pq-transport.md, listed next to Tor and I2P, is the
operator guide: a two-node regtest quick start that ends in a cleanup
step, a two-line BIP324 primer, how to switch the feature on and off
(numeric booleans, the warnings for a non-numeric value or a double
negative, -v2transport=0), how to verify it (the startup line,
getpqtransportinfo, getpeerinfo transport_pq and transport_pq_status,
-netinfo's 2p and getnetworkinfo connections_pq), the fallback and load
shedding in plain words, what it protects and what it doesn't, where
the log is, a troubleshooting table keyed by log text and counter name,
and upgrading and rolling back, with -mlkemportable as the escape hatch.

Every string, default, threshold and window is the code's: the feature
is off by default in this stack, inbound outcomes and successful
switches are debug-only, nothing it records survives a restart, and
v1.0.0 (checked with its release binary) ignores v2pqtransport=1 in
qbit.conf but refuses to start with -v2pqtransport on its command line.

doc/release-notes-240.md says what it protects and doesn't, the default
and how to switch it on and off. The specification, the guide and the
release note link to each other. bitcoin-core-differences.md,
rpc-delta-reference.md (getpqtransportinfo, getpeerinfo and
getnetworkinfo's fields) and fuzzing.md (the hybrid v2 transport
targets) are updated.

The interface names the three documents quote are marked for the token
check that follows, and the guide's quick start blocks for its test.

Part of #184 (part 8, #215, H3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test/lint/lint-pq-doc-tokens.py, which the lint runner runs with the
other test/lint/lint-*.py checks, reads every Markdown file under doc/
that marks a name with <!--pq:KIND--> before an inline code span (not
the archived release notes), and fails when a marked name doesn't exist:

- opt: an option qbitd -help or -help-debug, or qbit-cli -help, lists,
  given those binaries; without them, an AddArg() call, which is what
  -help prints;
- rpc: a method registered with RPCHelpMan;
- field: an RPC result field set with pushKV(), or a dotted path;
- value: exactly a C++ string literal;
- log: part of a C++ string literal, with … for each printf conversion,
  at word boundaries.

An empty, malformed or unterminated marker, a marker in a fenced code
block and an unclosed code block fail with the file and line, so a typo
can't drop a name, and the specification and the operator guide must
mark some. Every run first tests the check: renaming -v2pqtransport,
the transport_pq_status field and the load shedding log string in
memory must make exactly those names fail, malformed markers must be
reported where they are, and near misses must not pass.

The lint job has no binaries; the quick start's functional test runs
the check with the build's, so CI also checks against real -help output.

Part of #184 (part 8, #215, H3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feature_pq_quickstart.py runs the three quick-start steps of
doc/user/network/pq-transport.md as written, in one bash shell, with
the build's qbitd and qbit-cli first on PATH; only the data directory
and the four ports are replaced, by the framework's. Each step must
print what the guide shows, where numbers, dates and the ML-KEM-1024
backend names may differ, and nothing on stderr. The quick start's
nodes are stopped and their data removed even when a step fails; their
logs are kept in the test directory.

It also checks that the qbit.conf line the guide gives switches the
feature on, through the real startup path, as the startup line and
getpqtransportinfo show, that -nov2pqtransport on the command line
overrides it, that the startup warnings the guide shows are what qbitd
prints, and that the interface names the docs quote exist, with options
from this build's -help and -help-debug output.

Writing the test found that the hybrid confirmation can verify before
the version handshake ends, which is why the guide waits for the first
ping, not for connections_pq, before it shows -netinfo.

Part of #184 (part 8, #215, H3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pull request that changes only public documentation is classified
public-docs and skips the lint job, so lint-pq-doc-tokens.py never saw
an edit to the operator guide, the specification or the release note
alone: a docs-only PR could quote an option, RPC field or log string
that doesn't exist. Run the check in the public-docs-lint job as well.
It needs only the sources, so it reads options from the AddArg() calls
there; feature_pq_quickstart.py still runs it against real -help output.

Part of #184 (part 8, #215, H3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The quick-start test replaced each of the guide's example ports in turn,
so a port the framework allocated for one node could equal a guide port
replaced later, and be rewritten. With --portseed=1456, node A's RPC port
is 18512, which node B's substitution then turned into 18513: both nodes
used one port and the test failed. Build the replacement map first and
apply it in one pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@djh58

djh58 commented Oct 22, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: eb5b7dc424

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@djh58
djh58 merged commit 5da8c1f into feature/pq-transport Oct 22, 2026
41 checks passed
djh58 added a commit that referenced this pull request Oct 27, 2026
* ci: check mainnet publication posture for 1.1.0

The "mainnet publication posture" job checks that the source being
released keeps the mainnet launch posture: the AuxPoW chain ID, the
genesis and ASERT launch data, the mainnet bootstrap, and the
default-mainnet build. The "Classify release target" step only set
mainnet_publish_gate_required for target branch 1.0.0, so pull
requests, merge-queue runs and pushes into 1.1.0, the next mainnet
release line, skipped the job and the Core Checks Gate never asked for
its result.

Set the output for 1.1.0 as well, and keep 1.0.0. The job's `if:` and
the Core Checks Gate both read this output, so for source and
release-policy changes into 1.1.0 the job now runs and a failure blocks
the gate. Pin both branches in the existing workflow contract test, and
update the release process, which names the branches the job runs for.

Refs #222

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* build: bump version to v1.1.0-rc1

Set CLIENT_VERSION_MINOR to 1, CLIENT_VERSION_BUILD to 0 and
CLIENT_VERSION_RC to 1 so the source identifies as v1.1.0-rc1, the
first release candidate of v1.1.0, the release after v1.0.1 that adds
the hybrid post-quantum v2 transport (#184). CLIENT_VERSION_IS_RELEASE
stays true: 1.1.0 is a release line, so candidate binaries carry no
commit suffix, as for v1.0.0 and the v1.0.1 candidates.

CMake appends "-rc1" to CLIENT_VERSION_STRING, so every binary reports
v1.1.0-rc1, the user agent becomes /qbit:1.1.0-rc1/, and the Windows
installer metadata and the RPC docs manifest's project version derive
from the same string. The Guix archive name comes from the v1.1.0-rc1
tag itself.

Regenerate the seven manual pages with contrib/devtools/gen-manpages.py
(help2man 1.49.3) from a build of this tree with the GUI, the wallet
and every tool. Besides the version and date lines, qbitd.1 and
qbit-qt.1 gain -v2pqtransport, which #184 added as a connection option.
The other options #184 added (-mlkemportable and the regtest-only
-test values) are debug options that only -help-debug lists, so the
pages don't show them.

util_tests/test_FormatSubVersion expected the "Test" client name to
format as CLIENT_VERSION_STRING, which fails for every version with a
release-candidate suffix: FormatSubVersion uses that string only for
the qbit user agent and formats any other name numerically. Apply the
same fix as the v1.0.1-rc1 preparation (#181), byte for byte, so that
merging 1.x.x into 1.1.0 brings an identical change: expect the
numeric form built from the version components. The qbit user-agent
checks on the next lines still cover CLIENT_VERSION_STRING.

Refs #222

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* doc: draft v1.1.0 release notes

Add doc/release-notes-1.1.0.md in the structure of the v1.0.1 notes
(#181). It covers every change merged to 1.1.0 since it branched from
1.x.x at 45e9e72: the hybrid post-quantum v2 transport, with the
parts and fixes merged through #233 (#223 to #254), and #249.

Behavior changes come first: the hybrid transport is on by default, how
to switch it off, the numeric -v2pqtransport value, the single I2P
session-creation limit and qbit-photon's OpenSSL configuration loading.
A compatibility table states how v1.1.0 nodes connect with v1.0.0 and
v1.0.1 nodes and over v1, in both directions, as
p2p_v2_previous_release.py and rpc_pqtransport.py test it. Known
limitations summarize the release fuzz campaign and the limits its
report on #222 states, the basis of the load-shedding threshold, the
portable ML-KEM code on AArch64 Linux, and the unsigned-platform waiver
(#70). The only performance figures are the handshake benchmark results
published in #240.

Fold in and delete the 1.1.0 fragments doc/release-notes-223.md, -240.md
and -249.md, and point the specification's and the operator guide's
release-note links at the new notes' hybrid transport section. The
v1.0.0 and v1.0.1 fragments are left alone.

The notes say that v1.1.0 contains every change in v1.0.1. That holds
once 1.x.x is merged into 1.1.0 after v1.0.1 ships; the short "Changes
in v1.0.1" section then gains a link to the v1.0.1 notes.

Refs #222

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* net: say how the load-shedding threshold was set

The comment on DEFAULT_PQ_SHED_THRESHOLD_PER_S said that the release
gate's flood measurement (#222, R3) would set its final value. R3 was
settled with CPU benchmarks of the handshake instead, and the threshold
stays at 1,000 offers per second, as the v1.1.0 release notes say. Say
so, so the comment no longer points at a change that is not coming.

Refs #222

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* doc: cite the new fuzz seeds in the v1.1.0 notes

#255 merged into 1.1.0 after the notes were drafted. It adds generated
seeds to the required fuzz corpus for the hybrid transport's targets, so
the test section now cites it alongside the earlier seed changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pq-transport Hybrid post-quantum v2 transport (#184)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant