From 532f43834b45055497eea85bc6c0d76cdc40b541 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 11 Aug 2026 09:13:41 +0200 Subject: [PATCH 1/6] =?UTF-8?q?swip-66:=20graffiti=20wall=20pattern=20?= =?UTF-8?q?=E2=80=94=20first=20draft=20from=20dictation=20round=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anti-GSOC: own-identity writers mine into the anchor neighbourhood (MOC / bare-index-over-BPS / mined span-index modes); motivation enumerates 8 arguments against the shared-key GSOC pattern. Guesses marked (?). Co-Authored-By: Claude Fable 5 --- SWIPs/swip-66.md | 188 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 SWIPs/swip-66.md diff --git a/SWIPs/swip-66.md b/SWIPs/swip-66.md new file mode 100644 index 00000000..f92395c8 --- /dev/null +++ b/SWIPs/swip-66.md @@ -0,0 +1,188 @@ +--- +SWIP: 66 +title: Graffiti wall pattern +author: Viktor Trón (@zelig) +discussions-to: https://discord.gg/Q6BvSkCv +status: Draft +type: Standards Track (Interface) +created: 2026-08-11 +--- + + + +- **Business line**: a permissionless public write-board — a **wall** — attached to any + 32-byte reference: anyone can write an entry under **their own signature**, everyone + can read, and every entry has an accountable author. Unlocks comments, annotations, + registrations and rendezvous on any content or identity without coordination with — + or permission from — whatever the reference points to **(?)**. +- **Dev line**: no new wire frames and no proto change; implement (1) writer-side + mining helpers — ephemeral-owner (MOC, SWIP-42) and span-index — in bee-js **(?)**, + (2) the wall-entry validation rule (span-index construction) wherever SOCs are + validated for ANCHOR-bound delivery, (3) reader plumbing over the existing + GSOC-style subscription, `/moc/subscribe`, and BPS ANCHOR cohorts (SWIP-60, + [#104](https://github.com/ethersphere/SWIPs/pull/104)); done per the conformance + section. +- **DISC: NO (?)** — standard SOC validation and the existing implicit-binding rule + `PO(addr, anchor) >= PO_MIN = 16`; the only touchpoint is that storage MUST NOT + reject a wrapped chunk with an arbitrary span and empty payload **(?)** — if bee's + current chunk validity check does, this flips to DISC: ?. + +## Simple Summary + +A **graffiti wall** is the set of single-owner chunks mined into the neighbourhood of +an anchor derived from a public reference. The reference serves as the feed **topic** +and derives the **anchor** — exactly as in the original GSOC pattern. What changes is +the writer's identity: instead of every writer signing with one shared, publicly +derivable owner key, each writer signs with **their own key** and spends mining effort +to land their entry near the anchor — by mining an ephemeral owner (the MOC pattern), +or by mining the feed **index** itself, carried in the span field of the wrapped chunk. +Anyone can write; everyone can read; no two writers ever share a key. + +## Motivation + +The original GSOC pattern hands one secret key to the world, and everything wrong with +it follows from that. The arguments, enumerated: + +1. In the Book of Swarm, signature attestation on a single-owner chunk is meant as an + **integrity check**: because the secret key belongs to a *single* owner, no double + signing on the same id is a reasonable requirement. A shared graffiti key voids the + premise — anyone can sign anything on any id, so the attestation attests nothing + and content at a SOC address loses its uniqueness guarantee **(?)** (Claude's + gloss — slash if the consequence is drawn differently). +2. **Pull sync.** Reconciling divergent versions at one address needs some digest on + the chunks — which exists to sync postage stamps, but not for the content + **(?)** (gloss: pull sync assumes one content per address; GSOC breaks the + assumption and forces content digests into the sync protocol — cf. the "chunk + checksum for divergent sync" draft, + [#101](https://github.com/ethersphere/SWIPs/pull/101)). +3. **Incentives.** The redistribution game had to mix the **wrapped address** into + the sample to motivate storage of multiple payloads at the same address **(?)** — + a storage-incentive complication existing solely to accommodate multi-version + SOCs. +4. **Stamping.** Currently the stamp for any SOC can be used as the stamp on *any + version* of the same SOC address — unlimited versions ride a single payment + **(?)**. +5. **Retrieval locality.** Retrieval of a multi-version SOC is only possible from + the closest node; caching is problematic — a cache cannot know whether its copy + is the version wanted **(?)**. +6. **Retrieval protocol.** A retrieve request has a single response; to exhaust the + versions at an address one must resort to a wanted (matching) / not-wanted (not + matching) request pattern **(?)** — which the protocol does not offer. +7. **No censorship resistance.** The pattern does not solve the problem it is often + assumed to: it is not censorship resistant **(?)**. +8. **Collision-prone even when honest.** The genuine happy path — all writers + well-intentioned — is already collision prone **(?)** (gloss: cooperating writers + race on the same id with no arbitration). + +## Specification + +Notation: `H` = keccak256, `H_BMT` = BMT hash, `‖` = concatenation, `ε` = the empty +byte string. `PO(x, y)` = proximity order; `PO_MIN = 16` (SWIP-60 protocol constant). +`SIG_O` = signature by owner key `O`; `ECRecover` recovers the signing address. + +### The wall: reference, topic, anchor + +A wall is named by a 32-byte reference `R` — typically the Swarm address of the +content or identity the wall is about **(?)**. The reference is used both ways: + +``` +T = R // the feed topic (?) +A = R // the anchor (?) — alternative: A = H(T) +``` + +An entry is **on the wall** iff it is a valid SOC whose address `addr` satisfies +`PO(addr, A) >= PO_MIN`. The wall is therefore not a data structure anybody maintains +— it is a *neighbourhood*, and membership is bought with mining effort. + +### Writing: three modes + +A writer holding identity key `O` (or minting one) produces a wall entry in one of +three ways. + +**Mode 1 — mined ephemeral owner (the MOC pattern, SWIP-42, +[#80](https://github.com/ethersphere/SWIPs/pull/80)).** The writer mines an ephemeral +keypair until the SOC address lands on the wall: + +``` +id = T // (?) — id is the topic directly +mine O_e until PO( H(id ‖ O_e), A ) >= PO_MIN +``` + +Content is unconstrained; the entry is a MOC and all MOC tooling applies. The +ephemeral owner is still *an* owner — unique to this writer, never shared — but the +entry is not linked to the writer's persistent identity unless the payload links it +**(?)**. + +**Mode 2 — normal feed, bare index, brokered (no mining).** When writers are +explicitly connected in publisher role to a broker (BPS, SWIP-60), a normal feed on +topic `T` is used and frames carry the **bare 8-byte index in the 32-byte id field** +— the carriage rule of SWIP-65 +([#106](https://github.com/ethersphere/SWIPs/pull/106)). Delivery is by the broker, +so no anchoring — and hence no mining — is needed; the cohort *is* the wall's live +face **(?)**. Every frame carries the full SOC, so the writer's own key signs as in +any feed. + +**Mode 3 — normal feed, mined index (the span-index construction).** The writer keeps +their real identity `O` and mines the feed **index** so that the entry lands on the +wall. The index is a uint64 and is written into the **span field of the wrapped +chunk**; the payload is left **empty** for quick filtering. The entry is byte-for-byte +a feed update on topic `T` at index `SPAN`: + +``` +mine SPAN (uint64) until PO( addr, A ) >= PO_MIN where + +id = H( T ‖ SPAN ) +sig = SIG_O( H( id ‖ H_BMT( SPAN ‖ ε ) ) ) +addr = H( id ‖ O ) +c = id ‖ sig ‖ SPAN ‖ ε +``` + +Because the index sits in the span field, the entry is **self-describing**: a reader +holding only the chunk (and knowing `T`) validates it with no external information — + +``` +ID = H( T ‖ SPAN ) // recompute id from topic and span +O = ECRecover( SIG, H( ID ‖ H_BMT(SPAN ‖ ε) ) ) +addr = H( ID ‖ O ) +valid iff PO( addr, A ) >= PO_MIN +``` + +The mined SPAN thus places a member of the writer's **MIC** in the proximity of the +anchor: the entry simultaneously belongs to the wall (by address) and to the writer's +own SOC universe (by owner). Reading the wall yields writer identities; each entry is +a standing invitation to subscribe to that writer's MIC or feeds for the actual +content **(?)** — the empty payload means the mode-3 entry itself carries no message, +it *announces* one **(?)**. + +### Reading the wall + +- **Full node in the anchor neighbourhood**: wall entries arrive with the reserve; + the GSOC-style subscription surfaces them as they land **(?)**. +- **Light client, live**: a BPS cohort with ANCHOR binding and implicit publishers + (SWIP-60) — the broker's implicit-publisher validation rule + `PO(addr, anchor) >= PO_MIN` is exactly wall membership, so brokers need no new + rules; mode-3 entries additionally validate by the span-index chain above **(?)**. +- **After the fact**: entries are ordinary stored chunks, individually retrievable by + address; neighbourhood *enumeration* by a light client is out of scope for this + SWIP **(?) — open: is there any storage-fed wall enumeration story, or is + after-the-fact reading full-node/broker-mediated only?** + +### Conformance + +1. A **wall entry** is valid iff it is a valid SOC and `PO(addr, A) >= PO_MIN`, with + `A` derived from the wall reference as specified above. +2. A **mode-3 entry** is valid iff, additionally, `id = H(T ‖ SPAN)` for the SPAN + carried in its wrapped chunk's span field and its payload is empty **(?)**. +3. Readers and brokers MUST ignore chunks in the anchor neighbourhood that fail the + applicable validity chain; ignoring is silent **(?)**. +4. A broker serving an ANCHOR-bound cohort MUST apply the implicit-publisher PO rule + of SWIP-60 unchanged; this SWIP adds no broker-side rules **(?)**. +5. Storage nodes MUST NOT reject a wrapped chunk on account of an arbitrary span + value with an empty payload **(?)**. + +## Copyright + +Copyright and related rights waived via +[CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 18f24720ebbbe7d6ea976acc4b8f13ccae6abfe5 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 11 Aug 2026 09:27:17 +0200 Subject: [PATCH 2/6] =?UTF-8?q?swip-66:=20round=202=20=E2=80=94=20anchor?= =?UTF-8?q?=20A=3DH(T),=20mode-1=20id=3DH(T),=20content=20on=20writer's=20?= =?UTF-8?q?feed=20(T,O),=20DISC=20NO=20firm,=20broker=20as=20aggregate=20f?= =?UTF-8?q?eed=20indexer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All 7 round-1 question marks resolved from dictation; motivation glosses confirmed and unmarked. New aggregation section (broker-side gloss marked ?). Co-Authored-By: Claude Fable 5 --- SWIPs/swip-66.md | 129 ++++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 58 deletions(-) diff --git a/SWIPs/swip-66.md b/SWIPs/swip-66.md index f92395c8..6cda4813 100644 --- a/SWIPs/swip-66.md +++ b/SWIPs/swip-66.md @@ -12,8 +12,9 @@ created: 2026-08-11 writers mining into an anchor neighbourhood. Freestanding pattern; BPS (SWIP-60) enters only as one of the write/read modes. --> -- **Business line**: a permissionless public write-board — a **wall** — attached to any - 32-byte reference: anyone can write an entry under **their own signature**, everyone +- **Business line**: a permissionless public write-board — a **wall** — named by any + topic (a mnemonic string or a content reference): anyone can write an entry under + **their own signature**, everyone can read, and every entry has an accountable author. Unlocks comments, annotations, registrations and rendezvous on any content or identity without coordination with — or permission from — whatever the reference points to **(?)**. @@ -24,21 +25,22 @@ only as one of the write/read modes. --> GSOC-style subscription, `/moc/subscribe`, and BPS ANCHOR cohorts (SWIP-60, [#104](https://github.com/ethersphere/SWIPs/pull/104)); done per the conformance section. -- **DISC: NO (?)** — standard SOC validation and the existing implicit-binding rule - `PO(addr, anchor) >= PO_MIN = 16`; the only touchpoint is that storage MUST NOT - reject a wrapped chunk with an arbitrary span and empty payload **(?)** — if bee's - current chunk validity check does, this flips to DISC: ?. +- **DISC: NO** — standard SOC validation and the existing implicit-binding rule + `PO(addr, anchor) >= PO_MIN = 16`; a wrapped chunk with an arbitrary span and empty + payload is already allowed by storage, so nothing changes below the pattern. ## Simple Summary A **graffiti wall** is the set of single-owner chunks mined into the neighbourhood of -an anchor derived from a public reference. The reference serves as the feed **topic** -and derives the **anchor** — exactly as in the original GSOC pattern. What changes is -the writer's identity: instead of every writer signing with one shared, publicly -derivable owner key, each writer signs with **their own key** and spends mining effort -to land their entry near the anchor — by mining an ephemeral owner (the MOC pattern), -or by mining the feed **index** itself, carried in the span field of the wrapped chunk. -Anyone can write; everyone can read; no two writers ever share a key. +an anchor derived from a public topic. The topic — typically a mnemonic string — +serves both as the feed topic and to derive the **anchor**, exactly as in the original +GSOC pattern. What changes is the writer's identity: instead of every writer signing +with one shared, publicly derivable owner key, each writer signs with **their own +key** and spends mining effort to land their entry near the anchor — by mining an +ephemeral owner (the MOC pattern), or by mining the feed **index** itself, carried in +the span field of the wrapped chunk. Anyone can write; everyone can read; no two +writers ever share a key — and a BPS broker on the wall's cohort doubles as the +**aggregate feed indexer** over all writers. ## Motivation @@ -49,32 +51,28 @@ it follows from that. The arguments, enumerated: **integrity check**: because the secret key belongs to a *single* owner, no double signing on the same id is a reasonable requirement. A shared graffiti key voids the premise — anyone can sign anything on any id, so the attestation attests nothing - and content at a SOC address loses its uniqueness guarantee **(?)** (Claude's - gloss — slash if the consequence is drawn differently). + and content at a SOC address loses its uniqueness guarantee. 2. **Pull sync.** Reconciling divergent versions at one address needs some digest on - the chunks — which exists to sync postage stamps, but not for the content - **(?)** (gloss: pull sync assumes one content per address; GSOC breaks the - assumption and forces content digests into the sync protocol — cf. the "chunk - checksum for divergent sync" draft, - [#101](https://github.com/ethersphere/SWIPs/pull/101)). + the chunks — which exists to sync postage stamps, but not for the content: pull + sync assumes one content per address; GSOC breaks the assumption and forces + content digests into the sync protocol — cf. the "chunk checksum for divergent + sync" draft ([#101](https://github.com/ethersphere/SWIPs/pull/101)). 3. **Incentives.** The redistribution game had to mix the **wrapped address** into - the sample to motivate storage of multiple payloads at the same address **(?)** — - a storage-incentive complication existing solely to accommodate multi-version - SOCs. + the sample to motivate storage of multiple payloads at the same address — a + storage-incentive complication existing solely to accommodate multi-version SOCs. 4. **Stamping.** Currently the stamp for any SOC can be used as the stamp on *any - version* of the same SOC address — unlimited versions ride a single payment - **(?)**. + version* of the same SOC address — unlimited versions ride a single payment. 5. **Retrieval locality.** Retrieval of a multi-version SOC is only possible from the closest node; caching is problematic — a cache cannot know whether its copy - is the version wanted **(?)**. + is the version wanted. 6. **Retrieval protocol.** A retrieve request has a single response; to exhaust the versions at an address one must resort to a wanted (matching) / not-wanted (not - matching) request pattern **(?)** — which the protocol does not offer. + matching) request pattern — which the protocol does not offer. 7. **No censorship resistance.** The pattern does not solve the problem it is often - assumed to: it is not censorship resistant **(?)**. + assumed to: it is not censorship resistant. 8. **Collision-prone even when honest.** The genuine happy path — all writers - well-intentioned — is already collision prone **(?)** (gloss: cooperating writers - race on the same id with no arbitration). + well-intentioned — is already collision prone: cooperating writers race on the + same id with no arbitration. ## Specification @@ -82,14 +80,13 @@ Notation: `H` = keccak256, `H_BMT` = BMT hash, `‖` = concatenation, `ε` = the byte string. `PO(x, y)` = proximity order; `PO_MIN = 16` (SWIP-60 protocol constant). `SIG_O` = signature by owner key `O`; `ECRecover` recovers the signing address. -### The wall: reference, topic, anchor +### The wall: topic and anchor -A wall is named by a 32-byte reference `R` — typically the Swarm address of the -content or identity the wall is about **(?)**. The reference is used both ways: +A wall is named by its **topic** `T` — typically a mnemonic string, possibly a +content reference **(?)**. Everything else is derived from it: ``` -T = R // the feed topic (?) -A = R // the anchor (?) — alternative: A = H(T) +A = H(T) // the anchor ``` An entry is **on the wall** iff it is a valid SOC whose address `addr` satisfies @@ -106,7 +103,7 @@ three ways. keypair until the SOC address lands on the wall: ``` -id = T // (?) — id is the topic directly +id = H(T) // note: id = A mine O_e until PO( H(id ‖ O_e), A ) >= PO_MIN ``` @@ -115,14 +112,14 @@ ephemeral owner is still *an* owner — unique to this writer, never shared — entry is not linked to the writer's persistent identity unless the payload links it **(?)**. -**Mode 2 — normal feed, bare index, brokered (no mining).** When writers are -explicitly connected in publisher role to a broker (BPS, SWIP-60), a normal feed on -topic `T` is used and frames carry the **bare 8-byte index in the 32-byte id field** -— the carriage rule of SWIP-65 -([#106](https://github.com/ethersphere/SWIPs/pull/106)). Delivery is by the broker, -so no anchoring — and hence no mining — is needed; the cohort *is* the wall's live -face **(?)**. Every frame carries the full SOC, so the writer's own key signs as in -any feed. +**Mode 2 — normal feed, bare index, brokered (no mining).** When writers connect in +publisher role to a broker (BPS, SWIP-60), a normal feed on topic `T` is used and +frames carry the **bare 8-byte index in the 32-byte id field** — the carriage rule of +SWIP-65 ([#106](https://github.com/ethersphere/SWIPs/pull/106)). Delivery is by the +broker, so no anchoring — and hence no mining — is needed. The publisher +parameterisation of the cohort is immaterial; the point is that both the topic +(implicit in the channel) and the bare indexes are known, so subscribers can follow. +Every frame carries the full SOC, so the writer's own key signs as in any feed. **Mode 3 — normal feed, mined index (the span-index construction).** The writer keeps their real identity `O` and mines the feed **index** so that the entry lands on the @@ -151,36 +148,52 @@ valid iff PO( addr, A ) >= PO_MIN The mined SPAN thus places a member of the writer's **MIC** in the proximity of the anchor: the entry simultaneously belongs to the wall (by address) and to the writer's -own SOC universe (by owner). Reading the wall yields writer identities; each entry is -a standing invitation to subscribe to that writer's MIC or feeds for the actual -content **(?)** — the empty payload means the mode-3 entry itself carries no message, -it *announces* one **(?)**. +own SOC universe (by owner). The mode-3 entry itself carries no message — it +*announces* a writer: the content lives on the **owner's feed with the same topic** +`T`. A reader recovers `O` from the entry and follows the feed `(T, O)` — from index +0, or as a subscription — for the actual graffiti. (The mined entry is formally an +update of that same feed at the astronomical index `SPAN`; sequential readers never +encounter it **(?)**.) ### Reading the wall - **Full node in the anchor neighbourhood**: wall entries arrive with the reserve; - the GSOC-style subscription surfaces them as they land **(?)**. + filtering **zero-length payloads with a conforming address** yields the mode-3 + entries directly, and the GSOC-style subscription surfaces entries as they land + **(?)**. - **Light client, live**: a BPS cohort with ANCHOR binding and implicit publishers (SWIP-60) — the broker's implicit-publisher validation rule `PO(addr, anchor) >= PO_MIN` is exactly wall membership, so brokers need no new - rules; mode-3 entries additionally validate by the span-index chain above **(?)**. -- **After the fact**: entries are ordinary stored chunks, individually retrievable by - address; neighbourhood *enumeration* by a light client is out of scope for this - SWIP **(?) — open: is there any storage-fed wall enumeration story, or is - after-the-fact reading full-node/broker-mediated only?** + rules; mode-3 entries additionally validate by the span-index chain above. +- **After the fact**: BPS mediated — see the aggregation section below. Individual + entries remain ordinary stored chunks, retrievable by address. + +### Aggregation: the broker as wall indexer + +The one piece that completes the pattern: a BPS broker serving the wall's cohort can +act as the **aggregate feed indexer** for the wall. The broker sees every entry as it +is published; each entry names a writer `O`, and each writer's content is a feed +`(T, O)`. The broker therefore holds, as a by-product of relaying, the directory of +all constituent feeds — and can serve a late joiner the aggregate: which writers have +written, and where each writer's feed stands **(?)** (gloss: the aggregate index is +broker-side state offered over the existing subscription — no new frames — with +history semantics per bps-history or, per writer, per SWIP-65 self-indexing where the +constituent feeds adopt it; slash if aggregation is meant more concretely, e.g. the +broker publishing its own index feed). ### Conformance 1. A **wall entry** is valid iff it is a valid SOC and `PO(addr, A) >= PO_MIN`, with - `A` derived from the wall reference as specified above. + `A = H(T)` derived from the wall topic as specified above. 2. A **mode-3 entry** is valid iff, additionally, `id = H(T ‖ SPAN)` for the SPAN - carried in its wrapped chunk's span field and its payload is empty **(?)**. + carried in its wrapped chunk's span field and its payload is empty. 3. Readers and brokers MUST ignore chunks in the anchor neighbourhood that fail the applicable validity chain; ignoring is silent **(?)**. 4. A broker serving an ANCHOR-bound cohort MUST apply the implicit-publisher PO rule - of SWIP-60 unchanged; this SWIP adds no broker-side rules **(?)**. + of SWIP-60 unchanged; this SWIP adds no broker-side rules. 5. Storage nodes MUST NOT reject a wrapped chunk on account of an arbitrary span - value with an empty payload **(?)**. + value with an empty payload (already the case — codified here so the pattern's + floor cannot regress). ## Copyright From 9734892dc8ccbf0eb08e6c3a92c5011fd422e532 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 11 Aug 2026 09:46:14 +0200 Subject: [PATCH 3/6] =?UTF-8?q?swip-66:=20round=203=20=E2=80=94=20tag-adve?= =?UTF-8?q?rtising=20business=20line=20+=20worked=20example,=20content=20f?= =?UTF-8?q?eed=20topic=20H(T)=3DA,=20broker=20publishes=20self-indexed=20c?= =?UTF-8?q?ontributors=20feed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit T usually a 32-byte reference; content feeds one hash deeper (no id clash with mined spans); aggregation concretized per SWIP-65 self-indexing with withhold-not-forge trust bound. Remaining guesses marked (?). Co-Authored-By: Claude Fable 5 --- SWIPs/swip-66.md | 83 ++++++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/SWIPs/swip-66.md b/SWIPs/swip-66.md index 6cda4813..9708da18 100644 --- a/SWIPs/swip-66.md +++ b/SWIPs/swip-66.md @@ -13,11 +13,12 @@ writers mining into an anchor neighbourhood. Freestanding pattern; BPS (SWIP-60) only as one of the write/read modes. --> - **Business line**: a permissionless public write-board — a **wall** — named by any - topic (a mnemonic string or a content reference): anyone can write an entry under - **their own signature**, everyone - can read, and every entry has an accountable author. Unlocks comments, annotations, - registrations and rendezvous on any content or identity without coordination with — - or permission from — whatever the reference points to **(?)**. + topic: anyone can write an entry under **their own signature**, everyone can read, + and every entry has an accountable author. Headline application: **tag-based + advertising** — announcing a videocast (`A`) on crypto-trading (`B`) on Monday the + 17th (`C`), the publisher puts an advert on the walls `H(A)`, `H(B)`, `H(C)`, + `H(A‖B)`, `H(B‖C)` and `H(A‖B‖C)`, and anyone following any of those tag + combinations discovers it. - **Dev line**: no new wire frames and no proto change; implement (1) writer-side mining helpers — ephemeral-owner (MOC, SWIP-42) and span-index — in bee-js **(?)**, (2) the wall-entry validation rule (span-index construction) wherever SOCs are @@ -32,9 +33,9 @@ only as one of the write/read modes. --> ## Simple Summary A **graffiti wall** is the set of single-owner chunks mined into the neighbourhood of -an anchor derived from a public topic. The topic — typically a mnemonic string — -serves both as the feed topic and to derive the **anchor**, exactly as in the original -GSOC pattern. What changes is the writer's identity: instead of every writer signing +an anchor derived from a public topic. The topic — usually a 32-byte reference, such +as a content address or a tag hash — serves both to form entry ids and to derive the +**anchor**, exactly as in the original GSOC pattern. What changes is the writer's identity: instead of every writer signing with one shared, publicly derivable owner key, each writer signs with **their own key** and spends mining effort to land their entry near the anchor — by mining an ephemeral owner (the MOC pattern), or by mining the feed **index** itself, carried in @@ -82,11 +83,12 @@ byte string. `PO(x, y)` = proximity order; `PO_MIN = 16` (SWIP-60 protocol const ### The wall: topic and anchor -A wall is named by its **topic** `T` — typically a mnemonic string, possibly a -content reference **(?)**. Everything else is derived from it: +A wall is named by its **topic** `T` — usually a 32-byte reference (a content +address, or the hash of a tag or mnemonic string). Everything else is derived from +it: ``` -A = H(T) // the anchor +A = H(T) // the anchor — also the topic of the writers' content feeds ``` An entry is **on the wall** iff it is a valid SOC whose address `addr` satisfies @@ -109,8 +111,7 @@ mine O_e until PO( H(id ‖ O_e), A ) >= PO_MIN Content is unconstrained; the entry is a MOC and all MOC tooling applies. The ephemeral owner is still *an* owner — unique to this writer, never shared — but the -entry is not linked to the writer's persistent identity unless the payload links it -**(?)**. +entry is not linked to the writer's persistent identity unless the payload links it. **Mode 2 — normal feed, bare index, brokered (no mining).** When writers connect in publisher role to a broker (BPS, SWIP-60), a normal feed on topic `T` is used and @@ -149,18 +150,18 @@ valid iff PO( addr, A ) >= PO_MIN The mined SPAN thus places a member of the writer's **MIC** in the proximity of the anchor: the entry simultaneously belongs to the wall (by address) and to the writer's own SOC universe (by owner). The mode-3 entry itself carries no message — it -*announces* a writer: the content lives on the **owner's feed with the same topic** -`T`. A reader recovers `O` from the entry and follows the feed `(T, O)` — from index -0, or as a subscription — for the actual graffiti. (The mined entry is formally an -update of that same feed at the astronomical index `SPAN`; sequential readers never -encounter it **(?)**.) +*announces* a writer: the content lives on the **owner's content feed for the wall**, +whose topic is `H(T)` — one hash deeper than the wall's own id preimage, so a content +update `id = H( H(T) ‖ IDX )` can never clash with a wall entry `id = H( T ‖ SPAN )` +even when the mined SPAN happens to be a low integer. Note `H(T) = A`: the anchor +doubles as the content-feed topic. A reader recovers `O` from the entry and follows +the feed `(H(T), O)` — from index 0, or as a subscription — for the actual graffiti. ### Reading the wall - **Full node in the anchor neighbourhood**: wall entries arrive with the reserve; filtering **zero-length payloads with a conforming address** yields the mode-3 - entries directly, and the GSOC-style subscription surfaces entries as they land - **(?)**. + entries directly, and the GSOC-style subscription surfaces entries as they land. - **Light client, live**: a BPS cohort with ANCHOR binding and implicit publishers (SWIP-60) — the broker's implicit-publisher validation rule `PO(addr, anchor) >= PO_MIN` is exactly wall membership, so brokers need no new @@ -170,16 +171,36 @@ encounter it **(?)**.) ### Aggregation: the broker as wall indexer -The one piece that completes the pattern: a BPS broker serving the wall's cohort can -act as the **aggregate feed indexer** for the wall. The broker sees every entry as it -is published; each entry names a writer `O`, and each writer's content is a feed -`(T, O)`. The broker therefore holds, as a by-product of relaying, the directory of -all constituent feeds — and can serve a late joiner the aggregate: which writers have -written, and where each writer's feed stands **(?)** (gloss: the aggregate index is -broker-side state offered over the existing subscription — no new frames — with -history semantics per bps-history or, per writer, per SWIP-65 self-indexing where the -constituent feeds adopt it; slash if aggregation is meant more concretely, e.g. the -broker publishing its own index feed). +The one piece that completes the pattern: a BPS broker serving the wall's cohort acts +as the **aggregate feed indexer** for the wall. The broker sees every entry as it is +published; each entry names a writer `O`, and each writer's content is a feed +`(H(T), O)`. The broker aggregates by **publishing its own feed of contributors for +the topic** — owner the broker's key, topic `H(T)` **(?)**, one update per wall entry +observed **(?)** — and making it **self-indexing** per SWIP-65 +([#106](https://github.com/ethersphere/SWIPs/pull/106)). A late joiner then needs +only the broker feed's latest update: the whirl-only pot in its payload indexes every +contributor in arrival order, gives O(log n) seek, and authenticates the whole +history against the broker's one signature. After-the-fact reading of a wall is thus +a single feed lookup, and trust in the broker is bounded: the broker can withhold a +contributor from its index, but cannot forge one — every indexed element is a wall +entry carrying its writer's own signature **(?)**. + +### Worked example: tag-based advertising + +A publisher announces a videocast (`A`) on crypto-trading (`B`) on Monday the 17th +(`C`). Each tag and tag combination is a wall: + +``` +T ∈ { H(A), H(B), H(C), H(A‖B), H(B‖C), H(A‖B‖C) } +``` + +For each wall the publisher writes one entry — mode 3, say: mine `SPAN` so that +`H( H(T ‖ SPAN) ‖ O )` lands within `PO_MIN` of `H(T)` — and puts the advert itself +on the content feed `(H(T), O)` **(?)**. A reader interested in crypto-trading +follows the wall `H(B)`; one interested specifically in Monday's videocasts follows +`H(A‖C)` if the publisher chose to serve that combination **(?)**. Live discovery is +the ANCHOR cohort on each wall; after the fact, one lookup of a broker's contributor +feed per wall. ### Conformance @@ -188,7 +209,7 @@ broker publishing its own index feed). 2. A **mode-3 entry** is valid iff, additionally, `id = H(T ‖ SPAN)` for the SPAN carried in its wrapped chunk's span field and its payload is empty. 3. Readers and brokers MUST ignore chunks in the anchor neighbourhood that fail the - applicable validity chain; ignoring is silent **(?)**. + applicable validity chain; ignoring is silent. 4. A broker serving an ANCHOR-bound cohort MUST apply the implicit-publisher PO rule of SWIP-60 unchanged; this SWIP adds no broker-side rules. 5. Storage nodes MUST NOT reject a wrapped chunk on account of an arbitrary span From 449a964b13167f226f31d2221e535067fbb15403 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 11 Aug 2026 09:54:54 +0200 Subject: [PATCH 4/6] =?UTF-8?q?swip-66:=20round=204=20=E2=80=94=20H(A|C)?= =?UTF-8?q?=20restored,=20ads=20need=20no=20content=20pointer=20(existence?= =?UTF-8?q?=20is=20the=20info),=20mining=20helpers=20in=20client=20libs,?= =?UTF-8?q?=20withhold=20wording=20confirmed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- SWIPs/swip-66.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/SWIPs/swip-66.md b/SWIPs/swip-66.md index 9708da18..54fa8fda 100644 --- a/SWIPs/swip-66.md +++ b/SWIPs/swip-66.md @@ -17,10 +17,11 @@ only as one of the write/read modes. --> and every entry has an accountable author. Headline application: **tag-based advertising** — announcing a videocast (`A`) on crypto-trading (`B`) on Monday the 17th (`C`), the publisher puts an advert on the walls `H(A)`, `H(B)`, `H(C)`, - `H(A‖B)`, `H(B‖C)` and `H(A‖B‖C)`, and anyone following any of those tag + `H(A‖B)`, `H(A‖C)`, `H(B‖C)` and `H(A‖B‖C)`, and anyone following any of those tag combinations discovers it. - **Dev line**: no new wire frames and no proto change; implement (1) writer-side - mining helpers — ephemeral-owner (MOC, SWIP-42) and span-index — in bee-js **(?)**, + mining helpers — ephemeral-owner (MOC, SWIP-42) and span-index — in client + libraries (bee-js or other JS libs), (2) the wall-entry validation rule (span-index construction) wherever SOCs are validated for ANCHOR-bound delivery, (3) reader plumbing over the existing GSOC-style subscription, `/moc/subscribe`, and BPS ANCHOR cohorts (SWIP-60, @@ -156,6 +157,8 @@ update `id = H( H(T) ‖ IDX )` can never clash with a wall entry `id = H( T ‖ even when the mined SPAN happens to be a low integer. Note `H(T) = A`: the anchor doubles as the content-feed topic. A reader recovers `O` from the entry and follows the feed `(H(T), O)` — from index 0, or as a subscription — for the actual graffiti. +When the entry's existence is itself the message (see the worked example), no content +feed need exist. ### Reading the wall @@ -183,7 +186,7 @@ contributor in arrival order, gives O(log n) seek, and authenticates the whole history against the broker's one signature. After-the-fact reading of a wall is thus a single feed lookup, and trust in the broker is bounded: the broker can withhold a contributor from its index, but cannot forge one — every indexed element is a wall -entry carrying its writer's own signature **(?)**. +entry carrying its writer's own signature. ### Worked example: tag-based advertising @@ -191,16 +194,17 @@ A publisher announces a videocast (`A`) on crypto-trading (`B`) on Monday the 17 (`C`). Each tag and tag combination is a wall: ``` -T ∈ { H(A), H(B), H(C), H(A‖B), H(B‖C), H(A‖B‖C) } +T ∈ { H(A), H(B), H(C), H(A‖B), H(A‖C), H(B‖C), H(A‖B‖C) } ``` For each wall the publisher writes one entry — mode 3, say: mine `SPAN` so that -`H( H(T ‖ SPAN) ‖ O )` lands within `PO_MIN` of `H(T)` — and puts the advert itself -on the content feed `(H(T), O)` **(?)**. A reader interested in crypto-trading -follows the wall `H(B)`; one interested specifically in Monday's videocasts follows -`H(A‖C)` if the publisher chose to serve that combination **(?)**. Live discovery is -the ANCHOR cohort on each wall; after the fact, one lookup of a broker's contributor -feed per wall. +`H( H(T ‖ SPAN) ‖ O )` lands within `PO_MIN` of `H(T)`. **No content feed is +needed**: this is an ad type where the entry's *existence* gives the information — +the wall names the tags, the recovered owner names the advertiser, and anything more +is discoverable through the writer's MIC and feeds. A reader interested in +crypto-trading follows the wall `H(B)`; one interested specifically in Monday's +videocasts follows `H(A‖C)`. Live discovery is the ANCHOR cohort on each wall; after +the fact, one lookup of a broker's contributor feed per wall. ### Conformance From d3bd3e3bdd9875ce8320cb2f666752c3bb08c8b1 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 11 Aug 2026 09:57:19 +0200 Subject: [PATCH 5/6] swip-66: broker contributors-feed parameters confirmed (owner = broker key, topic = H(T)) Co-Authored-By: Claude Fable 5 --- SWIPs/swip-66.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SWIPs/swip-66.md b/SWIPs/swip-66.md index 54fa8fda..371884a8 100644 --- a/SWIPs/swip-66.md +++ b/SWIPs/swip-66.md @@ -178,7 +178,7 @@ The one piece that completes the pattern: a BPS broker serving the wall's cohort as the **aggregate feed indexer** for the wall. The broker sees every entry as it is published; each entry names a writer `O`, and each writer's content is a feed `(H(T), O)`. The broker aggregates by **publishing its own feed of contributors for -the topic** — owner the broker's key, topic `H(T)` **(?)**, one update per wall entry +the topic** — owner the broker's key, topic `H(T)`, one update per wall entry observed **(?)** — and making it **self-indexing** per SWIP-65 ([#106](https://github.com/ethersphere/SWIPs/pull/106)). A late joiner then needs only the broker feed's latest update: the whirl-only pot in its payload indexes every From e63f747bbbe3dd433fb88e74b80dabe989fc7c22 Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 12 Aug 2026 06:31:28 +0200 Subject: [PATCH 6/6] =?UTF-8?q?swip-66:=20contributors=20feed=20granularit?= =?UTF-8?q?y=20=E2=80=94=20one=20update=20per=20entry=20(timestamp-key=20c?= =?UTF-8?q?ompatible),=20per-contributor=20dedup=20as=20MAY?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last (?) resolved; draft fully slashed. Co-Authored-By: Claude Fable 5 --- SWIPs/swip-66.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SWIPs/swip-66.md b/SWIPs/swip-66.md index 371884a8..a7494ac4 100644 --- a/SWIPs/swip-66.md +++ b/SWIPs/swip-66.md @@ -178,8 +178,10 @@ The one piece that completes the pattern: a BPS broker serving the wall's cohort as the **aggregate feed indexer** for the wall. The broker sees every entry as it is published; each entry names a writer `O`, and each writer's content is a feed `(H(T), O)`. The broker aggregates by **publishing its own feed of contributors for -the topic** — owner the broker's key, topic `H(T)`, one update per wall entry -observed **(?)** — and making it **self-indexing** per SWIP-65 +the topic** — owner the broker's key, topic `H(T)`, **one update per wall entry +observed**, the granularity most compatible with the self-indexed pot's default +timestamp key (a broker MAY instead dedup to one update per contributor) — and +making it **self-indexing** per SWIP-65 ([#106](https://github.com/ethersphere/SWIPs/pull/106)). A late joiner then needs only the broker feed's latest update: the whirl-only pot in its payload indexes every contributor in arrival order, gives O(log n) seek, and authenticates the whole