docs: de-link spike findings from spike/ code (moved to private repo)

This commit is contained in:
vjrj 2026-07-15 20:27:45 +02:00
parent 2d4dec5304
commit 5b64fe14b3

View file

@ -1,9 +1,9 @@
# Spike — Block 2 (social layer) de-risking findings
*Throwaway research spike. Language: English (findings), per code convention.
Code lives in [`spike/block2_spike/`](../../spike/block2_spike/) and is marked
throwaway — **not production, not on the pub workspace, no deps added to Block
1**. This document is the deliverable: what we learned, the risks, and a
The throwaway spike code (`spike/block2_spike/`) has been moved out of this
public repo and preserved privately — it was **not production, not on the pub
workspace, no deps added to Block 1**. This document is the deliverable: what we learned, the risks, and a
recommendation on whether/how to take on the social round (Phase 3).*
> **Status: this is a spike, not a green light to build Block 2.** CLAUDE.md and
@ -34,7 +34,7 @@ See "How to run" at the end.
**Result: works, deterministic, one-way. This unblocks "one identity, one backup".**
- Implemented [`NostrKey.deriveFromSeed`](../../spike/block2_spike/lib/src/nostr_key.dart):
- Implemented `NostrKey.deriveFromSeed`:
`HKDF-SHA256(rootSeed, info: "org.comunes.tane/nostr/secp256k1/v1")` → reduce
to a valid secp256k1 scalar `d ∈ [1, n-1]` by rejection (bump a counter in the
HKDF info on the astronomically-rare miss, so the result stays a pure function
@ -80,13 +80,13 @@ trust do NOT fit behind the same interface — they share a *connection*, not a
*contract*.**
### The seam works (offers)
- [`OfferTransport`](../../spike/block2_spike/lib/src/offer_transport.dart) =
- `OfferTransport` =
`publish(Offer)` / `discover(DiscoveryQuery)` / `retract(id)`. The Nostr
NIP-99 backend ([`NostrOfferTransport`](../../spike/block2_spike/lib/src/nostr_offer_transport.dart)
+ [`Nip99Codec`](../../spike/block2_spike/lib/src/nip99.dart)) sits entirely
NIP-99 backend (`NostrOfferTransport`
+ `Nip99Codec`) sits entirely
behind it. A second backend (ActivityPub/FEP-0837) could replace it without
the domain noticing.
- [`Offer`](../../spike/block2_spike/lib/src/offer.dart) is agnostic by
- `Offer` is agnostic by
construction: a chosen *summary* + coarse geohash, **no FK into seed tables,
no full inventory, no exact address** — exactly the Offer↔Lot split
[sharing-model.md](sharing-model.md) §2 and
@ -114,14 +114,14 @@ manageable**:
private encrypted DM vs. assert/read a signed certification. Forcing DMs and
certifications behind `OfferTransport.publish/discover` would overload it.
- **Recommendation — now demonstrated in code, not just asserted:**
[`NostrConnection`](../../spike/block2_spike/lib/src/nostr_connection.dart) is
`NostrConnection` is
**one shared** socket + key + sign + REQ/EOSE lifecycle, and **all three thin
interfaces sit on top of the same instance** —
[`OfferTransport`](../../spike/block2_spike/lib/src/nostr_offer_transport.dart)
`OfferTransport`
(NIP-99),
[`MessageTransport`](../../spike/block2_spike/lib/src/nostr_message_transport.dart)
`MessageTransport`
(NIP-17) and
[`TrustTransport`](../../spike/block2_spike/lib/src/nostr_trust_transport.dart)
`TrustTransport`
(custom WoT). The trust test even runs offer discovery and trust annotation
over one connection. This is the shape to lift into `commons_core`: shared
connection, per-concern contract. Trying to make `OfferTransport` also carry
@ -139,7 +139,7 @@ that whole happy path now runs — what's left is production hardening (below).
**Result: the flow works end to end. The real risk is NIP maturity/ecosystem,
not the mechanics.**
- Built a hermetic in-process [`MiniRelay`](../../spike/block2_spike/lib/src/mini_relay.dart)
- Built a hermetic in-process `MiniRelay`
(NIP-01 subset: EVENT/REQ/EOSE/CLOSE, filter by `kinds`/`authors`/`#g`,
addressable-event replacement for kind 30402). No network → CI-safe, no flaky
public-relay dependency.
@ -183,13 +183,13 @@ The plan ([network-trust.md](network-trust.md) §4) calls messaging "grande" and
[open-decisions.md](open-decisions.md) §D.3 singled it out as the tightest
coupling to Nostr. So the spike built it rather than hand-waving:
- **NIP-44 v2 encryption** ([`Nip44`](../../spike/block2_spike/lib/src/nip44.dart)):
- **NIP-44 v2 encryption** (`Nip44`):
secp256k1 ECDH → HKDF conversation key → ChaCha20 + HMAC-SHA256 with
length-hiding padding. Conversation key is symmetric (A→B == B→A, tested); a
wrong key cannot decrypt (throws, tested); short messages pad to the same
bucket so ciphertext length doesn't leak plaintext length (tested).
- **NIP-17 / NIP-59 gift-wrap onion**
([`NostrMessageTransport`](../../spike/block2_spike/lib/src/nostr_message_transport.dart)):
(`NostrMessageTransport`):
rumor (kind 14, unsigned) → seal (kind 13, signed by sender, NIP-44 to
recipient) → gift wrap (kind 1059, signed by a **throwaway ephemeral key**,
NIP-44 to recipient).
@ -223,12 +223,12 @@ There is no settled NIP for a web of trust, so — exactly as
anticipates — the spike models **its own WoT, Duniter-compatible**, over Nostr:
- **Certifications as events**
([`NostrTrustTransport`](../../spike/block2_spike/lib/src/nostr_trust_transport.dart)):
(`NostrTrustTransport`):
a custom addressable kind (30777) keyed by (issuer, `d`=subject), so one live
"A vouches for B" per pair; re-certifying renews, revoking replaces (tested).
Certifications expire (Ğ1 semantics) and are public (like the on-chain Ğ1 WoT).
- **The membership rule is pure and separately tested**
([`WebOfTrust`](../../spike/block2_spike/lib/src/web_of_trust.dart)) — the
(`WebOfTrust`) — the
`commons_core`-worthy piece: Duniter's two rules, **N certifications from
existing members** (sigQty) and **within distance D of the bootstrap
referents** (stepMax), iterated to a fixpoint (becoming a member can push
@ -263,8 +263,9 @@ entity, and optionally importing the on-chain Ğ1 WoT (level 3) as a trust sourc
([open-decisions.md](open-decisions.md) §D.1, §D.6). That is what should drive
the Phase-3 estimate and the funding ask.
4. **Do not start Block 2 from this spike.** It is throwaway research code (not
vector-verified, no offline delivery, no persistence, single relay). Delete
`spike/block2_spike/` once these findings are absorbed. The funded social
vector-verified, no offline delivery, no persistence, single relay). The
`spike/block2_spike/` code has been moved out of this public repo (preserved
privately) now these findings are captured. The funded social
round should **rebuild on vetted client libraries in `commons_core`**, using
the shapes proven here — one `NostrConnection`, three interfaces, the pure
`WebOfTrust` rule — and spend its risk budget on hardening and bootstrap, not
@ -283,11 +284,12 @@ entity, and optionally importing the on-chain Ğ1 WoT (level 3) as a trust sourc
## How to run
The throwaway spike code has been moved out of this public repo and is preserved
privately; nothing here was wired into `app_seeds` or `commons_core`'s production
graph, and the Block 1 suite is untouched. For the record, it ran as:
```sh
cd spike/block2_spike
cd spike/block2_spike # in the private archive
dart pub get
dart test # derivation · privacy · roundtrip · messaging · trust · web_of_trust (30 tests)
```
Nothing here is wired into `app_seeds` or `commons_core`'s production graph; the
Block 1 suite is untouched.