Skip to content

hive/bp7: hum as a DTN endpoint, holding what space hands it until the ensemble answers - #99

Merged
adiled merged 1 commit into
mainfrom
hive/bp7
Oct 25, 2026
Merged

adiled merged 1 commit into
mainfrom
hive/bp7

Conversation

@adiled

@adiled adiled commented Oct 25, 2026

Copy link
Copy Markdown
Owner

An audit of the bp7 forager against a real humd, with a DTN node on the other end, found it unsafe on any real DTN path: one asker's answer went to another, every duplicate bundle was grown again, an ask was lost to any restart, and replies went to a port nobody listens on. Against the old forager, 1 of the 19 DTN tests below passes. This rebuilds the forager as a DTN endpoint and proves it end to end, as far as a nurse on another humd of the ensemble.

What was wrong

  • A bloom's sid was bp7-{now_ms}, so two bundles in the same millisecond, even at different foragers, shared a bloom: one asker got another's answer and the other's was lost.
  • DTN delivers at least once; each copy of a bundle got its own inference and its own reply.
  • A bundle was dropped when humd was away mid-bloom, when it refused, or when the forager restarted.
  • Replies went to the datagram's source address, a router's ephemeral send port, and an answer over a datagram (9 KB on macOS) failed to send at all.
  • Replies had no CRC (RFC 9171 4.3.1), a fixed hour of lifetime, and asked for a delivery report addressed to the forager, which then answered the report as a prompt.
  • Expired, corrupted and anonymous bundles were answered, and so were bundles for any endpoint that began with ours. Each fragment of a fragmented ask was grown as a prompt of its own.
  • Every bundle spawned a bloom and a link with no bound; the README's run command and example CLI did not exist.

The forager now

  • dtn.rs: a bundle is an ask, a piece of one, or dropped for a reason: CRC, RFC 9171's checks, another endpoint, an administrative record, hum's own answer, no source, past its lifetime (5.5; by its age when it has no clock, 4.4.2). An answer is one bundle to the asker, CRC-32 on every block, the ask's lifetime, no reports asked for, hop limit 32, and block 193 {"hum": "reply", "to": <ask id>, "ok": ...}; longer than BP7_MTU (1232, any IPv6 path) it is fragmented, block 193 in every fragment (5.8; RFC 7122 3.2).
  • spool.rs: an ask is on disk before anything is done with it and stays until its answer has gone home. A copy, whole or in pieces, is a duplicate until the ask expires; pieces in any order and overlap make one whole, up to 4 MiB; at most BP7_HOLD asks are held.
  • hum.rs: one bee for the life of the process. Each bloom's sid and mid are the ask's key and attempt, its dusk the ask's expiry. humd going away, or no-nurse, tries the ask again with backoff until it expires; any other error is answered as what broke.
  • BP7_ROUTE sends every answer to the router; BP7_ALLOW takes bundles only from the IPs named (RFC 7122 5); BP7_BLOOMS bounds the blooms; BP7_LABEL lets two foragers share a humd. The Orchfile passes every setting through, a bad one stops the forager with the reason on stderr, and the README is rewritten for what it does.

bp7 0.10.7 to 0.12.1

  • 0.10.7's validate() refused every RFC-conformant bundle from a node with no clock (the 4.4.2 check was inverted), and its creation timestamps could repeat across threads; 0.11 fixes both. 0.12 brings BPSec, the way to authenticated asks.

Tests (cd hives/bp7 && cargo test, against a real humd)

  • tests/dtn.rs, 19: duplicates, a 16-bundle burst, bounded blooms, expiry, status reports, loops, conformance of the reply, fragments both ways, corruption, anonymity, another endpoint, a broken bloom, waiting for a nurse, a forager restart, the spool forgetting.
  • tests/outlive.rs: an ask mid-bloom and one arriving while humd is away are both answered when it is back.
  • tests/ensemble.rs: a bundle lands on a forager whose humd has no nurse and is grown by a nurse on another humd, over TCP. examples/humd.rs is the near humd.
  • 7 unit tests on bundles and the spool. Ten runs in a row, all green.

What is not yet (in the README)

  • An ask whose grower leaves the ensemble mid-bloom waits out its lifetime: humd removes the peer but never breaks off the blooms it was growing, and keeps each bloom's grower after it wilts. tests/grower_gone.rs reproduces it, ignored until humd is fixed.
  • humd keeps every sid a connection opens until it closes, so a long-lived forager grows humd's memory with every bloom.
  • No BPSec, custody or status reports yet; UDP only; one answer per ask.

Found on the way, for the kernel

  • Heard once is held only for tones that cross the ensemble; humd's own socket never drops a repeated mid.
  • hum_identity::enter_as is check-then-act on the filesystem: two processes entering one hive and label at once exit each other's entry, and humd refuses the loser.

