From cb5f09b21673e618c4a9778d46de31a42409e9cc Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sat, 5 Sep 2026 23:54:30 +0000 Subject: [PATCH 1/5] Add pay2seed to the OpenSwarm family: consent at upload and a paid seed market OpenSwarm pays a seeder per verified piece served, and nothing pays anyone to stay. An archive, a backup, a dataset waiting for its buyer or a podcast's back catalogue earns nothing the month nobody downloads it, so it dies the way every swarm always has. And nothing in BitTorrent says who put a swarm there or whether they were allowed to, which is why a seeder is presumed to be doing something wrong. pay2seed is the member document for both halves. An attestation, signed at upload with a fixed basis (own, licensed, open-license, public-domain, personal) and a notice endpoint, is what a hub requires before it will list anything; public claims get a claim window and a standing, and a notice voids them. An offer escrows a budget at an ippay hub for a swarm, public or private, to be held by M seeders for N days at a price per GiB-month, bought over x402 exactly as a pass is. Seeders take leases, prove each period by storage challenge or by a probe over the ordinary wire, and are paid through the payee they already have. Public feeds ride on ipdb; ipfile.pin on c0mpute is the same offer on the auction. Also: the family table, stack diagram and registry rows; the ip seed command group; PRD 0006; the protocol row on /openswarm. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01SKAohrRkqLKVQL2cGCAkR5 --- apps/logicsrc-web/src/app/openswarm/page.tsx | 3 +- docs/openswarm.md | 11 + docs/openswarm/cli.md | 23 + docs/openswarm/pay2seed.md | 589 +++++++++++++++++++ docs/openswarm/spec.md | 5 + prd/0006-add-pay2seed-spec.md | 182 ++++++ prd/README.md | 1 + 7 files changed, 813 insertions(+), 1 deletion(-) create mode 100644 docs/openswarm/pay2seed.md create mode 100644 prd/0006-add-pay2seed-spec.md diff --git a/apps/logicsrc-web/src/app/openswarm/page.tsx b/apps/logicsrc-web/src/app/openswarm/page.tsx index b9b83be..fcaed91 100644 --- a/apps/logicsrc-web/src/app/openswarm/page.tsx +++ b/apps/logicsrc-web/src/app/openswarm/page.tsx @@ -17,7 +17,8 @@ const PROTOCOLS: Array<[name: string, line: string]> = [ ["ipaudio", "Audio releases and tracks on ipfile swarms: renditions, seek tables, gapless, royalties"], ["ipvideo", "Video on demand: CMAF renditions, segment index, subtitles, thumbnails, an HLS bridge"], ["iplive", "Live streams: segment fan-out over paid relays with backpressure"], - ["ipname", "How a Moshpit name or a domain resolves to a publisher key and a catalogue"] + ["ipname", "How a Moshpit name or a domain resolves to a publisher key and a catalogue"], + ["pay2seed", "Consent at upload, seed offers with escrowed budgets, leases, proof of seeding, and the market a seeder client picks work from"] ]; const ROLES: Array<[role: string, work: string, paid: string]> = [ diff --git a/docs/openswarm.md b/docs/openswarm.md index 61393a4..d6a012b 100644 --- a/docs/openswarm.md +++ b/docs/openswarm.md @@ -34,6 +34,7 @@ a different product. The member protocols keep their `ip` names. | `ipvideo` | Video on demand on `ipfile` swarms: CMAF renditions, segment index, subtitles, thumbnails | [`ipvideo.md`](./openswarm/ipvideo.md) | | `iplive` | Live streams: segment fan-out over peers, paid relays, backpressure | [`iplive.md`](./openswarm/iplive.md) | | `ipname` | How a Moshpit name resolves to a publisher key and a catalogue | [`ipname.md`](./openswarm/ipname.md) | +| `pay2seed` | Consent at upload, seed offers with escrowed budgets, leases, proof of seeding, the seed market | [`pay2seed.md`](./openswarm/pay2seed.md) | Supporting documents: @@ -50,6 +51,8 @@ Supporting documents: | ipaudio ipvideo iplive ipdb (catalogue) | | releases titles channels feeds, entries | +---------------------------------------------------------------+ + | pay2seed: attestation, offers, leases, challenges, market | + +---------------------------------------------------------------+ | ipfile: manifest, per-file key pair, encrypted pieces, | | key grants, credit window, vouchers per served piece | +-------------------------------+-------------------------------+ @@ -132,6 +135,13 @@ serves more than the window unpaid. Its head is a BEP 44 mutable item under the feed key, so any DHT node can find the latest catalogue of any publisher with one `get`. +**One seed market.** A `pay2seed` offer is money escrowed at a hub for a +swarm to be kept for a period, public or private, and it cannot be listed +without a signed attestation of who put the data there and on what basis. +Seeders take leases, prove they hold and serve the pieces every period, and +are paid per GiB-month. It is how a torrent client becomes a legitimate file +sharer: consent on the way in, proof on the way out, and a payout for staying. + ## What it does not define A media player. Transcoding settings beyond what a manifest must declare. A @@ -146,6 +156,7 @@ key and the spec says so. | Browser WebTorrent player, hybrid Node seeder, wss trackers | `profullstack/media-streamer` (bittorrented.com) | Speaks the vanilla wire this family extends; needs the `ipfile` extension to pay | | DHT crawl (bitmagnet) and `/dht` browse | `profullstack/media-streamer`, `dht-infohash-crawler` | Observes `ipfile` swarms as opaque infohashes; `ipdb` is how it would learn what they are | | Pay-per-pass grants, HLS manifest sealing | `media-streamer` IPTV and seedbox rails | The central-proxy version of what `ippay` moves into the swarm | +| Headless seeder daemon with an add API and per-torrent seed time | `torlink` (`torlnk serve`) | The `pay2seed` seeder client, once it polls a market and answers challenges | | x402 v2 offer, `X-PAYMENT` proof, verify and settle | `profullstack/x402-gateway`, CoinPay | `ippay` pass purchase reuses it unchanged | | CloudEvents 1.0 + Standard Webhooks signing | `profullstack/autoblog` | Every OpenSwarm event uses the same envelope and headers | | MTP/1 post-quantum transport, name pins | `profullstack/moshpit-transport`, `moshpit-proxy` | Optional tunnel for native peer links; `ipname` pin kind | diff --git a/docs/openswarm/cli.md b/docs/openswarm/cli.md index b145352..4ab153e 100644 --- a/docs/openswarm/cli.md +++ b/docs/openswarm/cli.md @@ -130,6 +130,29 @@ ip node hello # diagnostic: handshake and p A c0mpute worker embeds the same library; `c0mpute worker start --openswarm` is the daemon form and `ip` is the operator's tool. +### 2.10 Seed market + +``` +ip seed attest ( | | ) --basis own|licensed|open-license|public-domain|personal + [--license ] [--notice ] [--description ] [--public | --private] +ip seed offer ( | | --feed ) --hub --days --seeders [,] + [--price ] [--proof-hours ] [--tracker ]... +ip seed offers [--hub ] [--public | --private] [--basis ] [--min-price ] [--max-size ] +ip seed take --hub # take a lease, fetch, seed until it ends +ip seed leases [--hub ] [--status proven|fetching|lapsed|ended] +ip seed status +ip seed void # requester: void and refund the unearned budget +ip seed notice --kind rights|illegal|personal-data|other --statement +``` + +`ip seed attest` is the consent step and is what `ip file add --public` and +`ip file pin` call first; `ip seed offer` on a private file key includes a +pass for the seeders when the manifest charges per GiB. `ip seed take` is +what a torlink daemon does on its own from `ip seed offers`; here it is the +manual form, and it sets the swarm's seed time to the lease's end so the +client's reaper cannot drop it early. Everything returns the record ids +(`pay2seed` §7.2) and, for `offers`, the projected `earnedUsd` per lease. + ## 3. Output examples ``` diff --git a/docs/openswarm/pay2seed.md b/docs/openswarm/pay2seed.md new file mode 100644 index 0000000..6bd6352 --- /dev/null +++ b/docs/openswarm/pay2seed.md @@ -0,0 +1,589 @@ +# pay2seed: Consent, Seed Offers and the Seed Market + +Status: 0.1 draft. Member of the [OpenSwarm](../openswarm.md) family. +Depends on the [core](./spec.md), [`ipfile`](./ipfile.md), [`ippay`](./ippay.md) +and [`ipdb`](./ipdb.md). + +## 1. Scope + +`pay2seed` is how somebody pays to have data kept alive on a swarm by +machines they do not control, and how the people running those machines are +paid for it. It turns a torrent client into a legitimate file sharer: every +swarm on the market carries a signed statement of who put it there and on +what basis, every seeder holds a lease that says what they were asked to +keep and for how long, and every payout is backed by proof that the bytes +were held and served. + +It covers: + +- **Consent.** A signed attestation, made when the data is added, that the + requester has the right to distribute it and accepts notice. A hub MUST + NOT list an offer without one. This is the part BitTorrent never had. +- **Offers.** A request to store, archive or share a swarm for a period, with + a budget, a minimum number of seeders and a proof cadence. Public offers + cover vanilla torrents and `ipdb` feeds; private offers cover `ipfile` + swarms, whose ciphertext a seeder holds without ever being able to read. +- **Leases.** The hub's binding of one seeder to one offer for one period at + one price. +- **Proof of seeding.** Storage challenges answered from the held pieces, and + probes in which a verifier fetches pieces over the ordinary wire. Both work + on a vanilla torrent, which is what makes public seeding payable. +- **Settlement.** GiB-month accrual per proven period, receipts, and payout + through an `ippay` hub. Served-byte vouchers, where the swarm charges for + them, are separate and additive. +- **The market.** A hub API that lists open offers so a client can pick + work, and a seeder client's expected behaviour. + +`ipfile.pin` ([c0mpute](./c0mpute.md) §3.1) is this document's offer shape +carried on c0mpute's job auction. `pay2seed` is the same request opened to +any seeder: a torlink daemon on a seedbox, a laptop that is on most of the +day, a c0mpute node. A node MAY treat a `pay2seed.offer` as an `ipfile.pin` +job and vice versa; §11 gives the mapping. + +### Non-goals + +Judging content. A hub sees an attestation, an infohash and, for a private +swarm, nothing else. It enforces that the attestation exists, is signed and +is honoured on notice; whether the attestation is true is the requester's +liability, and §9 is how a false one is undone. Not a DRM: a public swarm is +public, a private swarm is `ipfile`'s encryption and no more. Not a token. +Not a replacement for `ippay` vouchers: a swarm that charges leechers keeps +charging them, and its seeders earn both ways. + +## 2. Terminology + +- **Requester.** The identity that posts an offer and funds it. Usually the + publisher of the swarm; for a public torrent it may be anyone who wants it + kept alive, subject to §3. +- **Seeder.** An identity that holds a lease and serves the swarm. +- **Hub.** An `ippay` hub that also implements this document's API (§7). + bittorrented.com is the reference hub. +- **Verifier.** Whoever issues challenges and probes: the hub, a c0mpute + verifier node, or the requester. A lease names who may. +- **Attestation.** The consent record (§3). +- **Offer.** The request (§4). **Lease.** One seeder's share of it (§5). +- **Period.** The unit of accrual and proof, `proof.everyHours` long. +- **Visibility.** `public` (a vanilla torrent or an `ipdb` feed, readable by + anyone) or `private` (an `ipfile` swarm; ciphertext only). + +Keys, records, signatures, amounts and times are as in the core. Every +record below is a core §3 record with the `type` shown. + +## 3. Consent: the attestation + +An attestation is the requester saying, under signature, what the data is +and why they may distribute it. It is made once per swarm, before the first +offer, and every offer for that swarm references it. + +### 3.1 Record + +```json +{ + "openswarm": "0.1", + "type": "pay2seed.attestation", + "subject": { "infohashV1": "4330ddb304b78b1398a77297f57ea22c1ef3fb56", "infohashV2": null, "file": null }, + "visibility": "public", + "basis": "own", + "license": "CC-BY-4.0", + "description": "Field recordings, Dartmoor, 2026", + "notice": "https://example.com/.well-known/pay2seed-notice", + "acceptsTakedown": true, + "createdAt": "2026-09-05T18:00:00.000Z", + "sigs": [{ "alg": "ed25519", "key": "ed25519:0d87…", "sig": "…" }] +} +``` + +| Field | Rule | +| --- | --- | +| `subject` | Exactly one of `file` (an `ipfile` file key; visibility MUST be `private`) or `infohashV1`/`infohashV2` (at least one; visibility MUST be `public`). A feed offer (§4.4) attests the feed key in `file`. | +| `visibility` | `public` or `private`. Fixed for the life of the swarm; changing it is a new swarm. | +| `basis` | Why the requester may distribute this. One of the values in §3.2. | +| `license` | REQUIRED when `basis` is `open-license`: an SPDX identifier. Otherwise OPTIONAL. | +| `description` | OPTIONAL, at most 280 characters, shown on public listings. | +| `notice` | An HTTPS URL or `mailto:` at which the requester receives notices (§9). REQUIRED for `public`; OPTIONAL for `private`, where the hub's own contact for the requester's account stands in. | +| `acceptsTakedown` | MUST be `true`. Present so the acceptance is in the signed bytes. | + +Signer: the requester key. For an `ipfile` subject the attestation MUST also +carry a signature by the file's publisher key, which is how a stranger is +prevented from attesting somebody else's private swarm. For a public +infohash no such proof of authorship exists; §3.3 is what stands in for it. + +### 3.2 Basis values + +| `basis` | Meaning | Allowed visibility | +| --- | --- | --- | +| `own` | The requester made it or holds the rights outright. | either | +| `licensed` | The requester holds a licence that permits redistribution. | either | +| `open-license` | Released under the SPDX licence in `license`. | either | +| `public-domain` | No rights subsist, or they were waived. | either | +| `personal` | The requester's own data, kept for themselves: backup, archive, sync. Nobody else is meant to read it. | `private` only | + +A hub MUST reject an attestation whose `basis` and `visibility` disagree +with this table, and MUST reject any attestation with an unlisted `basis`. +Hubs MAY refuse to list particular bases (a hub for personal backup only, a +hub for open data only); the hub record says which (§7.1). + +### 3.3 Public infohashes and the claim window + +Anyone can attest a public infohash, so the attestation is a claim, not a +proof. A hub lists a public offer only after a **claim window** (`claimHours` +in the hub record, default 24) during which the infohash and the +attestation are visible at `GET /offers/` and to anyone following the +hub's `pay2seed.offer.listed` event, and a notice (§9) voids it. An offer +for a swarm that already has an honoured attestation from a different key +is refused unless the new attestation's basis is `open-license` or +`public-domain`, which are the two bases that do not depend on who is +asking. + +A requester's standing (§7.5) records every attestation of theirs that was +voided. A hub SHOULD refuse new public attestations from a key whose voided +count exceeds its accepted count, and MUST refuse them from a key with three +or more voided attestations in the last year. + +## 4. Offers + +An offer is money on the table for a swarm to be kept for a period. It is +bought from the hub exactly as an `ippay` pass is bought, so the budget is +settled before anyone starts seeding. + +### 4.1 Record + +```json +{ + "openswarm": "0.1", + "type": "pay2seed.offer", + "hub": "ed25519:c9f1…", + "requester": "ed25519:0d87…", + "attestation": "sha256:8e1c…", + "subject": { "infohashV1": "4330ddb304b78b1398a77297f57ea22c1ef3fb56", "infohashV2": null, "file": null, "feed": null }, + "visibility": "public", + "sizeBytes": 7462331904, + "days": 30, + "seeders": { "min": 3, "max": 10 }, + "priceUsdPerGibMonth": "0.150000", + "budgetUsd": "12.000000", + "proof": { "everyHours": 6, "verifiers": ["hub"] }, + "pass": null, + "trackers": ["udp://tracker.opentrackr.org:1337/announce", "wss://tracker.openwebtorrent.com"], + "startsAt": "2026-09-05T18:00:00.000Z", + "expiresAt": "2026-10-05T18:00:00.000Z", + "payment": { "network": "eip155:8453", "nonce": "0x2f0a…" }, + "createdAt": "2026-09-05T18:00:00.000Z", + "sigs": [{ "alg": "ed25519", "key": "ed25519:c9f1…", "sig": "…" }] +} +``` + +| Field | Rule | +| --- | --- | +| `hub` | The hub that sold it and holds the budget. Signer of the record. | +| `requester` | Who paid, bound at purchase as `ippay` §3.3 binds a payer. | +| `attestation` | Record id of an attestation whose `subject` and `visibility` match this offer's. | +| `subject` | As the attestation, plus OPTIONAL `feed`: an `ipdb` feed key (§4.4). | +| `sizeBytes` | Ciphertext or torrent payload size. For a feed, the size at posting; §4.4 says how growth is paid. | +| `days` | Period the swarm is to be held from `startsAt`. | +| `seeders.min` | Leases the hub will hand out before the offer is `active`. `max` caps them; extra seeders beyond `max` earn only vouchers. | +| `priceUsdPerGibMonth` | What one seeder earns for holding the whole swarm for 30 days, pro rata. | +| `budgetUsd` | Escrowed. MUST be at least `max * priceUsdPerGibMonth * sizeBytes/2^30 * days/30`, rounded up to six places. | +| `proof.everyHours` | Period length. 1 to 168. | +| `proof.verifiers` | Who may challenge: `hub`, `requester`, or `c0mpute` (any node with `c0mpute:role:verifier`). | +| `pass` | For a `private` swarm whose `pricing.perGib` is non-zero: an `ippay.pass` scoped to the file, so a seeder can fetch it as a paying peer. `null` otherwise. | +| `trackers` | Announce URLs the seeder MUST add. At least one `wss://` tracker when the requester wants browser peers to find it. | +| `startsAt`, `expiresAt` | `expiresAt` is `startsAt + days`. Leases never outlive it. | +| `payment.nonce` | The settlement that funded `budgetUsd`. | + +### 4.2 Purchase + +`POST /offers` with an unsigned draft (every field but `hub`, `payment`, +`sigs`, `createdAt`) and no `X-PAYMENT` header answers `402` with an x402 +offer for `budgetUsd` plus the hub's fee (§6.3), exactly as `ippay` §3.2 +answers a pass purchase, and with the same `X-OpenSwarm-Payer` binding. +Repeating the request with a valid proof answers `201` with the signed +offer. A hub MUST verify the referenced attestation before quoting, and MUST +refuse a `private` offer whose `sizeBytes` disagrees with the manifest. + +An offer is `pending` until `seeders.min` leases exist, `active` from then +until `expiresAt`, and then `settled`. It is `voided` by a notice (§9) or by +the requester (`DELETE /offers/`, signed), in which case the unearned +budget is refunded to the requester's registered payout address, less what +leases have accrued. + +### 4.3 Public swarms + +A `public` offer is a vanilla torrent. The seeder fetches it from the swarm +like any client, using `trackers` and the DHT, and serves plaintext. Nothing +in the wire changes; the only new thing on the network is another well +behaved seeder that stays. This is how a podcast archive, a dataset, a +software release or an out-of-print recording with a clean basis gets kept +alive by strangers who are paid for it. + +### 4.4 Public feeds + +An offer whose `subject.feed` is set covers an `ipdb` feed: every segment +swarm the feed publishes, present and future, and the duty to keep the +feed's head reachable. A leased seeder MUST follow the feed (`ipdb` §5.4), +fetch each new segment within one period of its appearance in the head, +seed all of them, and re-announce the head as a BEP 44 mutable item at least +once per period so the catalogue stays findable when the publisher is +offline. `sizeBytes` is the feed's size at posting; each new segment is +added to the lease's accrual base at the period in which it was proven, and +the hub MUST NOT hand out leases whose projected accrual exceeds +`budgetUsd`. The requester tops up with a further offer for the same feed. + +### 4.5 Private swarms + +A `private` offer is an `ipfile` swarm. The seeder holds ciphertext and, +unless it also holds a grant, cannot read a byte of it. Where the manifest +has `keys.public` set the data is readable by anyone with the manifest, and +the offer is still `private` in this document's sense: what is being paid +for is storage of a specific swarm, and what the attestation says is that +the requester may store it. `personal` is the basis for a backup nobody else +is ever meant to read; the swarm's `pricing` SHOULD be zero and its manifest +SHOULD not be catalogued. + +## 5. Leases + +### 5.1 Record + +```json +{ + "openswarm": "0.1", + "type": "pay2seed.lease", + "hub": "ed25519:c9f1…", + "offer": "sha256:1b7f…", + "seeder": "ed25519:a41e…", + "slot": 1, + "priceUsdPerGibMonth": "0.150000", + "startsAt": "2026-09-05T18:30:00.000Z", + "endsAt": "2026-10-05T18:00:00.000Z", + "graceHours": 24, + "createdAt": "2026-09-05T18:30:00.000Z", + "sigs": [{ "alg": "ed25519", "key": "ed25519:c9f1…", "sig": "…" }] +} +``` + +| Field | Rule | +| --- | --- | +| `offer` | Record id of the offer. | +| `seeder` | The seeder key. It MUST be a registered `ippay` payee (§6.4), which is where the money goes. | +| `slot` | 1-based, at most `offer.seeders.max`. A seeder holds at most one lease per offer. | +| `priceUsdPerGibMonth` | Copied from the offer at lease time so a later offer edit never changes what was agreed. | +| `graceHours` | Time from `startsAt` in which the seeder MUST pass its first proof, or the lease is `abandoned` and the slot reopens. Long enough to fetch the swarm. | + +A lease is `fetching` until its first proof, `proven` while its most recent +period passed, `lapsed` when a period failed, `abandoned` after +`graceHours` without a first proof, `ended` at `endsAt`, and `voided` with +its offer. + +### 5.2 Taking a lease + +`POST /leases` with `{ "offer": , "seeder": , "sig" }`, `sig` by +the seeder key over `"openswarm:pay2seed:lease:v1" || offer id`. The hub +answers `201` with the lease, or `409` when the offer has no free slot or +this seeder already holds one, or `403` when the seeder's standing (§7.5) +is below the offer's or hub's floor. + +A hub SHOULD hand out slots to seeders in order of standing, and MUST NOT +hand more than `seeders.max`. A seeder SHOULD NOT take a lease it cannot +serve within `graceHours`; an abandoned lease counts against standing. + +## 6. Proof and settlement + +### 6.1 Storage challenges + +Once per period, a verifier named in the offer sends the seeder: + +```json +{ + "openswarm": "0.1", + "type": "pay2seed.challenge", + "lease": "sha256:5c02…", + "period": 7, + "nonce": "1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f", + "pieces": [17, 2048, 91, 4400], + "deadline": "2026-09-07T12:10:00.000Z", + "createdAt": "2026-09-07T12:00:00.000Z", + "sigs": [{ "alg": "ed25519", "key": "ed25519:c9f1…", "sig": "…" }] +} +``` + +The seeder answers within `deadline` with a proof: + +```json +{ + "openswarm": "0.1", + "type": "pay2seed.proof", + "challenge": "sha256:9d3a…", + "answers": ["sha256:…", "sha256:…", "sha256:…", "sha256:…"], + "createdAt": "2026-09-07T12:00:04.000Z", + "sigs": [{ "alg": "ed25519", "key": "ed25519:a41e…", "sig": "…" }] +} +``` + +Each answer is `SHA-256(nonce || piece bytes)` for the piece at that index, +which a verifier that holds the piece hashes and a copy of the piece can +check, and which cannot be answered from the hash alone. A verifier that +does not hold the pieces (the hub, for a swarm it does not store) uses a +probe instead. `pieces` MUST be at least four indices chosen uniformly at +random per challenge; `deadline` MUST be at least ten minutes after +`createdAt`, which is enough to read four pieces from a spinning disk and +not enough to fetch them from the swarm. + +### 6.2 Probes + +A verifier connects to the seeder as an ordinary peer, over BEP 3 for a +vanilla torrent or the `ipfile` extension for a private swarm, and requests +the challenge's pieces. The seeder's ordinary serving is the proof; the +verifier records `{ "lease", "period", "pieces", "receivedBytes", "peer" }` +signed by its own key as the proof record's `answers` equivalent, with +`type` still `pay2seed.proof`. A probe MUST fetch from the seeder's own +announced address; a swarm being alive is not evidence that this seeder is +keeping it alive. + +For a `private` swarm whose `pricing.perGib` is non-zero the verifier pays +vouchers for the probe like any leecher, and those vouchers are the seeder's +in the usual way. Hubs SHOULD prefer probes for public swarms, whose pieces +anyone can fetch, and challenges for private swarms they already pin. + +### 6.3 Accrual and receipts + +A lease earns, for each period in which its proof passed: + +``` +earnedUsd = priceUsdPerGibMonth * (sizeBytes / 2^30) * (everyHours / 720) +``` + +computed with integer arithmetic in micro-USD, rounding down. A failed or +missed period earns nothing and moves the lease to `lapsed`; two +consecutive failures end it and reopen the slot. Earned amounts are +credited to the seeder's `ippay` payee balance and reported with a receipt: + +```json +{ + "openswarm": "0.1", + "type": "pay2seed.receipt", + "lease": "sha256:5c02…", + "period": 7, + "proof": "sha256:e77b…", + "earnedUsd": "0.002604", + "balanceUsd": "0.018228", + "createdAt": "2026-09-07T12:00:05.000Z", + "sigs": [{ "alg": "ed25519", "key": "ed25519:c9f1…", "sig": "…" }] +} +``` + +The hub's fee is charged to the requester at purchase, `hubBps` of +`budgetUsd` with `hubBps` no less than the hub record's `minHubBps`; it is +never deducted from a seeder's earnings. Payout is `ippay` §5.6, on the +same schedule and floor as voucher earnings. + +### 6.4 Payees + +A seeder is an `ippay` payee (`ippay` §6.1). Registering once covers +vouchers, key grants and leases. A seeder key that is not registered cannot +take a lease, because there would be nowhere to pay. + +## 7. The hub + +### 7.1 Hub record + +An `ippay.hub` record that offers this document adds: + +```json +"pay2seed": { + "base": "https://bittorrented.com/api/openswarm/pay2seed", + "bases": ["own", "licensed", "open-license", "public-domain", "personal"], + "claimHours": 24, + "minStanding": 0, + "verifiers": ["hub", "c0mpute"], + "maxDays": 365 +} +``` + +`bases` is what this hub will list. `minStanding` is the floor for taking a +lease. A hub MAY run a market for private backups only, or for open data +only, by narrowing `bases`. + +### 7.2 API + +All bodies are JSON. Records are verified on receipt. Paths are relative to +`pay2seed.base`. + +| Method and path | Auth | Purpose | +| --- | --- | --- | +| `POST /attestations` | signed record | Register an attestation. `201` with its id, `409` if one is honoured for the subject by another key (§3.3). | +| `GET /attestations/` | none | The record and `{ "status": "claimed|honoured|voided", "notices": n }`. | +| `POST /offers` | x402 + `X-OpenSwarm-Payer` | Buy an offer (§4.2). | +| `GET /offers?visibility&basis&minPrice&maxSize&feed&status` | none | The market: open offers with free slots, newest first, paged. | +| `GET /offers/` | none | The offer, its status, leases held, slots free. | +| `DELETE /offers/` | requester signed | Void and refund the unearned budget. | +| `POST /leases` | seeder signed | Take a slot (§5.2). | +| `GET /leases/` | none | The lease, status, periods proven, `earnedUsd`. | +| `GET /seeders//leases?status` | none | A seeder's leases; what a client resumes after a restart. | +| `POST /proofs` | seeder or verifier signed | Answer a challenge or report a probe. Returns the receipt. | +| `GET /leases//challenges?since` | seeder | Pending challenges for a seeder that cannot receive them pushed. | +| `GET /seeders/` | none | `{ "standing", "leases", "proven", "failed", "abandoned", "balanceUsd" }`. | +| `GET /requesters/` | none | `{ "standing", "attestations", "voided", "offers" }`. | +| `POST /notices` | signed | A notice against an attestation (§9). | +| `POST /webhooks` | signed | Register a CloudEvents endpoint for a requester or seeder key. | + +### 7.3 Delivering challenges + +A hub pushes challenges to a seeder's registered webhook when it has one, +and otherwise leaves them to be polled from +`GET /leases//challenges`. A seeder without a webhook MUST poll at +least every `everyHours / 4` hours. `deadline` is measured from +`createdAt`, not from delivery, so a seeder that polls slowly fails +challenges; the cadence is the seeder's problem to meet. + +### 7.4 The market listing + +`GET /offers` is what a seeder client shows its operator, or chooses from +on its own. Each row carries the offer, the attestation's `basis`, +`description` and `visibility`, the free slot count, and the projected +`earnedUsd` for a full lease. A client MUST show `basis` and `visibility` +wherever it shows an offer, so an operator knows whether they are being +asked to hold ciphertext for a stranger's backup or to seed an openly +licensed dataset in the clear. + +### 7.5 Standing + +Seeder standing is `proven - 2 * failed - 3 * abandoned` over the trailing +year, floored at zero. Requester standing is `honoured - 3 * voided`, the +same way. Standing is public at `GET /seeders/` and +`GET /requesters/`, and portable in the sense that it is one hub's +opinion signed by that hub; a second hub MAY read it and MAY ignore it. + +## 8. Client behaviour + +A seeder client (torlink's headless daemon is the reference) does the +following, and a conformant one does all of it: + +1. Holds a seeder key, registered as an `ippay` payee with a payout + address, and stores the key as an OpenCreds `key` item. +2. Polls `GET /offers` on the hubs it is configured for, filtered by the + operator's policy: which `visibility` and `basis` values to accept, the + minimum `priceUsdPerGibMonth`, the maximum `sizeBytes`, free disk to keep + in reserve. +3. Takes a lease, fetches the swarm (as a paying peer when `offer.pass` is + set), adds the offer's `trackers`, and seeds with a per-torrent seed time + of at least `lease.endsAt`, so the client's own reaper never drops a + swarm it is being paid to keep. +4. Answers challenges from local pieces within `deadline`, and serves + probes as ordinary peer traffic. +5. On restart, resumes every lease in `GET /seeders//leases?status=proven,fetching` + before taking new ones. +6. Stops seeding and deletes the data when a lease ends or is voided, unless + the operator has chosen to keep it; for a `private` swarm the ciphertext + is worthless without a grant, and for a `personal` one it MUST be + deleted. +7. Shows the operator, for every held lease, the `basis`, `visibility`, + `earnedUsd` so far, and the next challenge due. + +A requester client (`ip` CLI, a web app) makes the attestation, buys the +offer, and watches `GET /offers/` or the events. + +## 9. Notices and takedown + +Anyone may `POST /notices` against an attestation: + +```json +{ + "openswarm": "0.1", + "type": "pay2seed.notice", + "attestation": "sha256:8e1c…", + "kind": "rights|illegal|personal-data|other", + "claimant": { "name": "…", "contact": "mailto:…" }, + "statement": "…", + "createdAt": "2026-09-06T09:00:00.000Z", + "sigs": [{ "alg": "ed25519", "key": "ed25519:77aa…", "sig": "…" }] +} +``` + +On a notice the hub MUST forward it to the requester's `notice` endpoint +and MUST, within `claimHours`, either void the attestation or record that +it was reviewed and stands. Voiding an attestation voids every offer that +references it: leases end, seeders are told by event and by their next +poll, the requester is refunded the unearned budget less leases' accrued +earnings, and the requester's `voided` count rises. A hub that voids on +every notice without review is conformant; a hub that never voids is not. + +For a `private` swarm the hub cannot inspect the content and does not +pretend to. What it has is the requester's identity, their attestation and +their standing, which is what accountability looks like for ciphertext. + +## 10. Security notes + +- **Sybil seeders.** One machine holding several leases on one offer under + several keys earns several times for one copy. `seeders.max` bounds the + loss; probes from distinct verifiers to the announced addresses, and a + hub refusing leases to keys that share a payout address on the same + offer, bound it further. Standing makes it expensive to repeat. +- **Answer relaying.** A seeder that holds nothing could fetch challenged + pieces from the swarm within `deadline`. Ten minutes is the floor; a hub + SHOULD set shorter deadlines for small swarms, and probes measure the + seeder's own serving rather than what it can fetch. +- **Hub as escrow.** The budget sits with the hub. A requester trusts the + hub with `budgetUsd` for `days`, as a pass buyer trusts it with `capUsd` + for 30. A hub's signed receipts and refunds are its auditable trail. +- **Attestation is a claim.** §3.3's window, standing and notices are the + defence. A hub that lists a public offer the moment it is paid is not + conformant. +- **Private means ciphertext, not secret existence.** A lease reveals to + its seeder the file key, the size and the requester's key. `personal` + swarms SHOULD use a fresh unrelated file pair (core §4.2) so that backups + are not linkable to a publisher's catalogue. + +## 11. Relationship to `ipfile.pin` + +An `ipfile.pin` job (c0mpute §3.1) is a `pay2seed.offer` with +`visibility: "private"`, `seeders.min = seeders.max = 1`, `days` and +`proof.everyHours` copied, `priceUsdPerGibMonth` derived from +`max_price_usd`, and c0mpute's storage challenge standing in for §6.1. A +c0mpute node that wins such a job holds a lease in this document's sense +and MAY report proofs to the hub to build standing there. A hub MAY +republish a `pay2seed.offer` as an `ipfile.pin` job on the c0mpute auction +when no other seeder takes it, which is how the market reaches the paid +storage network without the requester doing anything. + +## 12. Events + +CloudEvents as core §8, `source` the hub base: + +| `type` | Data | Subscriber | +| --- | --- | --- | +| `pay2seed.attestation.registered`, `.honoured`, `.voided` | attestation id | requester | +| `pay2seed.offer.listed`, `.active`, `.settled`, `.voided` | offer id, status | requester, market followers | +| `pay2seed.lease.granted`, `.proven`, `.lapsed`, `.ended`, `.voided` | lease id, period, `earnedUsd` | seeder, requester | +| `pay2seed.challenge.issued` | the challenge record | seeder | +| `pay2seed.notice.received`, `.resolved` | notice id, outcome | requester | + +## 13. Implementations + +| Piece | Where | Status | +| --- | --- | --- | +| Reference hub: attestations, offers, leases, probes, market, notices | `profullstack/media-streamer` (bittorrented.com) | planned; the seedbox rental rail is the pay-per-pass half of it today | +| Seeder client: market polling, leases, per-lease seed time, challenges | `torlink` (`torlnk serve` and `watch`) | planned; per-torrent seed time landed as baairon/torlink#186 | +| Shared client and promo library | `@profullstack/pay2seed` | planned | +| Seeder and verifier on the storage network | `profullstack/c0mpute` via `ipfile.pin` | mapping in §11 | +| Requester CLI | `ip seed …` ([cli.md](./cli.md)) | proposed | + +## 14. Conformance + +A **hub** is conformant when it: refuses offers without an honoured +attestation (§3); enforces the basis table and claim window (§3.2, §3.3); +escrows the budget before listing (§4.2); issues at least one challenge or +probe per period and pays only proven periods (§6); publishes the market +and standing (§7); forwards and resolves notices within `claimHours` (§9). + +A **seeder client** is conformant when it does all of §8. + +A **requester client** is conformant when it makes an attestation the +signer actually holds the key for, buys offers only against it, and +receives notices at the `notice` endpoint it named. + +## 15. Version history + +- 0.1 (2026-09-05): first draft. Attestation with basis table and claim + window; offers bought over x402 with escrowed budgets; leases with + slots and grace; challenges and probes; GiB-month accrual and receipts; + public feeds on `ipdb`; notices; the `ipfile.pin` mapping. diff --git a/docs/openswarm/spec.md b/docs/openswarm/spec.md index 1a89cf7..994379d 100644 --- a/docs/openswarm/spec.md +++ b/docs/openswarm/spec.md @@ -502,6 +502,11 @@ Member conformance is defined per document; the checklist is collected in | `ipvideo.title` | publisher key | ipvideo | | `iplive.channel`, `iplive.head` | channel key | iplive | | `ipname.pin` | publisher key | ipname | +| `pay2seed.attestation` | requester key (and file's publisher key for `ipfile` subjects) | pay2seed | +| `pay2seed.offer`, `pay2seed.lease`, `pay2seed.receipt` | hub key | pay2seed | +| `pay2seed.challenge` | verifier key | pay2seed | +| `pay2seed.proof` | seeder key, or verifier key for a probe | pay2seed | +| `pay2seed.notice` | claimant key | pay2seed | ## 12. Version history diff --git a/prd/0006-add-pay2seed-spec.md b/prd/0006-add-pay2seed-spec.md new file mode 100644 index 0000000..6924909 --- /dev/null +++ b/prd/0006-add-pay2seed-spec.md @@ -0,0 +1,182 @@ +--- +openprd: "0.2" +id: "0006" +title: "Add pay2seed to the OpenSwarm family: consent at upload and a paid seed market" +status: Draft +authors: + - anthony@profullstack.com +created: 2026-09-05 +updated: 2026-09-05 +repo: profullstack/logicsrc +discussion: +implementation: +tags: + - openswarm + - pay2seed + - ipfile + - ippay + - ipdb + - bittorrent + - torlink + - bittorrented + - c0mpute +supersedes: +superseded-by: +--- + +## Problem + +BitTorrent is the best distribution network ever built and it has never +been a legitimate business, because two things are missing from the +protocol. Nobody is paid to seed, so a swarm lives exactly as long as +somebody feels like leaving a client open. And nothing about an upload says +who put it there or whether they were allowed to, so every index is a pile +of unattributed infohashes and every seeder is presumed to be doing +something wrong. + +OpenSwarm (PRD 0005) fixed the first half for encrypted media: `ippay` pays +a seeder per verified piece served. It does not pay anyone to *stay*. A +piece that nobody downloads this month earns nothing, so an archive, a +backup, a dataset waiting for its buyer or a podcast's back catalogue is +exactly as unfunded as it was before. `ipfile.pin` on c0mpute pays a +storage node to hold a swarm, but only a c0mpute node, only for a private +`ipfile` swarm, and only through c0mpute's auction. + +The second half, consent, is not addressed anywhere. bittorrented.com +indexes what the DHT crawl finds and runs a seedbox rental rail, and +neither has a place for "I made this, here is the licence, here is where to +send a notice". A user of ours who wants to share their own work, keep an +off-site copy of their own data, or seed an openly licensed dataset for +money has no way to say so that anyone else can check. + +## Goals + +- **Consent is part of the upload.** A signed attestation with a fixed set + of bases (`own`, `licensed`, `open-license`, `public-domain`, `personal`) + and a notice endpoint, without which no hub lists the swarm. Public + claims get a claim window and a public standing; false ones are voided + on notice and count against the requester. +- **Anyone can pay to have anything kept.** An offer escrows a budget at a + hub for a swarm to be held for N days by M seeders at a price per + GiB-month. Public offers cover vanilla torrents and `ipdb` feeds; private + offers cover `ipfile` ciphertext. +- **Anyone can be paid to keep it.** A seeder takes a lease, proves each + period by challenge or probe, and is paid per proven period through the + same `ippay` payee it already has for vouchers. torlink's headless daemon + is the reference client; a laptop qualifies. +- **The market is one HTTP surface.** `GET /offers` is what a client picks + work from. bittorrented.com is the reference hub. +- **Reuse.** Offers are bought like passes (x402, `X-OpenSwarm-Payer`), + payouts are `ippay` §5.6, challenges are c0mpute's storage challenge, + feeds are `ipdb`, events are the family envelope. One new document, six + new record types, no new primitive. + +## Non-Goals + +- Deciding what content is acceptable. The hub enforces that an + attestation exists, is signed and is honoured on notice. Its truth is + the requester's liability. +- Encrypting public swarms. Public is public; private is `ipfile`. +- A reputation system that crosses hubs. Standing is one hub's signed + opinion. +- A token, a chain, or a DAO. +- The seeder client, the hub, or the library themselves. This PRD adds the + specification; implementation is tracked in each repo (§ Requirements + lists them). + +## Users + +- **A creator or label** who wants their own release kept online for a + year without running a server, and wants it on record that they are the + one who put it there. +- **A person keeping a backup** who wants three copies of ciphertext held + by strangers for 30 days and deleted after, and never wants the strangers + to know what it is. +- **An open data or open source publisher** with a 4 GB installer or a + dataset who wants ten seeders paid to stay, and downloaders to pay + nothing. +- **A seedbox operator or torlink user** with disk and bandwidth to spare + who wants to be paid for holding swarms, and to see, before accepting, + whether they would be holding a stranger's ciphertext or seeding a + CC-BY dataset in the clear. +- **bittorrented.com** as the hub: attestations, offers, the market, and + the seedbox rental rail it already runs, with IPTV and other passes sold + beside it. +- **A c0mpute node** that already bids on `ipfile.pin` and now sees the + same offers from any hub. + +## Requirements + +1. `docs/openswarm/pay2seed.md` specifies: the attestation record and + basis table; the claim window and requester standing; the offer record, + its purchase over x402, and its lifecycle; public swarms, public feeds + on `ipdb`, private swarms; the lease record and slot rules; storage + challenges and probes; GiB-month accrual, receipts and payout through + `ippay`; the hub record extension and API; seeder client behaviour; + notices and takedown; the `ipfile.pin` mapping; events; conformance. +2. `docs/openswarm.md` lists `pay2seed` in the family table, the stack + diagram, "What it defines" and "What already exists". +3. `docs/openswarm/spec.md` §11 registers the six `pay2seed.*` record + types and their signers. +4. `docs/openswarm/cli.md` gains the `ip seed` command group. +5. The `/openswarm` page on logicsrc.com lists `pay2seed` among the + protocols. +6. Implementations, tracked outside this repo: + - bittorrented.com (`profullstack/media-streamer`): the reference hub. + - torlink (`baairon/torlink`, via the `ralyodio` fork): the seeder + client. Per-torrent seed time (#186) is the first piece. + - `@profullstack/pay2seed`: the shared client (hub calls, records, + signing) and the promo surfaces the Profullstack media sites embed. + - c0mpute: `ipfile.pin` interop per §11. + +## UX Notes + +- An attestation is one form on the upload screen: what is it, why may + you share it, where do notices go. The basis list is a radio group; + `personal` is greyed out unless the swarm is private. +- The market listing shows, for every offer, the basis and visibility in + words a seeder can act on: "Open licence (CC-BY-4.0), public, 7.4 GB, + 30 days, earns about $1.04". A seeder client MUST show the same. +- A seeder's own view is a table of leases: what, basis, earned so far, + next challenge due, ends on. torlink's `/downloads` grows a `lease` + block per torrent. +- A requester sees, per offer: status, seeders held out of wanted, proven + periods, and a spend bar against the budget. + +## Success Metrics + +- A public swarm with an honoured attestation and a funded offer on + bittorrented.com is held by its `seeders.min` within `graceHours`, and + still has that many proven seeders at `expiresAt`. +- A torlink daemon takes a lease, passes every challenge for 30 days, and + is paid out without an operator touching it. +- An `ipfile.pin` job from c0mpute and a `pay2seed.offer` from + bittorrented.com are served by the same node with one code path. +- A notice against a public attestation ends every lease on it within + `claimHours` and the requester's standing shows it. + +## Risks & Open Questions + +- **Sybil seeding.** One machine, many keys, one copy. `seeders.max`, + probes to announced addresses, and refusing a shared payout address on + one offer bound it; they do not remove it. Open: whether a hub should + require a small stake per lease that a failed period forfeits. +- **Answer relaying on challenges.** A seeder holding nothing can fetch + the four pieces inside a ten-minute deadline on a fast link. Probes are + the stronger evidence for public swarms; deadlines should scale with + piece count. Open: whether a challenge should also demand a piece the + swarm currently has no other seeder for. +- **Claim window versus liveness.** Twenty-four hours before a public + offer is listed is a day of nobody being paid. Open: a shorter window + for `open-license` and `public-domain` bases, which a licence file in + the swarm can substantiate. +- **Hub as escrow.** Budgets sit with the hub for up to a year. A hub + going away takes them. Same trust as `ippay` passes, longer duration. + Open: whether budgets should be released to the hub per period rather + than at purchase. +- **Private and `personal` swarms are unreviewable by design.** Consent is + the requester's signature and standing, nothing else. This is stated + in the spec rather than hidden. +- **Naming.** The rest of the family uses `ip` names. `pay2seed` is the + product name and reads as one to a seeder choosing work; the record + types are `pay2seed.*` so the two never need reconciling. diff --git a/prd/README.md b/prd/README.md index 4ef71fd..4b5782c 100644 --- a/prd/README.md +++ b/prd/README.md @@ -16,3 +16,4 @@ Status lives in each file's front-matter and is the source of truth: | [0003](./0003-add-logicsrc-opencontext-spec.md) | Add the LogicSRC OpenContext specification | Draft | opencontext, context, agents, permissions, provenance, schemas | | [0004](./0004-add-logicsrc-opencreds-spec.md) | Add the LogicSRC OpenCreds specification | Draft | opencreds, credentials, vault, encryption, portability, schemas | | [0005](./0005-add-logicsrc-openswarm-spec.md) | Add the LogicSRC OpenSwarm specification family | Draft | openswarm, ipfile, ipdb, ipaudio, ipvideo, iplive, bittorrent, x402, c0mpute | +| [0006](./0006-add-pay2seed-spec.md) | Add pay2seed to the OpenSwarm family: consent at upload and a paid seed market | Draft | openswarm, pay2seed, ipfile, ippay, ipdb, bittorrent, torlink, bittorrented, c0mpute | From 3794857a6f6b9e9214ddf03eee4eca0494a30bf3 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sun, 6 Sep 2026 00:43:34 +0000 Subject: [PATCH 2/5] Split pay2seed into client and server halves, and add pay2stream and paid2stream The rule is now in the names. pay2* is the client protocol: the side that pays, over HTTPS, and plays. paid2* is the server protocol: the BitTorrent side that earns. One hub implements both halves of a pair; a requester or viewer implements only pay2*; a seeder, relay or gateway only paid2*. pay2seed keeps consent, offers, the requester's market and notices. paid2seed takes leases, storage challenges and probes over the wire, GiB-month accrual and receipts, the seeder client, and the ipfile.pin mapping. pay2stream and paid2stream do the same for a live channel over iplive. A broadcaster attests the channel (with the two rules that separate a licensed rebroadcast from a stolen feed), buys relays by the hour, and publishes listings; viewers buy tickets. Relays take leases and are proven present by a verifier that pulls segments as a peer; a gateway is a relay that also serves standard HLS, clear or sealed, with the M3U and XMLTV pair every IPTV app asks for, so VLC, TiviMate, Kodi and a television play a paid swarm with nothing installed. Ace Stream showed BitTorrent can carry live TV to millions; this is that with consent, payment and an open spec. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01SKAohrRkqLKVQL2cGCAkR5 --- apps/logicsrc-web/src/app/openswarm/page.tsx | 5 +- docs/openswarm.md | 30 +- docs/openswarm/cli.md | 15 +- docs/openswarm/paid2seed.md | 325 +++++++++++ docs/openswarm/paid2stream.md | 276 +++++++++ docs/openswarm/pay2seed.md | 558 +++++-------------- docs/openswarm/pay2stream.md | 251 +++++++++ docs/openswarm/spec.md | 14 +- prd/0006-add-pay2seed-spec.md | 33 +- prd/README.md | 2 +- 10 files changed, 1080 insertions(+), 429 deletions(-) create mode 100644 docs/openswarm/paid2seed.md create mode 100644 docs/openswarm/paid2stream.md create mode 100644 docs/openswarm/pay2stream.md diff --git a/apps/logicsrc-web/src/app/openswarm/page.tsx b/apps/logicsrc-web/src/app/openswarm/page.tsx index fcaed91..9db3725 100644 --- a/apps/logicsrc-web/src/app/openswarm/page.tsx +++ b/apps/logicsrc-web/src/app/openswarm/page.tsx @@ -18,7 +18,10 @@ const PROTOCOLS: Array<[name: string, line: string]> = [ ["ipvideo", "Video on demand: CMAF renditions, segment index, subtitles, thumbnails, an HLS bridge"], ["iplive", "Live streams: segment fan-out over paid relays with backpressure"], ["ipname", "How a Moshpit name or a domain resolves to a publisher key and a catalogue"], - ["pay2seed", "Consent at upload, seed offers with escrowed budgets, leases, proof of seeding, and the market a seeder client picks work from"] + ["pay2seed", "Client protocol for paid seeding: consent at upload, seed offers with escrowed budgets, the requester's market"], + ["paid2seed", "Server protocol for paid seeding: leases, storage challenges and probes over the wire, GiB-month settlement, the seeder client"], + ["pay2stream", "Client protocol for paid live streams: consent for channels, relay offers by the hour, tickets, watching on any HLS player"], + ["paid2stream", "Server protocol for paid live streams: relay leases, presence proofs, gateways serving standard HLS with an M3U and EPG"] ]; const ROLES: Array<[role: string, work: string, paid: string]> = [ diff --git a/docs/openswarm.md b/docs/openswarm.md index d6a012b..aedd2ae 100644 --- a/docs/openswarm.md +++ b/docs/openswarm.md @@ -34,7 +34,10 @@ a different product. The member protocols keep their `ip` names. | `ipvideo` | Video on demand on `ipfile` swarms: CMAF renditions, segment index, subtitles, thumbnails | [`ipvideo.md`](./openswarm/ipvideo.md) | | `iplive` | Live streams: segment fan-out over peers, paid relays, backpressure | [`iplive.md`](./openswarm/iplive.md) | | `ipname` | How a Moshpit name resolves to a publisher key and a catalogue | [`ipname.md`](./openswarm/ipname.md) | -| `pay2seed` | Consent at upload, seed offers with escrowed budgets, leases, proof of seeding, the seed market | [`pay2seed.md`](./openswarm/pay2seed.md) | +| `pay2seed` | Client protocol for paid seeding: consent at upload, seed offers with escrowed budgets, the requester's market | [`pay2seed.md`](./openswarm/pay2seed.md) | +| `paid2seed` | Server protocol for paid seeding: leases, storage challenges and probes over the wire, GiB-month settlement, the seeder client | [`paid2seed.md`](./openswarm/paid2seed.md) | +| `pay2stream` | Client protocol for paid live streams: consent for channels, relay offers, tickets and listings, watching as a peer or on any HLS player | [`pay2stream.md`](./openswarm/pay2stream.md) | +| `paid2stream` | Server protocol for paid live streams: relay leases per hour, presence proofs, gateways serving standard HLS, M3U and EPG | [`paid2stream.md`](./openswarm/paid2stream.md) | Supporting documents: @@ -51,8 +54,10 @@ Supporting documents: | ipaudio ipvideo iplive ipdb (catalogue) | | releases titles channels feeds, entries | +---------------------------------------------------------------+ - | pay2seed: attestation, offers, leases, challenges, market | - +---------------------------------------------------------------+ + | pay2seed / paid2seed | pay2stream / paid2stream | + | consent, offers, market | consent, relay offers, tickets | + | leases, challenges, payout | leases, presence, HLS gateways | + +------------------------------+--------------------------------+ | ipfile: manifest, per-file key pair, encrypted pieces, | | key grants, credit window, vouchers per served piece | +-------------------------------+-------------------------------+ @@ -135,12 +140,19 @@ serves more than the window unpaid. Its head is a BEP 44 mutable item under the feed key, so any DHT node can find the latest catalogue of any publisher with one `get`. -**One seed market.** A `pay2seed` offer is money escrowed at a hub for a -swarm to be kept for a period, public or private, and it cannot be listed -without a signed attestation of who put the data there and on what basis. -Seeders take leases, prove they hold and serve the pieces every period, and -are paid per GiB-month. It is how a torrent client becomes a legitimate file -sharer: consent on the way in, proof on the way out, and a payout for staying. +**One seed market, one stream market.** A `pay2seed` offer is money +escrowed at a hub for a swarm to be kept for a period, public or private, +and it cannot be listed without a signed attestation of who put the data +there and on what basis. On the `paid2seed` side, seeders take leases, +prove they hold and serve the pieces every period, and are paid per +GiB-month. `pay2stream` and `paid2stream` do the same for a live channel: +a broadcaster attests it and buys relays by the hour, relays and gateways +prove they are online and serving, and a gateway turns the swarm into +standard HLS so any television plays it. The naming is the rule: `pay2*` +is the client protocol, the side that pays over HTTPS; `paid2*` is the +server protocol, the BitTorrent side that earns. It is how a torrent +client becomes a legitimate file sharer: consent on the way in, proof on +the way out, and a payout for staying. ## What it does not define diff --git a/docs/openswarm/cli.md b/docs/openswarm/cli.md index 4ab153e..5b191f4 100644 --- a/docs/openswarm/cli.md +++ b/docs/openswarm/cli.md @@ -130,7 +130,7 @@ ip node hello # diagnostic: handshake and p A c0mpute worker embeds the same library; `c0mpute worker start --openswarm` is the daemon form and `ip` is the operator's tool. -### 2.10 Seed market +### 2.10 Seed and stream markets ``` ip seed attest ( | | ) --basis own|licensed|open-license|public-domain|personal @@ -145,8 +145,17 @@ ip seed void # requester: void and refun ip seed notice --kind rights|illegal|personal-data|other --statement ``` -`ip seed attest` is the consent step and is what `ip file add --public` and -`ip file pin` call first; `ip seed offer` on a private file key includes a +``` +ip stream offer --hub --from