first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
# advertise
## Purpose
Encodes and decodes `cd:advertise` envelope payloads — the gossip messages a peer emits to announce which chunks it currently holds for a given file. Enforces DMP-08 (presence bitset's `bitLength` MUST equal `totalChunks`; padding bits MUST be zero), DMP-09 (generation field is unsigned 32-bit), and DMP-21 (presence MUST be framed via `@suaveplan/bitpacker-compressed`).
## Features
- `encodeAdvertisePayload(payload)` — Zod-validates, cross-checks the presence bitset's embedded `bitLength` against `totalChunks`, base64-wraps the bitset, and emits UTF-8 JSON bytes.
- `decodeAdvertisePayload(bytes)` — inverse, with the same DMP-08 cross-check on the receive side so receivers never trust a malformed advertisement.
- Both functions throw `DmpError` with `DMP.BITSET_LENGTH_MISMATCH`, `DMP.BITSET_PADDING_NONZERO`, `DMP.CODEC_MISMATCH`, or `DMP.ENVELOPE_MALFORMED` depending on the failure class.
## Usage: Basic Example
```ts
import {
chunkIdsToRawBitset,
decodeAdvertisePayload,
encodeAdvertisePayload,
encodeFramedBitset,
} from "@suaveplan/drive-mesh-protocol";
const totalChunks = 64;
const raw = chunkIdsToRawBitset([0, 5, 17, 63], totalChunks);
const presence = encodeFramedBitset(raw, totalChunks);
const wire = encodeAdvertisePayload({
fileId: "f".repeat(64),
presence,
totalChunks,
generation: 7,
timestamp: Date.now(),
});
const back = decodeAdvertisePayload(wire);
```
## Usage: Advanced Example
```ts
import {
chunkIdsToRawBitset,
decodeAdvertisePayload,
encodeAdvertisePayload,
encodeFramedBitset,
GenerationTracker,
} from "@suaveplan/drive-mesh-protocol";
const tracker = new GenerationTracker();
function handleIncomingAdvertise(bytes: Uint8Array, peerId: string): void {
const adv = decodeAdvertisePayload(bytes);
const r = tracker.observe(peerId, adv.fileId, adv.generation);
if (!r.accept) return; // DMP-09 stale-drop
// Update peer-index for (peerId, adv.fileId) using adv.presence.
}
```
## API Reference
- `encodeAdvertisePayload(payload: ChunkAdvertisePayload): Uint8Array`.
- `decodeAdvertisePayload(bytes: Uint8Array): ChunkAdvertisePayload`.
- Errors: `DMP.BITSET_LENGTH_MISMATCH`, `DMP.BITSET_PADDING_NONZERO`, `DMP.CODEC_MISMATCH`, `DMP.ENVELOPE_MALFORMED`.
## Implementation Notes
The encode-side cross-check between `bitLength` (recovered from the framed presence bitset) and `totalChunks` is intentional defence-in-depth: it stops a bug in the issuer from emitting an advertisement whose receivers would later reject. Generations are validated up to `0xFFFF_FFFF` at the schema layer; wrap-detection logic lives in `generation/monotonic.ts`. The `unwrapWire` helper passes through bare nulls / non-objects so the schema is the single source of "this isn't a valid envelope" rejection — every malformed input ends up at the schema gate, not at a JS-level type error.