Skip to content

docs: audit README against the tree and correct every stale claim - #104

Merged
JC-000 merged 1 commit into
masterfrom
docs/readme-audit
Aug 14, 2026
Merged

docs: audit README against the tree and correct every stale claim#104
JC-000 merged 1 commit into
masterfrom
docs/readme-audit

Conversation

@JC-000

Copy link
Copy Markdown
Owner

Audit-and-correct pass over README.md. Every factual claim was checked
against the tree, the build output, or a measured run; nothing was kept
because it read plausibly. One commit, claim-by-claim below.

Environment: fresh worktree off origin/master (fc82ad5), submodules
--init --recursive, make ip65-libs && make ip65-blob, cc65 from
Homebrew, macOS. make clean before every backend/flag change.

Claims changed

Sizes and label counts

wasnowhow verified
"38 KB binary (ip65 build), 1738 labels"47,105 B PRG, 2292 labelsmake clean && make; ls -l build/c64-https.prg, grep -c '^al ' build/labels.txt
"38 KB binary (uci build), 1816 labels"62,977 B PRG, 2400 labelsmake clean && make BACKEND=uci, same commands

Added a sentence noting that much of both PRGs is deliberate fill = yes
zero padding, so neither number is a code-size measurement.

Source file names

The architecture diagram and prose named http.asm, tls13.asm,
tls_record.asm, tls_handshake.asm, hkdf.asm. git ls-files '*.asm'
returns exactly two files — src/constants.asm and src/entropy.asm
and neither is in $(TOP_SRCS) (the Makefile wildcards src/*.s). All
five names corrected to .s.

Crypto claims

wasnowhow verified
diagram: "ECDHE P-256""ECDHE X25519"supported_groups/key_share carry only 0x001D (src/tls_handshake.s:204), and ServerHello is checked for 0x001D at :494
"Target: TLS_CHACHA20_POLY1305_SHA256""…the only suite the ClientHello offers"cipher_suites_length = 0x0002, one entry 0x1303 (:77-86), echo-checked at :380
added "no AES anywhere in src/crypto/"ls src/crypto/
"Key exchange: … ~3.6 min/op"12,635 jiffies = 211 s (3.5 min)ran tools/bench_x25519.py: 12,635 jiffies, 13.9 s wall, warp 15.1x, correctness PASS
provenance list omitted ECDSA entirelyadded the libs/nistcurves P-256 lineMakefile links build/lib/nistcurves-p256.a; src/crypto/ecdsa_verify.s is the dispatcher
../c64-wireguard, ../c64-aes256-ecdsa linksfull GitHub URLsgh repo view JC-000/<repo> → PUBLIC for all of them; relative ../ links do not resolve on GitHub

net_abi.inc

The README said TLS/HTTP "consume networking only through
src/net_abi.inc". Replaced with the measured position, matching what
CLAUDE.md now says:

  • grep -rn 'net_abi' src/ tools/ cfg/ Makefile tests/ returns only
    comments — no .include, so none of its 12 .imports is checked.
  • Declared 12; actually imported by TLS/HTTP/boot 11 (from
    grep -h '\.import.*net_' src/boot.s src/http.s src/tls*.s);
    intersection 6, union 17.
  • ip65's .export list (src/net/ip65/net.s, net_banner.s) has none
    of net_dhcp_acquire, net_tcp_set_recv_cb, net_local_ip,
    net_resolved_ip, net_last_error, net_tcp_state; UCI's has all six.

Memory map

The old block was a single pre-W1 layout presented as universal, with
crypto at $4000-$5FFF and the quarter-square table at $7800. Replaced
with two per-backend tables read out of build/c64-https.map after a
clean build of each, plus a small "common to both" block. Spot checks
from build/labels.txt: sqtab_lo = $BC00 (not $7800), sqtab_hi =
$BE00, cert_buf = $A000, tcp_recv_buf = $C000.

Also corrected: "we negotiate max_fragment_length … 512 or 1024 bytes" →
the extension always sends value 1 = 512 (src/tls_handshake.s:281-297),
and TLS_RECORD_MAX = 512 in src/constants.inc.

Zero page table

Was missing $1E-$21 (TLS record layer) and stopped $FB-$FE a byte
short of zp_count's two bytes; $22-$3C lumped ECDSA, fe25519 and
x25519 together. Split per src/constants.inc, which is now cited as
the authority.

Build section

  • "The Makefile automatically builds ip65 … into a flat binary blob" —
    false, and it contradicted the prerequisites block three sections
    above. .incbin is invisible to make's dependency graph. Rewritten,
    with the deterministic blob size/hash (6,951 B, cf1a5ff7…, which I
    reproduced in this worktree).
  • Added the make clean-between-BACKEND=/flag rule and why (include
    path, not -D), and the PRG-vs-object hash point.
  • Added make USE_NISTCURVES_ONCHIP=1 and make package, since the
    release table above them refers to onchip images.

Known Issues

wasnowhow verified
"ECDSA P-256 verify runs ~85 s/op … ~60-70 min at stock 1 MHz"measured e2e figures (U64E 80.8 s REU/48 MHz, 45.5 s onchip/48 MHz, 1,157.7 s REU/1 MHz; ip65+onchip no-REU 2,159.7 s honest 1 MHz)quoted from the measurement record in CLAUDE.md, each explicitly tagged as taken at the libs/nistcurvesv0.6.0 pin — HEAD is v0.9.1 and the sweep has not been re-run
"Real-internet targets need a sibling-style optimized P-256"droppedthat work landed; libs/nistcurves is the shipped P-256
"P-384 ECDSA is currently stubbed"still stubbed, and no P-384 target buildsran it: make p384-overlay from clean → No rule to make target 'build/labels.txt'; after a main build → Segment 'LIB_NISTCURVES_SHA384_TABLES' overflows memory area 'OVERLAY_REGION' by 1536 bytes
— (new)USE_X25519_SIBLING=1 links on neither backendran both: ld65: Error: Duplicate external identifier: 'reu_mul_tables_init'. Note for the CLAUDE.md owners: CLAUDE.md still describes the v0.6.0-era X25519_RODATA/P256_CODE overflows; at the current v0.10.0 pin the failure has moved and is a duplicate-symbol error instead. I did not touch CLAUDE.md in this PR.

Test automation

wasnowhow verified
"253 tests across 11 suites"266 across 11tools/run_all_tests.py --workers 4, run twice, 266/266 both times
x25519 "71 tests … (--slow for RFC 7748 vectors)"73 tests, vectors always runrunner output; tools/test_x25519.py:708 makes --slow an accepted no-op
"~5 min with ECDSA"~2 mintime on the full run: 2:05
"--skip-slow … (~5s wall time)"255 assertions, ~45 stime on --skip-slow: 46.2 s, 255/255
bench "~3.6 min C64 time, ~8s warp"12,635 jiffies / 211 s, ~14 s wallmeasured, above
chained_hmac listed as the only standaloneadded test_finished_verify.py (18/18 measured), test_ecdsa_kat_oracle.py (6 vectors, 3 valid + 3 negative, counted in KAT_VECTORS), test_package_verify.py (31 passed, measured)ran each
"see scripts/setup-tap-networking.sh in c64-test-harness"scripts/setup-bridge-tap.sh in this repono such file in the harness's scripts/; this repo has the bridge script
../c64-test-harness/docs/bridge_networking.mdfull URLrelative link does not resolve on GitHub
https_e2e API tableadded the https_listener.py rowgrep -n 'def start_https_listener' etc.
bridge section: "the phase3 HTTPS run is ~2-3 h at 1 MHz"the measured 2,159.7 s = 36.0 min, attributed to the macOS rig it was actually taken on, with an explicit "do not assume the bridge rig matches"CLAUDE.md "ip65 / stock-C64 wall-clock"

Added a short paragraph naming the macOS feth/pcap rig
(tools/rig-up-macos.sh, patched VICE) since that is where the ip65
number comes from and the section previously described only the Linux
bridge.

UCI section

  • TURBO_MHZ=1 "~2-3 h total" → the handshake+GET itself measured
    1,157.7 s (~19 min); the 2-3 h figure was the timeout budget, not a
    runtime.
  • 600 * (48 / TURBO_MHZ)600 * max(1, 48 / TURBO_MHZ)
    (tools/uci/test_https_local.py:229).
  • Added EXTERNAL_LISTENER / EXTERNAL_HOST / EXTERNAL_PORT (:253-258).
  • Added test_https_bad_finished.py to the script list with a note on
    why the evil listener is hand-rolled.
  • Intro now says "Ultimate 64 Elite and C64 Ultimate", matching the
    release table.

Release

Added the v0.2.0 link to "I just want to run it". Confirmed against
gh release view v0.2.0: 12 assets, four .prg, six .d64,
c64-https-listener.py, MANIFEST.txt — the section's file names all
match. Nothing else in that section changed.

Preserved deliberately

  • "Before you build or test" (restored in docs: restore the build prerequisites block lost from the README #103) is untouched. I
    re-checked its claims incidentally, because this worktree started with
    uninitialised submodules and no blob: the flow it prescribes is what I
    had to run, and src/net/ip65/ip65_blob.s:22 is indeed the .incbin
    line the quoted error names.
  • "I just want to run it" changed only by the addition of the
    release link.

Not verified — left alone

  • "Live internet HTTP GET (UCI backend) has not been re-verified since
    the FPGA-fence rework." Needs U64E LAN access.
  • The Linux bridge rig's phase1/phase2/phase3 pass status. This bench is
    macOS; I removed the unqualified "All three phases pass" wording rather
    than assert or deny it, and left the scripts documented.
  • Hardware wall-clock numbers were cited, not re-measured — the U64E
    is queue-shared and each run is ~20 min at 1 MHz, and the C64U is
    offline. Every such figure now carries its pin (v0.6.0) and the note
    that HEAD (v0.9.1) has not been re-swept.
  • "VICE 3.9 previously appeared to crash on chained HMAC-SHA256" — a
    historical note about a fixed harness bug; left as-is.

🤖 Generated with Claude Code

Every factual claim in README.md re-verified against the source, the
build output, or a measured run. Corrections, with how each was checked:
- Binary sizes / label counts: 38 KB + 1738/1816 labels -> 47,105 B /
2292 (ip65) and 62,977 B / 2400 (uci), from `make clean && make
[BACKEND=uci]` plus `grep -c '^al ' build/labels.txt`.
- Architecture diagram named `.asm` files the ca65 conversion removed
(`http.asm`, `tls13.asm`, `tls_record.asm`, `tls_handshake.asm`,
`hkdf.asm`). Only `src/constants.asm` and `src/entropy.asm` survive,
and neither is built.
- Diagram said "ECDHE P-256"; the ClientHello carries exactly one group,
0x001D x25519.
- `src/net_abi.inc` was described as the interface TLS/HTTP consume
networking through. Nothing `.include`s it; declared and used surfaces
overlap in 6 of 17 symbols; ip65 exports none of `net_dhcp_acquire`,
`net_tcp_set_recv_cb`, `net_local_ip`, `net_resolved_ip`,
`net_last_error`, `net_tcp_state`.
- Memory map predated the W1 hot/cold split and presented one layout as
universal. Replaced with per-backend maps read from
`build/c64-https.map`; the quarter-square table is at $BC00, not $7800.
- max_fragment_length "512 or 1024" -> always value 1 = 512.
- X25519 "~3.6 min/op" / "12,782 jiffies" -> measured 12,635 jiffies =
211 s (3.5 min), 13.9 s wall under warp, via tools/bench_x25519.py.
- "253 tests across 11 suites" -> 266 across 11, measured twice.
x25519 71 -> 73; `--slow` is now a no-op. `--skip-slow` "~5s" -> ~45 s;
full run "~5 min" -> ~2 min.
- ECDSA verify wall-clock ("~85 s/op", "~60-70 min at 1 MHz") and the
ip65 e2e "~2-3 h" replaced with the measured figures from CLAUDE.md,
each tagged with the libs/nistcurves v0.6.0 pin they were taken at.
- "The Makefile automatically builds ip65 ... into a blob" contradicted
the prerequisites block and is false: `.incbin` is invisible to make.
- Added the `make clean`-between-flags trap, the two currently broken
build flags (USE_X25519_SIBLING, P-384) with their exact ld65 errors,
and the v0.2.0 release link.
- Relative sibling-repo links (`../c64-wireguard` etc.) never resolved on
GitHub; replaced with real URLs and split into vendored vs. not.
The "I just want to run it" and "Before you build or test" blocks are
unchanged apart from the release link.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JC-000
JC-000 merged commit 7236aba into masterAug 14, 2026
JC-000 added a commit that referenced this pull request Aug 15, 2026
…st measurement
Two deferred follow-ups, both left with reasons recorded by earlier lanes.
PR #111 renamed tests/test_*.py -> tests/rig_*.py because those files wear
pytest's discovery convention while collecting zero tests, so a bare `pytest`
overstates coverage. It explicitly deferred the identical problem in
tools/uci/, whose blast radius runs through CLAUDE.md. This is that change:
six scripts renamed, following #111's precedent exactly.
`norecursedirs` already closed the default-invocation path, but the rename is
what holds from an arbitrary working directory, since `testpaths` only applies
at the rootdir. Both halves are now pinned by the guard rather than one.
tools/test_pytest_boundary.py grows a RIG_DIRS tuple covering both directories
and a new test_norecursedirs_covers_every_rig_dir. Probed in both directions:
a stray tools/uci/test_*.py fails test_rig_dirs_hold_no_pytest_named_files, and
dropping tools/uci from norecursedirs fails the new check. Bare `pytest` is now
31 passed / exit 0; `pytest tests/` and `pytest tools/uci/` both exit 5 with an
explanation naming the right README.
New tools/uci/README.md mirrors tests/README.md. Reference sweep covers
CLAUDE.md, README.md, conftest.py, pytest.ini, tests/README.md, both phase_f
docs, tools/https_e2e/, tools/package/listener/, and the cross-invocations
(rig_https_print_body and rig_https_local_p384 both delegate to
rig_https_local; import-checked after the rename). Zero references exist in
c64-test-harness or any other sibling c64-* repo, and no open issue or PR in
the org names any of the six.
README was audited claim-by-claim in #104; CLAUDE.md never was. Every item
below was confirmed by running a command, not by reading:
- The ip65 blob section contradicted itself and the Makefile. It claimed
`ip65-blob` is phony with "no rule connecting the two" and that a fresh
clone dies at the `.incbin`. Measured: deleting the blob and running plain
`make` rebuilds it byte-identically (6,951 B, cf1a5ff7...) and links the
usual 47,105 B PRG. The real fresh-clone blocker is the missing ip65 .lib
archives, which fail at the blob's ld65 step. `make ip65-blob` is not a
required step. Fixed here and in README.
- "the committed blob" / `touch ip65-build/ip65-c64.bin` advice: the blob is
gitignored, so on a fresh clone there is nothing to touch.
- Fence macro: 17 bytes per site, not 14 (counted from the macro body).
26 sites, not 24 — 11 write + 14 read + 1 pre-loop settle at net.s:199.
- uci_drain_resp/uci_drain_status: 22 call sites in net.s, not 13. All 22
still `bcs` out, so that half was right.
- CIA1 TOD read order is HOUR -> TENTHS; MIN and SEC are never read and have
no equate in uci_cmd.s.
- UCI memory table: NET_CODE is $2000-$3B65 and NET_BSS_TAIL $3B66-$41FF
(cfg grew NET_CODE by $40 for the C64U WiFi iface-fallback loop).
UCI_BSS_REGION is size 0, so quoting a 512 B span contradicted its own
label and overlapped NET_BSS_TAIL.
- src/exports.s no longer exports ip65_init/ip65_process; those moved to
src/net/ip65/exports.s. Named the real backend-agnostic set instead.
- KEEP_DEBUG_ON_PASS is the env var; UCI_DEBUG_KEEP_ON_PASS is only the
Python variable name, so the documented spelling did nothing.
- rig_https_local_p384.py defaults to 90 minutes (5400 s), not 30.
- C64_SKIP_BUILD: 15 scripts, not 14 (ran the recipe the file itself quotes).
- EMBED_P256_OVERLAY's stated mutual exclusions have no $(error) guard: one
is a silent auto-disable, the other is unguarded. Only the
USE_NISTCURVES_ONCHIP exclusions are hard errors.
- "All in-tree VICE-driven tests go through default_vice_config()" is false:
8 suites do, 15 other files still build ViceConfig directly — including
run_all_tests.py, which hand-spells the REU flags.
- Smoke-test list is 8 entries, not 7, and the 97/97 total predates
test_finished_verify.py joining it.
- pytest counts 30 -> 31; src/boot.s:107-114 -> 110-117.
Wall-clock figures are labelled rather than re-measured, per instruction: a
pin-vs-commit table resolves 2ceb5b1 / f0127a0 / cb6eab4 to libs/nistcurves
v0.6.0 (verified with git ls-tree), every table states the pin it was taken
at, and "at HEAD" captions that no longer refer to HEAD are gone. No hardware
was used and no benchmark was re-run.
Deliberately NOT included: the x25519 sibling failure. It is measured and
understood (both backends exit 2 on a duplicate reu_mul_tables_init export,
not the overflow CLAUDE.md records), but PR #113 rewrites the same spans and
fixes the underlying collision, so the prose belongs there. Details handed to
that lane. docs/library-ingestion-architecture.md's two stale references are
owned and taken by the contract lane.
Python and docs only — no .s, no .cfg, no Makefile, so no PRG changes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@JC-000