Added deterministic `resources.json` and `regen_resources.py`.
Extended `verify_resource.py` with receiver assembly/proof and requested negative cases.
Updated specification, audit, status, and tool documentation.
Fixed an unrelated nondeterministic wrong-ticket test in verify_stamps.py.
Confirmed vector regeneration is byte-identical.
Confirmed no tracked reliance on specenv or user-specific paths.
git diff --check: pass.
Complete pinned suite: 16 passed, 0 failed.
Clone Portability
Added fresh-clone setup instructions using repository-local .venv in README.md (line 28) and tools/README.md (line 12).
Documented that any virtual-environment path works and activation is optional.
Added .venv/ and venv/ to .gitignore (line 17).
Confirmed no tracked project files reference your specenv or rnsenv.
Verification Infrastructure
Added verify_all.py (line 1), which:
Enforces versions from tools/requirements.txt.
Runs every verifier independently.
Summarizes all failures.
Confirmed it rejects the older RNS 1.1.3/LXMF 0.9.3 environment.
Resource Audit
Added Tier 1 report: resource-tier1-rns-1.2.4.md (line 1).
Added verify_resource.py (line 1).
Corrected §10 and stale flow documentation:
Direct LXMF Resource threshold is 319 bytes.
Advertisement d is total logical-resource size.
Resource packets contain slices of one encrypted stream.
Exhausted requests can also request parts.
RESOURCE_RCL rejects advertisements; ordinary receiver cancellation is local-only.
Validation:
Passed: 16
Failed: 0
ALL VERIFIERS PASS
Remaining Resource work is deterministic resources.json vectors and negative/rejection cases.
§3 now requires confirming the latest upstream version each session and
verifying package signatures before install. Records that upstream is
migrating off GitHub (1.2.5 ≈ last GitHub release) toward rngit/rnpkg
self-hosting over Reticulum, and that signed wheels must be rnid-checked
against Mark Qvist's release identity rather than trusting a bare
`pip install` from PyPI (PyPI carries no .rsg).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two new files plus pointers from README.md and agent.md.
playbook.md — companion to agent.md. Where agent.md governs what
evidence is admissible as you add to the spec, playbook.md covers
how to navigate the work itself: triage checklist for wire-format
bugs, common debugging anti-patterns (the stale-sibling-binary trap,
trusting LLM training data on Reticulum specifics, chasing
intermittent symptoms with retries), the three layers of test
trustworthiness, and how to work productively in a code-as-spec
domain. Includes an incident registry seeded with the §6.2/§6.6
signed_data signalling bug surfaced in mobile-app today plus older
HEADER_2, REQUEST path_hash, DEST_LINK, and stale-binary incidents.
Append-only — every future interop fix gets a registry entry per §8.
templates/AGENTS.md — drop-in boilerplate for new Reticulum
implementation projects in any language. Uppercase plural matches
the emerging AGENTS.md convention (Claude Code, Codex, Cursor,
Copilot Workspace). Sections: read-these-first reading list,
cardinal rules summary, project-specific FILL-IN placeholders,
contributing-findings-back obligation, attribution. Project-specific
bits use HTML comments so they're obvious to edit. §5 attribution
points back to this repo and is mandatory per CC BY 4.0.
templates/README.md — names the template, says where to put it,
restates the attribution expectation.
agent.md and README.md updated with pointers to the new files so
anyone reading the front door of the repo finds them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Upstream RNS 1.2.4 (2026-05-07) announces it is "probably the last
release that is also published to GitHub" — pip continues until rnpkg
is complete and RNS is self-hosting. All 13 verifiers pass against
1.2.4 / 0.9.7; no wire-format, signing, or protocol behavior changed
between 1.2.0 and 1.2.4, so the changes here are purely currency:
- Pin tools/requirements.txt to rns==1.2.4 / lxmf==0.9.7 so the
verifier stays reproducible if upstream stops mirroring to PyPI
before the migration is ready.
- Add an "Upstream distribution shift" watch-list to todo.md (local
Reticulum node, repo destination hash, rnpkg install/upgrade
commands, rsg signature verification, mirroring source citations).
- Bump SPEC.md frontmatter and re-anchor ~50 line citations across
Identity.py, Transport.py, Resource.py, Link.py, Reticulum.py,
Packet.py, and LXMF/* (Identity.py drift was the heaviest at +13
to +31 lines; Transport.py was variable). Fix one numeric
(MAX_RANDOM_BLOBS = 32 → 64) and one semantic (§6.6.3 LRPROOF MTU
clamp citation pointed at the wrong location — corrected to point
at the transit-relay clamp at Transport.py:1539-1556).
- Update §10.4 decompression-bomb hazard to note upstream's 1.1.9 cap
adoption, with citations to Resource.py:686-691 and Buffer.py:95-97
plus a "do not use one-shot bz2.decompress()" warning.
- Re-anchor 11 flows/ files (version pins + ~30 line citations).
- Bump version labels in tools/README.md, test-vectors/README.md, and
4 verifier docstrings + 2 hardcoded print strings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reporter implemented §7.2.6 minimum-leaf path-request responder + §7.3
ratchet rotation in thatSFguy/reticulum-lora-webclient and surfaced
five small gaps. Each is fixed below; the first is a real spec
correction backed by a new runtime verifier.
#### 1. §7.3 dedup-mechanism claim was wrong (verified)
Earlier §7.3 claimed transit nodes dedup on '(destination_hash,
ratchet_pub)' tuples. Reporter pointed out this can't be right:
upstream's RATCHET_INTERVAL = 30 min × ANNOUNCE_INTERVAL = 5-15 min
means most upstream announces share a ratchet across 2-6 emissions.
If relays really dropped on ratchet_pub equality, upstream wouldn't
function.
Confirmed by new tools/verify_ratchet_dedup.py: builds two announces
with same ratchet_pub but distinct random_hash[:5], walks the
upstream replay-defence machinery (Transport.py:1707,1732,1745
'not random_blob in random_blobs' check) by hand. Both announces
ACCEPTED — dedup is keyed on random_blob, not on ratchet_pub.
§7.3 rewritten:
- Drops the wrong dedup claim with an explicit ⚠️ Spec correction
callout naming the bug.
- Reframes ratchet rotation as forward-secrecy hygiene, not a
mesh-visibility requirement.
- Points at §4.5 step 6.3 / §4.1 for the actual replay-defence
mechanism.
- Documents upstream's at-most-every-30-min rotation cadence
(rotate_ratchets is a no-op if RATCHET_INTERVAL hasn't elapsed).
- Says clean-room MAY rotate per-announce or follow upstream's
cadence — either is interop-correct.
#### 2. Path-response ratchet rotation guidance — §7.3.4 (new)
Added explicit guidance: path-response announces SHOULD reuse the
current ratchet rather than rotate. Burst-rotating on identical-target
path? requests would burn ratchet-ring slots without forward-secrecy
benefit. Upstream's no-op-if-recent gate enforces this implicitly.
#### 3. Leaf dedup-table size — §7.2.6 step 4
Added: 'A leaf-appropriate cap is 128–256 entries with FIFO eviction;
the upstream max_pr_tags = 32000 is sized for a transit node.'
#### 4. PR_TAG_WINDOW body cache for leaves — §7.2.6 trailing
Added: 'Leaves may skip the §7.2.5 PR_TAG_WINDOW body cache' with
explanation that step 4's dedup table already collapses identical-tag
retransmits and a leaf isn't fanning to multiple downstream relays.
#### 5. PLAIN destination recipe link — §7.2.1
Added: 'The path-request destination is a PLAIN destination ... per
the PLAIN/GROUP recipe in §1.4.3 (the identity == None branch).'
Surfaces the connection that's currently buried in §1.4 titled 'GROUP
destinations' but actually covers PLAIN too.
agent.md §5 audit table updated — §7.3 entry corrected to note the
prior 'verified' claim was actually mis-attributed; the test result
came from incidental random_hash rotation, not ratchet rotation.
13 of 13 verifiers in tools/ now pass.
Closes#1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes Tier 1 #2. Without this, a client can't send any LXMF body
larger than LINK_PACKET_MAX_CONTENT ≈ 360 B, can't receive a NomadNet
page that doesn't fit in one MTU, and can't transfer files via rncp.
SPEC.md §10 (new): full Resource fragmentation protocol with citations
to RNS/Resource.py. 13 sub-sections covering preparation pipeline
(metadata prefix → optional bz2 → random_hash prefix → SHA-256 over
data||random_hash → link.encrypt of the WHOLE blob → part-split into
SDU-sized chunks → 4-byte map_hash hashmap with collision guard within
COLLISION_GUARD_SIZE = 2*WINDOW_MAX + HASHMAP_MAX_LEN), wire context
inventory (RESOURCE_ADV / RESOURCE / RESOURCE_REQ / RESOURCE_HMU /
RESOURCE_PRF / RESOURCE_ICL / RESOURCE_RCL), the msgpack dict for the
advertisement (t/d/n/h/r/o/i/l/q/f/m), the request payload format with
the hashmap_exhausted sentinel, the lazy-hashmap RESOURCE_HMU
continuation that lets large hashmaps avoid breaking small-MTU links,
the proof body
resource_hash(32) || full_proof = SHA256(data||hash) (32)
returned in a PROOF-type packet, the sliding window dynamics
(WINDOW=4 → WINDOW_MAX_FAST=75 / WINDOW_MAX_VERY_SLOW=4 with rate
detection), multi-segment cutover at MAX_EFFICIENT_SIZE = 1 MiB - 1
with the lazy `__prepare_next_segment` pattern, and the
encryption-before-split layering that means a missing part can't be
decrypted in isolation.
flows/send-resource.md: 10-step chronology from RNS.Resource()
construction through advertise → req/parts loop → HMU continuation →
final RESOURCE_PRF → multi-segment fan-out, with a wire-byte ladder
diagram and a per-step source map.
Side fixes found while drafting:
- SPEC.md §2.5 contexts table was wildly incomplete and had a real
bug: KEEPALIVE was listed as 0xFD; upstream is 0xFA per
RNS/Packet.py:87. 0xFD is actually LINKPROOF (the regular
DATA-receipt context, §6.5). Replaced with the full upstream
context inventory: NONE, RESOURCE_*, CACHE_REQUEST, REQUEST,
RESPONSE, PATH_RESPONSE, COMMAND, COMMAND_STATUS, CHANNEL,
KEEPALIVE, LINKIDENTIFY, LINKCLOSE, LINKPROOF, LRRTT, LRPROOF.
- SPEC.md §6.5 reworded: "send back a PROOF packet (no context
byte specifics)" → "send back a PROOF-type packet with
context = LINKPROOF (0xFD)" for clarity.
- The previously-numbered §10 "Test vectors" and §11 "Source map"
are renumbered to §11 / §12 so the new Resource section lands in
its correct protocol-stack position. agent.md §5 audit table
updated accordingly.
flows/README.md status table updated; receive-resource.md added as
the next pending flow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bootstrapped from the working notes of two reverse-engineering efforts:
- reticulum-lora-webclient (web/Capacitor)
- reticulum-mobile-app (Kotlin Multiplatform / Android)
SPEC.md consolidates byte-level wire format findings that aren't in the
upstream Reticulum manual. Each section grounded in upstream Python
source citations (file + line) where possible.
agent.md establishes the verification rules:
- Every claim is verified, unverified, or speculation; markers required
- Verification means a runnable script or a source citation
- PRs that quietly remove markers get rejected
tools/ and test-vectors/ are placeholder scaffolding with READMEs
describing the work needed.
Sections in SPEC.md flagged as currently UNVERIFIED:
- §2.3 Originator HEADER_1 -> HEADER_2 conversion
- §4.3 app_data 3-element variant with capabilities
- §7.1 path? always precedes LXMF (vs only on stale paths)
- §7.4 ratchet ring count default = 8
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>