…e ensemble answers

An audit of the bp7 forager against a real humd, with a DTN node on the other end, found it unsafe on any real DTN path: one asker's answer went to another, every duplicate bundle was grown again, an ask was lost to any restart, and replies went to a port nobody listens on. Against the old forager, 1 of the 19 DTN tests below passes. This rebuilds the forager as a DTN endpoint and proves it end to end, as far as a nurse on another humd of the ensemble.

**What was wrong**
- A bloom's sid was `bp7-{now_ms}`, so two bundles in the same millisecond, even at different foragers, shared a bloom: one asker got another's answer and the other's was lost.
- DTN delivers at least once; each copy of a bundle got its own inference and its own reply.
- A bundle was dropped when humd was away mid-bloom, when it refused, or when the forager restarted.
- Replies went to the datagram's source address, a router's ephemeral send port, and an answer over a datagram (9 KB on macOS) failed to send at all.
- Replies had no CRC (RFC 9171 4.3.1), a fixed hour of lifetime, and asked for a delivery report addressed to the forager, which then answered the report as a prompt.
- Expired, corrupted and anonymous bundles were answered, and so were bundles for any endpoint that began with ours. Each fragment of a fragmented ask was grown as a prompt of its own.
- Every bundle spawned a bloom and a link with no bound; the README's run command and example CLI did not exist.

**The forager now**
- `dtn.rs`: a bundle is an ask, a piece of one, or dropped for a reason: CRC, RFC 9171's checks, another endpoint, an administrative record, hum's own answer, no source, past its lifetime (5.5; by its age when it has no clock, 4.4.2). An answer is one bundle to the asker, CRC-32 on every block, the ask's lifetime, no reports asked for, hop limit 32, and block 193 `{"hum": "reply", "to": <ask id>, "ok": ...}`; longer than `BP7_MTU` (1232, any IPv6 path) it is fragmented, block 193 in every fragment (5.8; RFC 7122 3.2).
- `spool.rs`: an ask is on disk before anything is done with it and stays until its answer has gone home. A copy, whole or in pieces, is a duplicate until the ask expires; pieces in any order and overlap make one whole, up to 4 MiB; at most `BP7_HOLD` asks are held.
- `hum.rs`: one bee for the life of the process. Each bloom's sid and mid are the ask's key and attempt, its dusk the ask's expiry. humd going away, or `no-nurse`, tries the ask again with backoff until it expires; any other error is answered as what broke.
- `BP7_ROUTE` sends every answer to the router; `BP7_ALLOW` takes bundles only from the IPs named (RFC 7122 5); `BP7_BLOOMS` bounds the blooms; `BP7_LABEL` lets two foragers share a humd. The Orchfile passes every setting through, a bad one stops the forager with the reason on stderr, and the README is rewritten for what it does.

**bp7 0.10.7 to 0.12.1**
- 0.10.7's `validate()` refused every RFC-conformant bundle from a node with no clock (the 4.4.2 check was inverted), and its creation timestamps could repeat across threads; 0.11 fixes both. 0.12 brings BPSec, the way to authenticated asks.

**Tests** (`cd hives/bp7 && cargo test`, against a real humd)
- `tests/dtn.rs`, 19: duplicates, a 16-bundle burst, bounded blooms, expiry, status reports, loops, conformance of the reply, fragments both ways, corruption, anonymity, another endpoint, a broken bloom, waiting for a nurse, a forager restart, the spool forgetting.
- `tests/outlive.rs`: an ask mid-bloom and one arriving while humd is away are both answered when it is back.
- `tests/ensemble.rs`: a bundle lands on a forager whose humd has no nurse and is grown by a nurse on another humd, over TCP. `examples/humd.rs` is the near humd.
- 7 unit tests on bundles and the spool. Ten runs in a row, all green.

**What is not yet** (in the README)
- An ask whose grower leaves the ensemble mid-bloom waits out its lifetime: humd removes the peer but never breaks off the blooms it was growing, and keeps each bloom's grower after it wilts. `tests/grower_gone.rs` reproduces it, ignored until humd is fixed.
- humd keeps every sid a connection opens until it closes, so a long-lived forager grows humd's memory with every bloom.
- No BPSec, custody or status reports yet; UDP only; one answer per ask.

**Found on the way, for the kernel**
- Heard once is held only for tones that cross the ensemble; humd's own socket never drops a repeated `mid`.
- `hum_identity::enter_as` is check-then-act on the filesystem: two processes entering one hive and label at once exit each other's entry, and humd refuses the loser.
@adiled
adiled changed the base branch from thrum/hum-thrum to main October 25, 2026 19:39
@adiled adiled closed this Oct 25, 2026
@adiled adiled reopened this Oct 25, 2026
@adiled
adiled merged commit 573e03f into main Oct 25, 2026
7 checks passed
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