Skip to content

The pay2seed hub: consent, escrowed offers, leases and the 1 percent - #211

Merged
ralyodio merged 1 commit into
masterfrom
pay2seed-hub
Sep 6, 2026
Merged

The pay2seed hub: consent, escrowed offers, leases and the 1 percent#211
ralyodio merged 1 commit into
masterfrom
pay2seed-hub

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

What this is

bittorrented.com becomes the reference hub for the OpenSwarm payment family. The four specs merged this morning (profullstack/logicsrc#143, live at logicsrc.com/openswarm); this is the implementation of the seed half.

The two rules everything rests on

Nothing is listed without consent. A requester signs an attestation saying what they are putting on a swarm and why they may share it, carrying a README that becomes the swarm's page. A public claim waits out a window in the open first, so a notice can void it before anyone is paid to seed something the requester had no right to. A private swarm is ipfile ciphertext a seeder holds without ever reading.

A promised floor is what the seeder is paid. The hub takes 1 percent of what crosses it, charged to whoever is paying, on top of what they quoted, and never taken out of a seeder's earnings.

Lanes

Every party is a key, and a key is a human or a bit: an autonomous agent holding its own key, earning its own money, under the same consent rules. Because either side of a payment can be either, the lane is stamped on every lease and receipt at the moment the money is agreed, so it survives a party later changing kind.

lane who pays whom
h2h a person pays a person
h2b a person pays an agent
b2h an agent pays a person
b2b an agent pays an agent

An agent selling in public must name a responsible operator key, so a notice always reaches somebody.

Money

Exact six-decimal strings over integer micros the whole way, becoming a number only at the Postgres boundary. A period earns price × size × time, rounded down, so a hub and a seeder computing it apart always agree; a budget is escrowed rounded up, so a proven period can never exceed what was set aside for it. A receipt's unique (lease, period) is the whole of the idempotency: a verifier that reports twice pays once.

Surface

  • POST /api/openswarm/pay2seed/attestations · parties · offers · leases · proofs · notices
  • GET /api/openswarm/pay2seed/offers — the market, honoured attestations only
  • GET /.well-known/openswarm-hub.json — the hub record, 1 percent and all four lanes
  • /swarm and /swarm/[id] — the market, and a swarm's page built from its README

Schema

Ten tables, all RLS-enabled and service-role only, already applied to the live database and verified. The migration file matches what was applied and is safe to re-run.

A note on the generated types

Regenerating src/lib/supabase/types.ts wholesale surfaced 129 unrelated type errors across the app, because the checked-in types were well behind the live schema. So only the ten new tables were added rather than the whole file. Bringing the rest current is real work and its own PR.

Checks

tsc --noEmit 0 errors. vitest run 2,770 passing, 206 files. ESLint clean on everything new. 23 new tests cover canonical bytes, signature verification (including that a signature cannot be replayed as another record type), the 1 percent, the four lanes, period arithmetic, and a README renderer that must never emit HTML somebody put in their own README.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SKAohrRkqLKVQL2cGCAkR5

bittorrented.com is now the reference hub for the OpenSwarm payment family
(logicsrc.com/openswarm). The specs were merged this morning; this is the
implementation.

A requester signs an attestation saying what they are putting on a swarm
and why they may share it, with a README that becomes the swarm's page.
Nothing is listed without one. A public claim waits out a window in the
open first, so a notice can void it before anyone is paid to seed
something the requester had no right to. A private swarm is ciphertext a
seeder holds without ever reading.

An offer escrows a budget for the swarm to be kept for N days by M
seeders at a price per GiB-month. It stays unpaid until the money
settles, because a budget that is not escrowed is not a promise anyone
should seed against. Seeders take slots by signing the offer id, prove
each period, and are paid per proven period; the receipt's unique
(lease, period) is the whole of the idempotency, so a verifier that
reports twice pays once. Two consecutive failures reopen the slot.

Every party is a key, and a key is a human or a bit: an autonomous agent
that holds its own key and earns its own money under the same consent
rules. Because both sides can be either, the lane is stamped on every
lease and receipt at the moment the money is agreed. h2h, h2b, b2h, b2b.
We carry all four.

The hub takes 1 percent of what crosses it, charged to whoever is paying
and never taken out of a seeder's floor. Money is exact six-decimal
strings over integer micros the whole way and only becomes a number at
the Postgres boundary, so a hub and a seeder computing a period apart
always agree.

Ten tables, all RLS-enabled and service-role only, applied to the live
database. The API is at /api/openswarm/pay2seed with the hub record at
/.well-known/openswarm-hub.json, and the market is at /swarm, where a
swarm's README is rendered through a subset renderer that emits no HTML
from the source and is sanitised again after.

The generated Supabase types were regenerating 129 unrelated breakages
across the app, so only the ten new tables were added rather than the
whole file. Bringing the rest current is its own piece of work.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SKAohrRkqLKVQL2cGCAkR5
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

99 finding(s)

HIGH/CRITICAL: 11 | MEDIUM: 29 | LOW: 59

Severity Rule Location
HIGH secret-private-key src/app/settings/seedbox-section.tsx:412
HIGH secret-generic-api-key docs/incidents/2026-05-okshanaby-supply-chain.md:18
HIGH tls-verification-disabled src/app/api/iptv-proxy/route.ts:38
HIGH tls-verification-disabled src/app/api/iptv/channels/route.ts:35
HIGH tls-verification-disabled src/app/api/iptv/playlists/[id]/route.ts:73
HIGH tls-verification-disabled src/app/api/iptv/playlists/route.ts:64
HIGH js-cors-origin-reflected src/app/api/public/shares/[slug]/checkout/route.ts:40
HIGH js-cors-origin-reflected src/app/api/public/vod/[slug]/checkout/route.ts:34
HIGH tls-verification-disabled src/lib/iptv/shares/upstream.ts:37
HIGH tls-verification-disabled workers/iptv-cache/epg-fetcher.ts:25
HIGH tls-verification-disabled workers/iptv-cache/playlist-fetcher.ts:62
MEDIUM secret-jwt .github/workflows/ci.yml:120
MEDIUM secret-jwt .github/workflows/ci.yml:121
MEDIUM secret-jwt .github/workflows/ci.yml:123
MEDIUM secret-jwt .github/workflows/ci.yml:162
MEDIUM secret-jwt .github/workflows/ci.yml:164
MEDIUM secret-jwt docs/tunein (2).py:9
MEDIUM secret-jwt docs/tunein.py:9
MEDIUM sh-remote-script-execution scripts/setup-server.sh:182
MEDIUM sh-remote-script-execution scripts/setup-server.sh:419
MEDIUM sh-remote-script-execution scripts/setup-server.sh:428
MEDIUM sh-unquoted-expansion-destructive scripts/setup-server.sh:1073
MEDIUM sh-unquoted-expansion-destructive scripts/setup-server.sh:1083
MEDIUM js-unescaped-html-sink src/app/api/player/route.ts:110
MEDIUM js-unescaped-html-sink src/app/api/player/route.ts:249
MEDIUM js-unescaped-html-sink src/app/blog/[slug]/page.tsx:40
MEDIUM js-unescaped-html-sink src/app/blog/[slug]/page.tsx:66
MEDIUM js-unescaped-html-sink src/app/email/email-content.tsx:566
MEDIUM js-open-redirect src/app/login/page.tsx:50
MEDIUM js-open-redirect src/app/pricing/page.tsx:161
MEDIUM js-open-redirect src/app/rent/[slug]/rent-client.tsx:170
MEDIUM js-unescaped-html-sink src/app/rss/rss-content.tsx:615
MEDIUM js-open-redirect src/app/vod/[slug]/vod-client.tsx:134
MEDIUM js-open-redirect src/app/watch/[slug]/watch-client.tsx:129
MEDIUM js-unescaped-html-sink src/app/youtube/youtube-content.tsx:546
MEDIUM js-open-redirect src/components/account/iptv-subscription-section.tsx:124
MEDIUM js-open-redirect src/components/account/iptv-subscription-section.tsx:156
MEDIUM js-unescaped-html-sink src/components/news/news-section.tsx:361
MEDIUM js-unescaped-html-sink src/components/news/news-section.tsx:734
MEDIUM redos-nested-quantifier src/lib/metadata-enrichment/metadata-enrichment.ts:317
LOW tls-verification-disabled docs/tunein (2).py:34
LOW tls-verification-disabled docs/tunein (2).py:37
LOW py-xpath-injection docs/tunein (2).py:37
LOW tls-verification-disabled docs/tunein (2).py:47
LOW py-xpath-injection docs/tunein (2).py:47
LOW tls-verification-disabled docs/tunein.py:34
LOW tls-verification-disabled docs/tunein.py:37
LOW py-xpath-injection docs/tunein.py:37
LOW tls-verification-disabled docs/tunein.py:47
LOW py-xpath-injection docs/tunein.py:47

…and 49 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit cf30c1a into master Sep 6, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant