Skip to content

peeroxide-cli 0.2.0: chat subsystem, init command, dd v2 tree protocol, progress UX#15

Merged
eshork merged 128 commits into
mainfrom
fix_init
May 14, 2026
Merged

peeroxide-cli 0.2.0: chat subsystem, init command, dd v2 tree protocol, progress UX#15
eshork merged 128 commits into
mainfrom
fix_init

Conversation

@eshork
Copy link
Copy Markdown
Collaborator

@eshork eshork commented May 12, 2026

Summary

  • Ships three big additions to peeroxide-cli: a peeroxide init bootstrap command, the dd v2 tree-indexed protocol rewrite (with progress UX), and the full chat subsystem (channels, DMs, inbox monitor, interactive TUI).
  • Adds public DHT wire-byte counter accessors to peeroxide-dht 1.2.0 → 1.3.0 (additive only — no breaking changes).
  • Extends the docs/ mdBook with new init/ and chat/ chapters and rewrites the dd/ chapter to cover both v1 and v2 protocols.

Motivation

The branch grew from three independent threads that share a common substrate (the existing peeroxide DHT primitives):

  1. peeroxide init removes the "edit a TOML file by hand before anything works" papercut for new users and gives us a single entry point for both config bootstrap and man-page installation.
  2. dd v2 addresses a structural ceiling in v1: the v1 chain was a sequential singly linked list, so a 1 GB payload required ~35,800 sequential mutable_get round trips on the critical path even though every chunk could otherwise be fetched in parallel. v2 turns the index layer into a canonical tree (O(log₃₁ N) round trips) and splits index (mutable) from data (immutable, content-addressed). On the publisher side, the rewrite also lands AIMD concurrency control, a stall watchdog, per-put timeouts, a sticky double-ctrl-c shutdown primitive, and a need-list mechanism that only advertises chunks the receiver actually tried and failed to fetch.
  3. chat is a brand-new feature: anonymous, end-to-end-encrypted, verifiable group + DM chat built on the existing DHT primitives, with no protocol changes to peeroxide, no relay code, and no peer cooperation required. It ships with feed rotation, per-feed chain anchoring, ordering + dedup invariants, an inbox-monitor framework, a unified chat::session consumed by both chat join and chat dm, and a full interactive TUI with status bar, slash commands, history replay, and bracketed paste.

The dd progress UX work (bar / log / JSON / off, TTY-aware auto-selection) was added alongside the v2 rewrite so users finally have actionable feedback on large transfers and tools can integrate via JSON-Lines instead of parsing stderr.

Changes

peeroxide-cli

Area Change
peeroxide init New command (src/cmd/init.rs, ~489 LOC). Replaces and removes the old peeroxide config init subcommand (src/cmd/config.rs deleted; Commands::Config removed from main.rs). Two mutually-exclusive modes: config bootstrap (default) and man-page installation (--man-pages [PATH]). Config mode supports --force (overwrite), --update (patch network.public / network.bootstrap while preserving comments), and writes a fresh commented TOML. Man-page mode writes generated roff to PATH/man1/ and cleans stale peeroxide*.1 files.
peeroxide chat New cmd/chat/ module tree (~35 source files). Subcommands: join, dm, inbox, whoami, profiles {list,create,delete}, friends {list,add,remove,refresh}, nexus. Full identity / profile / friend / known-users / nexus system. XSalsa20-Poly1305 + Ed25519 + BLAKE2b crypto. Per-session feed rotation with ±50% wobble, per-feed (id_pubkey, feed_pubkey) chain anchoring, ChainGate strict ordering with 5 s force-release, 1000-entry DedupRing, summary eviction on feed overflow (20→15 + walkback). Inbox monitor with parallel DHT scan (8 lookups + parallel mutable_gets per cycle, ~2-4 s wall-clock vs ~10-20 s in earlier nested-serial design). Shared chat::session consumed by join and dm; DM derives dm_channel_key and per-pair dm_msg_key via Ed25519→X25519 ECDH, sends per-epoch nudges, and best-effort retracts the invite on shutdown.
dd CLI Renamed deaddropdd; leave/pickupput/get. dd put dispatches on --v1 (default v2); dd get auto-dispatches on the root record's first byte (0x01→v1, 0x02→v2).
dd v2 protocol Full rewrite as a tree-indexed protocol under wire byte 0x02. New module tree at cmd/deaddrop/v2/ (build, tree, wire, keys, queue, publish, fetch, need, stream). Canonical-shape index tree with 31-slot non-root nodes and a 30-slot root that carries file_size: u64 + crc32c: u32. Data chunks use immutable_put + a reserved per-deaddrop salt byte (currently fixed at 0x00); index nodes use mutable_put with deterministic per-index keypairs. Receiver does BFS fetch with PARALLEL_FETCH_CAP = 64, sliding-window --timeout, and an ephemeral need-feed announcing only attempted-and-failed positions on a 20 s cadence. Soft depth cap of 4 → 27.7M data chunks (~27 GB).
dd progress UX New cmd/deaddrop/progress/ subsystem (state, format, rate, events, json, bar, log, mode, reporter). TTY-aware mode selection: --json > --no-progress > stderr-TTY → indicatif bar > periodic 2 s stderr log. Bar layouts: 2-bar (v1 put, v2 put) or 4-bar (v2 get) using indicatif::MultiProgress with a wire-rate / amplification line. JSON-Lines schema documented in docs/src/dd/operations.md (events: start, progress, result, ack, done).
dd put robustness Shared sticky Shutdown primitive (first SIGINT/SIGTERM cancels gracefully; second exits 130). Per-put PUT_TIMEOUT = 30 s enforced in dispatcher. EWMA AIMD controller (α 0.1, decision interval 20, fast-trip threshold 10, shrink 0.75×, grow +2). Stall watchdog kicks concurrency off the floor every 5 s if 30 s pass with no successful put (rate-limited to once / 120 s).
dd put perf Dedup'd ChunkId work queue with Lane::High priority for need-driven republishes. Interleaved index/data dispatch. Initial sender concurrency 128. Sliding-window get timeout.
Chat subsystem (part 2) Publisher rewrite: single persistent worker drains a bounded mpsc(64), batches ≤ --batch-size (default 16) over a serial pipeline (join_all(immutable_put) → mutable_put with 200/500/1000 ms retries → announce). Replaces the per-message tokio::spawn race that previously advertised FeedRecords whose referenced immutables had not yet propagated. Stdin EOF triggers a banner-prefixed graceful drain by default; --stay-after-eof preserves the script-then-watch flow.
Chat subsystem (part 3) Inbox monitor + INBOX status segment + /inbox slash command (shared between chat join and the chat inbox CLI; brief-lock concurrency, lock never held across .await). chat dm becomes a thin consumer of chat::session and gets the TUI / status bar / scroll history / inbox monitor / slash commands / all the same flags for free (join.rs goes from ~666 → ~141 LOC; the bulk of dm's command path now lives in dm_cmd.rs at ~145 LOC, with dm.rs reduced to a small helper shim). NameResolver extracted as canonical pubkey → display-name resolution (friend alias > known screen name > vendor fallback).
Interactive TUI New cmd/chat/tui/ module (mod, status, commands, input, interactive, line, terminal). Auto-selection between interactive TUI (TTY stdin + stdout) and line mode (everything else). Pinned status bar with activity dot, sticky-width left segments, centered INBOX segment (yellow-on-black when unread > 0), and right-side Feeds / DHT / channel segments. Bounded 500-line in-memory scrollback ring replays through the scroll region on resize / Ctrl-L. Ctrl-C semantics: nonempty buffer clears, empty buffer arms a 2 s force-quit window with a transient overlay. Backpressured publisher sends are wrapped in a select! with ctrl_c so SIGINT stays responsive even mid-batch. RAII TerminalGuard restores raw mode / bracketed paste / cursor / colors / scroll region on every exit path (clean shutdown, Ctrl-C, panic).
DHT counter wiring dd progress reads live wire-byte counters from HyperDhtHandle::wire_counters() and displays a W ↑ rate ↓ rate (×amp) line.
CLI ergonomics New global -v / --verbose count flag (warn / info / debug; RUST_LOG overrides). New global --no-public flag. Runtime bootstrap resolution (in cmd/mod.rs::resolve_bootstrap) is two-stage: CLI --bootstrap overrides the config file's network.bootstrap for the base list (not additive), then --public adds default public HyperDHT bootstrap nodes, an empty list auto-fills with the defaults, and --no-public removes the defaults. The legacy --firewalled flag is replaced by --no-public + the auto-public default. Chat-scoped globals: --debug, --probe, --line-mode (env: PEEROXIDE_LINE_MODE=1).
Manpages peeroxide-init(1) and peeroxide-chat(1) join the existing pages. peeroxide init --man-pages [PATH] replaces the old --generate-man <DIR> flag and cleans stale peeroxide*.1 before writing.
Tests New tests/chat_integration.rs (~889 LOC). tests/local_commands.rs expanded from ~225 to ~1063 LOC. tests/live_commands.rs updated for the renamed dd put / dd get surface. Unit-test coverage added across chat::ordering, chat::name_resolver, chat::inbox_monitor, chat::tui::status, chat::tui::interactive, chat::tui::commands, chat::tui::input, and dd v2 v2::need::compute_need_entries.

peeroxide-dht

Area Change
Wire-byte counters New public WireCounters type in io.rs (atomic bytes_sent / bytes_received handles + new + snapshot). Io gains a pub wire: WireCounters field and a wire_counters() accessor. DhtHandle and HyperDhtHandle gain wire_stats() -> (u64, u64) and wire_counters() -> WireCounters. Drives the dd progress amplification line.
rpc.rs Threads the shared WireCounters through DhtHandle and the spawn path. No existing signature changes.
hyperdht.rs Adds public wrapper accessors that delegate to the inner DHT handle. No existing signature changes.

peeroxide

Area Change
Cargo.toml Bumps the peeroxide-dht dependency from 1.2.0 to 1.3.0. No source changes.

Documentation (docs/)

Area Change
New chapters docs/src/init/overview.md; docs/src/chat/{overview,user-guide,interactive-tui,wire-format,protocol,reference}.md.
Rewritten chapters docs/src/dd/{overview,architecture,format,operations,future-direction}.md (now cover v1 + v2 side-by-side; future-direction.md collapsed to a one-liner stating v2 is shipped).
Updated chapters docs/src/SUMMARY.md (adds Setup → init and Commands → chat sections); docs/src/introduction.md (mentions init, chat, dd v1/v2; lists eight primary commands); docs/src/appendices/limits-and-performance.md (new Chat + Dead Drop (v2) constants tables); docs/src/appendices/security-model.md (minor edits); docs/AGENTS.md (small note updates).
Other docs/ascii_art.txt added (decorative ASCII art asset).
Build Verified with mdbook build docs/ — clean build, no broken links.

Repository documentation

Path Change
.github/PULL_REQUEST_TEMPLATE.md New PR template with PR Checklist, Public API Changes section, and Testing checklist.
peeroxide-cli/README.md Updated for the new init, chat, dd v2, and progress flag surface; man-pages section now describes the recursive 23-page output (including nested chat subcommands like peeroxide-chat-profiles-create(1) and peeroxide-chat-friends-add(1)); config-path precedence includes $XDG_CONFIG_HOME and platform dirs::config_dir() lookups.
peeroxide-cli/CHANGELOG.md Adds [Unreleased] entries documenting the dd rename + new progress UX flags + JSON event schema (the chat/init work is implicitly part of the 0.2.0 bump documented in Cargo.toml).
CONTRIBUTING.md Tiny clarification edit.
AGENTS.md, peeroxide-cli/AGENTS.md, docs/AGENTS.md Refreshed for the eight-subcommand surface, the cmd/chat/ tree, the deaddrop split into v1.rs + v2/, current v2 constants (DATA_PAYLOAD_MAX=998, NON_ROOT_INDEX_SLOT_CAP=31, ROOT_INDEX_SLOT_CAP=30, SOFT_DEPTH_CAP=4, etc.), the new chat constants (MAX_RECORD_SIZE=1000, HISTORY_CAP=500, GAP_TIMEOUT=5s, …), and the now-shipped state of dd v2 (the previous "v2 not yet implemented" note is gone).

Version bumps

Crate From To Notes
peeroxide-cli 0.1.0 0.2.0 Binary crate; not subject to library SemVer.
peeroxide-dht 1.2.0 1.3.0 MINOR — additive public API only.
peeroxide 1.2.0 1.2.0 Unchanged source; only its dependency on peeroxide-dht bumped.
libudx 1.2.0 1.2.0 Unchanged.

New peeroxide-cli dependencies (binary only)

blake2, ed25519-dalek, curve25519-dalek, sha2, xsalsa20poly1305, chrono, memmap2, crossterm (with event-stream + bracketed-paste), arc-swap, toml_edit, fs2. All required by the chat or dd-v2 work. No new library-crate dependencies.

Public API Changes

All changes below are: [x] Additive only (semver-compatible) / [ ] Breaking (requires maintainer approval)

Crate Symbol Change
peeroxide-dht io::WireCounters New public struct (bytes_sent: Arc<AtomicU64>, bytes_received: Arc<AtomicU64>).
peeroxide-dht io::WireCounters::new New public fn.
peeroxide-dht io::WireCounters::snapshot New public fn — returns (sent, received).
peeroxide-dht io::Io::wire New public field (additive — Io already had private fields, so adding a new field is non-breaking per Cargo SemVer rules).
peeroxide-dht io::Io::wire_counters New public method.
peeroxide-dht rpc::DhtHandle::wire_stats New public method — returns (u64, u64).
peeroxide-dht rpc::DhtHandle::wire_counters New public method — returns WireCounters.
peeroxide-dht hyperdht::HyperDhtHandle::wire_stats New public method — returns (u64, u64).
peeroxide-dht hyperdht::HyperDhtHandle::wire_counters New public method — returns WireCounters.

No existing public symbols were removed, renamed, or had their signatures changed. No breaking changes in peeroxide, peeroxide-dht, or libudx. peeroxide-cli's CLI/flag surface changed substantially, but it is a binary and not subject to library SemVer rules.

MSRV: unchanged (workspace rust-version = 1.85).

Testing

  • cargo test --workspace — all suites green (unit + integration + Node.js cross-language interop test hyperswarm_cross_language_connect).
  • cargo test -p peeroxide-cli --test live_commands -- --ignored — public-network live tests green: test_live_lookup, test_live_announce_then_lookup, test_live_cp_send_recv, test_live_dd_roundtrip.
  • cargo clippy --workspace --all-targets -- -D warnings — clean.
  • cargo +1.85.0 check --workspace --all-targets — MSRV gate green (let-chain syntax that was failing on the remote Check MSRV workspace job has been rewritten into stable Rust 1.85 control flow in peeroxide-cli/src/cmd/chat/{name_resolver,session,tui/interactive}.rs and peeroxide-cli/tests/chat_integration.rs).
  • mdbook build docs/ — clean build, no broken links.

Notes

  • This branch was long-lived and several themes were reworked along the way (need-list semantics, progress reporter, ctrl-c handling, chat publisher / receiver, DM → chat::session refactor, status-bar inbox segment, resize replay). The description above reflects the end state, not the commit history.
  • The shipped dd v2 wire byte is 0x02. The design was iterated under the internal working name "v3"; the canonical spec at peeroxide-cli/DEADDROP_V2.md and all user-facing surfaces use v2, and in-tree source comments now point at DEADDROP_V2.md and the new docs/src/dd/ chapters (no remaining DEADDROP_V3.md references).
  • The per-deaddrop salt byte in v2 data chunk headers is currently forced to 0x00 (the slot is reserved for future per-deaddrop randomization). Documented honestly in docs/src/dd/format.md.
  • The chat dm and chat join interactive sessions share the same chat::session orchestration so any future session-level feature (status segment, slash command, shutdown step) lands in one place for both.
  • The chat MAX_RECORD_SIZE budget is 1000 B per record; combined with the 180 B envelope overhead this gives ≤ 820 B for screen_name + content. Over-length content surfaces WireError::RecordTooLarge at point-of-attempt with a clear error; multi-frame / continuation messages are out of scope by design.

Working files cleanup

Design notes, implementation prompts, and working spec drafts that lived next to the code during development have been folded into the docs/ mdBook or deleted from the working tree. None of them ship in the published peeroxide-cli crate. Summary of what happened to each:

Path Lines Disposition
INIT_COMMAND_PLAN.md (root) 84 Always gitignored via *_PLAN.md; never tracked. Not in this PR. Implementation now documented in docs/src/init/overview.md.
peeroxide-cli/CHAT.md 1457 Deleted. Content folded into docs/src/chat/{wire-format,protocol}.md.
peeroxide-cli/CHAT_CLI.md 448 Deleted. Content folded into docs/src/chat/{user-guide,interactive-tui,reference}.md.
peeroxide-cli/CHAT_IMPL_PROMPT.md 305 Deleted. Implementation prompt; no shipped equivalent needed.
peeroxide-cli/DEBUG_FLAG.md 22 Deleted. The --debug flag is implemented and documented in docs/src/chat/user-guide.md and docs/src/chat/reference.md.
peeroxide-cli/DHT_REF.md 112 Deleted. Generic content folded into docs/src/concepts/dht-primitives.md; chat-specific size math dropped (already covered by docs/src/chat/reference.md).
peeroxide-cli/DEADDROP_V2.md 493 Deleted. dd v2 is now documented across docs/src/dd/{architecture,format,operations}.md.
docs/ascii_art.txt 9 Decorative ASCII-art asset. Pending review: keep as a future logo/banner, or remove if unused.

(The .claude/, .mcp.json, and testfile paths in the workspace root are untracked / gitignored and not in the PR.)

eshork and others added 29 commits May 13, 2026 23:06
Rename the CLI command for brevity and clarity:
- `deaddrop` → `dd` (short for "Dead Drop")
- `deaddrop leave` → `dd put`
- `deaddrop pickup` → `dd get`

Bump peeroxide-cli to 0.2.0 for the breaking CLI change.

Add stale man page cleanup to `init --man-pages` so renamed
pages (e.g. peeroxide-deaddrop.1) are automatically removed.

Update all documentation, tests, and man page content to
reflect the new naming.
…trap resolution

Remove the --firewalled flag and simplify --public/--no-public semantics.
The public flag now controls inclusion of default HyperDHT bootstrap
nodes rather than firewall advertisement:

- public=Some(true): add DEFAULT_BOOTSTRAP to bootstrap list
- public=None + empty bootstrap: auto-add DEFAULT_BOOTSTRAP
- public=Some(false): remove DEFAULT_BOOTSTRAP entries

Extract resolve_bootstrap() with additive merge logic, remove
FIREWALL_OPEN/FIREWALL_CONSISTENT propagation from CLI to SwarmConfig,
and update tests (3×6 matrix, integration) to match new semantics.
Introduces a verbose flag (count-based: -v for info, -vv for debug) that
controls tracing output without requiring RUST_LOG. At -v, shows config
source and resolved bootstrap nodes; at -vv, adds decision logic and DHT
internals. RUST_LOG still takes precedence when set.
Co-authored-by: Copilot <copilot@github.com>
Every profile now always has a screen name. When no name is
user-supplied, one is derived deterministically from the profile's
32-byte Ed25519 seed using Docker-style word lists.

- Add names.rs: vendored ADJECTIVES (108) + SURNAMES (236) with
  generate_name_from_seed(&[u8; 32]) -> String
- Wire into create_profile: always writes name file; derives from
  seed when screen_name is None
- Wire into load_profile: derives name on the fly when name file
  is absent (existing profiles get a name without disk writes)
- Add unit tests: determinism, format, underscore count, seed
  independence; profile tests for user-name preservation and
  seed-derived name format

No new crate dependencies. Uses rand 0.9 StdRng already in tree.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
eshork added 18 commits May 13, 2026 23:06
- chat/overview.md: known_users moved out of the 'Each profile
  includes' bullet list and into its own paragraph that explicitly
  calls it process-wide and not per-profile. Each profile now lists
  only its actual local contents (seed, optional name, optional bio,
  friends list).

- peeroxide-cli/CHAT.md, CHAT_IMPL_PROMPT.md, DEBUG_FLAG.md, DHT_REF.md:
  each working/historical design document now carries the same
  'proposed for removal / see docs/src/' banner already on CHAT_CLI.md
  and DEADDROP_V2.md, so any reader stumbling onto one of them is
  pointed at the canonical shipped documentation in docs/src/.
- chat/protocol.md: DM nudges are NOT empty announces. They are
  encrypted InviteRecord mutable_puts (lure truncated to 800 bytes)
  followed by an announce on the recipient's inbox topic, matching
  the regular inbox-invite write path. (inbox.rs)

- dd/format.md: the v2 receiver-done sentinel is a raw empty byte
  string at the need topic, NOT the encoded need-list with count=0.
  The decoder treats zero-byte values specially. (fetch.rs / need.rs)

- dd/format.md: define what need-list {start, end} entries actually
  mean (half-open [start, end) ranges of data-chunk indices in the
  canonical DFS file order; sender republishes those chunks plus
  the full index-tree path needed to reach them).

- chat/tui/interactive.rs: the stale comment on the history VecDeque
  said the ring is bounded to 10_000 lines, but HISTORY_CAP is 500.
  Comment now states the actual constant.
- chat/overview.md: sender does not post an Invite record TO the inbox
  topic. They generate a one-shot invite-feed keypair, mutable_put the
  encrypted InviteRecord at that feed, and then announce that feed on
  the recipient's current inbox topic. Matches inbox.rs::send_dm_invite
  and send_private_invite (and send_dm_nudge for nudges).

- dd/architecture.md: the v2 need-list lifecycle has two distinct
  cadences:
    - mutable_put of the encoded missing-range need-list every 20s
      (need_publish_interval)
    - announce keepalive on the need topic every 60s
      (NEED_REANNOUNCE_INTERVAL via run_need_announcer)
  Documenting both instead of conflating them into 'announce ranges
  every 20s'.
…v2/mod.rs

- docs/src/dd/{overview,architecture}.md: 'Merkel tree' -> 'Merkle tree'.
- docs/src/dd/operations.md: add a paragraph noting the inherited
  top-level globals (--config, --no-default-config, --public, --no-public,
  --bootstrap, -v) that dd put / dd get also accept, with a cross-link
  to init/overview.md's bootstrap-resolution algorithm.
- peeroxide-cli/src/cmd/deaddrop/v2/mod.rs: remove the leftover 'working
  name v3' historical breadcrumb from the module docstring.
dd put / dd get also accept --config, --no-default-config, --public,
--no-public, --bootstrap (repeatable), and -v/--verbose. These are
inherited from the top-level Cli struct (see init/overview.md for
the bootstrap-resolution algorithm). The previous edit attempt missed
this section because operations.md was rewritten with a different
section structure than I'd assumed.
…semantics

The earlier wording said 'start with the combined list from the config
file and --bootstrap CLI flags', but config.rs uses
'flags.bootstrap.clone().or(file_config.network.bootstrap)' — i.e. CLI
--bootstrap OVERRIDES the file's network.bootstrap, it does not
concatenate.

Split the resolution into two explicit stages: (1) base-list selection
via the override rule in config.rs, (2) the public-default adjustment
in resolve_bootstrap. Also note that init itself uses its own local
--public/--bootstrap to populate the generated config and does not
run this resolution.
…, PR body

CLI --bootstrap overrides the config file's network.bootstrap; it
does not combine with it. Earlier copy in three places still said
'config + CLI merge' / 'additive (config + CLI)', which doesn't match
peeroxide-cli/src/config.rs's flags.bootstrap.or(file_config...) call.

- docs/src/init/overview.md: drop 'config/CLI merge' from the section
  intro and from the trailing init-vs-runtime note.
- peeroxide-cli/AGENTS.md: rewrite resolve_bootstrap docstring to
  state CLI overrides config, then describe the public-default
  adjustment.
- PR body (gh pr edit): replace the 'additive (config + CLI → ...)'
  sentence with the correct two-stage description.
These three docs predate the new bootstrap-resolution rules and the
auto-public default. They claimed (incorrectly) that the public
bootstrap set requires --public, that the node is isolated without
--public, and that cp's --public both selects public bootstraps AND
flips the firewall open.

Reality (per peeroxide-cli/src/config.rs and cmd/mod.rs::resolve_bootstrap):
- CLI --bootstrap OVERRIDES config bootstraps (not combine).
- --public adds default public bootstrap nodes; an empty list
  auto-fills with the defaults; --no-public removes them.
- cp uses build_dht_config(cfg) like every other runtime command;
  --public does not flip the firewall state.

Rewrite all three sections to match.
…ment cold-start scan

- peeroxide-cli/src/cmd/init.rs: --public --help no longer claims the
  node is 'publicly reachable (not behind NAT/firewall)'; it now
  describes what the flag actually does: marks network.public = true
  in the generated config so runtime subcommands add default public
  HyperDHT bootstrap nodes. Generated config comments correspondingly
  updated to describe the real bootstrap-resolution rule (CLI override,
  auto-public defaults, --no-public removes).

- docs/src/chat/protocol.md: Reader Discovery Loop section now also
  documents the cold-start historical scan (20 epochs × 4 buckets = 80
  concurrent lookups on session start, ~20-minute backward window),
  matching peeroxide-cli/src/cmd/chat/reader.rs:240-246. Previously
  only the steady-state 8-lookup loop was documented.
--public / network.public only drives bootstrap node selection at
runtime; it does not flip firewall semantics. The 'Open if public=true,
Consistent otherwise' wording in the Swarm Setup bullet predates the
new bootstrap-resolution rules and conflicted with the corrected
init/overview.md, cp/architecture.md, and cp/protocol.md.

Replace with an honest description and a cross-link to the canonical
flag-resolution section in init/overview.md.
The peeroxide-cli/DHT_REF.md working file is one of the candidate-removal
docs in the PR, but sections A.1-A.4 (plus the generic part of A.5) are
broadly useful as a 'what operations does peeroxide-dht expose' reference
for anyone implementing on top of the DHT layer. Rather than deleting
that content, move it into the docs/ mdBook:

- docs/src/concepts/dht-primitives.md: replace the 'Content coming in
  Phase 3a' stub with the four-primitive reference. immutable_put/get,
  mutable_put/get, announce/lookup, and TTL each get an explanatory
  paragraph plus a property table. The mutable_put 1002-byte size-budget
  derivation is kept because it is broadly useful for protocol authors;
  the chat-specific size math from DHT_REF.md A.5 is dropped because the
  chat::wire constants are already documented in docs/src/chat/reference.md.
- docs/src/SUMMARY.md: list the new chapter under # Concepts.
- docs/src/dd/{architecture,format}.md and docs/src/chat/wire-format.md:
  add a one-sentence cross-link to the new primitives reference.
- peeroxide-cli/DHT_REF.md: delete from the working tree (content
  preserved in the mdBook chapter).

Net working-files removal candidates count goes down by one.
…ttern

Earlier wording dismissed announce/lookup as 'not general-purpose value
storage', but chat and dd v2 both deliberately use it as a general
rendezvous mechanism: the announcer's 32-byte pubkey acts as a pointer
to a further mutable_put slot owned by the same keypair. That sidesteps
mutable_put's single-writer-per-pubkey limitation and lets readers
parallelize one lookup followed by N parallel mutable_gets.

Add a 'The Rendezvous Pattern' subsection that:
- Spells out the 3-step pattern (topic derivation -> ephemeral keypair
  + mutable_put + announce -> reader lookup + parallel mutable_gets).
- Tabulates the four concrete uses in this workspace:
  * chat::crypto::announce_topic   -> FeedRecord
  * chat::crypto::inbox_topic      -> InviteRecord
  * dd::v2::keys::need_topic       -> encoded need-list
  * dd::v2::keys::ack_topic        -> (announcer count only)
- Notes that relay_addresses is left empty in all of those uses and
  that epoch+bucket rotation bounds traffic-analysis exposure.

Soften the section intro accordingly.
…vous cleanup

Working files folded into docs/src/ (see prior commit 0ba7795 for the
DHT_REF.md fold-in) or otherwise superseded by the mdBook chapters are
now deleted from the working tree:

- peeroxide-cli/CHAT.md            (chat protocol design notes)
- peeroxide-cli/CHAT_CLI.md        (chat CLI design notes)
- peeroxide-cli/CHAT_IMPL_PROMPT.md (chat implementation prompt)
- peeroxide-cli/DEADDROP_V2.md     (dd v2 design / spec; superseded by
                                    docs/src/dd/{architecture,format,operations}.md)
- peeroxide-cli/DEBUG_FLAG.md      (working note for the chat --debug flag)

Also:

- .gitignore: drop redundant literal task-artifact filenames now matched
  by the *_PLAN.md / *_PROMPT.md / *_TODOS.md globs; add *_TODO.md to
  the same family; add .claude/ and .mcp.json (local agent / MCP state
  that should never land in git).
- SECURITY.md: drop the '48 hours' SLA from the vulnerability-report
  acknowledgement line; keep the 90-day resolution target.
- docs/src/concepts/dht-primitives.md: rewrite the rendezvous-pattern
  section as a generic concept. An announce 'topic' is just a 32-byte
  address; the announcer's pubkey acts as a pointer to a further
  mutable_put slot. Add a section on extending the 20-announcers-per-
  topic-per-node cap with epoch/bucket salt in the topic-derivation
  hash. Push concrete chat/dd uses out to those subsystems' own
  protocol/wire-format chapters instead of duplicating them here.
peeroxide chat had 14 generated man pages (one per leaf subcommand,
including nested profiles/friends groups). Following the cp / dd
pattern, consolidate them into a single peeroxide-chat(1).

manpage.rs:
- Add 'peeroxide-chat' to the CONSOLIDATED list.
- Extend the consolidated renderer to recurse into nested subcommand
  groups. Leaves now render as .SS join, .SS dm, ... and nested leaves
  as .SS profiles list, .SS friends add, etc.
- Surface the parent command's own non-global args in an OPTIONS
  section before COMMANDS, guarded so cp/dd (which have no non-global
  parent args) still produce no spurious OPTIONS section.
- Add a peeroxide-chat long_about covering identity model, channels
  vs DMs, DHT rendezvous discovery, the encryption/signing model,
  and TUI-vs-line-mode auto-selection.
- Add per-leaf long_about for the 14 chat leaves and groups: join,
  dm, inbox, whoami, profiles + list/create/delete, friends + list/
  add/remove/refresh, nexus.
- Add 9 worked examples for peeroxide-chat.
- Add peeroxide-chat to the standard exit-status block and see-also
  entries; cross-link from the top-level peeroxide(1) SEE ALSO list.
- Refresh peeroxide-dd long_about to document both v1 (0x01) and v2
  (0x02), the soft depth cap of 4 (~27 GB at default chunk size), and
  the auto-dispatch on the get side.
- Add 3 new peeroxide-dd-put examples covering --v1, --no-progress,
  --json; one new --json example on the get side.
- Strip roff escape codes (\fB...\fR) from the top-level long_about
  entries, since clap_mangen's render_description_section escapes
  them and they were showing up as literal text in DESCRIPTION.

peeroxide-cli/README.md: drop the prior 23-page disclosure; man pages
are back to 9 (one per top-level command).

Verified: 'peeroxide init --man-pages /tmp/peeroxide-man-check' produces
exactly 9 pages, and 'mandoc /tmp/.../peeroxide-chat.1' renders cleanly
with proper SYNOPSIS / DESCRIPTION / OPTIONS / COMMANDS (including
nested profiles/friends leaves) / EXAMPLES / EXIT STATUS / SEE ALSO.
The nexus section described --set-name / --set-bio but did not tell
the reader where the underlying name and bio files actually live, that
they can be edited directly, or what the practical size budget is.

Add a 'Screen Name and Bio Files' subsection covering:
- File locations under ~/.config/peeroxide/chat/profiles/<profile>/
- Both files are optional (vendor-name fallback when name is absent)
- Two ways to populate: --set-name/--set-bio (with trim semantics) or
  edit directly. Multi-line bios are supported; the friends list shows
  the cached first line of each friend's bio (per reference.md), but
  chat nexus --lookup shows the full record.

Add a 'Size Limit' subsection covering:
- 1000-byte cap on the NexusRecord (3 framing bytes + name + bio bytes)
- Practical bio budget of ~950-990 UTF-8 bytes with a typical screen name
- Exact error message on overflow ('record too large: N bytes exceeds
  1000 byte limit') and the recovery path (file is still saved on disk;
  only the publish is skipped).
The chat docs previously mentioned --stealth only as a one-liner
('Shorthand for --no-nexus --read-only --no-friends'), with no
guidance on when stealth is sufficient, what it does not protect
against, or how it interacts with --no-inbox.

Add a new top-level 'Stealth Mode' section in docs/src/chat/user-guide.md
between 'Personal Page: nexus' and 'Interactive Usage':

- What --stealth suppresses: the three publishing-side operations,
  with the exact DHT primitive each suppresses (announce on rendezvous
  topics, mutable_put of FeedRecord/NexusRecord, periodic mutable_get
  on friends' identity pubkeys).
- What --stealth does NOT suppress: channel discovery still issues
  lookups + per-announcer mutable_gets; inbox monitoring is independent
  and keeps polling the profile's inbox topics; DM under stealth is
  receive-only; network-level metadata (IP visible to DHT peers) is
  unchanged.
- Inbox-vs-pubkey note: the wire-level inbox lookup does NOT carry the
  pubkey (the topic is keyed_blake2b(hash(pubkey), ...)), but an
  observer who already knows the pubkey can derive the same topic and
  correlate the polling source IP. Pair with --no-inbox if that matters.
- When --stealth is enough: fresh profile, lurking before posting.
- When --stealth is not enough: pubkey already known + IP correlation
  matters. Calls out the exploitable chain (pubkey -> derived inbox /
  Nexus / announce topics -> DHT lookups from your IP) and points at
  a trustworthy VPN (different egress, traffic mixing, no per-flow
  logging) as the appropriate transport-layer mitigation.
- Three recipes: --stealth, --stealth --no-inbox, --stealth --profile burner.

Also update docs/src/chat/reference.md: the terse --stealth row now
notes that the flag does NOT suppress inbox polling, with a cross-link
to the new user-guide section.
docs/ascii_art.txt grew from a stray decorative file into the project's
canonical banner. The original taglines were claim-checked against the
shipped code and two of them needed rewording:

  ENCRYPTED BY DEFAULT.   (kept: true for chat / cp; nuance documented
                           in the protocol docs)
  ANONYMOUS BY DESIGN.    -> PSEUDONYMOUS BY DESIGN.
                           (peeroxide does not provide transport-layer
                           anonymity; see chat user-guide Stealth Mode
                           for the full breakdown)
  SPEAK FREELY.           -> NO SERVERS. NO ACCOUNTS. NO GATEKEEPERS.
  LEAVE NO TRACE.         (replaced; local files persist and DHT-side
                           queries are visible to peers serving them)
  TRUST NO ONE.           (kept: aspirational, refers to the no-central-
   TALK TO ANYONE.         authority DHT model)

Embed the banner in three places:

- peeroxide-cli/src/main.rs: a new LONG_VERSION const composes
  env!('CARGO_PKG_VERSION') + the ascii_art.txt include_str! contents
  and is wired through clap's #[command(long_version = ...)]. Result:
  'peeroxide -V' stays terse ('peeroxide 0.2.0', script-friendly) and
  'peeroxide --version' shows the banner with the version header on top.
- peeroxide-cli/README.md: code-fenced banner block at the top of the
  crate README so crates.io / GitHub readers see it first.
- docs/src/introduction.md: same banner block at the top of the mdBook
  introduction.
peeroxide-cli 0.2.0: new chat and init commands, dd v2 protocol,
progress UX, global --no-public/-v flags, consolidated chat manpage,
new mdBook chapters, embedded ASCII banner. Removes legacy
'config init', '--generate-man', and '--firewalled'.

peeroxide-dht 1.3.0: additive WireCounters API and wire_stats /
wire_counters accessors on Io, DhtHandle, and HyperDhtHandle. No
breaking changes.

peeroxide (unreleased): notes the peeroxide-dht 1.2 to 1.3 dep bump.
…stall guide

Now that peeroxide-cli ships to crates.io and via the
rightbracket/peeroxide homebrew tap, the top-level docs need to reflect
that:

  * README:
    - Architecture diagram gains peeroxide-cli as the top layer.
    - New 'Install the CLI' section between Architecture and the
      library-focused Quick Start: brew (auto-tap three-segment form),
      cargo, and brew --HEAD for build-from-source. Includes the
      prebuilt platform matrix and links to the tap.
    - Existing 'Quick Start' renamed 'Quick Start (library)' for
      clarity since casual users will land on Install first.
    - Crates list gains a peeroxide-cli row with the crates.io badge.

  * AGENTS.md:
    - Workspace crates table flips peeroxide-cli's 'Published' column
      from 'binary only' to 'crates.io + homebrew tap'.
    - Prose follow-up rewritten to match.
…, drop chat-internal noise

Trim the [0.2.0] section to focus on what's new and notable for the
upgrading user rather than exhaustively enumerating chat's flag surface
(every chat flag is by definition new because the whole subcommand is
new). Net: 56 -> 41 bullets, 91 -> 76 lines.

Specifically:

  * Collapsed the seven chat-specific Added bullets (chat join, chat dm,
    interactive TUI, Ctrl-C semantics, inbox monitor, parallel scan,
    burst-ordered publishing, scrollback preserve, history replay, probe
    flag) into one solid 'peeroxide chat' bullet that describes what it
    IS and refers readers to docs/src/chat/ for the full reference.
  * Dropped chat-internal Fixed bullets (Ctrl-C backpressure
    responsiveness, per-feed chain anchoring) — those bugs only existed
    during this development cycle and never shipped publicly because
    'chat' itself is new in 0.2.0.
  * Dropped chat-internal Changed bullets (EOF graceful drain, auto
    line-mode, chat dm full TUI consumer) for the same reason.
  * Surfaced the library API consumption explicitly: the dd progress
    display bullet now names the new peeroxide-dht 1.3.0 wire-counter
    API (HyperDhtHandle::wire_stats / wire_counters) and points readers
    at peeroxide-dht/CHANGELOG.md for the full additive symbol set.

dd renames, bootstrap resolution, --json behavior, man-page
consolidation, dd v2 + progress UI, and the brew tap distribution all
stay — those affect anyone upgrading from 0.1.0.
@eshork eshork changed the title fix_init: init command, dd v2 tree protocol, progress UX, chat subsystem peeroxide-cli 0.2.0: chat subsystem, init command, dd v2 tree protocol, progress UX May 14, 2026
@eshork eshork marked this pull request as ready for review May 14, 2026 04:00
@eshork eshork merged commit 3f1d288 into main May 14, 2026
8 checks passed
@eshork eshork deleted the fix_init branch May 14, 2026 04:11
This was referenced May 14, 2026
Sign up for free to 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