00 — Overview: The Soapbox Stack Operator Manual
One manual answering: can TresPies/DojoGenesis run community ops, company ops, funding, client sites, and publishing on the Soapbox.pub / Nostr stack — and where do we build?
About this manual (build-in-public)
Built and maintained by TresPies Design / DojoGenesis. Researched and written via orchestrated AI agent waves — recon agents → adversarial kill-passes → writer agents → human-operator direction — not a single pass, and not one model.
- Provenance: every chapter carries its own Verified/Amended dates and per-claim confidence tiers; a public correction-log discipline means errors found in our own working documents get logged, not hidden.
- Platform-agnostic by design: nothing here assumes a specific agent or model lane — bring Claude Code, Cursor, Windsurf, OpenCode, DojoGenesis Gateway, or a plain terminal.
- How to adapt: fork the reading order, keep the protocol chapters as reference, swap the worked-example specifics (a firm's niche, a team's hooks) for your own.
- Canonical home: nostr.trespies.dev/manual · agent-readable faces at
/manual/llms.txtand/manual/index.json(per-chapter sha256).
Verified: 2026-07-09. Research: 8 parallel agents (chapters 01–06, 08–09), each primary-sourced with numbered citations. Synthesis (this file + ch. 07): main thread, per the amended orchestrator gate (operator-ratified 2026-07-09). One mid-flight adjudication is recorded honestly: Armada was first verdicted nonexistent, then found at the repo level, verified, and corrected (ch. 02) — the product is 26 days old and simply unindexed. Delta pass 2026-07-10 (ch. 05 + ch. 10): NostrHub velocity 866 commits/29 days; no bounty/contest program exists anywhere in the 60-post blog index; new MCP/agent-surface addendum — the "acting NostrHub MCP server" niche is verified empty; ⚠ nostrdeploy.com is DNS-dead (Stacks deploy lane tripwired, affects ch. 04's route too); Stacks 1.0 = the generic template platform, MKStack = one published stack on it.
The four original questions
| Asked | Verdict (one line) | Where |
|---|---|---|
| "Armada for community and company operations — can it do filesharing?" | Armada is real (b. 2026-06-13): E2E Discord-shaped chat on the new Concord protocol — pilot for internal ops; Ditto is the mature public-community layer. Filesharing: media yes (Ditto plaintext, Armada E2E) — folders/ACLs/versioning no; keep Drive | 02 |
| "Agora for funding" | Yes, today, permissionless: $0 platform fee, non-custodial wallet-to-wallet BTC (on-chain, not Lightning). Caveats: activist brand gravity, donors need BTC, no tax-receipt path for anonymous gifts | 03 |
| "Shakespeare for sites and funnels — free mockups?" | Mockups: effectively free (free Gemini tier / BYO cheap key / local models; Soapbox takes no cut). Funnels: top-of-funnel native (forms, Lightning/Cashu); conversion capture wires to Beehiiv/ManyChat/Cal.com/Stripe | 04 |
| "Get deep into NostrHub — I have credentials" | Dev hub relaunched as 2.0 on 2026-06-28: NIPs + apps + git-over-Nostr, "configurable meritocracy." Sign in with NIP-07 extension or NIP-46 bunker — never paste your nsec. Publishing = kinds 30617 (repo), 31990 (app), 30023 (long-form) | 05 |
The stack at a glance
Full portfolio (45+ products incl. Toybox experiments): master map in ch. 01.
Verdicts in brief
| Dimension | Verdict | Confidence |
|---|---|---|
| Company durability | Alive and shipping (same-day commits; 12 humans; OpenSats/HRF grants since 2023) — risk is grant non-renewal, not product death; Ditto/Shakespeare fork-survivable, Armada not yet | High |
| Licensing boundary | Apps AGPL-3.0 (client-facing mods must be published); Nostrify MIT confirmed; MKStack template ships NO license file (ask Soapbox before closed commercial reuse — ch. 10); mkstack-nsp AGPLv3 | High |
| Community ops | Ditto now (themes, 50+ Mastodon apps, real self-host: Deno+Postgres); no native DMs, no NIP-29 yet | High |
| Internal/company ops | Armada pilot only — E2E by default, serverless over public relays, unaudited 0.x moving daily | Med |
| Filesharing | Media-in-feed/chat yes; Drive/Dropbox replacement no (no folders/ACLs/versions; Blossom Drive deprecated → Bouquet) | High |
| Funding | Agora: permissionless, $0 fees, on-chain BTC only; fiat off-ramp + receipts are your problem | Med-High |
| Sites/funnels | Near-$0 mockups; native Lightning/Cashu; conversion stack external; no CMS for client self-edit | High |
| Publishing | NostrHub = discovery/credibility layer (metadata only, no artifact hosting); interop with gitworkshop/ngit verified | High |
| i18n readiness | Stack is EN-only except Agora (i18next, 16 locales, es 99.96%) — fork that pattern; no ratified Nostr language-tag NIP | High |
| Visualization | Recharts ships inside MKStack; Atlas-class census dashboards run zero-server (MapLibre+PMTiles+DuckDB-WASM); Ditto has no widget surface; 30023 forbids HTML | High |
How to read this manual
- Operator quick path: this page → 07 — Integration Playbook (runbooks + a worked-example integration blueprint).
- Builder path (v2, added 2026-07-10): 11 — Nostrify → 12 — Building Consumer Apps → 06 — Foundations as reference → 15 — Production before anything ships. Relay decisions: 13; shipping channels: 14.
- Product deep-dives: 01 (company) · 02 (Ditto+Armada) · 03 (Agora) · 04 (Shakespeare) · 05 (NostrHub) · 10 (MKStack — the framework layer and agent contract).
- Protocol layer: 06 — Nostr Foundations — read §1 (keys) before touching your credentials; master NIP table at the end.
- Steering direction (build phase): 08 — i18n → 09 — Visualization → 07 §9 blueprint.
Chapter index
| Ch. | File | One line |
|---|---|---|
| 01 | 01-soapbox-company.md | Who Soapbox is: history, funding, licenses, 45-product master map, durability |
| 02 | 02-community-ops.md | Ditto (public community) + Armada (E2E team chat) + the filesharing verdict |
| 03 | 03-agora.md | Non-custodial BTC fundraising: mechanics, fees, compliance reality, white-label |
| 04 | 04-shakespeare.md | AI site builder: cost mechanics, mockup economics, funnel wiring, deploy paths |
| 05 | 05-nostrhub.md | NostrHub 2.0: auth with your keys, publishing repos/apps/articles, governance |
| 06 | 06-nostr-foundations.md | Keys, relays, kinds, payments, Blossom, moderation, DVMs — the protocol reference |
| 07 | 07-integration-playbook.md | Runbooks A–D, integration matrix, build opportunities, a worked-example modded-stack blueprint (TresPies) |
| 08 | 08-i18n-integration.md | i18n platforms × stack readiness: fork Agora's pattern, add Lingo.dev, symmetric parity gate |
| 09 | 09-visualization-integration.md | Viz components × stack: MapLibre+PMTiles+ECharts, the zero-server Atlas pattern |
| 10 | 10-mkstack.md | The framework layer: scaffold anatomy, AGENTS.md agent contract + .mcp.json, provider lanes, license flag |
| 11 | 11-nostrify.md | Nostrify: one interface, many backends — stores, pools, signers, schema, 20 policies, uploaders, the 9-package map |
| 12 | 12-building-consumer-apps.md | The builder curriculum: all 19 skills as definition-of-done, the Shakespeare→OpenCode→clone ladder, scaffold-vs-product, the AI-team pattern |
| 13 | 13-relay-operations.md | Relay ops for an app team: strfry/khatru/Ditto/ditto-relay menu, archive-first pattern, monitoring, spam, budget |
| 14 | 14-distribution-native.md | Distribution + native: Zapstore, Capacitor, Lockdown Mode, the PWA-push gap, signer channels |
| 15 | 15-production-secrets-longevity.md | Production: durability economics, deletion vs law, operator liability, degradation, secrets/custody, exit playbook, incident catalog |
v2 wave (2026-07-10): chapters 11–15 added under the consumer-app builder lens; chapters 01/04/06/10 amended (see each chapter's Amended line). Gap analysis and production audit: maintainers' companion notes (not part of this public chapter set).
v2.1 editorial pass (2026-07-10): generalized for public, platform-agnostic readers; provenance section added.
Source of truth: this repository's notes/manual/ directory, mirrored to the maintainers' internal workspace (internal: OTH-24 manual v1, OTH-25 v2 + hosting, PIP-94 build).
Soapbox.pub — Company Dossier
A grant-and-donation-funded, AGPL-copyleft software studio building the Nostr protocol's social, fundraising, and AI-website-builder layer — founded by Truth Social's former head of engineering.
Verified: 2026-07-09 · Amended: 2026-07-10 (v2 wave) — added the plural AI-team-members section (Dirk Rost, Quilly, Sheila, producer-role culture, cross-ref ch. 12); reclassified HRF funding from recurring grantor to prize/event partner and flagged OpenSats' BTC-denominated treasury as a second-order risk; added a bus-factor finding to Durability & Risk, cross-ref ch. 15.
Confidence: High for company facts, funding sources, licenses, and product inventory (primary-sourced, cross-checked against GitLab/GitHub). Medium for team headcount and exact grant dollar amounts (not publicly itemized). Low for the durability of any single "Toybox" experiment (many are days-to-weeks old with zero external stars).
What Soapbox Is
Soapbox describes itself as an open-source studio building "tools for Freedom Online" — decentralized, ad-free, non-custodial software with no VC and no shareholders [1]. Its ethics pledge states the stakes plainly:
"We would rather shut down than compromise our principles for profit." [3]
That is not rhetorical flourish — it is the operating thesis an operator has to underwrite before building on this stack. Everything below tests whether the thesis is currently backed by cash, commits, and people.
History Timeline
Soapbox has gone through three distinct eras: a Fediverse-frontend company (2019–2022), a Nostr-infrastructure company funded by its founder's Truth Social exit (2023–2024), and — starting mid-2025 — an AI/"vibe-coding" studio. The throughline is Alex Gleason, who founded Soapbox in 2019 as a Mastodon-frontend fork, was hired by Trump Media & Technology Group in January 2022 to adapt that frontend for Truth Social, served as TMTG's Head of Engineering through mid-2023, then resigned to build on Nostr full-time the same month OpenSats funded him to do it [5][6][23].
| Era | Date | Event |
|---|---|---|
| Fediverse FE | 2020-06-15 | Soapbox FE v1.0 announced — Mastodon-compatible frontend [2] |
| Fediverse BE | 2021-10 | Chooses Pleroma as backend over Mastodon ("slow, expensive, not innovating") [19] |
| Truth Social | 2022-01 | TMTG hires Gleason to adapt Soapbox as Truth Social's frontend [6] |
| Fediverse BE | 2022-08-19 | Soapbox BE (a Pleroma fork) renamed Rebased [19] |
| Fediverse FE | 2022-12-25 | Soapbox 3.0 released [2] |
| Nostr pivot | 2023-02-26 | Mostr launches — first Fediverse↔Nostr bridge [2] |
| Nostr pivot | 2023-07-17 | Gleason resigns from Truth Social; OpenSats funds Soapbox 1 year from its $5M Dorsey-donated Nostr fund to build Ditto [23] |
| Nostr build-out | 2024-04-17 | Nostrify (MIT-licensed framework) announced [2] |
| Nostr build-out | 2024-06-14 | Ditto announced publicly, incl. mobile [2] |
| Nostr build-out | 2025-03-23 | Ditto 1.3 — Cashu/ecash support [2] |
| AI pivot | 2025-05 to 2025-07 | Company "pivots to AI-assisted programming tools" per its own quarterly report [30] |
| AI pivot | 2025-07-10 | Shakespeare (AI website builder) announced [2] |
| AI pivot | 2025-11-24 | "Our Stance on AI" / "Really Open AI" ethics post [12] |
| AI + activism | 2026-01-23 | Agora born at HRF's "AI Hack for Freedom" hackathon, Bitcoin Park Austin — team wins 25M sats [20] |
| AI + activism | 2026-03-06 | Sheila, an AI agent, takes over Soapbox's own bookkeeping [24] |
| Current wave | 2026-06-04 | Agora publicly launches at Oslo Freedom Forum with World Liberty Congress [2] |
| Current wave | 2026-06-13 | Armada repo created (see dedicated note below) [18] |
| Current wave | 2026-07-08/09 | Latest blog post + same-day commits across ~10 repos (verified day of writing) [2][8] |
Confidence: High — dates are primary-sourced from the company's own blog index and GitLab activity timestamps.
Team & Funding
Soapbox is not a solo-founder shop. Its /about page lists 11 named humans plus Gleason (founder/BDFL) across engineering, DevRel, product, and "VibeOps," plus an AI persona ("Quilly 🪶") credited as a team member [15] — see AI Team Members, below, for the fuller and still-growing picture. It has no revenue product: transparency copy states flatly, "We make no income from our users or apps. No ads. No data sales. No venture capital. No shareholders" [4]. It survives entirely on grants and donations, fiscally sponsored by And Other Stuff (AOS) while pursuing 501(c)(3) status, with OpenSats as its one demonstrated recurring grant source [4][23]. (Corrected 2026-07-10 — this line previously also named Human Rights Foundation as a second grant source; see the Funding correction note below the diagram.)
This is not a dormant arrangement. Soapbox's own Open Collective ledger shows an AOS-disbursed W2 payroll run for Software Engineer / DevRel / Marketing roles ($25,977.35) and a conference-travel line for bitcoin++ Nairobi ($6,794.02), both dated July 2026 — the same month this dossier was written [21]. OpenSats has funded Soapbox continuously since its first grant in Q3 2023, with 9 consecutive quarterly public reports through April 2026 and no visible funding gap [4][23][30].
Funding correction (2026-07-10): a same-day audit checked HRF's own 2026 Bitcoin Development Fund grant rounds — 20+ projects per round, names like Snort, Coracle, Zapstore, and Elsat recur — and found no round naming Soapbox, Ditto, or Agora [33]. Soapbox's actual HRF touchpoints are prize/event partner, not grantor: Agora's 25M-sat win at HRF's "AI Hack for Freedom" hackathon (Jan 2026) and the Oslo Freedom Forum as Agora's public-launch venue (Jun 2026). OpenSats continuity is grantee-self-reported — 9 consecutive quarterly transparency reports through April 2026, with no grantor-side (OpenSats-published) 2026 confirmation independently found [4][30]. Net: one demonstrated funding line, self-reported, not the two-funder diversification the diagram previously implied. New second-order risk: OpenSats' own treasury is BTC-denominated, so a Bitcoin bear market mechanically shrinks its USD disbursement capacity — a risk layer beneath ordinary grant-renewal risk. This corrects the diagram above and the "funder concentration" bullet in Durability & Risk, below; it does not change the "alive-and-shipping" verdict (same-day commits and cleared payroll are unaffected facts).
Confidence: High for funding sources and current-month payroll evidence (primary, dated). Medium for total headcount — the payroll line names only 3 roles, so how many of the 12 listed team members are W2 vs. volunteer/contributor is not reconciled in public data.
AI Team Members — Plural and Evolving
At least three named AI personas do Soapbox's own work, each announced separately, with no roster page tying them together [31][32][24] — verify currency before citing any single one as the example:
- Dirk Rost — code review and merge-request approval across Soapbox's projects, giving the team what its own post calls a "bird's-eye view" (cross-project oversight, not one-repo scope): "He runs on open-source infra with a personality defined in plain markdown anyone can read" [31]. The stated payoff is velocity — "so quality scales as fast as we ship" [31].
- Quilly 🪶 — docs, community, and GitLab housekeeping (blog posts, team-page updates, asset management); self-described as "the newest member of Team Soapbox" [32]. Announced late January 2026 — though the post's own byline (Jan 2025) and visible date (Jan 2026) disagree, a small currency flag worth resolving before citing the date hard.
- Sheila — took over Soapbox's own bookkeeping starting 2026-03-06 (History Timeline, above) [24].
"Newest member," in Quilly's own post, implies the roster keeps growing — treat any single AI-team name-check, including this one, as a snapshot that can go stale within months.
This is also where Soapbox's build culture surfaces directly: a "producer role" — a human prompting, directing, and reviewing AI-generated work rather than hand-writing it — runs through both how Soapbox builds itself (the three names above) and how it expects app builders to work this stack. Inferred, synthesis-level: the practical workflow ladder is Shakespeare for ideation → a local agent session (e.g. OpenCode) for deep work → cloning MKStack directly once the app's shape is known. Ch. 12 (new) covers this ladder and the full 19-skill curriculum; treat the ladder itself as this manual's synthesis of the tooling, not a single Soapbox-published claim.
Master Portfolio Map
This table covers every Soapbox product and tool found across soapbox.pub, GitLab, and GitHub — including the 17-item "Toybox" experiments line, which is a real, distinctly-named page (soapbox.pub/toybox), separate from the developer-facing "Toolbox" (soapbox.pub/toolbox) [7][27]. License cells marked "confirmed" were verified by fetching the repo's actual LICENSE file or GitHub license badge; everything else is unverified and should not be assumed.
| Product | Category | Status (as of 2026-07-09) | Repo | License | Hosted / Self-host |
|---|---|---|---|---|---|
| Ditto | Core app | Active — major update Mar 2026 | gitlab.com/soapbox-pub/ditto | AGPL-3.0 (confirmed) [9][16] | Both — ditto.pub or self-host |
| Shakespeare | Core app | Active — primary 2025-26 focus | gitlab.com/soapbox-pub/shakespeare | AGPL-3.0 (confirmed) [11][17] | Runs in-browser, local-first |
| Agora | Core app | Active — public launch Jun 2026 | gitlab.com/soapbox-pub/agora | AGPL-3.0 (confirmed) [13] | Hosted; non-custodial by design |
| Armada | Core app | Active — extreme velocity, see note below | gitlab.com/soapbox-pub/armada | AGPL-3.0 (confirmed) [14][18] | Serverless default; self-host via armada-relay |
| Mostr Bridge | Core app | Active/mature (since 2023) | gitlab.com/soapbox-pub/mostr | Not verified | Hosted (mostr.pub) |
| Soapbox (legacy FE) | Legacy core app | Maintenance — superseded by Ditto | github.com/soapbox-pub/soapbox | AGPL-3.0 (confirmed) [22] | Self-host |
| Rebased | Legacy core app | Maintenance — Pleroma-fork backend | github.com/soapbox-pub/rebased | Not verified | Self-host |
| Nostrify | Dev framework | Active — foundational | gitlab.com/soapbox-pub/nostrify | MIT (confirmed) [22] | Library, self-host by nature |
| MKStack | Dev framework | Active — used by 3rd parties (e.g. Divine) | gitlab.com/soapbox-pub/mkstack | Not verified (fetch blocked) | Local scaffolding tool |
| NostrHub | Dev tool | Active — "2.0" relaunch Jun 2026 | nostrhub.io | Not verified | Hosted |
| Stacks | Dev infra | Active — 1.0 shipped Aug 2025 | getstacks.dev | Not verified | Self-host (Docker) |
| Ditto Relay | Dev infra | Active | gitlab.com/soapbox-pub/ditto-relay | Not verified | Both — relay.ditto.pub or self-host |
| Ditto Extension | Dev tool | Active | Chrome Web Store | Not verified | Browser extension |
| Nostr WS Inspector | Dev tool | Active | Chrome Web Store | Not verified | Browser extension |
| Nostrbook | Dev docs/MCP | Active | nostrbook.dev | Not verified | Hosted + MCP server |
| Relay Kit | Dev tool | Active — third-party, not Soapbox's own | github.com/samthomson/relaykit | Not verified | Self-host install script |
| Soapbox Signer | Dev tool | Active, announced Dec 2025 | Not located | Not verified | Browser extension (NIP-07) |
| Mi | Dev/consumer tool | Newer, minor | mi.shakespeare.wtf | Not verified | Browser-local relay |
| Quilly | AI persona | Active — internal + public-facing | n/a (not a repo) | n/a | Hosted |
| Sheila | AI agent | Active since Mar 2026 | Not located | Not verified | Internal tool (accounting) |
| Zuka | AI product | Active since May 2026 | Not located | Not verified | Hosted PWA |
| Firefly | Newer app | Active (commit Jul 6, 2026) | gitlab.com/soapbox-pub/firefly | Not verified | Not confirmed |
| shock | Newer infra | Active (commit Jul 2, 2026) | gitlab.com/soapbox-pub/shock | Not verified | Not confirmed |
| strfry (fork) | Dev infra | Active (commit Jun 29, 2026) | gitlab.com/soapbox-pub/strfry | Not verified | Self-host relay |
| AOS (software) | Experimental | Active (commit Jun 28, 2026) — name collides with AOS the fiscal sponsor; unrelated | gitlab.com/soapbox-pub/aos | Not verified | Not confirmed |
| Agora Server | Core app companion | Active (commit Jun 25, 2026) | gitlab.com/soapbox-pub/agora-server | Not verified | Self-host (optional) |
| Agora Pay | Core app companion | Active (commit today) | gitlab.com/soapbox-pub/agora-pay | Not verified | Not confirmed |
| openclaw-armada | Core app plugin | Active (commit today) | gitlab.com/soapbox-pub/openclaw-armada | Not verified | Plugin |
| Birdblast, Monorail, Tile Studio | Experimental | Active, undocumented (commits Jun 16–21, 2026) | gitlab.com/soapbox-pub/* | Not verified | Not confirmed |
| Toybox (17 apps): Treasures, Birdstar, Surveil, Espy, Nostrdamus, Lief, Nests, Plektos, ZapTrax, Podstr, Color Slide, Blobbi Island, Polaroids, Relaying Earth, Clawstr, Zappix, Bookstr | Experimental/toys | Mixed — several months-old, some now graduated (Clawstr has its own launch post) | soapbox.pub/toybox | Not verified | Hosted demos |
| Chorus | Toybox-adjacent | "Vibe coding experiment," Jun 2025 | Not located | Not verified | Hosted |
| Inkwell | Small app | Jan 2026 | Not located | Not verified | Hosted, no-account markdown→Nostr |
Confidence: High for status/dates (GitLab activity timestamps are live data). Medium-Low for license on unverified rows — treat as unknown, not as AGPL by default.
Licenses & Ethics Commitments
The verified license pattern is a two-tier copyleft strategy: consumer-facing applications are AGPL-3.0 (network copyleft — if you run a modified version as a service, you must publish your changes), while the underlying framework is MIT (permissive — free to embed without triggering disclosure) [9][11][13][14][16][22]:
| Layer | License | Confirmed examples |
|---|---|---|
| Applications | AGPL-3.0 | Ditto, Shakespeare, Agora, Armada, legacy Soapbox FE |
| Core framework/library | MIT | Nostrify |
| Docs/skills content | CC-BY-SA-4.0 | nostr-skills, openclaw-skills |
| Crypto utilities | Unlicense | seeded-rsa |
The Ethics Pledge commits to: 100% open source for anything public-facing, never selling/trading user data, refusing backdoors or "unjust takedown compliance," and refusing acquisition by anyone who doesn't share the ethics [3]. The November 2025 "Really Open AI" post extends this to AI specifically, defining open AI as open code + open training data + open weights + open access, explicitly invoking the Open Source Initiative's Open Source AI Definition and the FSF's "Four Essential Freedoms" [12].
Confidence: High on the licenses actually checked; Medium on the ethics pledge's enforceability (it is a public commitment, not a legal instrument).
Durability & Risk Assessment for an Operator
Signals of life: same-day GitLab commits across ~10 repos, a 6-year unbroken blog cadence (101 posts, June 2020–July 2026), 3 continuous years of OpenSats funding, and payroll that cleared this month [2][4][8][21]. This is a live, shipping company, not a grant-funded zombie.
Structural risks for an operator building on this stack:
- No recurring revenue anywhere. 100% dependent on grant renewal + donor goodwill; OpenSats grants are typically annual, not perpetual [4][23].
- Funder concentration is actually funder singularity — corrected 2026-07-10. OpenSats is the only demonstrated, self-reported recurring grant line; HRF is a prize/event partner (Agora hackathon win + Oslo launch venue), not a second grantor — the "OpenSats + HRF diversification" framing this bullet previously carried does not hold (see Funding correction, above). One-line dependency reads as more fragile than two, not less; compounding factor: OpenSats' own treasury is BTC-denominated, so its USD disbursement capacity is exposed to Bitcoin price independent of grant-renewal risk [4][20][31][33].
- BDFL governance — Gleason is founder, "BDFL," and the person the Truth Social/Ditto origin story centers on; the "we'd rather shut down than compromise" ethic is principled but explicitly not built for business continuity at all costs [3][6].
- Extreme product sprawl — 45+ named products/experiments, many with zero stars and undocumented purpose (Birdblast, Monorail, Tile Studio) — classic sign of a studio prioritizing breadth over hardening any one surface [8].
- AGPL-3.0 is a real constraint, not just a badge — if you fork and modify Ditto/Shakespeare/Agora/Armada and run the modified version as a network service for clients, you must publish your changes. Using Nostrify or MKStack as a library/scaffold to build separate, independently-licensed client work (as Divine has done) does not carry the same obligation [9][11][22][29].
- Bus-factor is concentrated, measured 2026-07-10 via repo APIs. Gleason authorship: Nostrify 85.2%, mkstack 71.9%, ditto-relay 99.3%, Ditto 47.4%. Ditto is the only repo in the portfolio with real secondary maintainers (Danidfra, marykatefain, derekross) — Nostrify, the framework every other product on this stack compiles against, is 85% one person [34]. See ch. 15 (new) for the per-component exit playbook this feeds.
Fork survivability if Soapbox disappears tomorrow, by tier:
- High — Ditto, Shakespeare: both are explicitly architected for self-host/local-first operation (not an afterthought), AGPL-licensed with real external forks already existing (Ditto: 14 forks) [16][17][9].
- Medium — Agora, Nostrify, MKStack: non-custodial/library-first design survives structurally, but thinner fork/community depth today.
- Low — Armada and anything in Toybox: weeks-old, near-zero external stars/forks; a solo fork today would likely mean you as sole maintainer.
Confidence: Medium — this section synthesizes verified facts into forward-looking judgment; treat the risk framing as informed inference, not fact.
The "Armada" Note
Armada is real, current, and not vaporware: GitLab shows the repo created 2026-06-13 and already at 484 commits, 46 releases, 62 tags by 2026-07-09 — roughly 18 commits/day sustained for 26 days, with commits recorded the same day this was written [18]. It's an end-to-end-encrypted, serverless community-chat app pitched as "Discord without the company," built on a new protocol called Concord plus a NIP-29 relay-based fallback for self-hosters, with a companion self-host backend (armada-relay) and at least one plugin (openclaw-armada) already shipping [14]. It sits on the homepage under "Community Platforms" next to Treasures, Blobbi, and Bookstr [1]. Velocity this high, this early, suggests Armada is Soapbox's current top internal priority — a separate chapter of this manual covers it in depth.
Open Questions
- Exact OpenSats grant dollar amounts — never itemized publicly; only narrative quarterly reports [4][30]. (HRF's Agora hackathon prize is a stated figure — 25M sats, Jan 2026 — but that's a one-time prize to the Agora team, not a Soapbox operating grant; see the Funding correction under Team & Funding, above.)
- MKStack's specific license — two fetch attempts were blocked/unrendered; inferred permissive from third-party ecosystem use (Divine ships its own separate license), not confirmed [28][29].
- Rebased's specific license — not independently verified this session (GitHub org page showed the field blank) [22].
- Whether Soapbox has completed 501(c)(3) conversion or is still solely AOS-fiscally-sponsored as of today [4].
- Reconciling "12 team members listed" against "3 W2 payroll roles disbursed" — volunteer/contributor vs. employee split is unclear [15][21].
- Current Mostr Bridge user count — the only hard figure found was "10k+ unique users, 70% Nostr→Fediverse direction," dated late 2023; no fresher public number was located [26].
- Exact nature/status of several zero-description GitLab repos (Birdblast, Monorail, Tile Studio, NostrHub v1) [8].
Sources
- soapbox.pub — accessed 2026-07-09 — homepage: mission, product list, ethics summary.
- soapbox.pub/blog — accessed 2026-07-09 — full 101-post chronological index, June 2020–July 2026.
- soapbox.pub/ethics — accessed 2026-07-09 — Ethics Pledge verbatim commitments.
- soapbox.pub/transparency — accessed 2026-07-09 — funding model, funders, quarterly report index (fetched twice for report titles/dates).
- alexgleason.me/work — surfaced via search 2026-07-09 — Gleason's own work history page.
- PRNewswire — Truth Social Head of Engineering Leaves for Jack-Dorsey-Backed Alternative, Nostr — accessed 2026-07-09 — Gleason/TMTG/Nostr pivot.
- soapbox.pub/toolbox — accessed 2026-07-09 — full developer-tools inventory (11 items).
- gitlab.com/api/v4/groups/soapbox-pub/projects — accessed 2026-07-09 — live repo list with last-activity timestamps, stars.
- soapbox.pub/ditto — accessed 2026-07-09 — Ditto product page: license, hosting model.
- soapbox.pub/agora — accessed 2026-07-09 — Agora product page: non-custodial claim, WLC quote.
- soapbox.pub/shakespeare — accessed 2026-07-09 — Shakespeare product page: AI providers, license, hosting.
- soapbox.pub/blog/our-stance-on-ai — accessed 2026-07-09 — "Really Open AI" ethics stance.
- gitlab.com/soapbox-pub/agora LICENSE — accessed 2026-07-09 — AGPL-3.0 confirmed.
- gitlab.com/soapbox-pub/armada README — accessed 2026-07-09 — Armada/Concord protocol description.
- soapbox.pub/about — accessed 2026-07-09 — full team roster and mission pillars.
- github.com/soapbox-pub/ditto — accessed 2026-07-09 — stars, forks, license, tech stack.
- github.com/soapbox-pub/shakespeare — accessed 2026-07-09 — stars, forks, license, architecture.
- gitlab.com/soapbox-pub/armada — accessed 2026-07-09 — commit/release/tag counts, creation date.
- soapbox.pub/blog/soapbox-be-is-now-rebased — accessed 2026-07-09 — Pleroma-fork lineage of Rebased.
- Web search, Human Rights Foundation grants — accessed 2026-07-09 — hrf.org Bitcoin Development Fund, Agora/hackathon coverage.
- opencollective.com/soapbox-pub — accessed 2026-07-09 — live financial ledger, July 2026 payroll/travel transactions.
- github.com/soapbox-pub — accessed 2026-07-09 — org-level repo/license table (confirms Nostrify = MIT).
- soapbox.pub/blog/soapbox-awarded-grant — accessed 2026-07-09 — OpenSats grant terms, $5M Dorsey fund detail.
- Web search — accessed 2026-07-09 — Sheila/Clawstr/Chorus/Inkwell descriptions via soapbox.pub/blog/announcing-sheila, soapbox.pub/blog/announcing-clawstr, soapbox.pub/blog/chorus-vibe-coding, soapbox.pub/blog/inkwell-born-in-walmart.
- soapbox.pub/blog/building-zuka — accessed 2026-07-09 — Zuka description, activist partner quote.
- Web search, Mostr bridge stats — accessed 2026-07-09 — mostr.pub, fediversereport.com bridging analysis (10k+ unique users / 70% direction figure dated late 2023).
- soapbox.pub/toybox — accessed 2026-07-09 — full 17-item experimental-apps inventory.
- gitlab.com/soapbox-pub/mkstack — accessed 2026-07-09 — commit count; license not visible/confirmed.
- Web search — accessed 2026-07-09 — github.com/divinevideo/divine-web confirms Divine is a separate org/team building on MKStack, not a Soapbox-owned product.
- soapbox.pub/transparency (report-index pass) — accessed 2026-07-09 — quarterly report titles confirming the "pivot to AI-assisted programming tools" dating (May–July 2025) and unbroken Q3-2023→Q1-2026 report sequence.
- soapbox.pub/blog/how-soapbox-ships-fast — accessed 2026-07-10 — Dirk Rost's role (code review, MR approval, "bird's-eye view"), open-infra + plain-markdown personality framing.
- soapbox.pub/blog/meet-quilly — accessed 2026-07-10 — Quilly's role (docs/community/GitLab), "newest member of Team Soapbox" self-description; note byline/date inconsistency (Jan 2025 byline vs. Jan 2026 visible date).
- HRF Bitcoin Development Fund grant-round announcements (hrf.org/latest and related 2026 posts) — accessed 2026-07-10 — 2026 rounds name Snort, Coracle, Zapstore, Elsat and others; no round found naming Soapbox, Ditto, or Agora, supporting the HRF reclassification to prize/event partner.
- Maintainers' internal production-secrets-longevity audit note (companion document, not part of this public chapter set), §2 fact 5 — Gleason-authorship percentages (Nostrify 85.2%, mkstack 71.9%, ditto-relay 99.3%, Ditto 47.4%) measured via GitLab/GitHub repo APIs, 2026-07-10; Ditto's secondary maintainers (Danidfra, marykatefain, derekross) named there.
Community & Company Operations — Ditto (public community) + Armada (team ops, early)
Verified: 2026-07-09 — Armada correction pass: 2026-07-09
Two Products, Not One Gap
"Community AND company operations, can it do filesharing" turns out to map onto two separate Soapbox products, not a gap in one:
| Ditto | Armada | |
|---|---|---|
| Job | Public, branded community server (Mastodon-API shaped) | Discord-shaped team chat (Concord protocol) |
| Maturity | Mature, ~2 years of public releases | Real, but 26 days old — created 2026-06-13, shipping ~daily |
| Encryption | No (community content is public-by-design) | Yes, by default |
| Filesharing | CAN (media) / PARTIAL (no drive) | CAN (encrypted media) / UNCONFIRMED (non-image files) |
Ditto (below, unchanged from the first research pass) is the public-facing answer. Armada — added in this correction pass — is the internal-team answer.
The Armada Verdict — Corrected 2026-07-09
The verdict below was wrong in the prior draft. Re-verified this pass by fetching the GitLab repo, its CHANGELOG, a sibling plugin repo, and Soapbox's own product page directly.
| Question | Answer |
|---|---|
| Does Soapbox.pub make a product called "Armada"? | Yes. |
| Confidence | High — primary-sourced (repo, CHANGELOG, sibling repo, landing page) |
| What it is | An E2E-encrypted, Discord-shaped Nostr chat app on a new "Concord" protocol — full section below |
| Repo | gitlab.com/soapbox-pub/armada — created 2026-06-13, AGPL-3.0, 484 commits, 46 releases as of this research [33] |
| Why the first pass missed it | 26 days old at verification time, no blog announcement, absent from both soapbox.pub/tools/ and soapbox.pub/toybox — the two index pages the first pass correctly and thoroughly checked. Only discoverable via the GitLab group directly, or one nav-menu link on the soapbox.pub homepage to /armada [36][41][42][43]. A product-index search was the wrong instrument for a 26-day-old, unannounced repo. |
| Flotilla — still correct | Flotilla (flotilla.social) is a real but separate, unrelated project (hodlbod/Coracle, not Soapbox) — that part of the original verdict stands [6]. |
armada.buzz | Re-tested this pass (https, http, archive) — still fails to resolve. Not Armada's domain; the real one is soapbox.pub/armada [44]. |
Sidebar — Flotilla, in one paragraph: A separate, Discord/Slack-shaped PWA built on "relays as groups" (NIP-29) [6]. Where Ditto is one branded server = one community, Flotilla lets a single relay host many independent group spaces. Not part of Soapbox's stack. Armada (below) is Soapbox's own answer to the same "Discord-shaped internal chat" need — and unlike Flotilla, it's E2E encrypted by default.
What Ditto Is
"Ditto is a Nostr community server. It has a built-in Nostr relay, a web UI, and it implements Mastodon's REST API." [7]
"Ditto is a self-hosted server featuring a community-centered Nostr relay with a built-in UI." [8]
Confidence: High (both quotes are from Soapbox's own blog). Note a real wrinkle: Ditto's current marketing copy pitches a lighter framing — "Deploy it on a $5 VPS, GitHub Pages, or a Raspberry Pi in your closet" [9] — that describes the static React/Vite frontend build [10][11], not the stateful Deno+Postgres backend that actually runs your relay, moderation, and Mastodon API [7][12]. GitHub Pages cannot run a database. Both descriptions are true of different halves of the same repo; see Architecture below.
Capabilities Matrix
| Capability | Status | Detail | Confidence |
|---|---|---|---|
| Built-in Nostr relay | Yes | Ships with the server; your instance is also a relay [7] | High |
| Mastodon-API compatible | Yes | "Works with most Mastodon apps" via NIP-46 bunker login — 50+ apps documented (Ivory, Tusky, Elk, etc.) [13][14] | High |
| Custom domain + branding | Yes | Themes (9 presets, 19 CSS tokens), NIP-05 domain-based identity | High |
| Bridges to Mastodon/Fediverse | Yes | Via Mostr Bridge [15] | High |
| Bridges to Bluesky | Yes, indirect | Mostr → Bridgy Fed relay chain; 15+ min propagation delay | Medium |
| Admin/moderator roles | Yes | Distinct admin & moderator roles; reports, post deletion, user approval [16] | High |
| Custom moderation policies | Yes | Scriptable policies incl. an OpenAI-backed content-scoring policy [16] | Medium |
| Full-text search | Yes | Postgres full-text search (NIP-50) [17] | Medium (search-summary sourced) |
| Native private DMs | Not documented | NIP-17/NIP-04 absent from Ditto's own official NIP reference list [18] | Medium |
| NIP-29 relay-groups (Discord-style) | No | That's Flotilla's model, not Ditto's [6] | High |
| NIP-72 Reddit-style communities | Partial | Listed in Ditto's NIP reference [18], but Ditto's actual community unit is the server/domain, not a NIP-72 sub-group — Chorus and Amethyst are the NIP-72-native clients [19][20] | Medium |
| Hosted (no self-host) option | Yes, one | ditto.pub — the official flagship instance, open enrollment, light moderation, free username [21] | High |
| Multi-tenant paid hosting | Not found | No pricing tiers or "spin up your branded instance for $X/mo" product located | Medium |
Architecture & Self-Hosting
Two independently-corroborated paths reach this same shape:
- Manual VPS path (third-party, detailed, technical — Linux/macOS only, Windows unsupported): install Deno v1.45.2, PostgreSQL, Nginx, Certbot; create a
dittosystem user;deno task setupgenerates.env(domain, DB credentials, media backend choice); provision Postgres; configure Nginx;certbot --nginxfor TLS [12]. - Docker path (community-maintained, not first-party): the
arteeh/dittoimage + a Postgres container on a shared network. Required env vars:DITTO_NSEC(relay keypair),DATABASE_URL,LOCAL_DOMAIN,PORT(default 6996), optionalDITTO_POLICY[22]. Linux amd64 only.
Confidence: Medium-High (both paths are documented, but neither is the current first-party about.ditto.pub guide, which 404'd during verification — official docs appear to be mid-migration between docs.soapbox.pub and about.ditto.pub as of this writing).
Filesharing Verdict
Precise question: can a team use this stack instead of Google Drive/Dropbox?
| Use case | Verdict | Why |
|---|---|---|
| Post images/video/audio in a feed or chat | CAN | Native Blossom upload path, configurable per-server [12][23] |
| Arbitrary file types (PDF, .docx, .zip) | PARTIAL | Blossom stores any blob by SHA-256 hash — protocol-agnostic to file type — but Ditto's own UI is built around feed content, not a file browser [23][24] |
| Folder structure / organized drive | CAN'T (workaround lapsed) | "Blossom Drive" (a separate, third-party app) layered folders on Blossom blobs via kind-30563 events — not a Ditto feature [25], and now deprecated/unmaintained upstream; its named successor Bouquet is an ad-hoc blob manager, not a drive (see ch. 06, Files & Media) |
| Team permissions on shared files | CAN'T (not found) | No sharing/ACL mechanism documented beyond "public server, anyone with the hash/URL can fetch it" [23][25] |
| Version history / collaborative editing | CAN'T | No documented feature; blobs are immutable, content-addressed — a new version is a new hash, not a revision |
| Size limits | Known ceiling | Blossom spec: 100 MiB hard cap, 20 MiB on free-tier servers, "no limit on total uploads or retention" beyond that [26][27] |
| NIP-96 (older HTTP file-storage NIP) | Deprecated | Formally marked "unrecommended," superseded by Blossom [28] |
Bottom line: Ditto can absolutely carry a team's social/community media (photos, video, voice notes, event flyers). It is not a Drive/Dropbox replacement for internal document collaboration — no folders, no permissions, no version history, no office-doc preview, and the 100 MiB ceiling rules out most video-editing or design-asset workflows. The one folder-layer workaround has effectively lapsed — Blossom Drive is deprecated upstream (successor Bouquet manages blobs, not folders; ch. 06) — so the gap stands unbridged. Confidence: High on the CAN/CAN'T calls, Medium on exact size figures (spec-level, not Ditto-instance-verified).
Company-Ops Fit
| Need | Ditto's answer | Verdict |
|---|---|---|
| Private 1:1 or group DMs | Not in Ditto's documented NIP set (no NIP-17/NIP-04 listed) [18] | Gap — use a different Nostr client, or Flotilla, for private chat |
| Team/relay-based groups (Discord-shaped) | Not Ditto's model | Use Flotilla (NIP-29) instead |
| Public communities / sub-groups (Reddit-shaped) | Interop-level only; native unit is the whole server | Use Chorus (NIP-72-native) for this shape instead |
| Announcements | Local feed + Explore tab, domain-scoped | Works |
| Events/calendar | NIP-52 calendar events supported [18] | Works |
| Bots / automation | Any Mastodon-API bot framework (access tokens), or native Nostr bots against the built-in relay [14][29] | Works, standard tooling |
| Search | Postgres full-text search across the instance [17] | Works |
| Data export / backup | Philosophical guarantee only — "your identity, content, and connections belong to you... take everything with you" [30] — no documented one-click export/GDPR tool found | Gap — portability is structural (signed events, any key), not a shipped export button |
| Multi-account / staff management | Admin + moderator roles; username revocation, admin promotion documented in v1.3 release notes [16][31] | Works, basic |
Pricing & Access
| Path | Cost | Notes |
|---|---|---|
| Join the flagship instance | Free | ditto.pub, open enrollment, light moderation [21] |
| Self-host, manual VPS | ~$5-10/mo (VPS) | Your time + a domain; AGPL-3.0, no license fee [12] |
| Self-host, Docker | ~$5-10/mo (VPS) | arteeh/ditto is community-maintained, Linux amd64 only [22] |
| Branded multi-tenant hosted plan | Not found | No evidence Soapbox sells managed hosting; self-host or join the flagship are the only two documented doors |
Setup Runbook (as documented)
- Provision a Linux VPS (Ubuntu recommended) and point a domain's DNS at it [12].
- Install system packages:
git curl unzip nginx postgresql-contrib certbot python3-certbot-nginx[12]. - Install Deno (pinned version, e.g. v1.45.2) [12].
- Create a dedicated
dittosystem user; clone the repo to/opt/ditto, chown to that user [12]. - Run
deno task setup— generates.envwith domain, DB credentials, and your media-backend choice (Blossom / S3 / local / IPFS / nostr.build) [12]. - Provision the Postgres database and user (
dittodbuser/dittodbby convention) [12]. - Copy and edit the Nginx site config with your
server_name[12]. - Run
certbot --nginxto obtain and wire up TLS [12]. - Visit your domain to confirm the Ditto template renders [12].
- Docker alternative to steps 2-8: run the
arteeh/ditto+ Postgres compose stack withDITTO_NSEC,DATABASE_URL,LOCAL_DOMAINset, behind your own reverse proxy [22]. - Set admin role on your own pubkey, then use the admin dashboard to configure moderation policies, NIP-05 approval flow, and theme [16].
Confidence: Medium — steps 1-9 are a verified third-party walkthrough, not the current first-party guide (which redirected/404'd mid-research); version-pinned details (Deno v1.45.2) may have drifted.
Armada
Verified: 2026-07-09. Repo is 26 days old at time of writing — expect drift; re-check before relying on specifics.
What Armada Is
"Armada is an end-to-end encrypted community chat app built on Nostr." [34]
"Discord without the company. Your keys. Your people." [41]
Discord-shaped: servers, channels, threads, voice, roles, DMs, events/RSVP, emoji reactions, markdown [41]. Unlike Ditto, encryption is the default, not absent — every chat/invite/rekey event is sealed before it touches a relay [34]. Confirmed sibling repos (armada-relay, openclaw-armada) independently corroborate the product and protocol name [36].
The Concord Protocol
"A serverless, end-to-end encrypted community protocol... All control/chat/invite/rekey traffic is gift-wrapped (NIP-59) over generic Nostr relays." [34]
Concord is Soapbox's own protocol — numbered CORD-01 through CORD-07, a sibling spec built on top of Nostr's NIPs, not a NIP itself. It runs on standard public Nostr relays (no special server), using NIP-59 gift-wrapping for every event plus NIP-42 relay auth with per-session derived keys [38].
| Spec | Function | Confidence |
|---|---|---|
| CORD-05 | Invite links — encrypted key bundle + community identity check | High (plugin docs) [38] |
| CORD-06 | "Rekeys and refoundings" triggered on membership change | High (plugin docs) [38] |
| CORD-07 | Blind LiveKit token broker for voice — broker "learns nothing about the community" [34] | High (README quote) |
| CORD-01–04 | Not identified in sources found this pass | Open question |
"Who sent it, which community it belongs to, even the community's name — all sealed." [41]
That metadata-privacy claim is stronger than typical E2EE (which usually still leaks who's-talking-to-whom). It is a DOCUMENTED claim in the README and marketing copy — this pass found no independent security audit confirming it holds up. Whether CORD-06 rekeying gives MLS- or Signal-style forward secrecy is not stated anywhere found — treat as unconfirmed, not as "it's MLS."
A second, non-default path exists: NIP-29 relay-groups for operators who want a self-hosted server instead of the serverless default [34]. Two live implementations already coexist in the codebase — concord-v1/ and concord-v2/ — the protocol has already been rebuilt once in under a month [39].
Architecture
Confidence: High on client/protocol shape (README + landing page agree); High that the NIP-29 self-host path is real but immature (armada-relay = 1 commit, created the same day as this research) [37].
Capabilities Matrix
| Capability | Status | Detail | Confidence |
|---|---|---|---|
| E2E encrypted group chat (Concord, default) | Yes | NIP-59 gift-wrapped chat/invite/rekey traffic over generic relays [34] | High |
| Metadata privacy (sender/community sealed) | Claimed | "Who sent it, which community it belongs to... all sealed" [41] | Medium — claim, not audited |
| Servers / channels / threads / roles | Yes | Discord-shaped, role-based moderation [41] | High |
| Voice + screen share | Yes | WebRTC/LiveKit; screen share shipped v0.1.0; 1:1 calls v0.9.0; identity verification v0.18.0 [35] | High |
| Voice privacy (blind broker) | Claimed | CORD-07 broker "learns nothing about the community" [34] | Medium |
| Image/media attachments in encrypted channels | Yes | Blossom-backed; encrypted images "decrypt and display inline" (v0.6.0, v0.8.3) [35][39] | High |
| Non-image files (PDF / .docx / .zip) | Unconfirmed | No changelog or doc evidence either way | Low — open question |
| Bluetooth mesh chat (offline) | Yes | Android only, v0.13.0 [35][41] | High |
| Push notifications without Google services | Yes | Android, v0.3.0 [35] | High |
| Self-hosted server option (NIP-29) | Yes, but immature | armada-relay: 1 commit, created 2026-07-09 [37] | High it's real; High it's immature |
| AI agent / bot community members | Yes | openclaw-armada plugin — agents decrypt invites, join, respond, DM [38] | High |
| Native iOS app | No | Web app only, works in mobile Safari [41] | Medium |
| Data export / portability | Not documented for Armada specifically | Nostr-key portability philosophy presumably applies (per Ditto's pattern) but not stated for Armada | Low — inferred |
Filesharing Verdict — Armada
Same precise question as Ditto: can a team use this instead of Google Drive/Dropbox?
| Use case | Verdict | Why |
|---|---|---|
| Post images/media in a channel or DM | CAN | Blossom-backed (BlossomServerListEditor.tsx in-repo) [39]; encrypted images decrypt and display inline inside Concord communities [35] |
| ...and is it E2E encrypted, not just server-hosted? | CAN — stronger than Ditto | Attachments ride inside the same gift-wrapped Concord protocol as messages, a real architectural difference from Ditto's plaintext-on-Blossom-URL model |
| Arbitrary file types (PDF, .docx, .zip) | UNCONFIRMED | No changelog/doc evidence of non-image attachment handling; Blossom itself is blob-type-agnostic, but Armada's UI behavior for non-image blobs isn't documented anywhere found |
| Folder structure / organized drive | CAN'T (not found) | No Blossom Drive or folder layer mentioned in README, CHANGELOG, or landing page |
| Team permissions on shared files | CAN'T (not found) | No ACL beyond community membership itself documented |
| Version history | CAN'T | Not documented; same content-addressed-blob limitation Blossom has generally |
| Size limits | Not confirmed for Armada | Underlying Blossom spec ceiling (100 MiB, per Ditto's research above) presumably applies but wasn't confirmed as Armada's actual configured limit |
Bottom line: narrower than Ditto's filesharing story, but more private where it exists. Armada CAN carry E2E-encrypted image/media drops inside a private team chat today — a genuinely stronger privacy story than Ditto's, since the content itself (not just the transport) is sealed. It is not confirmed to handle arbitrary document types, and there is no evidence at all of folders, permissions, or version history — so it's still not a Drive/Dropbox replacement, for different reasons than Ditto (missing breadth here, vs. Ditto missing E2EE and hitting a hard size cap). Confidence: Medium overall — image-attachment behavior is CHANGELOG-confirmed; everything else in this table is an absence-of-evidence call on 26-day-old software, not a confirmed "no."
How to Try It Today
- Web:
soapbox.pub/armada→ "Open Armada" — works in any browser, including mobile Safari/iOS (no native iOS app) [41]. - Android: native app via Capacitor; configured for Zapstore distribution (
zapstore.yamlin-repo) [39][46]; native push bypasses Google services; Bluetooth mesh works fully offline [35][41]. - Desktop: Electron builds for Linux and Windows (no Mac build found or mentioned) [34][41].
- Join a community via an invite link — the unlock key rides in the URL fragment, so it never touches a server [41].
- Self-host (NIP-29 path): marketing copy claims a "complete open-source server stack," but the backing repo (
armada-relay) had one commit, created the same day as this research — treat self-hosting as not yet real-world ready [37][41]. armada.buzzdoes not resolve — usesoapbox.pub/armada[44].
Maturity Warning
26 days old, ~daily releases, self-described as "Early version (0.x)" on its own landing page [41]. In the 17 days since v0.1.0 (2026-06-22), the protocol has already gone through a v1→v2 rebuild, an encryption-label change reversed days later, and a self-host backend that only started today [35][39][37]. The most recent 25 releases (v0.17.0 → v0.25.3) landed in just five days (2026-07-05 to 2026-07-09) — velocity is accelerating, not settling [40]. At least two named committers (Alex Gleason, Chad Curtis) — a small team, not a solo project, but this pass found no visible external community contributions [40]. Good to pilot and watch closely; do not bet confidential company operations on it without your own review — nothing here has been independently security-audited.
Incumbent Comparison
| Dimension | Ditto-centered Nostr stack | Armada (Concord) | Slack | Discord | Circle | Mighty Networks |
|---|---|---|---|---|---|---|
| Community (public) | Strong — branded server, themes, bridges out to Bluesky/Mastodon, no platform lock-in | Weak fit — private-by-default, no branded public-server story | Weak fit (internal-tool DNA) | Strong, mainstream, zero setup | Strong, purpose-built | Strong, purpose-built |
| Internal company ops | Weak — no native private DM spec, no threads-as-Slack-channels model | Promising but 26 days old — Discord-shaped, E2E encrypted by default, zero track record | Best-in-class | Good (voice-first) | Moderate | Moderate |
| Filesharing | Weak — 100 MiB cap, no folders/permissions/versioning | Partial — E2E-encrypted image/media, but no folders/permissions/versioning; non-image types unconfirmed | Good (with Drive/Box integration) | OK (free tier capped ~10-25 MB/msg; paid raises it) | Good (course/doc-shaped) | Good (course/doc-shaped) |
| Cost at small-team scale | ~$5-10/mo self-host, or free (flagship, no admin control) | Free (hosted default relays); self-host (NIP-29) path exists but backend repo has 1 commit as of this research | Free tier thin; Pro ~$7.25-8.75/user/mo [32] | Free; Nitro $4.99-9.99/mo optional [32] | From $89/mo [32] | From $41/mo [32] |
| Data portability | Structurally strong (your keys, signed events) but no shipped export tool | Same Nostr-key philosophy presumably applies; not independently confirmed for Armada specifically | Low (vendor lock-in) | Low | Low-Moderate | Low-Moderate |
| Setup effort | High (VPS + Deno + Postgres + Nginx, or Docker) | Low for default path (open web app, invite link); self-host path not yet production-real | None (SaaS) | None (SaaS) | None (SaaS) | None (SaaS) |
| Where Nostr loses outright | No mature private-DM story on Ditto itself, no file-drive replacement, self-hosting is real ops burden, no managed multi-tenant SaaS to buy | Unaudited crypto, protocol already rebuilt once (v1→v2) in under 30 days, ~2 named committers, self-host not production-ready | — | — | — | — |
Confidence: High on Ditto-column facts (cited above); High on Armada being real, Medium-Low on its unaudited/unconfirmed claims (marked throughout); Medium on incumbent pricing (current as of search date, subject to normal SaaS pricing drift) [32].
Integration Points
| Connects to | Mechanism | Enables |
|---|---|---|
| Mastodon apps (Ivory, Tusky, Elk, 50+ others) | Mastodon REST API + NIP-46 bunker login | Staff can use familiar mobile/desktop clients against your Ditto server [13][14] |
| Fediverse (Mastodon servers) | Mostr Bridge | Cross-posting/following between Ditto and any ActivityPub server [15] |
| Bluesky | Mostr Bridge → Bridgy Fed relay chain | Indirect follow/reply bridging, ~15+ min lag |
| Blossom media servers | BUD-03 upload spec, SHA-256 addressing | Resilient, mirrorable media hosting decoupled from any one server [23][26] |
| Any Nostr client (Damus, Amethyst, primal, etc.) | Standard relay connection to your built-in relay | Members aren't locked into Ditto's own UI [7] |
| Shakespeare (Soapbox's AI site builder) | "Edit and remix the entire platform" [9] | Rebrand/customize a Ditto or MKStack-based deployment without deep React expertise |
| MKStack-built apps (Chorus, Plektos, etc.) | Shared Nostr event layer | Content posted in sibling apps (events, groups) can surface in a Ditto feed if event kinds overlap [19][20] |
| OpenClaw AI agents (Armada) | openclaw-armada plugin — CORD-05 invite decrypt, NIP-42 multi-key auth | Bots/AI agents join Concord communities as real members, monitor channels, respond, DM [38]. OpenClaw itself is an independent open-source agent framework, not a Soapbox product [45] |
| Blossom media servers (Armada) | Same BUD-03 upload spec as Ditto, configurable per-client | Encrypted image/media attachments inside Concord channels [39] |
| Zapstore (Armada) | zapstore.yaml release config in-repo | Android distribution outside Google Play, cryptographically signed releases [39][46] |
| Generic Nostr relays (Armada) | NIP-59 gift-wrap + NIP-42 auth, no special server required | Concord communities run on any standard public relay — no Armada-specific infrastructure needed [34] |
| NIP-29 relays (Armada self-host path) | armada-relay (early — 1 commit as of this research) | Operator-controlled team server, trading some Concord privacy for admin control [34][37] |
Open Questions
- Is
about.ditto.pub's canonical self-hosting guide currently live at a different path than what redirected/404'd during this research pass? Worth re-checking directly before an actual deployment. - Whether Ditto's NIP-72 listing in its own reference means it reads and displays NIP-72 community events from other clients (interop) or only that some internal code path touches the kind — not confirmed either way.
- Whether any DM capability exists via a non-listed NIP or a Mastodon-API "direct" visibility mapping — not found in official docs, but not exhaustively ruled out either.
- Whether "Stacks" (
getstacks.dev) is a Docker-compose bundler specifically for Ditto, or a broader "AI project template" sharing site unrelated to production deployment — direct fetch only returned a page title, inconclusive. - Exact current numeric count behind the "27+ content types" marketing claim vs. the "60+ event kinds" reference-doc claim — likely two different countings (curated UI content types vs. raw protocol event kinds) but not reconciled to a single verified number.
- Armada: What are CORD-01 through CORD-04? Only CORD-05 (invites), CORD-06 (rekey/refounding), and CORD-07 (voice broker) were identifiable in sources found this pass.
- Armada: Does CORD-06 rekeying provide forward secrecy comparable to MLS or Signal's double ratchet? Not stated anywhere found — don't assume either way.
- Armada: Are attachment blobs encrypted client-side before reaching the Blossom server (true E2EE at the storage layer), or only the message pointer/metadata? The CHANGELOG's "decrypt and display inline" language implies the former but doesn't say so explicitly.
- Armada: Does the UI actually support non-image file types today? No evidence either way was found.
- Armada: Is
armada.buzza domain Soapbox owns and intends to launch on, or unrelated/parked? Unresolved after three independent failed fetch attempts across two research passes — usesoapbox.pub/armadaregardless. - Armada: Is a native iOS app planned, or is web-only a deliberate design choice? Not stated.
- Armada: How real is the "one-command server stack" self-host claim on the landing page, given
armada-relayhad exactly one commit at time of writing? Worth re-checking in a few weeks.
Sources
- Star Wars: Armada Fleet Builder — unrelated app, false-lead check. Accessed 2026-07-09.
- Soapbox Tools — full product/tool list with URLs; no Armada listed. Accessed 2026-07-09.
- Soapbox Toybox — full experiments list; Armada absent as a project. Accessed 2026-07-09.
https://armada.buzz/http://armada.buzz— fetch failed both protocols (SSLWRONG_VERSION_NUMBER). Accessed 2026-07-09.- Web search
"armada.buzz"— zero Nostr/Soapbox-related results. Accessed 2026-07-09. - Flotilla — GitHub — "A nostr relay-based communities PWA modeled after discord." Accessed 2026-07-09.
- Announcing Ditto — Soapbox Blog — original server architecture (Deno, Postgres, built-in relay, Mastodon REST API). Accessed 2026-07-09.
- Creating Curated Communities on Nostr with Ditto — community/moderation model. Accessed 2026-07-09.
- Soapbox Launches Massive Update to Ditto — relaunch marketing copy, "$5 VPS, GitHub Pages, Raspberry Pi." Accessed 2026-07-09.
- gitlab.com/soapbox-pub/ditto / raw README — React 18 + Vite + TypeScript frontend stack. Accessed 2026-07-09.
- github.com/soapbox-pub/ditto — README mirror, same frontend-stack description. Accessed 2026-07-09.
- Setting up your own nostr community with Ditto — freedomweaver.tech — third-party manual VPS self-hosting walkthrough (Deno, Postgres, Nginx, Certbot). Accessed 2026-07-09.
- Unlocking 50+ Mastodon Apps for Nostr with Ditto — Mastodon-API client compatibility. Accessed 2026-07-09.
- Web search — Mastodon API access-token/bot-automation mechanics applied to Ditto. Accessed 2026-07-09.
- Introducing Mostr: a Fediverse Nostr bridge and How to Follow Bluesky Accounts from Nostr — bridge mechanics, Bluesky via Bridgy Fed relay chain, ~15 min+ lag. Accessed 2026-07-09.
- Ditto 1.3: Explore Nostr — Soapbox Blog — admin tools (username revocation, admin promotion), moderation features. Accessed 2026-07-09.
- Web search summary of stacker.news/items/863195 discussion — full-text search in Postgres, moderator/admin capabilities (direct fetch returned no content; sourced via search snippet only — lower confidence). Accessed 2026-07-09.
- Ditto Nostr Reference — about.ditto.pub/reference — official supported-NIPs list and event-kind count. Accessed 2026-07-09.
- NIP-72 — Moderated Communities — spec definition, kind 34550/4550. Accessed 2026-07-09.
- Chorus — GitHub (andotherstuff/chorus) and Chorus: An Experiment in Vibe Coding — NIP-72-native community app built with MKStack, distinct from Ditto's model. Accessed 2026-07-09.
- ditto.pub and web search on its flagship-instance status — free, open enrollment, light moderation. Accessed 2026-07-09.
- arteeh/ditto — Docker Hub — community-maintained Docker image, env vars, Postgres dependency. Accessed 2026-07-09.
- Blossom — GitHub (hzrd149/blossom) and NIP-B7 — Blossom media — protocol spec, SHA-256 addressing, mirroring. Accessed 2026-07-09.
- Blossom Uploader — Nostrify — integration reference. Accessed 2026-07-09.
- Blossom Drive: Store & Retrieve Data on Public Servers — folder/drive layer on top of Blossom, kind 30563 events. Accessed 2026-07-09.
- Web search on Blossom spec limits — 100 MiB hard cap, 20 MiB free-tier convention. Accessed 2026-07-09.
- blossom.nostr.build — example Blossom server, up to 100MB. Accessed 2026-07-09.
- NIP-96 — HTTP File Storage Integration — marked unrecommended, superseded by Blossom. Accessed 2026-07-09.
- Web search — Nostr bot / Mastodon API automation patterns (nostrdon bridge example). Accessed 2026-07-09.
- Web search summary — Ditto's "your identity, content, and connections belong to you" data-portability claim. Accessed 2026-07-09.
- Ditto 1.3 release notes — staff/role management features. Accessed 2026-07-09.
- Web search — current Slack, Discord Nitro, Circle.so, Mighty Networks pricing tiers (2026). Accessed 2026-07-09.
Armada correction pass (2026-07-09) — sources 33-46:
- gitlab.com/soapbox-pub/armada — main repo page: created 2026-06-13, AGPL-3.0, 484 commits, 17 branches, 62 tags, 46 releases. Accessed 2026-07-09.
- gitlab.com/soapbox-pub/armada/-/raw/main/README.md — verbatim definition of Armada and the Concord protocol, NIP-59 gift-wrap mechanics, NIP-29 self-host alternative, install instructions. Accessed 2026-07-09.
- gitlab.com/soapbox-pub/armada/-/raw/main/CHANGELOG.md — chronological feature history v0.1.0 (2026-06-22) through v0.25.x (2026-07-09): encrypted image attachments, voice/screen-share, Bluetooth mesh, Concord v2 cutover. Accessed 2026-07-09.
- gitlab.com/api/v4/groups/soapbox-pub/projects — GitLab group API listing; confirms
armada,armada-relay, andopenclaw-armadaas sibling repos under soapbox-pub. Accessed 2026-07-09. - gitlab.com/soapbox-pub/armada-relay — self-hosted backend repo: 1 commit, 1 branch, 0 releases, created 2026-07-09 — key maturity signal for the self-host claim. Accessed 2026-07-09.
- gitlab.com/soapbox-pub/openclaw-armada/-/raw/main/README.md — confirms Concord protocol spec numbering (CORD-01–07), NIP-59/NIP-42/kind-3313/kind-1059 mechanics, and OpenClaw AI-agent integration. Accessed 2026-07-09.
- GitLab repository-tree API for soapbox-pub/armada (root + recursive
src/) — confirms parallelconcord-v1/andconcord-v2/implementations andBlossomServerListEditor.tsx(media/attachment config UI). Accessed 2026-07-09. - GitLab releases API for soapbox-pub/armada — 25 dated releases v0.17.0 through v0.25.3, spanning 2026-07-05 to 2026-07-09; named authors Alex Gleason and Chad Curtis. Accessed 2026-07-09.
- soapbox.pub/armada — the actual product landing page (not linked from
/tools/or/toybox): tagline, feature list, encryption claims, self-host claim, "Open Armada" CTA. Accessed 2026-07-09. - soapbox.pub/blog/ — full blog index (101 posts); confirms
/armadaexists only as a main-nav link, no dedicated announcement post found. Accessed 2026-07-09. - soapbox.pub/tools/ — re-verified this pass; Armada still absent from the product/tools index, consistent with the original chapter's finding and explaining the discovery gap. Accessed 2026-07-09.
https://armada.buzz,http://armada.buzz, and a web-archive lookup — re-tested this pass; all three fail (SSLWRONG_VERSION_NUMBERon both protocols; archive fetch could not connect). Confirms this is not Armada's working domain. Accessed 2026-07-09.- github.com/openclaw/openclaw and open-claw.bot/docs/channels/nostr — background confirming OpenClaw is an independent open-source AI-agent framework (not a Soapbox product) with an existing Nostr channel plugin pattern. Accessed 2026-07-09.
- zapstore.dev and github.com/zapstore/zapstore — background on Zapstore, the Nostr-native Android app store Armada's repo is configured for (
zapstore.yaml); Armada's own live listing was not independently confirmed. Accessed 2026-07-09.
Agora — Funding Rails
Agora is Soapbox's non-custodial, on-chain-Bitcoin fundraising app for activists — campaigns move wallet-to-wallet with zero platform fee, no KYC, and no approval step.
Verified: 2026-07-09
What It Is
Built by Soapbox (Alex Gleason's company) on Nostr + Bitcoin, Agora started as a hackathon project called Pathos and relaunched as Agora on June 2, 2026 at the Oslo Freedom Forum, announced by the World Liberty Congress (WLC) with Human Rights Foundation (HRF) backing. It lives at agora.spot (web) and on ZapStore (Android), with source at gitlab.com/soapbox-pub/agora.
"Agora never holds funds. Donations move wallet-to-wallet on Bitcoin." [1]
"Recipients sign up in seconds. No bank. No paperwork. No approval." [1]
The platform describes itself as "the world's first Borderless Micro-Philanthropy Network" [8] — positioned specifically for activists, dissidents, and political prisoners, not general-purpose crowdfunding.
How a Donation Flows
Campaign donations run entirely on-chain — not Lightning. (Lightning appears elsewhere in the broader app for social tipping — see Integration Points.)
Donors pick Public (standard address, visible forever, widest wallet support, fastest) or Silent (BIP-352, unlinkable fresh output, fewer wallets support it, slower, no push notifications) [6]. Creators choose which to accept, or both [7].
Campaign Creation & Eligibility
| Question | Answer | Evidence |
|---|---|---|
| Who can create a campaign? | Anyone with a Nostr key — no application | "No permission needed... no gatekeepers" [7] |
| Is it WLC/HRF-gated? | No — permissionless by mechanism. WLC/HRF campaigns are featured, not exclusive | [1][3] |
| KYC for creators or donors? | None documented at any Agora-controlled step | FAQ costs/creators + donor guide [6][7] |
| How does trust work, then? | Opt-in and social: orgs publish a public "verification statement" vouching for campaigns they endorse | "your reputation does the work" [7] |
| Is anything moderated? | Yes — Agora runs its own relay + Blossom file server, zero-tolerance CSAE policy, NCMEC/law-enforcement cooperation | Safety page [8] |
| Could TresPies or a client org start a campaign today? | Technically yes — nothing in the flow blocks it | Inferred from permissionless signup; no ToS document was located |
| Would it fit the brand? | Not natively — every launch campaign is a human-rights/political-prisoner cause | [1][3] |
The gate here is social and reputational, not technical: Agora doesn't vet campaigns, but it also doesn't market itself to small businesses or general nonprofits — a non-activist campaign would be real and fundable, just off-brand and undiscoverable without its own audience.
Fees & Costs
| Cost | Charged by | Rate | Paid by |
|---|---|---|---|
| Platform fee | Agora | $0 — "no platform fees" [1], "we take nothing" [10] | n/a |
| Bitcoin network (miner) fee | Bitcoin network | Market rate, varies with congestion | Donor, on top of the gift [6] |
| Donor's wallet/exchange fee | Donor's own provider (Cash App, Strike, etc.) | Varies by provider | Donor |
| BTC→USD conversion | Recipient's chosen exchange | Standard trading/withdrawal fee | Recipient, later, off-platform |
Agora's own cut is genuinely zero — the FAQ instructs donors to "pay the amount plus the network fee" [6], meaning the only unavoidable cost is Bitcoin's own transaction fee, not a Soapbox markup.
Custody, Compliance & Fiat Reality (US Operator Lens)
Custody is real and non-custodial: the receiving address is derived from the recipient's own Nostr secret key, and funds can be exported straight to Sparrow, BlueWallet, Trezor, or Ledger [7]. But "no KYC at Agora" doesn't mean "no KYC anywhere" — it just relocates to both ends of the transaction:
| Step | Touches Agora? | KYC/AML checkpoint? |
|---|---|---|
| Campaign created, address derived from nsec | Yes (app only) | None |
| Donor pays from their own wallet | No — wallet-to-wallet | Only if the donor's own wallet/exchange requires it (most retail apps do) |
| BTC confirms on-chain | No — Bitcoin network only | None |
| Recipient wants a US tax-deductible receipt | No | Not practically possible for anonymous/Silent-payment gifts — no donor name/address is ever captured |
| Recipient converts BTC to spendable USD | No — recipient's own exchange | Standard exchange KYC/AML applies here |
Two general considerations for a US-based 501(c)(3) or small business — not legal advice: (1) the IRS treats crypto as property (Notice 2014-21); issuing a compliant donor receipt requires the donor's identity and fair-market-value at receipt, which Silent Payments structurally prevent and Public payments only partially provide (an address isn't a legal name). (2) Funds sit in BTC, subject to price movement, until someone manually off-ramps them — a $10,000 goal today isn't a fixed $10,000 in the bank the way a GoFundMe balance is.
Self-Host / White-Label
Agora is open source (AGPL-3.0) and built to be redeployed, not just used [4]:
| Requirement | Detail |
|---|---|
| License | AGPL-3.0 — copyleft; a hosted fork must offer source to its users |
| Stack | React 18, Vite, TypeScript, Tailwind 3 + shadcn/ui, React Router, TanStack Query, Nostrify + nostr-tools, Capacitor (mobile), Vitest |
| Hosting | Builds to static files — GitLab/GitHub Pages, Netlify, Vercel, or any VPS; Docker Compose path documented |
| Config | agora.json (gitignored) for per-deployment branding/relays; CONFIG_FILE env var for a custom path |
| Multi-tenant SaaS option | None found — no hosted "Agora for Teams" product; this is fork-and-run, not buy-a-plan |
| Skill needed | A developer fluent in a modern React/Nostr stack, plus a relay (and optionally a Blossom file server) to operate |
Verdict: technically real, but it's a codebase to fork and operate, not a product to purchase. Realistic for a dev-capable client; not a weekend project for a non-technical one.
Traction & Credibility
| Signal | Detail |
|---|---|
| Launch | June 2, 2026, Oslo Freedom Forum [9] |
| Origin | HRF's "AI Hack for Freedom," Bitcoin Park, Austin, Jan 17–18, 2026; Team Soapbox ("Pathos") took 2nd place, 0.25 BTC = 25M sats [2][12] |
| Early hackathon traction | 100+ users within 24 hours of the original build [2] |
| Backers | Human Rights Foundation, World Liberty Congress (Leopoldo López), Soapbox, "And Other Stuff" [10] |
| Independently verified press | PRNewswire wire release, syndicated to Yahoo Finance, StreetInsider, Amsterdam Aesthetics, Norwegian outlet Geopolitika [9][11] |
| Self-reported-only press | Forbes, Associated Press — claimed on Agora's own sponsors page; not independently located in this research [10] |
| Only public dollar figure found | Venezuela pilot: $8,420 of $10,000 goal, 247 donors, 12 countries [1] |
| Platform-wide totals ($ raised, live campaigns) | Not published anywhere found |
Confidence: Medium. The launch itself and WLC/HRF backing are solidly corroborated by an independent wire release; platform-wide traction is not — the one dollar figure available comes from Agora's own marketing page, not an independent scan.
Incumbent Comparison
| Platform | Fee | Custody | KYC | Rails | Where it beats Agora |
|---|---|---|---|---|---|
| Agora | 0% + BTC network fee | Non-custodial, always | None | On-chain BTC only | (baseline) |
| GoFundMe | 2.2–2.9% + $0.30/donation [16] | Custodial | Government ID + bank account, ~20 countries [16] | Card/bank, USD | Discovery, trust, card checkout, disputes, tax help |
| Kickstarter | 5% + ~3%+$0.20 processing | Custodial, all-or-nothing | Bank/tax info | Card, USD | Built-in audience, reward mechanics |
| Patreon | ~10% + ~3% processing | Custodial | Bank/tax info | Card, USD | Recurring billing, tiered perks, mainstream reach |
| Open Collective | 0–10%, fiscal-host dependent | Semi-custodial (fiscal host holds funds) | Fiscal host's own KYC | Card/bank, USD | Public expense ledger, legal nonprofit wrapper |
| Geyser.fund | 0% (own node) to 5% (Geyser wallet) + 10% for promotion [17] | Non-custodial (Boltz-Exchange swap) | None for creators [17] | On-chain BTC + Lightning | Lightning speed/cost, built bitcoiner audience, reward campaigns, longer track record |
Confidence: Medium — Agora's own figures are primary-sourced; incumbent fee figures are drawn from secondary aggregation (help-center pages and pricing trackers), not each platform's raw fee schedule fetched directly.
Where Agora structurally loses, regardless of fees: no card/fiat donor checkout (a donor must already own or acquire BTC themselves), no Lightning option for cheap micro-donations, no reward or membership mechanic, five weeks of track record versus GoFundMe's ~15 years, and a brand that reads as off-topic for anything that isn't a human-rights cause.
Integration Points
| Connects to | Mechanism | Enables |
|---|---|---|
| Lightning wallets | NIP-57 zaps (kind 9734 request / 9735 receipt) [13] | In-app tipping of posts/activists — a separate rail from campaign donations |
| Remote Lightning wallet | NIP-47 Nostr Wallet Connect (NWC) [14] | One-tap zap payment without exposing keys to the client |
| Browser Lightning extension | WebLN [4] | Alternate in-browser zap path |
| On-chain BTC wallets | BIP-352 Silent Payments | Unlinkable, private per-campaign donation addresses |
| Nostr relay network | Nostrify + nostr-tools; Agora runs its own relay + Blossom file server [4][8] | Campaign posts, updates, social graph, moderation |
| Hardware/desktop wallets | Seed export | Move campaign funds to Sparrow, BlueWallet, Trezor, Ledger |
| Static hosting / Docker | AGPL-3.0 open-source build [4] | Self-hosted, white-label forks (copyleft obligation attached) |
| Android distribution | ZapStore [9] | Censorship-resistant install path outside Google Play |
| External sites (e.g. Shakespeare-built pages) | None found | No embed/iframe/widget documented — only linking out to an agora.spot URL is confirmed possible |
| Zap Goals (NIP-75, kind 9041) [15] | Not confirmed in use | The ecosystem-standard Lightning crowdfunding-goal event exists, but Agora's on-chain goal bar is not documented as using it — likely custom logic instead |
Open Questions
- Exact Nostr event kind(s) recording the on-chain campaign/donation itself (site says donations are "public... on Nostr" [1] but doesn't name the kind)
- Whether NIP-75 powers campaign goal-tracking, or if it's bespoke
- Mechanism for posting campaign updates to donors (standard Nostr notes vs. a dedicated notification path)
- Any embed/widget capability for third-party sites — none located
- Platform-wide traction (total raised, total live campaigns, total donors) beyond the single featured example
- Whether the claimed Forbes/AP coverage is substantive — could not independently locate either
- Full workings of the org "verification statement" feature (concept confirmed, UI/workflow not)
- No Terms of Service document was located despite being referenced from the FAQ
- No iOS listing found — web and Android/ZapStore only, cause unconfirmed
Sources
- soapbox.pub/agora — Soapbox, "Agora — GoFundMe Without Borders." Accessed 2026-07-09. Supports: tagline, custody quote, zero-fee claim, payment options, Venezuela campaign figures.
- soapbox.pub/blog/building-pathos — Soapbox blog, "Building Agora: From Hackathon to Real-World Activism." Accessed 2026-07-09. Supports: Pathos→Agora rename, hackathon origin, 25M-sat prize, 100+ users in 24h.
- soapbox.pub/blog/agora-connecting-freedom-fighters — Soapbox blog. Accessed 2026-07-09. Supports: launch story, WLC/HRF/Bitcoin Park partnership, Venezuela pilot, 2026 expansion countries.
- gitlab.com/soapbox-pub/agora — GitLab repository + README. Accessed 2026-07-09. Supports: AGPL-3.0 license, tech stack, self-hosting/Docker instructions, config model, Nostr kinds, Lightning zaps via NWC/WebLN.
- agora.spot — Live app. Accessed 2026-07-09. Supports: navigation structure, "zero platform fees."
- agora.spot/help/donors and agora.spot/help — Donor guide + FAQ. Accessed 2026-07-09. Supports: 3-step donation flow, network-fee statement, wallet list, Public vs. Silent explanation.
- agora.spot/help/activists and agora.spot/verify — Activist guide + verify page. Accessed 2026-07-09. Supports: non-custodial confirmation, wallet export options, permissionless creation, org verification-statement model.
- agora.spot/safety and agora.spot/about — Safety + about pages. Accessed 2026-07-09. Supports: "Borderless Micro-Philanthropy Network," no-Lightning-for-donations, CSAE policy, own relay + Blossom server.
- PRNewswire — World Liberty Congress Launches Agora at Oslo Freedom Forum. Accessed 2026-07-09. Supports: June 2, 2026 launch date, López/Fain quotes, launch countries, ZapStore/Android distribution.
- agora.spot/sponsors — Sponsors page. Accessed 2026-07-09. Supports: partner list, self-reported press coverage.
- Yahoo Finance — World Liberty Congress Launches Agora (syndicated PRNewswire). Accessed 2026-07-09.
- HRF — HRF Sponsors AI Hack for Freedom in Austin, TX, Jan. 17-18. Accessed 2026-07-09. Supports: hackathon structure, prize tiers (0.5/0.25/0.1 BTC), dates.
- NIP-57 — Lightning Zaps. Accessed 2026-07-09. Supports: kind 9734/9735 definitions.
- NIP-47 — Nostr Wallet Connect. Accessed 2026-07-09. Supports: NWC mechanics.
- NIP-75 — Zap Goals. Accessed 2026-07-09. Supports: kind 9041, amount/relays tags, goal semantics.
- GoFundMe Help Center — Requirements to receive funds and Learn about GoFundMe fees. Accessed 2026-07-09. Supports: GoFundMe fee rates, ID/bank/country requirements.
- Geyser Guide — Geyser Fees and How on-chain contributions work. Accessed 2026-07-09. Supports: Geyser fee tiers, Boltz-Exchange non-custodial swap, no-KYC-for-creators.
- Kickstarter, Patreon, and Open Collective fee figures — aggregated via web search of secondary pricing-comparison sources (not each platform's own pricing page fetched directly). Accessed 2026-07-09. Confidence: Medium; treat as directionally accurate, not verbatim-verified.
Shakespeare — AI Website/App Builder on Nostr
A browser-only, open-source AI coding environment that chats you into a React app, stores the code on your machine (not Soapbox's servers), and deploys it to a free subdomain or anywhere else you point it — priced by the AI token, not the seat.
Verified: 2026-07-09 · Amended: 2026-07-10 (v2 wave) — flagged the BYOK claim UNRESOLVED (a live re-fetch of the announcement post shows no BYOK mechanism mentioned; custody location unknown) without deleting the original claim; added the NostrDeploy.com DNS-tripwire annotation (not previously present in this chapter, unlike ch. 10); flagged Stacks CLI provider-key storage as unknown/unfound.
Confidence: High for architecture, storage, license, and deployment-target facts (multiple primary Soapbox sources agree). Medium for exact current pricing/free-tier durability and for funnel/data-capture mechanics (documented capability exists; where captured data actually lands is not spelled out by Soapbox and is partly inferred from the tool's no-backend architecture). Low for anything competitive (Lovable/Bolt/Webflow/Framer cells are general-knowledge, not independently fetched from those vendors this pass).
What It Is
"Shakespeare is a web interface where you can build websites through conversation with AI assistants." [2]
It is Soapbox's consumer-facing wrapper around MKStack, its Nostr app framework: "bringing the power of MKStack to everyone through natural conversation" [1]. Announced July 10, 2025 and publicly launched July 14, 2025 at a Human Rights Foundation event [2][10], it is not a template picker — it is a full in-browser IDE. Act 2 (Oct 2025) reframed it as "a full-featured development environment, entirely in your browser": code editor, hot-reload preview, in-chat terminal, file uploads [3].
Founder Alex Gleason's framing: "It used to be hard to build websites using decentralized protocols. But Nostr + AI makes this something that anyone can do now." [10] Soapbox runs it "trust-based" rather than subscription-funded, with no outside revenue disclosed [14]. It's a lineage product: Block's Goose agent framework (Jan 2025) → Stacks, a Nostr template platform (May 2025) → the browser-native rebuild that became Shakespeare (Jul 2025), each step shedding a barrier (terminal, Node.js install) [22].
How It Works
Everything — the AI agent loop, the TypeScript compiler, the git client, the file system — runs client-side. There is, per the repo's own README, "no backend (except for a couple microservices in the services/ directory; these are fully configurable within Shakespeare's settings)" [5].
Stack: React PWA frontend, IndexedDB/LightningFS for storage, isomorphic-git for version control, esbuild-wasm to compile TypeScript to a running React app — all in-tab [5]. Generated apps get MKStack's scaffolding: 50+ Nostr NIPs, ShadCN components, and Nostr-native primitives (profiles, DMs, zaps, Cashu wallets) available to the AI on request [6]. Full stack: React, not a generic static-site generator — MKStack is the template; the AI, called "Dork" in the underlying framework, decides what to wire up [9].
Confidence: High — architecture is corroborated across the README, the launch post, and Act 2.
Cost Mechanics — The Free-Mockups Verdict
Yes. You can generate close to unlimited client mockups at near-zero marginal cost — but the mechanism matters, because only one of four paths is genuinely "free" without caveats. The underlying model: Shakespeare doesn't sell AI capacity itself, it brokers access to independent Nostr Service Providers (NSPs) who set their own pricing (pay-per-use, subscription, or free tiers) and settle in USD or Bitcoin [11].
| Path | Who gets paid | Marginal cost per mockup | Catch |
|---|---|---|---|
| Free Gemini Flash NSP | Nobody — Soapbox-subsidized | $0 | "We're currently offering a free Gemini Flash NSP so you can try Shakespeare without adding credits!" [2] — promotional language, no published end date or usage cap found |
| BYO API key, budget model (Z.ai GLM 4.5, etc.) | Model provider, directly | Cents | GLM 4.5 is "often 60–80% less expensive than Claude with comparable results" [20]; Shakespeare "takes no cut" on BYOK [3] |
| BYO API key, premium (Claude Sonnet 4.5) | Model provider, directly | ~$4/M prompt + $16/M completion tokens via the Shakespeare AI proxy [12] | Best quality; still no Shakespeare markup |
| Local model (Ollama, GPT-OSS, DeepSeek R1, Gemma 3) | Nobody | $0 after setup [17][20] | Needs decent hardware; slower, lower ceiling than frontier cloud models |
| Official paid NSP (MKStack + Claude Sonnet 4) | Soapbox, via Stripe or Lightning | Pay-per-use, rate not published | The only path where it applies: "Shakespeare only takes a cut if you use our paid NSP: MKStack + Claude Sonnet 4" [13] |
There is no subscription anywhere in this picture — Shakespeare's own comparison page needles v0 for exactly the model you'd otherwise be locked into: "Five monthly free credits, then subscription-based ($20-$100/mo)... No pay-as-you-go option" [16]. Practical read: run free-tier or a cheap BYOK model for the volume mockup pass, reserve Sonnet 4.5 for the deal that's actually going to close.
Confidence: High on the mechanism; Medium on "free forever" — the Gemini Flash tier is described as a current promotion, not a permanent commitment, so verify live before quoting a client $0.
BYOK flag — UNRESOLVED (2026-07-10): the two "BYO API key" rows above could not be re-verified live this pass. Soapbox's own Shakespeare announcement post [2] — one of this table's own citations — was re-fetched and documents only NSP-marketplace payment (USD via Stripe or Bitcoin Lightning) plus the free Gemini Flash NSP; it does not mention a BYOK mechanism at all. Act 2 [3] does document "BYOK providers" as a real feature, so the capability itself is not in doubt — what's unresolved is the custody model: where a pasted API key is stored (browser-local, alongside the IndexedDB/LightningFS project data, versus sent to and held by a Shakespeare-operated server) is not stated in any source fetched. This does not overturn the rows above — they stay — but verify directly in-product (add a BYOK key, then check browser devtools > Application > IndexedDB/localStorage) before trusting a client's API key to it.
Funnel Capability Matrix
This is the section that needs the most caution. "Add a contact form" is a literal documented example prompt [1] — but Shakespeare has no conventional backend or database; Nostr's native data model is public, signed, relay-broadcast events, not a private leads table [5]. A generated form's UI will render; where its submissions land is not specified by Soapbox and depends entirely on what you prompt it to wire up.
| Funnel need | Native? | Mechanism | Gap-fill if not native |
|---|---|---|---|
| Landing page / mockup | Yes | Chat → React site, live preview [1][3] | — |
| Contact form (UI) | Yes | Documented prompt example [1] | — |
| Contact form (usable lead data) | No (inferred) | No default backend/DB; Nostr events are public, not a private CRM [5] | Prompt it to fetch() a Formspree/Basin endpoint or a webhook |
| Email list capture | No | Nostr has no email primitive | Embed Beehiiv/ManyChat signup script — already in your stack |
| Scheduling | No | Not Nostr-specific; nothing baked in | Cal.com/Calendly iframe — trivial, it's a normal React page |
| Bitcoin/Lightning payment | Yes | NIP-57 zaps, NIP-60 Cashu, NIP-61 nutzaps, NIP-47 wallet-connect ship in MKStack's 50+ NIPs [6]; Agora runs a live donation flow on this exact stack [24] | — |
| Credit-card payment (Stripe) | No | Not a Nostr primitive | Stripe Checkout link or JS embed — doable, not one-click |
| Analytics | Not documented | No mention in any primary source found | Plausible/Fathom/GA script tag |
Verdict: the awareness layer (site, form UI, scheduling embed) and Bitcoin-native payment are genuinely one-prompt-away. The conversion/data layer for a mainstream (non-Nostr) small-business client — the part that actually makes a "funnel" — needs the same external services most funnels already run (Beehiiv, ManyChat, Cal.com, Stripe), stitched in by prompt rather than provided.
Confidence: Medium — the gap is a structural inference from the architecture, not a Soapbox-stated limitation.
Deployment Paths
Runbook, as documented: build in chat → click deploy → live instantly at a *.shakespeare.wtf subdomain, "no setup required and no configuration headaches" [1]. Act 2's showcased projects confirm the pattern live: mi.shakespeare.wtf, linkbio.shakespeare.wtf [3]. For anything else — "use our built-in, free, deployment service or export your complete project and deploy anywhere you choose" [3] — via ZIP or a git push to GitHub/GitLab/custom, including nostr:// Git URLs for censorship-resistant backup needing no GitHub account [3][5].
The fully decentralized route (content-addressed on Blossom servers, pointer events on Nostr relays, resolvable via nsite:// or gateway domains — the same nsite spec used across the wider Nostr static-hosting ecosystem [27]) is documented for MKStack projects deployed via the Stacks CLI (npm run deploy → Blossom + relays → NostrDeploy.com) [7][9], not spelled out step-by-step for the browser Shakespeare export path specifically — treat as reachable with extra tooling, not a native one-click Shakespeare button.
⚠ Tripwire (2026-07-10), cross-ref ch. 10:
nostrdeploy.comdoes not resolve — a direct DNS check hit ENOTFOUND, which reads as dead-or-broken domain, not a server outage [28]. Both the NostrDeploy.com mention above and in the mermaid diagram are therefore unverifiable and possibly dead; don't promise this decentralized-deploy lane to a client or build a runbook on it until the domain resolves again. Ch. 10 carries the full tripwire detail, including the untested nuance that the underlying Blossom+relay substrate is content-addressed and may still work even if the NostrDeploy.com index/gateway itself is gone. Stacks CLI provider-key storage — unknown, flag before wiring real keys (2026-07-10): ch. 10 documentsstacks configureas where the Stacks CLI's model-provider keys (OpenRouter, Routstr, PayPerQ) get set, but where that command actually persists the key (plaintext config file, OS keychain, encrypted store) was not found in this pass — the term is search-drowned by the unrelated "Stacks" blockchain project, which floods generic web results. Before wiring a real client-facing provider key through the Stacks CLI lane, runstacks configureand inspect its output/config file directly rather than assuming a security posture.
Confidence: High for the *.shakespeare.wtf free path and the 8-target export list (corroborated across two independent Soapbox comparison posts [16][17]). Medium for custom-domain mechanics on either path — working examples exist (espy.you) but no published runbook was found.
Code Ownership & Export
Two licenses are in play and they answer different questions:
| What | License | Source |
|---|---|---|
| The Shakespeare tool itself (if you fork/self-host it) | AGPLv3 | "Fully auditable codebase under AGPL 3.0 license" [2][5] |
| Code Shakespeare generates for your project | No license imposed (inferred) | Soapbox's own comparison: unlike Replit, which "automatically licenses apps with MIT," Shakespeare's code "stored in browser — Soapbox has no control over it" [17] |
That second row is the load-bearing fact for an agency workflow: there is no platform to be banned from. Soapbox's contrast: a Replit ban deletes your account and "all Replit App will have been deleted"; Shakespeare's code lives in your browser's IndexedDB from the start, so "prevents account bans" really means "no account to ban" [17]. Export is one click to a ZIP or a full-history git push — no lock-in step exists to remove [3][5].
Confidence: High for the tool's own AGPLv3 status (confirmed on the repo). Medium-High for "no license on generated output" — strongly implied by the architecture and Soapbox's own competitor framing, but never stated as a standalone legal claim.
Limits & Workflow
- Iteration is chat-native, not context-free. Shakespeare's own debugging guide flags "context overload: the conversation has become so long that each prompt is expensive" as the signal to start a fresh chat rather than keep pushing [19].
- Debugging is a 4-step protocol Soapbox documents explicitly: gather evidence → isolate the problem → provide context → test systematically with rollback available, closing with "start with evidence, not assumptions" [19].
- Model choice is the real cost/quality lever, not a plan tier: "If you choose a cheap model, you can expect cheap results" [19][20].
- Version control is built in, framed as "a time machine for your code" — one-click rollback via a three-dot menu, or raw git commands in the in-browser terminal for power users [21].
- Editing an existing (including imported) site is supported — Shakespeare can import external projects, not just greenfield-generate [18].
- No multiplayer/live-collab feature was found anywhere in the primary sources (community, Act 2, or resources pages) — the "Shakespeare Community" is a Nostr-based chat/forum for builders to swap tips, not in-project co-editing [4].
Confidence: High — sourced from Soapbox's own guides, not marketing copy.
Agency Comparison
| Shakespeare | Lovable | Bolt.new | v0 (Vercel) | Webflow | Framer | |
|---|---|---|---|---|---|---|
| Client mockups, cost | BYOK/free-tier/local → effectively $0–cents [2][3] | Free tier + paid plans (subscription) | Free tier + paid plans (subscription) | 5 free credits, then $20–$100/mo, no pay-as-you-go [16] | Free-to-build, paid to publish/subscribe | Free-to-build, paid to publish/subscribe |
| Production backend | Nostr events/relays; no managed DB out of the box, though Shakespeare claims support for "backend frameworks like databases, containerization" [16] | Managed Supabase DB, one prompt away | Multi-framework, backend via integrations | "Front-end only. No backends, no databases" — Shakespeare's own characterization [16] | CMS + hosting, no custom app logic | Marketing sites; limited app logic |
| Ownership / lock-in | Local-first, no imposed license, no account to ban [5][17] | Code not Vercel/Lovable-owned, but lives on their platform | Platform-hosted by default | Vercel "collects your source code, billing info... and location data," "can't delete your own data" — per Shakespeare's framing [16] | Platform-locked (hosted CMS) | Platform-locked |
| Payments baked in | Bitcoin/Lightning/Cashu (NIP-57/60/61/47) native [6]; card payments need manual wiring | One-click Stripe pattern | Manual wiring | Manual wiring | Native e-commerce | Forms native; commerce via add-ons |
| Templates / visual editor | None — chat-only | Chat-only, growing gallery | Chat-only | Chat-only | Deep editor + template marketplace | Deep editor, design-forward templates |
| Non-technical handoff | Needs re-prompting or hand-edits; no CMS | Similar chat-only friction | Similar | Similar | Best-in-class for marketers | Best-in-class for marketers |
| Track record | ~1 year old (launched Jul 2025) [2][10] | Longer, larger base | Longer, larger base | Vercel-backed, mature | Decade+, mature | Mature, design-standard |
Where Shakespeare wins: true pay-per-use with no subscription tax, real code ownership with no ban risk, native Bitcoin/Lightning rails, export to 8+ hosts instead of one [16][17]. Where it loses: no visual/drag-and-drop editor, no managed database or one-click Stripe/CRM the way Lovable ships it, no template marketplace, younger track record — plus a client-comfort tax: "log in with a Nostr key" and "your data lives on relays" are unfamiliar to a small-business client used to plain email/password SaaS.
Confidence: Medium — Shakespeare's claims about itself and about v0/Replit are primary-sourced [16][17]; the Lovable/Webflow/Framer cells reflect general market knowledge, not a fresh fetch of those vendors' own pricing/docs this pass.
Integration Points
| Connects to | Mechanism | Enables |
|---|---|---|
| MKStack | Shakespeare is the browser front-end to MKStack's template + 50+ NIPs [1][6] | Every Nostr-native primitive (profiles, zaps, Cashu, DMs) available on request |
| Agora (built with Shakespeare+MKStack at an HRF hackathon) | Reference implementation, not an embeddable widget | Proof a live Lightning-donation/crowdfunding flow works in production on this stack [24][25] |
| Ditto | Separate product (Nostr community server, self-hostable or ditto.pub) [23] | Pairing pattern: Shakespeare = funnel/marketing site; Ditto = retained community/membership layer post-conversion — a two-product package, not a plug-in |
| NostrHub | Discovery/publishing hub for Nostr apps, NIPs, repos [26] | Distribution channel for your own templates/portfolio in the Nostr dev ecosystem — minor to client funnels directly |
| Beehiiv / ManyChat (common funnel-stack tools) | Embed signup script or webhook into generated site | Real email capture — the piece Nostr doesn't natively provide |
| Cal.com / Calendly | iframe/embed, no Nostr dependency | Scheduling step in a funnel — trivial since output is a normal React page |
| Stripe | JS SDK or Checkout link, manually prompted | Credit-card conversion step for non-Bitcoin clients |
| Git hosts (GitHub/GitLab/custom) + Nostr Git | Native git integration, incl. nostr:// remote [3][5] | Backup/handoff independent of any single platform |
Confidence: Medium — product relationships are documented; the specific "how to combine these for an agency package" framing is this chapter's synthesis, not a Soapbox-stated playbook.
Open Questions
- Whether the free Gemini Flash NSP is still active today (2026-07-09) and whether it carries a rate limit or usage cap — described in launch copy as a current promotion, not a permanent commitment, and no post after Dec 2025 was found confirming its status [2].
- The exact DNS/runbook steps for attaching a fully custom domain (either to
*.shakespeare.wtfor to an nsite/Blossom deployment) — working examples exist (espy.you) but no step-by-step was found [3]. - Whether a contact-form submission can be routed to a private Nostr DM (NIP-17) to the site owner as a semi-native lead-capture path, versus requiring an external HTTP service — plausible given MKStack's NIP coverage, but not demonstrated in any source fetched.
- Whether analytics of any kind (even privacy-respecting/Nostr-native) exists or is roadmapped — zero mentions found across marketing pages, blog, and FAQ listing.
- Current product state beyond the last confirmed changelog entry (Dec 16, 2025, "AI-First Development on Mobile") — seven months of potential unlogged change between then and today; verify live before any client-facing commitment [15].
- Where a BYOK key is actually stored (browser-local vs. Shakespeare-operated server) — see the BYOK flag in Cost Mechanics, above; unresolved as of this pass, inspect in-product before trusting a client's key to it.
- Where the Stacks CLI persists a provider key set via
stacks configure— see the Deployment Paths tripwire, above; unresolved, search-drowned by the unrelated Stacks-blockchain project.
Sources
- Shakespeare — Open Source AI Website Builder — product marketing page: pricing claims, provider list, deployment/export claims. Accessed 2026-07-09.
- Introducing Shakespeare: AI-Powered Website Builder on Nostr — launch post (2025-07-10): what it is, NSP payment model, free Gemini Flash tier, AGPLv3 license. Accessed 2026-07-09; re-fetched 2026-07-10 — confirms no BYOK mechanism is mentioned in this post (see the BYOK flag in Cost Mechanics).
- Shakespeare: Act 2 — Build Freely — 2025-10-01 update: local storage (IndexedDB/LightningFS), BYOK providers, git integration incl.
nostr://, free deploy service, example live subdomains. Accessed 2026-07-09. - Announcing the Shakespeare Community — community structure, channels, no multiplayer/payment features mentioned. Accessed 2026-07-09.
- gitlab.com/soapbox-pub/shakespeare and raw README — tech stack (React PWA, IndexedDB+LightningFS, isomorphic-git, esbuild-wasm), "no backend" claim, AGPLv3 license. Accessed 2026-07-09.
- MKStack — AI-Powered Nostr App Framework — 50+ NIPs list incl. payments (NIP-57/60/61/47), React/TypeScript stack, NostrDeploy.com integration. Accessed 2026-07-09.
- Stacks — AI-First Development Platform — Docker-based template platform,
stack.json/agent.json/CONTEXT.md, NostrDeploy.com deploy workflow, Dork AI agent providers. Accessed 2026-07-09. - Soapbox Toolbox — full tool inventory used to map integration points (Ditto Relay, Nostrify, NostrHub, Relay Kit, etc.). Accessed 2026-07-09.
- MKStack: Vibe Coding for Everyone — step-by-step Dork generation pipeline,
npm run dev/npm run deploy, cost caution ("this gets expensive"). Accessed 2026-07-09. - Shakespeare Launches as Open Source Competitor to AI-site builders — PR Newswire — launch date/venue, Alex Gleason quote. Accessed 2026-07-09.
- Understanding Nostr Service Providers (NSPs) — NSP payment structure (pay-per-use, subscription, free tiers), how to pick one. Accessed 2026-07-09.
- ai.shakespeare.diy — OpenAI-compatible API with NIP-98 auth; GLM-4.5 and Claude Sonnet 4.5 per-token pricing. Accessed 2026-07-09.
- Shakespeare — Product Hunt — maker comments, "Shakespeare only takes a cut if you use our paid NSP" quote, user feedback. Accessed 2026-07-09.
- Inside Soapbox's push to keep AI open for everyone — TechCabal — Gleason's origin motivation quote, "trust-based" business framing, no revenue/funding disclosed. Accessed 2026-07-09.
- Soapbox Blog index — full Shakespeare-related post list with dates, used to establish most-recent-confirmed-update timeline (through 2025-12-16). Accessed 2026-07-09.
- Shakespeare vs. v0 by Vercel — official feature/pricing comparison table (Soapbox's own framing). Accessed 2026-07-09.
- Shakespeare vs. Replit — official comparison incl. ban/ownership contrast, deployment target list. Accessed 2026-07-09.
- Shakespeare Resources — FAQ & Guides — guide index (importing projects, editing code, deploying, version control, debugging, prompting) with dates; FAQ question titles (answer text not rendered). Accessed 2026-07-09.
- Debugging in Shakespeare — 4-step debug protocol, context-overload guidance, cost-effective prompting tips. Accessed 2026-07-09.
- Which AI Model Should I Use with Shakespeare? — model recommendations by budget, GLM 4.5 vs Claude cost comparison, local-model framing. Accessed 2026-07-09.
- Using Version Control in Shakespeare — rollback UI, GitHub/GitLab sync workflow, terminal git access, Nostr git backup. Accessed 2026-07-09.
- The Shakespeare Origin Story — Goose → Stacks → Shakespeare evolution narrative. Accessed 2026-07-09.
- Ditto — Your Content. Your Vibe. Your Rules. and github.com/soapbox-pub/ditto — Ditto product description, self-host/hosted options. Accessed 2026-07-09.
- Agora: Connecting Freedom Fighters to Uncensorable International Support — Agora product description, Bitcoin/Lightning support flow. Accessed 2026-07-09.
- Building Agora: From Hackathon to Real-World Activism — confirms Agora built with MKStack and Shakespeare at an HRF hackathon. Accessed 2026-07-09.
- gitlab.com/soapbox-pub/nostrhub and NostrHub — protocol/app discovery hub description. Accessed 2026-07-09.
- Web search summaries on the
nsitespec (github.com/lez/nsite, nsite.info) — static-site-on-Nostr mechanics (Blossom storage + relay pointer events); community/protocol-level source, not Soapbox-authored, used only to explain the decentralized deploy path's underlying spec. Accessed 2026-07-09. nostrdeploy.com— direct DNS resolution check: ENOTFOUND, domain does not resolve. Checked 2026-07-10; cross-ref this manual's ch. 10 — MKStack, source [26], which ran the same check the same day.
NostrHub — Soapbox's Developer Hub for NIPs, Apps, and Repos
A Nostr-native site (nostrhub.io) where developers browse and propose protocol specs (NIPs), list apps, host git repos over Nostr, and discuss all three — ranked not by a company or a vote but by a "configurable meritocracy" of experts each visitor picks themselves.
Verified: 2026-07-09 · Delta pass: 2026-07-10 (repo velocity, rating-scale reconfirm, contribution-program check, MCP/agent-surface addendum)
Confidence overview: High for feature inventory, NIP kind numbers, and the login flow (source-code-verified against the live GitLab repo). High for the governance model's mechanics and quotes (primary blog post, fetched directly). Medium for distribution/audience size and cross-tool integration with Shakespeare/Ditto (no hard numbers or explicit docs found — flagged in Open Questions). Low for anything about the DVM marketplace's current (2.0-era) status.
What It Is
"It's not a democracy. It's not a dictatorship. It's a meritocracy you configure yourself." [3]
NostrHub is built by Soapbox (founder Alex Gleason) and has shipped twice. v1 launched June 10, 2025 as "the ultimate resource for Nostr developers" — NIPs, apps, repos, comments [1]. It got overrun by spam [3]. NostrHub 2.0 — a from-scratch rewrite (the GitLab repo was recreated June 11, 2026 [4]) — was announced June 28, 2026, eleven days before this was verified [3]. Everything below describes 2.0 unless marked v1-only.
Framed against its own inspiration: "We started by trying to escape GitHub's oligarchy over the NIPs. We ended up rebuilding the good parts of GitHub itself... on a substrate nobody owns, governed by a meritocracy you control." [3]
Feature Map
| Surface | What it is | Underlying Nostr mechanism | Confidence |
|---|---|---|---|
| NIPs explorer | Official nostr-protocol/nips + community-submitted custom NIPs, comment threads on each | Custom NIPs published as kind 30023 long-form articles [3][16]; official NIPs mirrored for browsing | High |
| Browse-by-kind | Pages like /kind/30023/ list every NIP touching a given event kind | Derived index over NIP content | Medium (URL pattern confirmed via search index [24], not directly fetched) |
| App directory | Apps listed/discovered by event kinds they support; 4-tier ProtonDB-style quality scale — Flawless / Incomplete / Isolated / Borked — each rating an ordinary portable Nostr event, not a NostrHub-locked score (reconfirmed 2026-07-10) | NIP-89 kind 31990 handler-info events + kind 31989 recommendations [3][13] | High |
| Git repos | Create in-browser or mirror a GitHub repo; GitHub-style issues/PRs/file browser | NIP-34: kind 30617 announce, 1617 patch, 1618/1619 PR, 1621 issue, pushed to a GRASP host [3][12][18] | High |
| Comments | Every NIP/app/repo has a decentralized comment thread | Nostr reply events | High [1] |
| Community chat | Built-in dev community + AI agent, "loosely inspired by Block's Sprout/Buzz," explicitly experimental | NIP-72 community space [1][3] | Medium |
| Plan Graph | Constellation-style map of ecosystem goals/problems/people; author calls it unsatisfying, still iterating | Custom, experimental | Medium |
| Profiles | Every dev's profile aggregates their published NIPs/apps/repos as a reputation signal | Standard Nostr profile + NostrHub-side aggregation | Medium |
| DVM marketplace (beta) | AI-powered Data Vending Machines — translation, image generation, on-demand compute | NIP-90 [1] | Low — was live in v1 [1]; not mentioned once in the 2.0 announcement [3] — status unconfirmed |
Signing In With Your Nostr Credentials
Since you already hold a keypair, this is the load-bearing question. NostrHub's actual login component (AuthDialog.tsx, QuickLoginDialog.tsx — read directly from the GitLab repo [8][9]) offers four paths. Ranked by safety:
| Method | How it works | Where your key lives | Safety | Confirmed on NostrHub |
|---|---|---|---|---|
| Browser extension (NIP-07) — Alby, nos2x | Extension exposes window.nostr; site asks it to sign, never sees the key | In the extension's own storage | Safest — use this | Yes — default path. If an extension is detected, QuickLoginDialog shows one "Log in" button that calls login.extension() [9] |
| Remote signer / bunker (NIP-46) — nsec.app, Amber, etc. | Paste a bunker://… URI, or click "Open signer app" to hand off via nostrconnect:// | On your phone or bunker service, never touches NostrHub's browser tab | Safe | Yes — under "Other ways to log in" in AuthDialog [8] |
| Key file upload | Upload a .txt file containing your nsec | Briefly loaded into NostrHub's JS runtime | Risky | Yes — explicit menu option [8] |
Paste nsec directly | Type/paste nsec1… into a password-style field | Touches NostrHub's JS runtime the moment you paste it | Risky — avoid for your real key | Yes, no extra confirmation step shown at paste time [8] |
No confirmation dialog gates the nsec-paste path beyond a generic warning shown only during new-key generation ("This key is your only way to access your account. If you lose it, you lose the account.") [8] — that warning is about loss, not about the security cost of pasting an existing key into a website. Treat the paste/upload options as absent for your primary identity.
First-login runbook
- Confirm your existing
nsecis already imported into a NIP-07 extension (Alby or nos2x), or have a bunker ready (nsec.app, Amber). - Visit nostrhub.io. If the extension is detected, you'll see a "Welcome back" dialog with a single Log in button.
- Click it — the extension prompts you to approve
getPublicKey/signEvent; your key never leaves the extension [9]. - No extension active? Click "Other ways to log in" → paste your
bunker://…URI, or click "Open signer app" to connect a mobile signer vianostrconnect://[8]. - Do not use the nsec-paste or file-upload option for your primary key. If you ever need to smoke-test something disposable, use a burner key there instead.
Publishing Workflows
All three content types share one shape — compose, sign, publish to a relay (or GRASP host for repos), NostrHub indexes the event:
(a) Publish/announce a git repo
- Choose "New repo" or "Mirror from GitHub" (paste a GitHub URL to import history) [3].
- NostrHub composes a kind 30617 repository-announcement event (name, description, clone URL, relay list, maintainers) and asks your signer to sign it [12].
- The signed event, plus the repo data, is pushed to a GRASP server — "a relay that doubles as a git host" [3][18].
- You get a GitHub-style page: issues (kind 1621), PRs (1618/1619), patches (1617), file browser — "except there's no GitHub underneath" [3].
- "Claiming"/updating = publish a new 30617 (it's replaceable, keyed by its
dtag) or a 30618 state event for branch/tag updates [12].
(b) List an app
- Publish a kind 31990 handler-information event: which event kinds your app supports (
ktags), per-platform URLs [13]. - No submission queue or approval gate — self-announced. NostrHub's directory just indexes 31990/31989 events by kind [3][13].
- Community attaches ProtonDB-style ratings and comments; ratings are ordinary Nostr events, portable to any client that reads them, not locked to NostrHub [3].
(c) Propose/discuss a NIP (long-form)
- Write the proposal in Markdown, publish as a kind 30023 long-form article (same event kind NIP-23 defines for blog posts), with
title/summary/dtags [16][3]. - It appears under "custom NIPs" with a comment thread [1][3].
- Anyone in your configured expert set can attach a NIP-32 label event (kind 1985) approving it [3][17].
- Enough of your experts' approvals move it toward "official" — for you specifically. There is no single global official flag; ranking is computed per viewer from their own expert list [3].
Governance Model
Confidence: High — this section is built almost entirely from direct quotes in the 2.0 announcement [3].
- Ships with a default expert list: "fiatjaf, hodlbod, Vitor Pamplona, PABLOF7z, jb55, Kieran, mattn, and the rest." [3] Users can add or remove experts from any profile; experts display a shield badge [3].
- Approvals are ordinary NIP-32 label events (kind 1985) attached to a NIP's kind-30023 article [3][17] — not a database flag, not a merge commit.
- History, in the founder's own words: "That first NostrHub got covered in spam. Then ManiMe added web-of-trust scoring to it, which improved the quality, but it was still missing a way for a NIP to become 'official.'" [3]
- Spam control: your combined experts' follow-lists cap the visible community (reported default ~1,000 people, configurable) — "This is what kills the spam that drowned NostrHub 1.0." [3]
vs. github.com/nostr-protocol/nips: the canonical NIPs repo runs on ordinary git-maintainer authority — a small set of people (fiatjaf among them) merge pull requests, and "official" means "merged." NostrHub inverts that: anyone can publish a NIP-shaped event, nothing is ever merged or rejected centrally, and "official" is a number that recomputes per visitor depending on whose approvals they've chosen to weight. It's a discovery/ranking layer next to the git repo, not a replacement governance body for the protocol itself — the canonical spec-acceptance process still runs on GitHub.
Distribution & Discovery Value
Confidence: Medium — no traffic/audience figures were found for nostrhub.io in any source checked; treat the following as qualitative, not a growth metric.
NostrHub sits inside Soapbox's own "Toolbox" page alongside Shakespeare, MKStack, Ditto, and Nostrify [19] — its built-in audience is Soapbox's existing Nostr-developer readership, not a separate acquisition channel. Because every listing is an ordinary Nostr event, discovery is protocol-native in principle: any NIP-89-aware client can in theory surface the same kind 31990 handler event NostrHub indexes [13]. In practice, propagation is not automatic — Zapstore (Android APK store) and nostrapps.com run their own separate catalogs, and this research did not confirm they consume NostrHub's specific events [22]. A repo announced on NostrHub, by contrast, interoperates cleanly with other NIP-34 clients out of the box, because they all read the same kind 30617 event — confirmed by NostrHub's own repo being independently browsable on the competing client gitworkshop.dev [21].
Stack Relations
| Tool | Confirmed relation to NostrHub | Confidence |
|---|---|---|
| MKStack | NostrHub 2.0's own package.json is literally named "mkstack", and it imports @nostrify/nostrify + @nostrify/react — Soapbox scaffolded its own flagship dev-hub with its own AI app-builder template [10] | High (source-verified) |
| Nostrify | The underlying protocol/client framework NostrHub runs on [10][19] | High |
| Nostrbook | Sister tool: docs + MCP server for Nostr kinds/tags, aimed at AI agents rather than NostrHub's human-facing NIP explorer [19]; nostrbook.dev live-verified 2026-07-10 [25] | High for existence/purpose (live-checked); still no direct code link to NostrHub — see the MCP/agent addendum below |
| Shakespeare | Soapbox's AI website builder | Not confirmed — no evidence found that Shakespeare deploys to or reads from NostrHub specifically |
| Ditto | Soapbox's community-server product; "Ditto Relay" is marketed as "the relay infrastructure powering Ditto" [19], not stated to also power NostrHub | Not confirmed |
| Toybox | — | Not found in any source during this research |
Operating Model & Self-Hosting
- Repo: gitlab.com/soapbox-pub/nostrhub — public, re-created 2026-06-11 (the 2.0 rewrite), last activity 2026-07-07 [4]. Velocity re-checked 2026-07-10: 866 commits in the 29 days since the rebuild — roughly 30/day sustained, hotter than even Armada's ~18/day (ch. 01) [26]. This is Soapbox's most actively poured foundation right now, not a parked launch.
- License: not declared in the GitLab project's own metadata at time of check (empty license field) [4], despite the README's "open source" framing [5]. Don't assume a specific OSS license without checking again.
- Cost: no pricing or paid tier found on the blog, product page, or repo — free to use.
- Community program: none. The full soapbox.pub blog index (60 posts, checked 2026-07-10) contains no bounty, contest, or call-for-contributions post [27]. Engagement with this ecosystem is artifact-first — you ship events, repos, or skills into it (see the addendum below); there is no formal program to apply to.
- Self-hosting: architecturally plausible — public Vite/React/TypeScript repo,
.env.examplepresent (currently just a Plausible-analytics domain toggle) [11] — but no explicit self-hosting guide was found. Inferred, not documented. - Relay operator: not conclusively identified. NostrHub is a client over whatever relays are configured per-event (each repo's own
relaystag; GRASP hosts for git specifically [18]) — no evidence ties it to Soapbox's own Ditto Relay.
Comparison
| NostrHub | GitHub | npm | Product Hunt | gitworkshop.dev | |
|---|---|---|---|---|---|
| Primary unit | NIPs + apps + repos + community | Code repos | JS packages | Product launches | Git repos (NIP-34 only) |
| Hosting | Metadata layer over relays + GRASP git hosts | Centralized (Microsoft) | Centralized registry | Centralized | Same NIP-34/GRASP substrate as NostrHub |
| Discovery | Kind-indexed browse + per-viewer expert ranking | Stars, search, trending | Search, download counts | Daily upvote leaderboard | Repos feed, npub browsing |
| Governance | Configurable meritocracy, per-viewer [3] | Corporate ToS + maintainer merge rights | npm Inc. (GitHub-owned), single registry | Company-run | No formal layer — "first implementation of the NIP-34 draft" [20] |
| Distribution | None — metadata/discovery only, no artifact hosting | Releases, Packages, Actions | Package artifacts themselves | One-day traffic spike, not ongoing reference | None — same scope limit as NostrHub |
The gap worth flagging for any agent-tooling/package-distribution use case: NostrHub has no package-registry equivalent. Listing an app publishes metadata (a handler event), not a binary or a build artifact — you still need somewhere else (Shakespeare's own deploy target, a normal host, or Zapstore for Android) to actually distribute the thing.
Integration Points
| Connects to | Mechanism | Enables |
|---|---|---|
| Any NIP-07 extension (Alby, nos2x) | window.nostr (NIP-07) [14] | Safe sign-in, key never leaves the extension |
| Any NIP-46 bunker (nsec.app, Amber) | bunker://…, nostrconnect://… (NIP-46) [15] | Remote signing, key never touches NostrHub's runtime |
| Any Nostr relay | WebSocket relay protocol | Where every NIP/app/repo/comment event actually lives — NostrHub is a lens over relays, not a silo |
Any NIP-34 git client (gitworkshop.dev, ngit CLI, gittr) | Shared kind 30617/1617/1618/1621 events [12] | A repo announced on NostrHub is clonable/browsable elsewhere without republishing — verified: NostrHub's own repo shows up on gitworkshop.dev [21] |
| Any NIP-89-aware client | kind 31989/31990 handler events [13] | An app listed on NostrHub is discoverable by other NIP-89-respecting clients in principle — not the same catalog as Zapstore's Android store |
| GRASP git host (ngit-relay, gitnostr.com, etc.) | GRASP — "Git Relays Authorized via Signed-Nostr Proofs" [18] | Actual git object storage + signed-push authorization behind a NostrHub-created repo |
| Soapbox's MKStack scaffold | Shared codebase lineage — package.json name "mkstack", @nostrify/* deps [10] | NostrHub is itself proof that an MKStack-built app is production-viable at Soapbox's own flagship-tool scale |
Addendum (2026-07-10): The Existing MCP / Agent Surface — and the Empty Slot
Delta pass, one day after the chapter's main verification. Confidence: High — each surface below was checked live on 2026-07-10.
| Surface | What it is | Posture toward outside contributors |
|---|---|---|
| nostrbook.dev | Docs site plus MCP server for NIP/kind/tag documentation — an agent queries protocol docs over MCP [25] | Read-only reference; ch. 01 inventories it, live-confirmed this pass |
| soapbox-pub/nostr-skills (GitHub) | Claude/agent skill files for Nostr development, CC-BY-SA-4.0 [28] | Explicitly invites community contributions — the one explicit, standing invitation to outsiders found this pass |
| soapbox-pub/openclaw-skills (GitHub) | Companion skills repo for OpenClaw agents (OpenClaw itself is independent, not a Soapbox product — ch. 02) [29] | Same CC-BY-SA-4.0 skills family (ch. 01's license table already groups them) |
The split that matters: everything above reads; nothing acts. Nostrbook serves docs to agents; the skills repos teach agents conventions. No MCP server was found — from Soapbox or anyone else — that performs NostrHub's actual verbs for an agent: publish a kind 31990 handler announcement (NIP-89 app listing), push to a kind 30617 repo (NIP-34), or attach a kind 1985 quality rating (NIP-32). That niche came back empty across every surface checked this pass — Soapbox's repo inventory, the toolbox, the 60-post blog index, and both skills repos [25][26][27][28].
INFERRED, practical read: this is the highest-leverage contribution slot this manual has surfaced. The event kinds are fully documented (this chapter + ch. 06); MKStack already ships a nostr MCP server for building apps (ch. 10) but nothing for publishing into the hub; and the skills repos prove Soapbox welcomes agent-tooling contributions and even name the content-license norm (CC-BY-SA-4.0 — code licensing is murkier, ch. 10). An "acting" NostrHub MCP server would make every flow in Publishing Workflows above scriptable by any MCP-capable agent — and since no bounty or contest program exists to enter (Operating Model above), shipping exactly this kind of artifact is the engagement mechanism.
Open Questions
- Is the DVM marketplace (beta, present in the June 2025 v1 [1]) still live in the June 2026 2.0 rewrite? Not mentioned once in the 2.0 announcement [3]; nostrhub.io is a client-rendered SPA that direct fetches can't confirm either way.
- Exact default "expert" count: one summarized source said "12"; the primary blog quote itself names seven and trails off with "and the rest" [3] — precise number unresolved.
- No OSS license file was confirmed despite repeated "open source" framing in Soapbox's own copy [4][5].
- No hard traffic or registered-developer figures found for nostrhub.io anywhere in this pass.
- Whether Shakespeare or Ditto integrate with NostrHub beyond sharing a Toolbox page — not found.
- NostrHub's own default/hardcoded relay set (beyond per-event relay tags) — not found in accessible config.
- Whether Zapstore's app-listing event kind is the same NIP-89 kind 31990 NostrHub indexes, or a separate proprietary kind — Zapstore's own NIP doc page 404'd during this research; unconfirmed.
Sources
- soapbox.pub/blog/announcing-nostrhub/ — "NostrHub: NIPs, Apps, and Repos on Nostr," June 10, 2025. Accessed 2026-07-09. Supports: v1 feature set, DVM marketplace (beta), NIP-72 community space.
- soapbox.pub/tools/nostrhub/ — Product page. Accessed 2026-07-09. Supports: tagline, feature list, profile/reputation framing.
- soapbox.pub/blog/nostrhub-2 — "NostrHub 2.0: A Meritocracy for Building Nostr Together," Alex Gleason, June 28, 2026. Accessed 2026-07-09. Supports: governance model (all verbatim quotes), Plan Graph, GRASP publishing flow, GitHub comparison, spam history.
- gitlab.com/soapbox-pub/nostrhub + GitLab API project metadata. Accessed 2026-07-09. Supports: repo creation date (2026-06-11), last activity (2026-07-07), empty license field.
- gitlab.com/soapbox-pub/nostrhub/-/raw/main/README.md — Accessed 2026-07-09. Supports: "configurable meritocracy" framing, tech stack, "NostrHub doesn't tell you what's true..." quote.
- GitLab API repository tree,
src/components/auth/listing. Accessed 2026-07-09. Supports: existence ofAccountSwitcher.tsx,AuthDialog.tsx,LoginArea.tsx,QuickLoginDialog.tsx. - GitLab API repository tree, root and
src/listings. Accessed 2026-07-09. Supports: React/Vite/TypeScript project shape. - AuthDialog.tsx source, via GitLab raw API. Accessed 2026-07-09. Supports: nsec paste, bunker URI, key-file upload, new-account generation flow, exact UI copy/warnings.
- QuickLoginDialog.tsx source, via GitLab raw API. Accessed 2026-07-09. Supports: extension-first "Welcome back" login flow,
login.extension(). - package.json, via GitLab raw API. Accessed 2026-07-09. Supports: project named
"mkstack",@nostrify/nostrify,@nostrify/react,nostr-tools,isomorphic-gitdependencies. - .env.example, via GitLab raw API. Accessed 2026-07-09. Supports: Plausible Analytics config, no default relay found in this file.
- NIP-34 — git stuff. Accessed 2026-07-09. Supports: kind 30617/30618/1617/1618/1619/1621/1630-1633 definitions, tag structures, clone-URL format.
- NIP-89 — Recommended Application Handlers. Accessed 2026-07-09. Supports: kind 31989/31990 definitions,
k/d/atags, discovery workflow. - NIP-07 — window.nostr capability. Accessed 2026-07-09. Supports: extension signing model,
getPublicKey/signEvent. - NIP-46 — Nostr Connect / remote signing. Accessed 2026-07-09. Supports: bunker/client roles,
bunker:///nostrconnect://URI formats, kind 24133. - NIP-23 — Long-form Content and [nostrhub.io/kind/30023/ search index result]. Accessed 2026-07-09. Supports: kind 30023 definition; NostrHub's use of it for custom NIPs.
- NIP-32 — Labeling (via search index). Accessed 2026-07-09. Supports: kind 1985,
l/Ltags, distributed-moderation use case. - ngit.dev/grasp/ — "Grasp: Git Repositories Authorized via Signed-Nostr Proofs." Accessed 2026-07-09. Supports: GRASP definition, two-service model (Smart HTTP Git + Nostr relay), implementations (ngit-relay, gitnostr.com).
- soapbox.pub/toolbox. Accessed 2026-07-09. Supports: full Soapbox tool list, one-line descriptions, Ditto Relay/NostrHub/Nostrbook positioning.
- gitworkshop.dev and gitworkshop.dev/about (via search index). Accessed 2026-07-09. Supports: gitworkshop.dev's own description as "Decentralized Git," first NIP-34 draft implementation, pairing with
ngit. - gitworkshop.dev/[email protected]/nostrhub. Accessed 2026-07-09. Supports: NostrHub's own repo is independently browsable via a competing NIP-34 client, confirming cross-client interoperability.
- zapstore.dev and github.com/zapstore/zapstore (via search index). Accessed 2026-07-09. Supports: Zapstore's separate Android-APK-focused catalog model. Note: zapstore.dev/docs/nips/app/ returned HTTP 404 during this research — exact event-kind overlap with NIP-89 unconfirmed.
- nostrhub.io — direct fetch. Accessed 2026-07-09. Supports: tagline only ("Build Nostr Together") — site is a client-rendered SPA, body content not retrievable via direct fetch.
- Search-engine index results for
site:nostrhub.io(Google/Bing snippets covering/apps/,/01/,/kind/30023/,/EE). Accessed 2026-07-09. Supports: existence and URL patterns of the browse-by-kind and per-NIP page structure, used because the live SPA could not be fetched directly. - nostrbook.dev — direct fetch. Accessed 2026-07-10. Supports: Nostrbook live as a docs site + MCP server for NIP/kind/tag documentation; a read surface only — no publish/act capability found.
- GitLab API project metadata re-check for
soapbox-pub/nostrhub. Accessed 2026-07-10. Supports: 866 commits in the 29 days since the 2026-06-11 recreation. - soapbox.pub/blog — full post index, 60 posts. Accessed 2026-07-10. Supports: no bounty, contest, or call-for-contributions program anywhere in the blog's history; no announcement of a NostrHub-acting MCP server.
- github.com/soapbox-pub/nostr-skills — Accessed 2026-07-10. Supports: Claude/agent skills for Nostr development, CC-BY-SA-4.0, README explicitly inviting community contributions.
- github.com/soapbox-pub/openclaw-skills — Accessed 2026-07-10. Supports: companion OpenClaw agent-skills repo in the same CC-BY-SA-4.0 skills family.
06 — Nostr Foundations for Operators
This chapter is connective tissue: the protocol-level concepts underneath every Soapbox product a builder will touch — Ditto (community/company ops) [1], Agora (fundraising) [2], Shakespeare (AI-built sites) [3], NostrHub (publishing) [4]. Other chapters cover each product's verdict in depth; this one covers the NIPs (Nostr Implementation Possibilities) those products are built from, so decisions in one tool make sense in the others. Verified live against the nostr-protocol/nips repo on 2026-07-09 — the spec has moved since 2025 training data, notably several deprecations below.
Worked examples throughout this chapter (a company domain, an operator's day-1 checklist, an agent-dispatch pattern) use the maintainers' own setup — trespies.dev, the operator Cruz, the DojoGenesis agent stack — as concrete, real data instead of placeholders. Swap in your own domain, team, and orchestrator wherever you see them.
Amended: 2026-07-10 (v2 wave — NIP-62, NIP-55/17/29/42/77 currency, onchain zaps, signer additions, local-first caching).
1. Identity & Keys for a Company
Confidence: High — verified against NIP-01, NIP-05, NIP-07, NIP-46, NIP-49, NIP-55 source, plus GitHub PR history for key migration.
A Nostr identity is one keypair: a private key (nsec) and a public key (npub) — bech32-encoded, human-readable wrappers around a raw secp256k1 hex key [5][6]. The npub is the identity. There is no username/password, no account database, no "forgot password" flow. Whoever holds the nsec is the account, permanently, on every relay and client at once.
Why this matters more than it sounds like it should: there is no ratified key-rotation mechanism. DOCUMENTED: "there is no native, widely-adopted key rotation or recovery in Nostr. Your public key is your identity, so you cannot update or rotate it while keeping the same account" [7] — a 2023 migration proposal (NIP-41) never merged [8]. If a key leaks, recovery is social: publish a note from the old key pointing followers to a new one, and wait for re-follows. NIP-26 (delegated signing), once floated as a fix, is now deprecated — "unnecessary burden" [5]. Treat nsec loss as identity loss, not a support ticket.
Key custody decision tree
NIP-07 vs NIP-46 — the two signer models
| NIP-07 (browser extension) | NIP-46 (remote signer / bunker) | |
|---|---|---|
| What it is | window.nostr object a browser extension injects; sites call getPublicKey()/signEvent() [9] | Client and signer talk over relays via encrypted bunker:// or nostrconnect:// events (kind 24133); nsec never touches the client device [10] |
| Where nsec lives | In the browser extension's storage, on that one machine | In the signer app/daemon — can be a phone, a server, hardware |
| Tools | Alby (Lightning wallet + signer, heavier), nos2x (signer-only, minimal, by Nostr's creator) [11] | nsec.app (desktop + mobile, non-custodial) [12]; Amber (Android, keeps nsec segregated in one app, acts as a NIP-46 signer for other apps) [13] |
| Best for | Solo desktop use, one machine | Multi-device use, teams, mobile-only, or keeping the key off the everyday client entirely |
Rationale straight from the spec: "Private keys should be exposed to as few systems - apps, operating systems, devices - as possible as each system adds to the attack surface" [10].
NIP-55 — the third signer standard
NIP-07 and NIP-46 aren't the whole story. NIP-55 standardizes signing through Android's intent system: a client sends a sign request as an Android Intent, the signer app — not the requesting app — shows the approval prompt and returns the signature, so the private key never enters the requesting app's process [46]. Status is draft optional, the same maturity marker most working NIPs in this manual carry, and Amber is the reference implementation [13][46]. Soapbox's own key-management guidance draws the line explicitly: apps should implement NIP-07, NIP-55, and NIP-46 so a site or app can "request signatures... without ever seeing the private key itself" — and never fall back to asking for a raw nsec [47]. Treat that trio as the minimum bar for anything you build that touches Nostr keys.
Two signer tools worth knowing that are newer than most Nostr key-management writeups:
- Soapbox Signer (Chrome/Firefox extension, announced Dec 2025) — NIP-07, multi-identity (switch between several npubs from one extension), per-domain permission grants, both NIP-04 and NIP-44 encryption, and identity export as JSON/CSV for backup or migration [48].
- Divine's Keycast — a managed remote-signer service behind Divine, a Nostr-native short-video app (iOS + Android/Flutter): users create an account or import an nsec once, keys are encrypted server-side, and apps request signing via OAuth-style grants over a NIP-46 bunker URL — the pattern for "feels custodial, isn't underneath" signing [49].
NIP-05 — your name@domain identifier
NIP-05 maps [email protected] to a pubkey via a JSON file at /.well-known/nostr.json [14]:
{
"names": {
"cruz": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9"
},
"relays": {
"b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9": [
"wss://relay.example.com",
"wss://relay2.example.com"
]
}
}
Clients GET https://trespies.dev/.well-known/nostr.json?name=cruz and check the returned pubkey matches your profile's claimed pubkey [14]. Two rules that trip people up: the endpoint must not redirect, and the local-part is restricted to a-z0-9-_. [14]. Critically, this is identification, not verification — NIP-05 "is not intended to verify a user, but only to identify them" [14]; it proves domain control, nothing about the person.
Two paths for @trespies.dev:
- Static file — trespies.dev already exists as a hosted static site; dropping a
.well-known/nostr.jsonthere is a no-server, static-hosting-compatible change [14]. This is the practical path for TresPies today. - Ditto self-service — if community ops move to a self-hosted Ditto instance, Ditto grants NIP-05 self-service under its own domain, with admin approve/deny per request [15]. That's a different domain identity (e.g.
@community.trespies.dev) than the static root-domain file — decide which one is canonical before publishing both.
Org-account patterns
[INFERRED from NIP-46 + NIP-05 mechanics — no single spec source covers "how a company does this."] Nostr has no built-in org-account primitive; every event needs exactly one signing key. Two patterns cover most real usage:
| Pattern | How it works | Trade-off |
|---|---|---|
| Shared brand account | One npub (@trespies), key held in a bunker (self-hosted or nsec.app); staff get NIP-46 connection grants to request signatures, never see nsec | Single voice, single point of custody failure; revoking a departed teammate = revoking their bunker grant, not rotating the key |
| Personal + domain identity | Each teammate runs their own npub with [email protected] NIP-05; "company" is expressed via shared domain + mutual follows/mentions, not one account | No single custody bottleneck; brand presence is diffuse across people |
Most orgs blend both — a brand account for official posts, personal NIP-05 identities on the same domain for individuals. Pick the brand-account bunker's admin (who can grant/revoke signer access) before anyone posts as @trespies.
Day 1 with your new credentials — checklist
- Confirm your npub/nsec pair is the one you were issued; do not generate a second one by accident in a different client.
- Back up nsec offline immediately — encode it as
ncryptsec(NIP-49: scrypt-derived key, XChaCha20-Poly1305 encryption) with a strong password [16]; store the password in a password manager and the encrypted string somewhere offline (not in this repo, not in Slack). - Install a signer — NIP-07 extension (Alby or nos2x) for daily browser use; if you'll ever touch a second device, set up a NIP-46 bunker (nsec.app) now rather than exporting raw nsec later.
- Publish your profile — kind 0 event: name, about, picture, and (once a wallet exists — Section 4) a
lud16lightning address. - Set up NIP-05 — publish
.well-known/nostr.jsonon trespies.dev per the format above; add thenip05field to your kind 0. - Publish a relay list — kind 10002 (NIP-65), 2-4 write relays + a few read relays (Section 2).
- First post — a kind 1 note, to confirm the full round-trip: signer signs → event lands on your write relays → visible on njump.me or any client.
2. Relays
Confidence: High — NIP-65, NIP-42, NIP-77 fetched directly; NIndexedDB confirmed against its own source file (2026-07-10); relay ecosystem facts cross-checked across two sources.
A relay is a WebSocket server that stores and forwards events — dumb by design, no protocol-level authority. Clients choose which relays to trust. NIP-65 (Relay List Metadata) lets a user publish a kind 10002 event listing preferred relays, each optionally marked read or write [17].
"When downloading events from a user, clients SHOULD use the write relays of that user." [17]
That's the outbox model: publish to your write relays, look for someone's posts on their write relays, look for mentions of them on their read relays — a directed lookup instead of querying every relay for everything. Spec guidance: keep the list small, 2-4 relays per category [17].
NIP-42 (relay auth) and NIP-77 (sync efficiency)
Two more relay-level mechanics worth knowing before picking a relay mix. NIP-42 is ephemeral challenge-response authentication: the relay sends ["AUTH", <challenge>], the client signs a throwaway kind-22242 event binding that challenge to the relay URL and sends it back [50]. Relays use this to gate access — restrict publishing to a whitelist, limit a private-message subscription to just its participants, or require auth before serving any request at all [50]. It's how a paid relay actually enforces "paid" instead of just asking nicely.
NIP-77 (negentropy) solves a different problem: efficient re-sync. Instead of a client re-downloading everything or diffing by event ID, negentropy does range-based set reconciliation — client and relay exchange compact fingerprints of what they already have and transfer only the gap [51]. The protocol originated in Doug Hoyte's strfry relay before being written up as NIP-77 [52]; independent client-side implementations exist too, e.g. @nostr-dev-kit/sync, which reports 10-100x bandwidth reduction over plain REQ/EVENT sync once both sides are mostly caught up [52]. Caveat that matters operationally: merged into the NIPs repo is not the same as deployed — most relays don't implement NIP-77 yet, so treat it as a bonus when the relay on the other end supports it, not a default to build around.
| Relay type | Examples | Fit |
|---|---|---|
| Public/free | relay.damus.io, nos.lol [18] | Fine for personal reach, no guarantees, higher spam exposure |
| Paid | nostr.wine, nostr.land (~$5-10/mo) [18] | Spam filtering because posting costs something; worth it once discoverability matters |
| Self-hosted | Ditto (bundles its own relay) [1]; Relay Kit — "one install script to deploy and manage Nostr relays, Blossom servers, and nsite gateways" [19]; Stacks — Docker-based scaffolding for full Nostr infrastructure [19] | Full moderation control, own domain identity, operational burden is yours |
Local-first caching — NIndexedDB
Everything above is about which server-side relay to trust. There's a client-side complement: NIndexedDB, from @nostrify/indexeddb, is a first-party persistent browser cache that implements the same NStore interface as a relay or any other Nostrify storage — so it drops in as a swap, not a rewrite [60]. It supports the full Nostr filter set (ids, authors, kinds, single-letter tag filters, since/until, limit, and search), resolves supersession correctly (a newer replaceable/addressable event overwrites the old one in the cache, so queries never surface a stale kind-0 profile), and applies NIP-09 deletion requests on write [60]. When IndexedDB itself isn't available — Apple's Lockdown Mode disables it, along with Service Workers and WASM, as part of the exact threat model Lockdown Mode targets [61] — NIndexedDB degrades to a silent no-op (event() does nothing, query() returns empty) instead of throwing, so an app built against it doesn't crash under Lockdown, it just stops caching [60]. The rest of the NStore/NRelay interface family — signer swaps, moderation policies, the other storage backends — is Chapter 11's job; this is the one piece operators need now, because it's the practical answer to "does the app work offline, and does it degrade gracefully when it can't."
Moderation implication: content policy lives at the relay + client layer, not a platform layer (expanded in Section 6). Running your own relay is how TresPies enforces house rules on its own community — but nothing stops content being copied to other relays first. Deletion requests (NIP-09, master table) are, by default, a request — not a guarantee; NIP-62 (Section 6) exists for a stronger, MUST-honor version, but adoption is thin enough that NIP-09's advisory norm is still the safer planning assumption.
3. Event Model & Content Kinds
Confidence: High for kind mechanics (direct NIP fetches); Medium for "which tool uses which" (mix of docs + search-derived, current as of 2026-07-09; adoption claims for NIP-17/NIP-29 refreshed 2026-07-10).
Every Nostr action is the same JSON envelope — id, pubkey, created_at, kind, tags, content, sig [6] — differentiated only by kind. An operator mostly touches these (NIP numbers cited individually below; kind registry per [5]):
| Kind | NIP | Plain-English function | Soapbox-stack surface |
|---|---|---|---|
| 0 | 01 | Profile (name, about, picture, nip05, lud16) | Every product — this is your identity card |
| 1 | 10 | Short text note (the "tweet") | Ditto feed, general posting |
| 3 | 02 | Follow list | Ditto, any client's "who you follow" |
| 30023 | 23 | Long-form content (articles/blogs, Markdown) | NostrHub docs, Ditto long-form posts — company blog/policy posts |
| 4 | 04 (deprecated) | Encrypted DM — legacy | Avoid for new work |
| 14 / 15 | 17 (+59) | Private chat message / file message, gift-wrapped | Ditto DMs, general private messaging |
| 34550 / 1111 | 72 (unrecommended) | Reddit-style community + moderator-approved posts | Legacy clients (Amethyst, Nostrudel, Satellite) [21] — not Ditto's model |
| 9 / 39000-39002 | 29 | Discord-style relay-enforced groups | Target model for Ditto's in-progress "groups" feature [15]; live today in 0xChat, chachi.chat [21] |
| 30311 | 53 | Live streaming event | Ditto live streams — town halls, AMAs [20] |
| 31922-31925 | 52 | Calendar events | Community scheduling |
NIP-04 vs NIP-17 — currency verdict
NIP-04 is deprecated — "Encrypted Direct Message (superseded by NIP-17)" [5]. NIP-17 wraps a plain (unsigned) message in a seal (kind 13) then a gift wrap (kind 1059), per NIP-59 [22][23]. The privacy gain is real: "Participant identities, each message's real date and time, event kinds… are all hidden from the public" [22] — each gift wrap uses a random one-time key so relays can't correlate messages by sender, and timestamps are randomized per layer to defeat timing analysis [23]. NIP-04 leaked all of that — sender, recipient, and timing were public even though content was encrypted. Any new DM feature should be NIP-17, full stop.
Adoption has moved from "the spec says so" to "the clients actually ship it": Amethyst, Primal, Nostur, Damus, noStrudel, and Coracle all run NIP-17 as their primary DM protocol now, and Amethyst's newer work extends the same gift-wrap machinery to sealed private replies on regular notes, not just 1:1 DMs [54]. Mostro — the P2P Bitcoin/Lightning exchange built on Nostr — made the same move at the protocol level: Mostro Protocol v2 (mostro-core v0.13.0) replaced its old relay-routed order messaging with NIP-44-encrypted, gift-wrapped kind-14 messages bound to a per-trade key, so relays now see only encrypted envelopes instead of order/dispute/settlement metadata [55]. Two different products, same direction of travel.
Watch item, not a recommendation yet — and already more moved-on than expected: a parallel track for group E2EE exists, adapting MLS (Messaging Layer Security, the Signal-Protocol successor standardized as RFC 9420) for Nostr. This is exactly the kind of ground that shifts between verification passes: the original proposal, NIP-EE, is now itself marked unrecommended in the live NIPs repo, superseded by a separate, non-NIP-numbered spec called the Marmot Protocol [56]. Marmot is still experimental but already has real implementations moving — MDK (its Rust reference stack), marmot-ts, and apps including White Noise, Pika, and Vector [57]. Don't build on NIP-EE directly; if group E2EE becomes a real requirement, evaluate Marmot instead, and re-verify its status before committing to it.
NIP-72 vs NIP-29 — currency verdict
Sharper than expected: NIP-72 is now marked unrecommended in the core spec — "unrecommended: try NIP-29 instead" [24]. Steering has moved from moderator-approval (Reddit-style) toward relay-enforced membership (Discord-style), though both still have active client support today (NIP-72: Amethyst, Nostrudel, Satellite; NIP-29: 0xChat, chachi.chat, groups.nip29.com, and Flotilla — partially [21]). NIP-29 itself is still draft optional, the same maturity marker NIP-72 carries [21] — steering intent isn't the same as ratified stability. Ditto does neither exactly — it runs Mastodon-style server/domain moderation (admins, reports, NIP-05 self-service) today, with NIP-29-style groups on its roadmap [15]. Operator read: don't build new tooling against NIP-72; use Ditto's current server-level moderation if it fits, and wait for its NIP-29 groups for Discord-style team space. For a consumer app specifically: NIP-29's implementation list is real but still small and still drafting — not a universal-adoption bet yet, the way NIP-65 or NIP-17 now are.
4. Payments
Confidence: High for NIP-57/47/60/61 mechanics (direct spec fetches) and for kind 8333 onchain zaps (Ditto reference doc fetched directly, 2026-07-10); Medium for wallet-option currency (fast-moving market, verify before funding anything).
Zap flow (NIP-57)
A zap request (kind 9734) is signed by the sender but sent directly to the recipient's LNURL callback — it is not itself published to relays [25]. Once paid, the recipient's Lightning service publishes a zap receipt (kind 9735) to the relays named in the request [25] — that receipt is the public, verifiable "this got paid" record clients render as a zap.
Nostr Wallet Connect (NIP-47)
NWC lets an app pay/check-balance/list-transactions against a wallet without ever holding the wallet's keys — connection is a nostr+walletconnect:// URI (wallet pubkey + per-client secret + relay) [26]. The wallet service runs as an always-on process; the client stores only its own secret and talks to it over relays [26] — this is what lets you connect one Alby Hub wallet to several apps with separate, revocable, spend-limited grants per app.
Cashu / nutzaps (NIP-60 / NIP-61) — status: active, still maturing
Both are live, non-deprecated NIPs [5]. NIP-60 stores an encrypted "wallet in relays" — mint references and unspent ecash proofs as kind 17375/7375/7376/7374 events, so wallet state follows the user across apps [27]. NIP-61 "nutzaps" collapse payment and receipt into one object: a Cashu token locked to the recipient's pubkey (P2PK) is the payment and the proof, no separate receipt needed [28]. Newer and less battle-tested than NIP-57 zaps; promising, not yet default.
Onchain zaps (kind 8333) — a third payment mechanism
As of Ditto 2.12 (shipped, per Soapbox's own post, May 2026) there's a third way to move value on Nostr, alongside Lightning/NIP-57 and Cashu/NWC [58]: onchain zaps — direct on-chain Bitcoin transfers, using kind 8333, defined as "attestation that an on-chain Bitcoin transaction paid a Nostr event or profile" [59]. The mechanism leans on a property of Nostr keys that isn't obvious until pointed out: because both Nostr and Bitcoin use secp256k1, a Nostr public key can double directly as a Bitcoin address — no separate wallet setup for sender or receiver [58]. Ditto layers Silent Payments on top for privacy — deriving a fresh, unlinkable receiving address per payment from a single published key, so on-chain analysis can't trivially cluster a recipient's zaps together [58]. Practical cost, in the team's own words: "It was costing $0.24 to send a transaction. For most of its life the average Nostr transaction was about $0.02" [58] — an onchain zap costs noticeably more than a typical Lightning-based zap, a tradeoff the team argued over before shipping and judged acceptable once you're sending amounts larger than a tip; onchain fees also float with mempool conditions, so treat $0.24 as a snapshot, not a constant. For anyone building rather than just operating: this is the same territory MKStack's onchain-bitcoin skill covers (Chapter 12) — worth knowing it exists before reinventing it.
Practical wallet options (verify before moving real money)
| Option | Custody | Fit |
|---|---|---|
| Alby Hub | Self-custodial; runs on your choice of 6 Lightning backends (LDK, LND, Greenlight, Phoenixd, Breez SDK, or a Cashu mint) [29][30] | Best fit for a technical operator wanting one controlled node behind NWC |
| coinos | Lower-friction web wallet, Lightning + ecash | Fast start, less control |
| Primal Wallet | Built into the Primal client | Fine for personal zapping, not a business Lightning node |
| Cashu mints | Ecash — the mint is a custodial trust point even though the token is bearer-instrument-like | Good for small nutzap balances; don't park significant value at a single mint |
Alby's own guidance signals the direction: they're "phasing out Alby's shared [custodial] wallet" for Alby Hub [31] — self-custody is where the tooling is consolidating in 2026, not custodial web wallets.
Tax reality (flag, not advice)
Sats received for goods/services are ordinary income at fair market value in USD on the date received — for a sole proprietorship, Schedule C, subject to self-employment tax [32]. The $10,000 Form 8300 cash-reporting trigger technically applies to crypto too, though enforcement is currently paused pending final regulations [32]. Not tax advice — get an accountant before treating zaps as real revenue.
5. Files & Media — The Storage Substrate
Confidence: High — Blossom BUD structure and NIP-96 deprecation directly confirmed from source.
NIP-96 vs Blossom — currency verdict
Settled. NIP-96 (HTTP File Storage Integration) carries an explicit deprecation banner: "unrecommended: deprecated in favor of NIP-B7" [33] — NIP-B7 being Blossom, which was folded into the core NIPs index as an active spec [5]. Blossom won. Don't build new file-upload integrations against NIP-96.
How Blossom works
Blossom is an HTTP-endpoint spec for storing blobs addressed by their SHA256 hash, authorized using the uploader's Nostr keypair [34] — same file, same identity everywhere; upload/delete rights ride on Nostr auth, not a platform account. The spec is a stack of BUD (Blossom Upgrade Document) documents:
| BUD | Covers |
|---|---|
| BUD-01 | Fetching blobs (GET/HEAD) |
| BUD-02 | Uploading blobs |
| BUD-03 | User Server List — which servers a user prefers |
| BUD-04 | Server-to-server mirroring |
| BUD-05 | Media optimization/transformation |
| BUD-06 | Upload constraints |
| BUD-07 | Payment-gated storage |
| BUD-08 | Nostr metadata tags for files |
| BUD-09 | Blob reporting/moderation |
| BUD-10 | URI schema |
| BUD-11 | Nostr-based auth tokens |
| BUD-12 | Blob lifecycle (delete/list) |
Mirroring (BUD-04) is what makes this resilient: if a file's home server disappears, any client can pull it from another server in the uploader's published list [34] — same file, same hash, different host. Ditto's product materials describe its media layer as Blossom-integrated [1]; the specific server hostname surfaced in search wasn't independently confirmable this session (install-guide fetch 404'd) — see Open Questions.
nsite — static sites on Blossom
nsite serves a full static website from a Blossom-hosted blob set, with the path-to-file mapping published as Nostr events under the owner's npub — "a static site deployed under your npub, where the raw data is stored on blossom servers, and the mapping from path to sha256 of the content is stored on relays" [35]. Effect: a website with no traditional host — if one gateway blocks it, it resolves through another, because resolution is relay-based, not DNS/server-based [35]. Directly relevant if Shakespeare-built output [3] ever needs a censorship-resistant deployment target beyond a conventional host.
Media metadata in posts (NIP-92)
An imeta tag attaches structured metadata — MIME type, SHA256 hash, dimensions, blurhash, alt text — to a URL inside a kind 1 note's content [36], so clients can render previews and verify integrity before fetching.
File-tool maturity
| Tool | Maturity | Fit for company docs |
|---|---|---|
| Blossom (protocol) | Active, in core NIPs | The substrate — always relevant |
| Blossom Drive | Deprecated / unmaintained, "hasn't been updated in over a year" [37] | Skip |
| Bouquet | Current recommended successor for managing Blossom blobs [37] | Reasonable for ad hoc file management today |
| nsite tooling (nsyte, nsite-cli, and related CLIs) | Multiple active repos surfaced in search; individual maintenance status not independently verified this session [35] | Good for static-site deployment, not general document storage |
6. Trust & Safety
Confidence: High — NIP-56, NIP-32, NIP-51, NIP-09, NIP-62 fetched directly; NIP-62's named third-party implementations are REPORTED (search-derived), not all independently confirmed shipped.
Three separate primitives, each doing one job:
- NIP-56 Reporting — kind 1984 events flag a pubkey or note under one of seven categories: nudity, malware, profanity, illegal, spam, impersonation, other [38]. The spec explicitly warns relays against fully-automated moderation on report volume alone — "reports are easily manipulated"; trusted moderator judgment is the intended backstop [38].
- NIP-32 Labeling — kind 1985 events attach a namespaced label (
Ltag) and value (ltag) to an event, pubkey, relay, or topic [39]. Broader than moderation — also used for content classification and licensing — but it's the primitive a moderation dashboard would consume. - NIP-51 Mute Lists — kind 10000, a personal list of pubkeys/hashtags/words/threads a user doesn't want to see [40]; can hold private (encrypted) entries alongside public ones.
NIP-09 vs NIP-62 — currency verdict
Reporting, labeling, and muting (above) are signals — they don't remove anything by themselves. Actual deletion is a separate, smaller pair of primitives, and the two are not equivalent:
- NIP-09 (Event Deletion Request) — a client asks relays to drop an event by publishing a kind-5 request naming it. Advisory only: the NIP uses SHOULD language, relays are free to ignore it, and nothing stops a relay that already forwarded the event from having it live on elsewhere. This is the deletion behavior most of the ecosystem has today.
- NIP-62 (Request to Vanish) — a materially stronger primitive, merged into the spec February 19, 2025 [53]. Relays that implement it MUST fully delete every event from the requesting pubkey (not just one named event), MUST ensure deleted events aren't re-broadcast, and — the clause with real teeth — paid or access-restricted relays MUST honor the request regardless of the requester's account status [53]: a relay can't hold data hostage by banning the account first. Requests can be targeted (naming specific relay URLs) or global (
["relay", "ALL_RELAYS"], broadcast as widely as possible) [53]. NIP-62 also SHOULDs relays into deleting NIP-59 gift wraps addressed to the pubkey — a vanish request can reach into someone's DMs, not just their public notes [53].
Adoption is thin. NIP-62 is real and legally meaningful wherever "right to be forgotten" regimes apply, but implementation is still early: rust-nostr has shipped it across all three of its storage backends (LMDB, SQLite, in-memory); the nostrcheck-server relay module and the Nestr client both show it as planned/in-progress per project trackers, not confirmed shipped [53]. Ditto's own support wasn't confirmed either way this session — verify directly before promising a user (or a regulator) that "vanish" actually works on your relay. Practical read: NIP-09 is still the safe default assumption for "will this actually disappear"; reach for NIP-62 when a deletion promise needs to be a MUST, not a SHOULD, and confirm relay-by-relay support before relying on it.
Why moderation is relay + client level, not platform level: no central authority can remove content network-wide. A relay operator (Ditto, in TresPies' case) decides what their relay stores, and clients decide what they render — reports and labels are signals those parties act on, not enforcement with teeth beyond one relay's boundary [15]. Practical consequence: you fully control your own relay's front door for events you're willing to purge on request — NIP-62 gives that control real force, NIP-09 gives it a polite ask — but neither can make content vanish once it has propagated to a relay outside your control.
7. Discovery & App Wiring
Confidence: Medium — NIP-89/34 mechanics are High (direct fetch); the DVM ecosystem's current health is a mix of DOCUMENTED spec status and REPORTED community activity.
NIP-89 (Recommended Application Handlers) is Nostr's "open in app" mechanism: apps publish kind 31990 advertising which event kinds they handle; users or contacts publish kind 31989 recommending a handler [41]. Same mechanism DVM marketplaces use for provider discovery (below).
NIP-34 (git over Nostr) replaces GitHub's centralized model with relay-published repo announcements (kind 30617), patches (kind 1617), and issues (kind 1621) [42] — not in the Soapbox stack directly, but a pattern any team could use for censorship-resistant code collaboration.
NIP-05 as lightweight SSO: since a NIP-05 identifier cryptographically ties a domain to a pubkey [14], "Login with Nostr" flows ask a user to sign a challenge event and check it resolves to the claimed name@domain — no password, no OAuth provider. Any app you build (via Shakespeare or otherwise) can use this for low-friction auth.
DVMs (NIP-90) — worked example: the agent-dispatch angle
Currency verdict: the base spec is deprecated. Its own file warns: "this got totally out of control, prefer use-case-specific microstandards" [43], unrecommended throughout. No single named successor — but the pattern moved rather than died: a dedicated nostr-protocol/data-vending-machines repo maintains the kind registry (5000-5999 requests → 6000-6999 results → 7000 status) as narrow per-job "microstandards" [44]. Discovery still runs through NIP-89 — newer projects (e.g. Agentry) auto-discover DVM providers via kind 31990 [45].
Worked example — if you run an agent orchestrator (DojoGenesis Gateway, or any other agent-dispatch stack): DVMs are structurally the same shape as an internal job-dispatch queue (the maintainers' own is dojo_agent_dispatch) — post a job, providers compete, deliver, get paid in sats instead of an internal ledger. Real and live, worth scouting for any orchestrator looking for an open, cross-network job market — but the "unrecommended" base spec plus an informally-governed sister repo means treat it as an early probe, not a dependency.
8. Master NIP Reference Table
The protocol reference card for this manual. Status reflects the live nostr-protocol/nips repo as of 2026-07-09 [5], amended 2026-07-10 with NIP-62, NIP-55, NIP-42, NIP-77, and kind 8333 (onchain zaps) — see the Amended note at the top of the chapter. Individual NIPs are cited in the relevant prose sections above; this table is the consolidated index.
| NIP / Protocol | Function | Soapbox-stack surface | Operator relevance |
|---|---|---|---|
| NIP-01 | Core event format, keys, kind 0 profile | Every product | Foundation — read once, applies everywhere |
| NIP-02 | Follow list (kind 3) | All clients | Who your account follows |
| NIP-05 | name@domain identifier via .well-known/nostr.json | Static hosting (e.g. trespies.dev) or Ditto self-service | Sets up you@yourdomain; day-1 task |
| NIP-07 | Browser-extension signing (window.nostr) | Any web client, incl. Shakespeare-built sites | Alby or nos2x for daily desktop use |
| NIP-09 | Event deletion request | All clients | Advisory only — not guaranteed removal; see NIP-62 for the MUST-delete alternative |
| NIP-17 + NIP-59 | Private DMs via gift-wrap | Ditto DMs, general clients | Current standard — use this, not NIP-04 |
| NIP-19 | bech32 entities (npub/nsec/note/nevent) | Universal | How keys/events are shared as text |
| NIP-23 | Long-form content (kind 30023) | NostrHub docs, Ditto articles | Company blog posts, policy docs |
| NIP-26 | Delegated signing | Deprecated — none | Ignore; don't build on it |
| NIP-29 | Relay-based groups (Discord-style) | Roadmap for Ditto groups; live in 0xChat, chachi.chat, groups.nip29.com, Flotilla (partial) | Team/private space, once Ditto ships it — still draft-status itself |
| NIP-32 | Labeling (kind 1985) | Ditto moderation backend | Content classification signal |
| NIP-34 | Git over Nostr | Not in Soapbox stack directly | Optional pattern for censorship-resistant code collab |
| NIP-41 | Key migration | Unmerged proposal, stalled | Don't rely on it — use social recovery instead |
| NIP-42 | Relay authentication (ephemeral kind-22242 challenge-response) | Paid/whitelisted relays, DM-gating | Gates access — expect it wherever a relay charges or restricts |
| NIP-46 | Remote signing (bunker) | nsec.app, Amber, Divine's Keycast; any NIP-46-aware client | Team + multi-device signing without exposing nsec |
| NIP-47 | Nostr Wallet Connect | Alby Hub → any app | Connect one wallet to many apps, revocable per app |
| NIP-49 | ncryptsec password-encrypted key backup | Manual step, any client | How you back up nsec safely |
| NIP-51 | Lists (mute list, bookmarks, etc.) | Ditto, any client | Spam/abuse control (kind 10000 mute list) |
| NIP-52 | Calendar events | Community clients | Scheduling for company/community events |
| NIP-53 | Live streaming (kind 30311) | Ditto live streams | Town halls, AMAs |
| NIP-55 | Android signer app intents | Amber; any Android Nostr app | Third signing standard alongside NIP-07/NIP-46 — never request raw nsec |
| NIP-56 | Reporting (kind 1984) | Ditto moderation queue | Abuse-reporting pipeline |
| NIP-57 | Lightning zaps | Ditto tipping; likely Agora's payment signal (unconfirmed — see Open Questions) | Value-for-content, community support |
| NIP-60 | Cashu wallet (wallet-in-relays) | Emerging wallet layer | Portable ecash balance across apps — still maturing |
| NIP-61 | Nutzaps (payment = receipt) | Emerging | Newer than zaps; promising, not yet default |
| NIP-62 | Request to Vanish — MUST-delete, not advisory | Not confirmed in Ditto; rust-nostr, others (thin adoption) | Stronger deletion primitive than NIP-09; verify per-relay before relying on it |
| NIP-65 | Relay list metadata / outbox model | All clients | Relay strategy — where you publish, where others find you |
| NIP-72 | Communities (Reddit-style) | Unrecommended — legacy clients only | Understand it; don't build new work on it |
| NIP-77 | Negentropy sync (range-based set reconciliation) | strfry origin; @nostr-dev-kit/sync client-side | 10-100x sync bandwidth once both sides support it — most relays don't yet |
| NIP-89 | Recommended application handlers | Shakespeare-built apps, NostrHub app directory, DVM discovery | "Open in app" discovery; also how DVM providers get found |
| NIP-90 | Data Vending Machines | Not in Soapbox stack directly; relevant to any agent orchestrator | AI-agent job marketplace pattern — base spec deprecated, successor is a sister kind-registry repo |
| NIP-92 | Media attachment metadata (imeta) | Ditto media posts | Rich previews, integrity hashes on posted media |
| NIP-94 | File metadata event | Pairs with Blossom uploads | Describes an uploaded file as a Nostr event |
| NIP-96 | HTTP file storage | Deprecated — replaced by Blossom | Ignore; use Blossom |
| NIP-B7 (Blossom) | Blob storage protocol, hash-addressed | Ditto media layer, Shakespeare sites, nsite | The file-storage substrate under the whole stack |
| NIP-5A (nsite) | Static websites served from Blossom + relays | Alternative deploy target to conventional hosting | Censorship-resistant hosting option |
| Kind 8333 (no NIP number yet) | Onchain zap — Bitcoin tx as payment attestation | Ditto 2.12+ wallet | Third payment rail alongside Lightning zaps and Cashu; Silent Payments privacy option |
Open Questions
- Whether Agora emits actual NIP-57 zap receipts, or uses a simpler direct wallet-to-wallet Lightning flow with Nostr only for identity/comms — Agora's own materials describe "wallet-to-wallet" Bitcoin movement without confirming zap-event mechanics [2]; verify in the Agora-specific chapter.
- Whether Ditto's media pipeline specifically points at
blossom.ditto.pub— this hostname surfaced in preliminary search results but the primary Ditto self-hosting doc 404'd on direct fetch this session; the general claim "Ditto integrates with Blossom" is solid [1], the specific hostname is REPORTED, not independently confirmed. - Exact reason NIP-06 (mnemonic-seed key derivation) was deprecated — confirmed deprecated in the index [5], but the deprecation-note text itself wasn't fetched; doesn't change the operator recommendation (use NIP-49
ncryptsecfor backup) but worth closing out if seed-phrase import ever comes up in a signer's UI. - Whether Ditto's planned NIP-29 groups feature has shipped by the time an operator would adopt it — roadmap item as of the fetched blog post [15], not yet confirmed live.
- DVM ecosystem health beyond the spec-status question — Agentry and similar discovery projects surfaced in search but weren't independently verified for maturity beyond their own landing page [45]; treat any DVM integration as a probe, not a plan.
Sources
- Soapbox — Ditto product page, plus Announcing Ditto and Ditto Server Keypair docs — accessed 2026-07-09 — Ditto architecture (relay + web UI + Mastodon API), Blossom integration, self-hosted relay.
- Soapbox — Agora and Agora: Connecting Freedom Fighters to Uncensorable International Support — accessed 2026-07-09 — Agora product description, non-custodial wallet-to-wallet design, Nostr-as-comms-layer framing.
- Soapbox — Shakespeare and Announcing Shakespeare — accessed 2026-07-09 — AI website builder on Nostr, NSP model, AGPL license.
- Soapbox — NostrHub — accessed 2026-07-09 — NIP/app/repo discovery hub description.
- nostr-protocol/nips README (NIP index + kind registry) — accessed 2026-07-09 — full NIP list, deprecation status, complete event-kind registry.
- NIP-01: Basic protocol flow description — accessed 2026-07-09 — event structure, key basics, kind 0 definition.
- Nostr Key Management: Complete Guide — accessed 2026-07-09 — current-practice statement on key rotation absence.
- NIP-41: simple account migration (unmerged PR #829) — accessed 2026-07-09 — confirms migration proposal never ratified.
- NIP-07:
window.nostrcapability for web browsers — accessed 2026-07-09 — extension API, methods. - NIP-46: Nostr Remote Signing — accessed 2026-07-09 — bunker/nostrconnect flow, roles, rationale quote.
- Best Nostr Browser Extensions 2026 — comparison — accessed 2026-07-09 — Alby vs nos2x positioning.
- nsec.app — accessed 2026-07-09 — non-custodial key storage/remote signer product description.
- Amber (GitHub) — accessed 2026-07-09 — Android NIP-46 signer, supported client list.
- NIP-05: Mapping Nostr keys to DNS-based identifiers — accessed 2026-07-09 — exact
.well-known/nostr.jsonformat, verification flow, caveats. - Creating Curated Communities on Nostr with Ditto — accessed 2026-07-09 — NIP-05 self-service, moderation model, NIP-29 roadmap note.
- NIP-49: Private Key Encryption (
ncryptsec) — accessed 2026-07-09 — password-encrypted key backup spec. - NIP-65: Relay List Metadata — accessed 2026-07-09 — outbox model, read/write markers.
- How to Choose Nostr Relays — accessed 2026-07-09 — relay-mix recommendations, paid relay examples/pricing.
- Soapbox Toolbox — accessed 2026-07-09 — full Soapbox product inventory, Relay Kit / Stacks descriptions.
- NIP-53: Live Streaming and Spaces — accessed 2026-07-09 — kind 30311 structure.
- Comparing Nostr Group Implementations — Nostrbook — accessed 2026-07-09 — plain-English NIP-29 vs NIP-72 comparison, client support lists.
- NIP-17: Private Direct Messages — accessed 2026-07-09 — gift-wrap DM design, kind 14/15.
- NIP-59: Gift Wrap — accessed 2026-07-09 — rumor/seal/wrap layering, metadata protection.
- NIP-72: Moderated Communities — accessed 2026-07-09 — verbatim deprecation notice, original community model.
- NIP-57: Lightning Zaps — accessed 2026-07-09 — zap request/receipt flow.
- NIP-47: Nostr Wallet Connect — accessed 2026-07-09 — connection URI, methods, roles.
- NIP-60: Cashu Wallet — accessed 2026-07-09 — wallet-in-relays architecture, kind numbers.
- NIP-61: Nutzaps — accessed 2026-07-09 — payment-as-receipt design, kind 9321.
- Alby Hub — accessed 2026-07-09 — self-custodial wallet product description.
- The 6 different Lightning backends for Alby Hub — accessed 2026-07-09 — backend options (LDK, LND, Greenlight, Phoenixd, Breez SDK, Cashu mint).
- Embrace Alby Hub — phasing out Alby's shared wallet — accessed 2026-07-09 — custodial-to-self-custodial market shift, verbatim quote.
- IRS: Report digital asset income — accessed 2026-07-09 — tax treatment of crypto/digital-asset income, Form 8300 status.
- NIP-96: HTTP File Storage Integration — accessed 2026-07-09 — verbatim deprecation notice pointing to NIP-B7/Blossom.
- Blossom protocol README / BUD specs — accessed 2026-07-09 — blob addressing, BUD-01 through BUD-12 overview, mirroring.
- Nsite.info — Let them eat static — accessed 2026-07-09 — nsite mechanics, Blossom + relay resolution; also source for nsite CLI tooling landscape.
- NIP-92: Media Attachments Metadata (
imeta) — accessed 2026-07-09 — imeta tag fields. - blossom-drive (GitHub) — deprecation note pointing to Bouquet — accessed 2026-07-09 — file-manager tool status.
- NIP-56: Reporting — accessed 2026-07-09 — kind 1984 structure, report categories.
- NIP-32: Labeling — accessed 2026-07-09 — kind 1985 structure, namespaces, use cases.
- NIP-51: Lists — accessed 2026-07-09 — mute list and other standard list kinds.
- NIP-89: Recommended Application Handlers — accessed 2026-07-09 — kind 31989/31990 discovery mechanism.
- NIP-34: git stuff — accessed 2026-07-09 — repo/patch/issue event kinds.
- NIP-90: Data Vending Machines — accessed 2026-07-09 — verbatim deprecation notice, original job-marketplace design.
- nostr-protocol/data-vending-machines (kind registry repo) — accessed 2026-07-09 — successor governance model for DVM kinds.
- Nostr Agent Network — Agentry — accessed 2026-07-09 — example of DVM provider discovery via NIP-89.
Amendment pack sources (2026-07-10 v2 wave):
- NIP-55: Android Signer Application — accessed 2026-07-10 — status header (
draftoptional), Intent/Content Resolver/Web signing methods. - Managing Nostr Keys — Soapbox Blog — accessed 2026-07-10 — NIP-07/NIP-55/NIP-46 framing as the three signing standards apps should implement, Amber description.
- Announcing Soapbox Signer — accessed 2026-07-10 — Dec 7 2025 launch, Chrome/Firefox extension, multi-identity, per-domain permissions, NIP-04/NIP-44, JSON/CSV identity export.
- Divine — Open Source, Divine (GitHub org), Keycast (GitHub) — accessed 2026-07-10 — Nostr-native short-video app; Keycast as its OAuth-style NIP-46 remote-signer service; iOS App Store + Flutter Android build.
- NIP-42: Authentication of Clients to Relays — accessed 2026-07-10 — ephemeral kind-22242 challenge-response flow, access-gating use cases.
- NIP-77: Negentropy Syncing — accessed 2026-07-10 — range-based set reconciliation spec,
draftstatus. - @nostr-dev-kit/sync (npm), hoytech/negentropy — accessed 2026-07-10 — negentropy's origin in Doug Hoyte's strfry relay; 10-100x bandwidth-reduction claim; client-side NIP-77 implementation.
- NIP-62: Request to Vanish, merge PR #1256 — accessed 2026-07-10 — MUST-delete / no-rebroadcast / paid-relay language verbatim; merge date (2025-02-19) confirmed directly via repo commit history; third-party implementation status (rust-nostr, nostrcheck-server, Nestr) cross-checked via search — REPORTED, not all independently confirmed shipped.
- Amethyst (GitHub) — accessed 2026-07-10 — NIP-17 as primary DM protocol across major clients (Amethyst, Primal, Nostur, Damus, noStrudel, Coracle); gift-wrapped private-note replies — search-derived, current release cycle.
- Mostro (GitHub) — accessed 2026-07-10 — Mostro Protocol v2 / mostro-core v0.13.0 migration to NIP-44 gift-wrapped kind-14 trade messaging.
- NIP-EE: E2EE Messaging using MLS — accessed 2026-07-10 — status header (
finalunrecommendedoptional), verbatim supersession notice pointing to the Marmot Protocol. - Marmot Protocol (GitHub) — accessed 2026-07-10 — NIP-EE's successor; experimental status, named implementations (MDK,
marmot-ts, White Noise, Pika, Vector). - Onchain Zaps in Ditto — Soapbox Blog — accessed 2026-07-10 — May 2026 post; Ditto 2.12 wallet, Nostr-keys-as-Bitcoin-addresses mechanic, Silent Payments, verbatim $0.24-vs-$0.02 fee quote.
- Ditto Nostr Reference — accessed 2026-07-10 — kind 8333 definition ("attestation that an on-chain Bitcoin transaction paid a Nostr event or profile").
- NIndexedDB.ts (soapbox-pub/nostrify) — accessed 2026-07-10 — full source of
@nostrify/indexeddb'sNStoreimplementation: filter support, replaceable/addressable supersession, NIP-09 deletion-on-write, IndexedDB-unavailable no-op behavior. - About Lockdown Mode — Apple Support — accessed 2026-07-10 — corroborates that Lockdown Mode disables IndexedDB-dependent web functionality, the real-world trigger for
NIndexedDB's no-op path.
07 — Integration Playbook: Running (and Modding) the Stack
How TresPies/DojoGenesis actually operates on the Soapbox stack — four runbooks, one integration matrix, the build opportunities, and the TresPies modded-stack blueprint (steering direction, 2026-07-09).
Verified: 2026-07-09. Synthesized in the main thread from chapters 01–06 and 08–09 (each chapter carries its own numbered primary sources; this chapter cites chapters). Facts cross-checked across chapters; two contradictions found and resolved during assembly (Armada existence — ch. 02 corrected; Blossom Drive deprecation — ch. 02 aligned to ch. 06).
0. The operating rule that governs everything: the AGPL boundary
Soapbox apps are AGPL-3.0: a modded, client-facing deployment must publish its modifications (ch. 01). The rule: i18n/viz mods live in the open layer (good citizenship in a grant-funded ecosystem, and it's the build-in-public playbook anyway); client work and IP live in config/content/plugin layers on top, never in the fork; anything that must stay closed gets built on Nostrify (MIT, confirmed). Ch. 10 nuance: the MKStack template ships no license file at all — likely an oversight and boilerplate norms suggest reuse is expected, but get explicit terms from Soapbox before a closed commercial fork — and mkstack-nsp (the AI backend) is AGPLv3, so a self-hosted modified NSP owes its source to its users. Ratified 2026-07-09 — ADR: 0001-mod-layer-rule (the maintainers' companion decision record; not shipped with this public chapter set; internal: PIP-94 phase 1 closed); residual item: ask Soapbox for explicit MKStack-template terms before any closed commercial fork.
1. Decision summary
| Need | Use | Not (yet) | Chapter |
|---|---|---|---|
| Public branded community | Ditto (self-host or ditto.pub while piloting) | Armada (too young for public-facing) | 02 |
| Internal team chat, E2E | Armada pilot (low-stakes channels first) | Betting company ops on 0.x | 02 |
| Team file storage (binary/office assets) | Keep Drive/Dropbox | Blossom as a drive (no folders/ACLs/versions) | 02, 06 |
| Team knowledge (md + git + memory autosync) | Git-anchored via NIP-34/GRASP (§9.3.6) | Needing Drive for md-native work | 05, §9.3 |
| Fundraising rail (BTC-native, censorship-resistant) | Agora | Replacing GoFundMe for normie donors (no card checkout) | 03 |
| Client mockups at volume | Shakespeare, free tier / cheap BYOK | — | 04 |
| Production client sites + funnels | Shakespeare + external conversion stack | Client self-editing (no CMS) | 04 |
| Developer distribution / credibility | NostrHub 2.0 (repos, apps, articles) | Expecting it to be a growth channel | 05 |
| Bilingual layer | Fork Agora's i18next pattern + Lingo.dev | Waiting for a Nostr language NIP (none ratified) | 08 |
| Equity-data dashboards | MapLibre+PMTiles+ECharts, zero-server | Viz inside Ditto (no widget surface) or 30023 (no HTML) | 09 |
2. Day 1 with your credentials (do this before anything else)
Ch. 06 §1 has the full version with sources; the sequence:
- Back up the nsec offline — password-encrypt it as
ncryptsec(NIP-49). There is no reset and no ratified key rotation; losing it is identity loss. - Install a NIP-07 signer extension (Alby or nos2x) on your main machine. The key never touches websites.
- Stand up a NIP-46 bunker (nsec.app; Amber on Android) the moment a second device or teammate is involved.
- Publish your kind-0 profile (name, bio, avatar) from a real client.
- Claim NIP-05:
[email protected]= one static JSON athttps://yourdomain.com/.well-known/nostr.json(no redirects allowed). Minutes on existing infra. - Pick relays deliberately — 2–4 write relays + a few read (NIP-65 outbox), not twenty.
- Log into NostrHub with the extension (never the nsec-paste or key-file options it also offers — ch. 05).
- First publishes: a kind-1 note; then a kind-30023 long-form to test the publishing path.
- Decide the org-account pattern: brand account (shared via bunker) + personal accounts; document custody (who holds what, where the ncryptsec backups live).
- Wallet: connect via NWC (NIP-47) for zaps; treat received sats as accounting events from day 1 (ch. 06 §4 flags the tax reality).
3. Runbook A — Community and company ops
Shape: Ditto = the storefront community (public, branded, bridged); Armada = the back office pilot (E2E, internal). They are different products, not versions of one thing (ch. 02).
| Step | Ditto (public) | Armada (internal pilot) |
|---|---|---|
| 1. Stand up | Join flagship ditto.pub to learn the UX, then self-host: Deno + Postgres + Nginx/Certbot (real ops, not a $5 static VPS — ch. 02) | Open the web app from soapbox.pub/armada; Zapstore for Android; Electron for desktop |
| 2. Brand | Custom domain, themes (9 presets / 19 CSS tokens; beyond that = fork → AGPL layer) | Create server → channels/threads/roles |
| 3. Team | Staff accounts via bunker; moderation = Mastodon-style admin tools | Invite via Concord invites (gift-wrapped over public relays; no server to run) |
| 4. Reach | Mastodon-API apps (50+ clients), Mostr bridge → Fediverse, → Bluesky (~15 min lag) | Keep to low-stakes channels until audited; export nothing sensitive yet |
| 5. Media | Blossom uploads (100 MiB cap; 20 MiB on free servers), plaintext URLs | E2E-encrypted images/media in-channel |
Filesharing verdict stands: neither is a Drive replacement (no folders/ACLs/versioning; the one folder app, Blossom Drive, is deprecated — Bouquet manages blobs only). Keep Drive; use the stack for community media and E2E chat attachments.
Watch item: Ditto has no native DMs today (NIP-17 absent) and implements neither NIP-72 nor NIP-29 groups (roadmap). For private conversation, Armada is the answer; don't promise DM privacy on Ditto.
4. Runbook B — Funding on Agora
When Agora: BTC-native donor base, censorship-resistance matters, zero platform fees matter, or client is an activist/civic org comfortable with crypto. When not: normie donors (no card checkout), US 501(c)(3) needing tax receipts (anonymous Silent-Payment gifts can't get IRS-compliant receipts — ch. 03), or discovery-dependent campaigns.
- Campaign creation is permissionless — any Nostr keypair; "featured" is curated (WLC/HRF human-rights positioning).
- Donations flow wallet-to-wallet on-chain BTC (incl. BIP-352 Silent Payments). Agora never holds funds; $0 platform fee; donor pays network fee.
- Lightning zaps exist in Agora only as social tipping, a separate rail from campaign donations — don't conflate them in client explanations.
- Publish campaign updates on Nostr; donations are publicly auditable (blockchain + Nostr) — a transparency feature for community orgs.
- Off-ramp is manual: raised BTC → exchange (KYC reappears there) → USD; plan the volatility window.
- Embedding: none. Shakespeare sites link out to the campaign; no widget exists (ch. 03). Zap-goal (NIP-75) integration unconfirmed.
- White-label: AGPL fork-and-run is feasible (static React/Vite, Docker) for a client's own campaign platform — publishes your mods per §0.
5. Runbook C — Sites, funnels, mockups on Shakespeare
Mockup economics (the agency loop):
| Lane | Cost | Use for |
|---|---|---|
| Free Gemini Flash tier (promotional — verify live before quoting) | $0 | Volume first-pass mockups |
| BYO API key, cheap model (e.g. GLM-class) | Cents per mockup; Soapbox takes no cut | Iteration rounds |
| Local models (Ollama) | $0 after hardware | Offline/batch |
| Paid NSP (MKStack + Claude Sonnet) | Paid via Stripe/Lightning | The deal that's closing |
Funnel wiring — what's native vs external:
The trap: forms render with one prompt but there is no backend — where submissions land must be explicitly wired to an external service or they land nowhere (ch. 04). Payments are the inverse: Lightning/Cashu is genuinely native (Agora proves it in production); Stripe is the manual add.
Deploy paths: free *.shakespeare.wtf for mockups → export ZIP/git (including nostr:// git URLs) → production on Netlify/Vercel/Cloudflare/Pages, or fully decentralized via nsite + Blossom (Stacks CLI). Custom domains observed live but no documented runbook — budget discovery time. Client caveat: no CMS/visual editor; whoever builds it owns the edits (a retainer surface — position it as such to a client).
6. Runbook D — Publishing on NostrHub 2.0
- Log in via NIP-07 extension (§2).
- Repo: create/mirror → signs a kind-30617 announcement → lives on a GRASP host (relay+git) → issues/PRs/browsing on NostrHub; the same repo is clonable via gitworkshop.dev/ngit (interop verified live — ch. 05).
- App: self-announce a kind-31990 NIP-89 handler event — no submission queue; community ratings accrue.
- Article/docs: kind-30023 markdown; your chosen experts' NIP-32 approvals drive per-viewer ranking ("configurable meritocracy").
- Expectations: it is a credibility and discovery layer, not a growth channel — metadata only (no artifact hosting), no auto-propagation to Zapstore/nostrapps (list those separately).
7. Integration matrix
| From | To | Mechanism | Enables | Status |
|---|---|---|---|---|
| Any product | Identity | Nostr keypair + NIP-07/46 signer | One login everywhere | Live |
| Ditto | 50+ Mastodon apps | Mastodon API + bunker login | Mobile clients day 1 | Live |
| Ditto | Fediverse / Bluesky | Mostr bridge (+Bridgy Fed) | Cross-network reach | Live (lag) |
| Ditto/Armada | Blossom servers | BUD specs | Swappable, mirrorable media | Live |
| Armada | Public relays | Concord gift-wrap (NIP-59) | Serverless E2E ops | Early (0.x) |
| Shakespeare | MKStack | Generation target | Nostr-native apps incl. Recharts, zaps | Live |
| Shakespeare site | Agora | Link-out only | Campaign traffic | Live (no embed) |
| Shakespeare site | Beehiiv/ManyChat/Cal.com/Stripe | Prompted API wiring | Real conversion funnel | Manual |
| Shakespeare/repos | NostrHub | kind 30617 / 31990 / 30023 | Publishing + discovery | Live |
| NostrHub repo | gitworkshop/ngit | NIP-34 + GRASP | Clone/PR from any NIP-34 client | Verified |
| Any app | Wallets | NIP-57 zaps, NIP-47 NWC, Cashu 60/61 | Payments without processors | Live |
| Agent orchestrators | Nostr jobs | DVM pattern (kind registry; base NIP-90 deprecated) | Paid agent job market | Scout |
8. Build opportunities
- DVM-shaped agent dispatch — the data-vending-machine pattern (post job → providers compete → paid in sats) is structurally identical to an internal agent-dispatch queue like
dojo_agent_dispatch(ch. 06, worked example) — the maintainers use this shape today. A thin bridge would put any agent orchestrator on an open job market. Scout, not dependency. - Ditto bots via Mastodon API — the entire Mastodon bot/tooling ecosystem works against a Ditto instance; cheapest automation surface in the stack.
- MCP ↔ Nostr bridge — publish/read Nostr events as MCP tools (post, zap, fetch community signals); nothing found in the ecosystem occupying this niche.
- The bilingual mod layer itself (§9.2) — first-mover OSS contribution with obvious upstream demand: Soapbox's own builder UI has no Spanish (ch. 08).
- Concord early expertise — protocol is 26 days old (CORD-01–07); early implementers get standing in whatever it becomes.
9. The TresPies modded-stack blueprint (steering direction, 2026-07-09)
What follows is the maintainers' own integration plan, published as a worked example of wiring this stack into a real firm — adapt the shape, swap the specifics (a different niche, a different orchestrator, a different git-mirror target) for your own.
Operator direction, verbatim: "integrate this stack with cutting edge internationalization platforms and visualization platform components then we'll build a Tres Pies stack on the modded version and git anchor it all in Nostr, mirror it to Dojo Genesis Git." (internal: PIP-94). Phase gates below assume §0's mod-layer rule is ratified first.
9.0 AI + orchestration layer (operator-specified 2026-07-09)
Direction, verbatim: "built featuring my dojo orchestration system and the providers of your choice: enterprise or TresPies or windsurf or cursor or whatever or Local: OpenRouter or Dojo Genesis."
The build engine is Dojo orchestration (Gateway agent-dispatch + Claude Code sessions) driving generation against MKStack's AGENTS.md agent contract — 348 lines at the template root, shipping with .mcp.json (Claude Code's own MCP config), opencode.json, and 19 skill files (ch. 10; note NIP.md is a different artifact — the NSP wire protocol in mkstack-nsp). Shakespeare becomes one optional front-end among several, not the required lane. All lanes target the same scaffold:
| Lane | Driven by | Models from | When |
|---|---|---|---|
| Enterprise | Claude Code / direct API | Anthropic enterprise (or the client's own) | Client work with contract/compliance requirements |
| TresPies-hosted | Dojo Gateway dispatch | TresPies keys | House builds, POCs, batch generation |
| IDE agents | Windsurf / Cursor / "whatever" | Their provider config | Operator-in-the-loop editing sessions |
| Local / routed | Any agent | OpenRouter or DojoGenesis Gateway (incl. local models) | Cost-floor mockup volume, offline work |
Rule: no lane lock-in. The scaffold + AGENTS.md contract must build identically from every lane — the zero-lock-in founder veto applied to our own tooling. Ch. 10 confirms it: a raw clone is Claude Code-ready out of the box (AGENTS.md + .mcp.json + skills), local lanes impose no model provider, and only hosted Shakespeare and Stacks/Dork touch a Soapbox-rostered model list. The one unbuilt extension point: a Dojo-Gateway AI_PROVIDER adapter for a self-hosted mkstack-nsp (only OpenAI/OpenRouter adapters exist today — and a self-hosted NSP is AGPLv3, so modified server source must be published).
9.1 i18n integration (from ch. 08)
- Runtime: keep i18next/react-i18next — already proven inside this stack by Agora (16 locales, es at 99.96% parity). Fork Agora's
src/i18n.tspattern into the Ditto/Armada-derived base rather than importing a foreign architecture. - Automation: layer Lingo.dev (Apache-2.0, CI-native AI-MT with glossary/brand-voice control) for machine passes; TresPies native-Spanish review stays the human gate.
- Gate: port the NS-style symmetric parity check into CI — Agora's existing gate fails only on extra keys, not missing ones; tighten it.
- Nostr layer: no ratified language NIP (Gleason's own NIP-37 closed unmerged; PR #1127 open) — adopt the de facto NIP-32 labels
["L","ISO-639-1"] ["l","es","ISO-639-1"]for bilingual content now. - Contribution play: Ditto and Armada have literally zero i18n today; Shakespeare's UI has no Spanish. A clean es layer is a visible, welcomed upstream contribution (and marketing).
9.2 Visualization integration (from ch. 09)
- Spec-layer rule: framework-agnostic engines (ECharts, MapLibre GL) bridge the React stack and Svelte house surfaces; framework-bound charts route per surface (Recharts inside Shakespeare/MKStack — it ships in the scaffold; LayerChart/unovis in Svelte dashboards).
- The Atlas pattern (zero-server): ACS/TIGER → Tippecanoe → PMTiles + Parquet on static hosting → MapLibre choropleths + DuckDB-WASM drill-down → bilingual labels via §9.1 hooks. No origin server anywhere; deployable to Pages/R2 or nsite/Blossom.
- Tripwire: PMTiles needs HTTP range requests; Blossom's spec only recommends them — verify per Blossom server or default to Cloudflare R2 (documented Protomaps path).
- Position: no Nostr+census precedent found — an Atlas on this substrate is first-of-kind for the maintainers' §1557/equity-data niche (swap in your own domain — the zero-server pattern doesn't care what the data is).
9.3 Git-anchor + mirror runbook
- Repo canonical home: GRASP host (relay + git server) with a kind-30617 announcement signed by the TresPies/DojoGenesis key (per repo).
- Add the GitHub mirror:
git remote add github [email protected]:DojoGenesis/<repo>.git—gh auth switch --user DojoGenesisfirst (existing guard, enforced by hook). - Push discipline: anchor push (ngit/GRASP) then mirror push; CI or a post-push hook keeps them in lockstep.
nostr://remotes are already supported by Shakespeare's git export (ch. 04). - Issues/PRs: NIP-34 events — workable from NostrHub or gitworkshop.dev interchangeably (verified interop, ch. 05).
- What stays off the anchor: anything under IP gate or client NDA — Nostr announcements are public and permanent; private repos stay GitHub-only until the maintainers are ready to publish them (their standing practice for moving in-progress internal work into the open).
- The knowledge layer rides this rail (operator call, 2026-07-09): because the operating model keeps knowledge as markdown in git (manuals, notes, ADRs, the memory system with local autosync), git-over-Nostr IS the internal file layer for knowledge work — anchored, mirrored, versioned. This narrows §1's "keep Drive" verdict to binary/office assets only (client spreadsheets, design files, video); md-native ops need no Drive.
9.4 Sequencing (30/60/90) and tripwires
| Window | Moves | Gate |
|---|---|---|
| 0–30d | Day-1 credentials (§2) · NIP-05 on your domain · Shakespeare mockup lane in client workflow · Armada internal pilot (low-stakes) · ratify mod-layer rule | Phase 1 (operator; internal: PIP-94) |
| 30–60d | i18n fork: Agora pattern → Ditto base + Lingo.dev CI + symmetric gate · first repo git-anchored + DojoGenesis-mirrored · NostrHub presence (repo + article) | Parity gate green |
| 60–90d | Atlas-pattern reference dashboard (PMTiles static) · Ditto community soft launch (bridged) · evaluate Agora campaign for a real client cause · upstream the es layer | First-of-kind demo live |
Tripwires: Armada security audit status (none yet — recheck before widening pilot) · Shakespeare free-tier lapse (promotional, no end date) · Soapbox grant renewals (their existential risk, ch. 01) · Blossom range-request support (per-server) · Concord protocol churn (already v1→v2 in 26 days).
10. Consolidated open questions
Highest-value unknowns across chapters (each chapter lists its own in full): Armada arbitrary-file attachments and audit roadmap (02) · Agora NIP-75 zap-goal use and Forbes/AP coverage verification (03) · Shakespeare custom-domain runbook and free-tier durability (04) · NostrHub DVM-marketplace status in 2.0 and license file (05) · Agora zap-vs-donation edge mechanics (03/06) · whether Soapbox would accept an es upstream contribution PR promptly (08 — ask them directly; they're reachable on their own products).
08 — i18n Integration: Localization Platforms × the Soapbox Stack
Where a modded, bilingual TresPies fork would actually plug in — verified against the real Ditto, Armada, Agora, and Shakespeare/MKStack repos, not just their marketing pages.
The tool landscape and stack-readiness research below (§1, §2, §4, and the comparison table in §5) apply to any team evaluating this stack for localization. §3 and the adoption runbook in §5 are the maintainers' own integration plan for their bilingual fork — a worked example of wiring a parity gate into a real firm; adapt the shape, swap the specifics for your own.
Verified: 2026-07-09
Confidence: High for stack-readiness facts (all four products' actual GitLab repos fetched — package.json, locale files, router code, AGENTS.md — not inferred from docs). High for the Nostr language-tagging verdict (both proposal PRs fetched directly, states confirmed). Medium for the landscape/tool comparison (aggregated from current search results, not independent primary-source fetches of every vendor). Medium-Low for cost figures, which move fast and weren't independently re-verified per vendor.
1. The 2026 Landscape — Legacy TMS vs. AI/Agent-Native
The field has split into two generations: TMSes built for a human-translator queue, and a newer layer built to sit inside a git repo and a CI pipeline with an LLM doing the first pass.
| Tool | Category | License | Agent/CI-native? | What's distinctive |
|---|---|---|---|---|
| inlang / Paraglide-JS | Compiler-based i18n library + ecosystem (Fink editor, Sherlock VS Code extension) | MIT [1] | Yes — git-native message files, Sherlock gives inline IDE translation, Fink lets a translator edit without cloning | Compiles to tree-shaken per-message functions; up to 70% smaller i18n bundles than runtime libraries [1] |
| Lingo.dev | "Localization engineering platform" — compiler + CLI + CI Action | Apache-2.0 [2] | Yes, explicitly — GitHub Actions/GitLab CI/Bitbucket Pipelines trigger translation on every push, PRs come back with localized strings [2] | Stateful "localization engines": glossary + brand-voice rules + per-locale model chains + AI quality scoring, configured once [2] |
| Tolgee | Self-hostable TMS, developer-first | Apache-2.0 core; Enterprise features dual-licensed (ee/ dir) [3] | Partial — strong git/CI integration and in-context editing, but built around a hosted/self-hosted web app, not a pure CLI | In-context editing (click text on the live page to translate it); native React/Vue/Next SDKs [4] |
| Weblate | Self-hostable TMS, translator-first | GPL-3.0 | Partial — mature git integration (its core design), but workflow center-of-gravity is a translator UI, not agent-driven | Oldest and most battle-tested OSS TMS (2012); the safe, boring choice [4] |
| Crowdin / Lokalise / Phrase | Incumbent SaaS TMS | Proprietary | Increasingly — Crowdin now brands "AI-powered localization," 700+ integrations | Deepest integration catalogs (Figma, Slack, Jira); enterprise support; no self-host on the free tiers [4] |
| i18next / react-i18next | Runtime i18n library | MIT | Neutral — it's a library, not a pipeline; agent-nativeness depends on what you wire around it | Largest ecosystem, browser + Node, non-ICU key format; already running in this exact stack (§2) |
| FormatJS / react-intl | Runtime i18n library, ICU-strict | BSD-3 | Neutral | Built on the browser's native Intl API; best when translators already work in ICU MessageFormat [5] |
| LinguiJS | Compiler-based i18n library | MIT | Neutral, but compiler step fits a CI pipeline cleanly | Smallest bundle (~3KB core vs. i18next's ~8-20KB) [5]; PO-file extraction, co-located messages |
What's genuinely cutting-edge mid-2026: the AI-MT layer, not the runtime library. REPORTED across multiple vendor blogs: the current pattern is glossary + brand-voice-trained LLM + "quality-based routing" that scores each translated segment and only escalates the weak ones to a human editor, rather than a human reviewing every string [6]. Lingo.dev and Crowdin both ship this; inlang's ecosystem (Fink/Sherlock) is compiler-and-editor-native rather than AI-MT-native — it assumes a human or an external MT step fills the message files.
Confidence: Medium — landscape claims are aggregated from current comparison articles and vendor pages, not independently fetched primary text for every row.
2. Stack i18n Readiness — Repo-Verified
Fetched each product's actual GitLab tree, package.json, and (where present) locale files and routing code. This is the load-bearing section — no marketing-page claims below.
| Product | Repo | i18n library in package.json | Locales shipped | Spanish? | Evidence |
|---|---|---|---|---|---|
| Ditto | soapbox-pub/ditto | none | none | No | No i18n/intl/locale dependency among 155 deps; src/ has no locale directory [7] |
| Armada | soapbox-pub/armada | none | none | No | Same check, 99 deps, zero hits; repo genuinely skeletal (concord-v1/concord-v2 protocol dirs, no locale dir) [8] |
| Agora | soapbox-pub/agora | i18next 26.0.5, react-i18next 17.0.4, i18next-browser-languagedetector 8.2.1 | 16: en, es, ar, fa, fr, hi, id, km, ps, pt, ru, sn, sw, tr, zh, zh-Hant | Yes — 2,401 of 2,402 keys (99.96%) | src/i18n.ts, src/locales/*.json fetched directly; keys diffed programmatically [9] |
| Shakespeare (builder UI) | soapbox-pub/shakespeare | i18next 25.5.2, react-i18next 15.7.3 | 6: en, ha (Hausa), ig (Igbo), pt, yo (Yoruba), zh | No | src/locales/ listed directly; 574 leaf keys in en.json [10] |
| MKStack template / scaffold | inferred from soapbox-pub/soapbox.pub (package name is literally "mkstack") + mkstack-nsp's NIP.md spec | none | none | No | Zero i18n deps in the marketing site's package.json; the 1,085-line "Nostr SPA Builder" spec that governs what gets scaffolded contains zero mentions of language/locale/i18n [11][12] |
Agora is the reference implementation, and it's better than a generic tutorial pattern. Its src/i18n.ts bundles only English eagerly (sync, no flash of untranslated content) and lazy-loads every other locale as its own Vite chunk on selection or detection — REPORTED as saving roughly 2MB off the initial bundle, DOCUMENTED directly in the code's own comments:
"Only English is bundled eagerly. Every other locale is loaded on demand via a dynamic import()" [9]
It also handles RTL languages (ar, fa, ps, +4 more) by flipping document.dir and relying on Tailwind's rtl: variant, and resolves region/script variants (pt-BR→pt, zh-TW/zh-HK→zh-Hant) to the right JSON chunk.
Agora already runs a parity gate — looser than the maintainers' own TresPies standard. Its AGENTS.md (written for AI coding agents working in the repo) states:
"
src/test/locales.test.tsfails the build if any locale ships a key that doesn't exist inen.json, but the inverse (a key missing from a non-English locale) is allowed and falls back to English at runtime" [13]
That's an asymmetric gate (blocks drift one direction, tolerates it the other) — looser than the maintainers' existing same-commit EN+ES parity rule. The same file also instructs agents to work the way the maintainers' own workspace already works:
"dispatch the per-language edits to subagents in parallel rather than translating fifteen files sequentially" [13]
Confidence: High — every figure above came from a direct fetch of the file in question, not a changelog or blog post.
3. Integration Architecture for a Modded TresPies Fork
Locale routing on static hosting — three options, one clear default:
| Pattern | How it works | Static-host fit | Verdict for this stack |
|---|---|---|---|
| Client-side detection, no URL segmentation | Browser Accept-Language + localStorage pick the locale; one bundle, one URL for every language | Perfect — this is what Agora ships today [9][14] | Recommended default. Zero extra deploy complexity, proven in-family |
Path-prefix routing (/es/...) | React Router locale segment; still one SPA bundle | Works if the host does SPA-fallback (serves index.html for unmatched paths) — true of Cloudflare Pages, Vercel, Netlify, and nsite/Blossom gateway resolution alike | Only worth it for SEO — a static Nostr SPA behind client-rendered content gets little SEO value from this today |
| Separate nsite deployment per locale | A named nsite (kind 35128, d tag = locale code) resolves to its own subdomain, independent of the root site (kind 15128 = npub-only) [15] | Fully supported — this is exactly what Agora's own nsiteSubdomain.ts implements for named sites in general [15] | Reserve for a fully independent censorship-resistant Spanish-language mirror, not routine bilingual support |
Typography/formatting for es: use the web-standard Intl API rather than hand-rolled formatting — Intl.DateTimeFormat('es', …), Intl.NumberFormat('es', …) (decimal comma, currency placement), and Intl.PluralRules('es') for the two-form (singular/plural) Spanish plural system, simpler than languages Agora already handles (Arabic has six plural forms). i18next's ICU/plural plugin and FormatJS both wrap this natively; nothing custom is needed. (Confidence: High — this is stable web-platform behavior, not a Soapbox-specific claim.)
On Shakespeare specifically — can you prompt it to generate a bilingual site? The MKStack scaffold has no i18n opinion baked in (§2), so yes, but the AI has to be told the pattern explicitly rather than reaching for a convention. INFERRED, practical recommendation: paste Agora's src/i18n.ts as a reference into the Shakespeare chat and ask it to replicate the lazy-load + detection pattern with en+es locale files — this reuses a pattern proven inside the same company's own product family rather than inventing one per generated site.
Confidence: Medium — the pipeline and routing recommendations synthesize DOCUMENTED repo evidence with INFERRED architectural judgment; flagged inline above.
4. Nostr-Layer i18n Gaps — No Ratified Standard
Verdict: there is no ratified NIP for natural-language content tagging. Two proposals exist; neither merged.
| Proposal | Author | Status | What it would have added |
|---|---|---|---|
| NIP-37 "Language Tag" | Alex Gleason — the Soapbox/Ditto founder himself [16] | Closed, unmerged, 2024-06-07 | A dedicated lang tag on text events |
| NIP-XXX "Internationalization and Localization" (PR #1127) | eznix86 | Still open, last activity 2026-03-04, unmerged | l-tag language marking plus a kind:0 (profile) language field |
| NIP-32 Labeling (ratified, general-purpose) | — | Ratified, in production use | The de facto working pattern: ["L","ISO-639-1"], ["l","en","ISO-639-1"] — a spec example states plainly, "Author is labeling their note language as English using ISO-639-1" [17] |
| NIP-50 Search | — | Ratified | Search-time filter only: language:<two-letter ISO 639-1 code> [18] |
| NIP-C0 Code Snippets | — | Ratified | Also uses an l tag — but for programming language ("javascript", "python"), a different namespace. Don't confuse the two l tags across NIPs [19] |
Even Soapbox's own founder couldn't get a dedicated language tag ratified. The reason it stalled, DOCUMENTED verbatim from the PR thread:
"I think users can't be trusted to specify this tag correctly and all automatic translation tools will already detect the language automatically anyway." [16]
Practical patterns for bilingual posting, given no standard: (a) dual posts — publish separate EN and ES events, each self-labeled via NIP-32's l/L tags, letting language-aware clients filter; (b) inline-both — one event containing both languages in the body, which sidesteps tagging entirely but degrades UX and can't be filtered; (c) per-language accounts — separate npubs per language, cleanest for filtering but fragments the identity (see the org-account tension already documented in chapter 06 [20]). What clients actually render: most Nostr clients show the raw event content regardless of any l tag present — language tags are a filtering/search signal, not a rendering instruction, so a bilingual post still displays as one undifferentiated block unless the client explicitly builds a language switcher around it. No evidence found of any major client (Ditto included) doing that today.
Confidence: High on the ratification status (both PRs fetched directly, states unambiguous); Medium on "what clients actually render," which is a general-knowledge claim not independently verified against every client's source in this session.
5. Recommendation
| Platform | License | OSS/AGPL fork fit | Agent/CI automatable | EN↔ES quality + glossary | Static-host fit | Cost @ small team |
|---|---|---|---|---|---|---|
| i18next + react-i18next | MIT | Excellent — already MIT, already living inside an AGPL-3.0 app (Agora) [9] | Neutral (library, not a pipeline) | Depends entirely on what feeds it | Excellent — proven in this exact stack | Free (library only) |
| Lingo.dev | Apache-2.0 | Excellent — Apache-2.0 is GPLv3/AGPLv3-compatible [2] | Best-in-class — CLI + CI Action is the point [2] | Strong — glossary + brand-voice + quality scoring built in [2] | Compiler targets Vite/Next; framework-agnostic CLI works with any static build | Usage-based; free/OSS tier for the CLI itself |
| inlang / Paraglide-JS | MIT | Excellent | Good — Sherlock/Fink are git-native, but no AI-MT layer of its own | None built-in — bring your own MT | Excellent — compiles to a static bundle by design | Free (self-hosted message files) |
| Tolgee | Apache-2.0 core | Good — core is Apache-2.0; EE features are dual-licensed [3] | Good — strong git/CI hooks, but centered on a hosted app | Good — in-context editing aids human review | Good | Free self-host tier (≤10 seats) [3] |
| Weblate | GPL-3.0 | Good — copyleft-native, philosophically closest to AGPL | Fair — mature git integration, translator-UI center of gravity | Good, human-review-first | Good | Free (self-hosted) |
| Crowdin | Proprietary | Poor — can't live inside a published AGPL fork | Good — many integrations | Strong — mature AI-MT + QA | Good | No self-host on entry tiers |
| LinguiJS | MIT | Excellent | Good — compiler step fits CI | None built-in | Excellent — smallest bundle | Free |
Ranked recommendation: don't replace the runtime — extend it. i18next/react-i18next stays (it's already MIT, already proven inside an AGPL-3.0 Soapbox product, and copying Agora's src/i18n.ts into Ditto/Armada is near-zero migration cost). Add Lingo.dev as the AI-MT + CI automation layer on top — it's the one tool on this list built specifically to sit in a CI pipeline with a glossary and brand-voice config, which is exactly the "cutting-edge" piece the current stack is missing. Skip a new TMS platform for now — tightening Agora's existing (asymmetric) locales.test.ts into a symmetric same-commit gate gets the maintainers' existing discipline without adopting Tolgee/Weblate's operational overhead; revisit if the team scales past ad hoc PR review.
5-step adoption runbook:
- Fork Agora's
src/i18n.ts+src/locales/en.jsonpattern into the Ditto/Armada-derived TresPies base — proven, in-family, zero new dependencies to evaluate. - Stand up Lingo.dev: one
i18n.jsonconfig pointing atsrc/locales/en.json, TresPies glossary + brand-voice rules configured once, CI Action wired to GitHub/GitLab so every push opens a translated-string PR. - Tighten the parity gate — extend the
locales.test.tspattern to fail the build on missing keys too, not just extra ones, matching the existing TresPies same-commit EN+ES rule. - Route every AI-MT'd PR through native-Spanish human review before merge — the existing TresPies review culture, unchanged; Lingo.dev's human-in-the-loop review surface can host this or a plain PR review works equally well.
- Deploy with client-side detection only (Agora's pattern) as the default; treat a separate nsite named-site (
d-tag =es) as an optional later add-on, not a day-one requirement.
Confidence: Medium-High — the runtime/parity-gate recommendation is grounded in verified repo evidence; the Lingo.dev pick is grounded in documented product capability but not independently load-tested by this research.
Open Questions
- Whether Lingo.dev's Vite compiler mode has been used in production with a Nostrify/
@nostrify/reactapp specifically — no direct evidence found either way, worth a small spike before committing. - Whether any Nostr client (Ditto or otherwise) actually reads NIP-32
l/Llanguage tags to drive a UI language filter today, versus the tag existing in the spec but going unused in practice — not independently verified against client source code this session. - Current status/adoption of eznix86's open i18n/l10n NIP proposal (#1127) beyond "still open as of 2026-03" — worth re-checking periodically since it's the only active path toward a ratified standard.
- Exact current pricing tiers for Lingo.dev at TresPies' likely usage volume — REPORTED as "usage-based" with an OSS/free CLI tier, but exact thresholds weren't independently priced out this session.
- Whether Shakespeare's AI model has actual training exposure to Agora's
src/i18n.ts(same company, public repo) versus needing the pattern pasted in manually every time — untested this session.
Sources
- inlang — Localization Tools; opral/paraglide-js (GitHub); Paraglide JS — inlang — accessed 2026-07-09 — MIT license, compiler architecture, bundle-size claim, Fink/Sherlock roles.
- Lingo.dev; lingodotdev/lingo.dev (GitHub); Lingo.dev CLI — How it works; Lingo.dev Compiler — Advanced Configuration — accessed 2026-07-09 — Apache-2.0 license, CLI/CI/compiler mechanics, glossary/brand-voice/quality-scoring "localization engine" model.
- Tolgee — Why open-source; Tolgee — Self-hosting Licensing; Tolgee EE License FAQ — accessed 2026-07-09 — Apache-2.0 core + dual-licensed EE features, free self-host seat limits.
- Open-Source TMS Comparison 2026: Weblate vs Tolgee vs Pontoon — IntlPull — accessed 2026-07-09 — Weblate/Tolgee positioning, Crowdin integration-count claim, git-vs-translator-UI framing.
- React i18n in 2026: react-intl vs i18next vs LinguiJS — auto18n; Best React i18n Libraries in 2026 — Tolgee — accessed 2026-07-09 — bundle-size comparison, extraction-method comparison, ICU-strictness framing.
- AI Localization in 2026: Engine, Review, Collaboration — Prismy; AI Localization: Automating Content Workflows — Crowdin Blog; How enterprise teams automate localization with AI — Smartling — accessed 2026-07-09 — glossary/brand-voice/quality-routing AI-MT workflow pattern.
- gitlab.com/soapbox-pub/ditto —
package.jsonandsrc/tree fetched directly via GitLab API 2026-07-09 — 155 deps, zero i18n-related packages, no locale directory; React 19.2.4/Vite 8/Tailwind/Nostrify confirmed. - gitlab.com/soapbox-pub/armada —
package.jsonandsrc/tree fetched directly 2026-07-09 — 99 deps, zero i18n packages,concord-v1/concord-v2protocol dirs present, no locale directory. - gitlab.com/soapbox-pub/agora —
package.json,src/i18n.ts, andsrc/locales/{en,es}.jsonfetched directly 2026-07-09 — i18next stack confirmed, 16 locales, 2,401/2,402 key parity computed programmatically, lazy-chunk architecture read from source comments. - gitlab.com/soapbox-pub/shakespeare —
package.jsonandsrc/locales/tree fetched directly 2026-07-09 — i18next stack confirmed, 6 locales (en/ha/ig/pt/yo/zh, no es), 574 leaf keys inen.json. - gitlab.com/soapbox-pub/soapbox.pub —
package.json(name:"mkstack") fetched directly 2026-07-09 — zero i18n deps, no locale directory insrc/. - gitlab.com/soapbox-pub/mkstack-nsp —
README.md,CONTEXT.md,NIP.mdfetched directly 2026-07-09 — NSP/Deno architecture,CLONE_URL-configurable base template, zero language/locale/i18n mentions across the 1,085-line spec. - gitlab.com/soapbox-pub/agora —
AGENTS.md— accessed 2026-07-09 — verbatim parity-gate behavior and agent-parallelization instruction for translation work. - gitlab.com/soapbox-pub/agora —
AppRouter.tsx— accessed 2026-07-09 — confirmed no path-prefix locale routing exists; only a/settings/languagesettings page. - gitlab.com/soapbox-pub/agora —
src/lib/nsiteSubdomain.ts— accessed 2026-07-09 — NIP-5A subdomain derivation for root (kind 15128) vs. named (kind 35128,d-tag) nsite events, corroborating chapter 06's nsite citation. - NIP-37: Language Tag (PR #632) — accessed 2026-07-09 — author Alex Gleason, closed unmerged 2024-06-07, verbatim fiatjaf objection quote.
- NIP-32: Labeling — accessed 2026-07-09 — verbatim ISO-639-1 language-labeling example.
- NIP-50: Search Capability — accessed 2026-07-09 —
language:<ISO 639-1>search filter extension. - NIP-C0: Code Snippets — accessed 2026-07-09 — confirms the
ltag here means programming language, a distinct namespace from natural-language tagging. - NIP-XXX: Internationalization and Localization (PR #1127) — accessed 2026-07-09 — author eznix86, still open, last activity 2026-03-04, unmerged; proposes
l-tag pluskind:0language field.
Visualization Integration — Charting and Mapping Across the Soapbox Stack
Where interactive charts and census-scale maps can actually live across Shakespeare, Ditto, Blossom/nsite, and Nostr long-form content — and the reference pattern for building an Atlas-class dashboard on top of it, with zero origin servers.
Verified: 2026-07-09
Confidence: High for the landscape survey, licensing, and the Shakespeare/MKStack embedding answer (a live dependency-manifest fetch is primary evidence). Medium-High for the static/Blossom-hosting and Nostr long-form answers (protocol specs are primary and quoted verbatim, but real-world server behavior varies). Medium for Ditto's embedding ceiling (no widget/plugin doc was found across two source sweeps — an absence, not a documented "no"). Medium for the Atlas reference architecture itself: every component is independently well-sourced, but no live Nostr-stack census/civic-data project was found to validate the combination end-to-end — this pattern is this chapter's synthesis, not a Soapbox playbook.
1. The 2026 Landscape
| Tool | Category | License | 2026 status | Fit note |
|---|---|---|---|---|
| Observable Framework + Plot | Static-site generator + grammar-of-graphics charting | ISC (D3 family) | Active; data loaders run at build time in any language and can shell out to binaries like DuckDB or ffmpeg [1][2][3] | Best match for a build-time, zero-server report/dashboard |
| D3 | Low-level rendering primitives | ISC | Foundational substrate — Plot, Vega, Nivo, and visx all sit on it [4] | Rarely hand-written now; used as a base layer |
| Apache ECharts | Full chart + geo + 3D framework | Apache 2.0 | Highest adoption of any JS-native chart lib by a wide margin; weekly npm downloads in the millions vs. Vega-Lite's low hundreds of thousands [5] | Broadest single-library feature set, including a native geo/map chart type |
| Vega-Lite | Declarative JSON chart grammar | BSD-3 | Active; strongest in Jupyter/Python (Altair) exploratory workflows [6] | Good AI-generation target (compact spec), weaker as a production UI polish layer |
| deck.gl + MapLibre GL JS | WebGL geo layers + vector-tile map engine | MIT (deck.gl) / BSD-3 (MapLibre) | deck.gl v9 lives under the OpenJS Foundation's Open Visualization space [7][9]; MapLibre is the Linux-Foundation-incubated fork of Mapbox GL JS from its Dec-2020 license change [10] | The map-heavy pairing; deck.gl's MapboxOverlay syncs its WebGL layers to MapLibre's camera exactly [7] |
| React layer — Recharts / visx / Nivo / Tremor | Chart components | All MIT | Recharts v3 is current; Tremor was fully open-sourced (incl. its paid "Blocks") when Vercel acquired it in Jan 2025 [11][12][13][41] | Recharts is also shadcn/ui's own official chart primitive [14] |
| Svelte — LayerChart / unovis | Chart components | MIT | Both confirmed Svelte-5-compatible [15][16] | Smaller ecosystem/training-data footprint than the React equivalents |
| evidence.dev | SQL + Markdown → static BI site | MIT | Active, DuckDB-powered, now ships its own in-browser AI dev agent [17] | Closest single-tool match to "Atlas as a reproducible, versioned report" |
| AI-assisted generation | Practice, not a library | n/a | LLMs default to Recharts or Chart.js when asked for a React chart [21]; Microsoft's Data Formulator 0.7 (2026) adds an iterative AI chart-authoring workspace [20]; MapLibre ships a dedicated agent-skills repo for AI coding tools [22][23] | Prompted generation is a first-class path now, not a novelty — see §5 |
Confidence: High — every cell above traces to a primary repo/license file or a 2026-dated comparison source.
2. Embedding in the Stack
The one fact that resolves most of this section: the MKStack template that Shakespeare scaffolds already depends on Recharts. A live fetch of its package.json shows React 19.2.5, Vite 8.0.10, Tailwind CSS v4.2.4, and Recharts v3.8.1 as a first-class dependency, alongside Radix UI, TanStack Query, and Nostrify [18]. A secondary write-up describes MKStack as "React 18.x... TailwindCSS 3.x" [43] — that's stale against the live manifest; treat any secondary MKStack description as provisional and re-fetch before quoting a client.
| Surface | Interactive charts/maps? | Mechanism | Ceiling |
|---|---|---|---|
| (a) Shakespeare-generated sites | Yes | Recharts is already in the dependency tree — prompt Shakespeare directly for a chart or dashboard page and it composes from an installed library, not a cold start [18] | Full client-side React chart; a live database view needs an external API wired in by prompt, same as any Shakespeare funnel gap (see this manual's Shakespeare chapter) |
| (b) Static/Blossom/nsite hosting | Yes, client-side only | Any JS chart/map library runs fine as static assets; maps need PMTiles (below) | No server-side rendering, no live query API, no request-time database — everything must be precomputed at build time or fetched from a separate hosted service |
| (c) Ditto communities | No native widget/embed surface found (inferred — absence across two doc sweeps, not a stated "no") | Ditto's own customization docs describe only theme CSS tokens (9 presets, 19 tokens) and "add new features... completely redesign the UI to fit your wants and dreams" via forking the React source with Shakespeare [28][29] | Getting a chart into Ditto means editing Ditto's own React+Vite frontend directly — not a drop-in widget slot |
| (d) Nostr long-form (kind 30023) | No | NIP-23 is explicit: "MUST NOT support adding HTML to Markdown" [24]. The only visual hook is the optional image tag, "a URL pointing to an image to be shown along with the title" [24][25] | A chart in a 30023 article can only be a static exported image, or prose linking out to a hosted interactive page — never an inline embed |
On (b), the fine print that matters for the Atlas pattern: nsite maps file paths to content via Nostr kind 34128 events (d tag = path, sha256 tag = content hash), storing raw bytes on Blossom servers; the spec is explicit that this is for "a simple application that doesn't use a backend" [26]. Blossom's own BUD-01 spec requires CORS — "Servers MUST set the Access-Control-Allow-Origin: * header on all responses" — but only recommends byte-range support: "servers should support range requests (RFC 7233 section 3)" [27]. That word "should" is load-bearing: PMTiles depends on range requests to work at all, so a given Blossom server may or may not serve a census-scale map efficiently — verify per-server before committing production tiles to nsite (see Open Questions).
Confidence: High for (a) and (d) — primary manifest and primary spec text. Medium-High for (b) — primary specs, but real-server range-support behavior is untested here. Medium for (c) — a documented absence, not a documented limit.
3. The Atlas Pattern — Reference Architecture
A census/ACS dashboard (Equity Atlas-class: county→tract drill-down, choropleth fill, bilingual labels) fits this stack only as a build-time-baked, fully static app. No component in the pipeline below needs an origin server at request time.
Build runbook:
- Pull ACS 5-year variables and TIGER/Line geometry (county, tract) via the Census API,
tidycensus/tigris(R), orcenpy(Python). - Join ACS attributes to geometry; export the attribute table only (no geometry) to Parquet for the DuckDB-WASM/pre-aggregation path.
- Convert geometry to GeoJSON, then to PMTiles with Tippecanoe (v2.17+ writes PMTiles directly) — e.g.
tippecanoe -zg -o tracts.pmtiles -l tracts tracts.geojson— tuning min/max zoom for the state→county→tract drill-down [30]. A documented precedent exists at this exact scale: a 650,000-plus-feature Texas census-block PMTiles build via Tippecanoe [31]. - Precompute heavy rollups (state/county summaries) as static JSON at build time — an Observable Framework data loader or a plain build script both work [1][2].
- Host PMTiles + JSON + Parquet as static assets. Cloudflare R2/Pages is the verified-safe default: Protomaps documents a direct Cloudflare integration [35], though a stock Cloudflare CDN path has been reported to corrupt PMTiles' byte-range responses unless served through a Worker or R2 directly [37] — configure CORS to allow the
rangeandif-matchheaders explicitly [36]. nsite+Blossom is reachable but its range-request support is a spec-level "should," not a guarantee (§2) — pilot it, don't default to it for production tiles. - Render with MapLibre GL JS plus the
pmtiles://protocol handler; add a choropleth fill layer keyed to the joined ACS variable. - Wire drill-down as client-side view state: zoom/tap swaps the active PMTiles zoom range and re-queries either the in-browser DuckDB-WASM Parquet or the pre-aggregated JSON for the side panel.
- Hook the bilingual layer at two boundaries only — the component string table, and the
Intl.NumberFormat/Intl.DateTimeFormatcall for chart ticks and tooltips — full locale-file strategy is the parallel i18n chapter's job, not this one's. - Ship the frontend as a static build — React via Shakespeare/MKStack, or a standalone Svelte 5 app — to Cloudflare Pages, GitHub Pages, or nsite. No origin server exists anywhere in this stack.
- Before first paint in production,
curl -Ithe tile host and confirmaccept-ranges: bytesandaccess-control-allow-originare actually present — MapLibre degrades silently (slow, not broken) when range requests aren't honored.
Confidence: High on each individual component (Tippecanoe, PMTiles, MapLibre, DuckDB-WASM are all independently well-documented). Medium on the architecture as a combination — no Nostr-stack precedent for census/civic-data work was found in this pass; TresPies would be first-of-kind here.
4. Framework Tension — React vs. Svelte 5
| Surface | Framework | Why |
|---|---|---|
| Shakespeare/MKStack-generated funnel or community site | React (follow the stack) | MKStack's scaffolding is React 19 + Vite + Recharts already installed [18]; hand-rewriting it in Svelte defeats the reason to use Shakespeare at all |
| Ditto community customization | React (follow the stack) | Ditto's own frontend is React 18+Vite (per this manual's Community Ops chapter); "beyond themes" customization means editing that codebase directly |
| Standalone dashboards (dash.trespies.dev, an Equity Atlas build) | Svelte 5 (house standard) | No Soapbox/MKStack dependency exists here — it's the maintainers' own infra and build, so their house Svelte 5 convention (documented in their own project-instructions file) applies without conflict |
| Shared spec layer across both | Framework-agnostic libraries | ECharts, Vega-Lite, Observable Plot, and MapLibre GL JS all render from a plain JS/JSON call, not a component tree — the same chart option object or map style can be wrapped by a thin React shell in a Shakespeare output and a thin Svelte shell in a standalone build |
Recommendation: treat ECharts (general charts) and MapLibre GL JS (maps) as the portable spec layer — write the chart option / map style once, wrap it twice. Use Recharts/shadcn charts only inside Shakespeare-generated React surfaces, where it's already a zero-cost dependency [14][18]. Use LayerChart/unovis only inside standalone Svelte 5 builds. Never hand-port Recharts JSX into Svelte or vice versa — re-point the same underlying option object instead; that's the actual reuse boundary, not the component code.
Confidence: High — grounded directly in the fetched MKStack manifest, this manual's own prior Ditto/Agora architecture findings, and the maintainers' own standing Svelte 5 convention.
5. Recommendation
| Option | Map-heavy equity-data fit | Static-host compatible | Bilingual labeling | AI/agent generatable | Bundle weight | License |
|---|---|---|---|---|---|---|
| MapLibre GL JS + PMTiles | High — purpose-built vector-tile map engine | High — confirmed GitHub Pages/R2 patterns [30][35] | Medium — labels are app strings, not lib-native | High — dedicated agent-skills repo exists [22][23] | Medium (~200KB core + separate tile files) | BSD-3 [10] |
| deck.gl | High — WebGL layers for large point/polygon counts | High — client-only, syncs to MapLibre's camera [7] | Medium — same as above | Medium — general WebGL/React knowledge, no dedicated skill repo found | Heavy (full WebGL2 stack) | MIT, OpenJS/vis.gl [9] |
| Apache ECharts | Medium-High — native geo/map chart type, less GIS-purpose-built than MapLibre | High — pure client JS, no build step required | Medium — locale config exists, set manually | High — huge training corpus, framework-agnostic | Medium (~1MB full; tree-shakeable) | Apache 2.0 [5] |
| Observable Plot / Framework | Medium — has a geo mark [32][33], not a full interactive map engine | High — Framework is a static-site generator [1] | Medium — build-time loaders can bake per-locale files | Medium-High — concise grammar is a good LLM target | Light (Plot) / build-tool (Framework) | ISC |
| Recharts (via shadcn/ui) | Low — no native geo support | High — pure client bundle | Medium — standard React i18n patterns apply | High — MKStack ships it by default; Claude/LLMs default to it for React asks [18][21] | Light (~150–290KB) | MIT |
| LayerChart / unovis (Svelte) | Low (LayerChart) / Medium (unovis has map primitives) | High — pure client bundle | Medium | Medium — smaller training-data footprint than React libs | Light | MIT |
| evidence.dev | Medium — SQL-first framework, not GIS-first | High for output; build step needs Node + a real DB connection | Low — not a localization-focused tool | Medium — ships its own AI dev agent in-IDE [17] | N/A (full framework, not a component) | MIT |
| DuckDB-WASM (data layer, not a chart lib) | Enabling — pairs with any option above | High — the mechanism that makes "static yet queryable" possible [38] | N/A | Medium | Heavy (~33MB wasm binary) [39] | MIT [40] |
Ranked pick:
- MapLibre GL JS + PMTiles — the map layer, non-negotiable for anything Atlas-shaped.
- Apache ECharts — the general chart layer: framework-agnostic, highest-adoption, one spec reusable across the React and Svelte shells in §4.
- Recharts (shadcn) inside Shakespeare/MKStack surfaces; LayerChart/unovis inside standalone Svelte 5 — not a single winner, a routing rule per §4.
Gap list — what still needs an external service:
- Address/geocoding lookups need the Census Geocoder API (or similar) called at request time — the one piece that can't be fully baked static.
- Range+CORS-verified tile hosting on Blossom/nsite specifically is unverified end-to-end (§2, §3) — Cloudflare R2/Pages is the proven fallback.
- The bilingual string/number pipeline itself is intentionally out of scope here — hook points only (§3, step 8); full design lives in the parallel i18n chapter.
- Any query workload that outgrows what DuckDB-WASM can hold client-side (multi-GB full-resolution microdata, not summary tables) needs a real backend or a hosted DuckDB/MotherDuck endpoint — the static pattern has a ceiling.
Confidence: Medium-High — license/adoption facts are High confidence; the equity-data-fit scoring is this chapter's judgment call, not a third-party benchmark.
Open Questions
- Whether any production Blossom server in the wild actually serves
Accept-Ranges: byteson blob GETs — BUD-01 only recommends it [27]; untested against a real nsite deployment this pass. - Whether Ditto has any embed/widget surface beyond CSS theme tokens that simply isn't publicly documented yet — two independent doc sweeps found none, which is evidence of absence, not proof of it.
- Exact current version numbers for Vega-Lite and Observable Framework's newest release notes beyond the ~1.13.x line observed on npm — worth a fresh check before a client-facing commitment.
- Whether MKStack's dependency set (React 19.2.5, Vite 8.0.10, Tailwind v4.2.4, Recharts v3.8.1, observed live 2026-07-09) has moved again by the time this is read — re-fetch
package.jsonfrom the GitLab repo rather than trusting this snapshot or any secondary blog description [18][43]. - No live example of a Nostr-stack census/civic-data dashboard was found — TresPies would be building the reference case, not following one.
Sources
- observablehq/framework — GitHub — static-site generator description, data-loader language support. Accessed 2026-07-09.
- Data loaders — Observable Framework — build-time data loaders, multi-language + binary invocation (DuckDB, ffmpeg). Accessed 2026-07-09.
- @observablehq/framework — npm — current release line (~1.13.x, ~2026-03). Accessed 2026-07-09.
- d3/d3 LICENSE — GitHub — ISC license confirmation. Accessed 2026-07-09.
- Best Apache ECharts Alternative In 2026 — LightningChart and echarts vs vega-lite — npm trends — Apache 2.0 license, relative weekly-download adoption. Accessed 2026-07-09.
- Vega and D3 — Vega docs — Vega-Lite BSD-3 license, relationship to D3. Accessed 2026-07-09.
- deck.gl homepage and Using with MapLibre — deck.gl docs — MapView/MapboxOverlay camera sync with MapLibre. Accessed 2026-07-09.
- Choropleths, Four Ways — Parker Ziegler — worked example of deck.gl choropleths on ACS 5-year California tract data. Accessed 2026-07-09.
- Open Source Data Visualization Project deck.gl v9 Released — OpenJS Foundation and visgl/deck.gl LICENSE — MIT license, OpenJS/vis.gl governance. Accessed 2026-07-09.
- maplibre/maplibre-gl-js — GitHub and LICENSE.txt — BSD-3 license, Linux Foundation incubation, fork lineage from Mapbox GL JS pre-Dec-2020. Accessed 2026-07-09.
- Recharts v3 vs Tremor vs Nivo: React Charts 2026 — PkgPulse — bundle-size and positioning comparison. Accessed 2026-07-09.
- Best React chart libraries in 2026 — LogRocket — bundle sizes, use-case framing for Recharts/Tremor/Nivo/visx. Accessed 2026-07-09.
- airbnb/visx — GitHub — MIT license, low-level D3+React primitives, adoption figures. Accessed 2026-07-09.
- Chart — shadcn/ui and Beautiful Charts & Graphs — shadcn/ui — official shadcn/ui chart components built directly on Recharts. Accessed 2026-07-09.
- techniq/layerchart — GitHub — Svelte 4/5-compatible composable chart components, MIT. Accessed 2026-07-09.
- f5/unovis — GitHub — multi-framework (incl. Svelte) viz framework, CSS-variable theming, MIT. Accessed 2026-07-09.
- evidence-dev/evidence — GitHub and Evidence Docs — MIT license, SQL+Markdown static BI site, DuckDB-powered, in-app AI agent. Accessed 2026-07-09.
- gitlab.com/soapbox-pub/mkstack — package.json (raw) — primary dependency manifest: React 19.2.5, Vite 8.0.10, Tailwind v4.2.4, Recharts v3.8.1, Radix UI, TanStack Query, Nostrify. Accessed 2026-07-09.
- MKStack — Soapbox — product framing, "AI-Powered Nostr App Framework." Accessed 2026-07-09.
- microsoft/data-formulator — GitHub and Data Formulator 0.7 — Microsoft Research — 2026 AI-native iterative chart-authoring tool. Accessed 2026-07-09.
- I Vibe-Coded a Complex Data Visualization Dashboard — Generative AI in the Newsroom — LLM default to Recharts/Chart.js when generating React chart code. Accessed 2026-07-09.
- maplibre/maplibre-agent-skills — GitHub — MIT-licensed, community-maintained AI coding-assistant skills for MapLibre. Accessed 2026-07-09.
- maplibre-pmtiles-patterns SKILL.md — GitHub — PMTiles generation/hosting/connection patterns for AI agents. Accessed 2026-07-09.
- NIP-23 — nostr-protocol/nips (raw) — primary spec text: HTML prohibition, optional
imagetag definition. Accessed 2026-07-09. - Kind 30023: Long-form Content — Nostrbook — secondary confirmation of kind 30023 structure. Accessed 2026-07-09.
- lez/nsite — GitHub README — kind 34128 path/hash mapping, "a simple application that doesn't use a backend," redundant frontend/relay/Blossom architecture. Accessed 2026-07-09.
- Blossom BUD-01 — hzrd149/blossom (raw) — primary spec text: range-request "should" language, CORS "MUST" requirement. Accessed 2026-07-09.
- soapbox-pub/ditto — GitHub — 9 theme presets, 19 CSS token properties, 100+ UI components. Accessed 2026-07-09.
- about.ditto.pub — customization docs: theme/profile personalization, Shakespeare-based deep customization framing ("completely redesign the UI"), no widget/embed architecture found. Accessed 2026-07-09.
- Creating PMTiles — Tippecanoe / Protomaps Docs — Tippecanoe v2.17+ direct PMTiles output, CLI workflow. Accessed 2026-07-09.
- Mapping 650,000+ Texas Census blocks with PMTiles — Walker Data — tigris + Tippecanoe pipeline at census-block scale, hosting notes. Accessed 2026-07-09.
- Geo mark — Observable Plot — GeoJSON/TopoJSON-based choropleth mark, Albers-USA projection example. Accessed 2026-07-09.
- Build your first choropleth map with Observable Plot — worked county-level choropleth tutorial. Accessed 2026-07-09.
- protomaps/basemaps — GitHub and LICENSE_DATA.md — BSD-3 code license, CC0 cartographic styling, OpenStreetMap ODbL data license ("Produced Works of the OpenStreetMap dataset under the Open Database License"). Accessed 2026-07-09.
- Cloudflare Integration — Protomaps Docs — documented PMTiles-on-Cloudflare hosting pattern. Accessed 2026-07-09.
- Cloudflare Community — R2 CORS for PMTiles — practical CORS/range header configuration for R2-hosted tiles. Accessed 2026-07-09.
- maplibre/demotiles Issue #35 — reported case of Cloudflare's CDN corrupting PMTiles range responses; Worker/R2-direct as the fix. Accessed 2026-07-09.
- Query — DuckDB Wasm docs — client-side SQL over remote Parquet via HTTP range requests. Accessed 2026-07-09.
- DuckDB Wasm: Analytical SQL Database in Your Browser — MotherDuck — in-browser analytics benefits, ~33MB wasm binary size note. Accessed 2026-07-09.
- Is DuckDB Open Source? Yes — the MIT License, Explained — Definite — MIT license, DuckDB Foundation perpetuity statute. Accessed 2026-07-09.
- Vercel acquires Tremor — Vercel Blog — Jan 2025 acquisition, Tremor Blocks made free/MIT. Accessed 2026-07-09.
- react-i18next / i18next Issue #1201 — confirms i18next has no built-in number/date formatting; Intl API or a date library is required alongside it — the i18n hook-point boundary referenced in §3. Accessed 2026-07-09.
- Using MKStack to Build Nostr Apps — PayPerQ Blog — secondary source describing MKStack as "React 18.x... TailwindCSS 3.x"; superseded by the primary manifest fetch [18] and flagged in Open Questions as a live-verify caution.
MKStack — The Framework Layer
The React/Vite/Tailwind scaffold every Nostr client on this stack forks from — and the piece that decides whether Claude Code, Cursor, Windsurf, or a custom Dojo-driven orchestrator can build here without Shakespeare, on any model provider.
Verified: 2026-07-09 · Delta pass: 2026-07-10 (NostrDeploy.com DNS tripwire; Stacks-platform relationship sharpened)
Amended: 2026-07-10 (v2 wave) — resolved .mcp.json concretely (nostr/js-dev packages + the five nostrbook tools, live-confirmed); cross-referenced new ch. 12 for the full 19-skill enumeration; catalogued all 17 Toybox experiments and disambiguated Chorus/Treasures/Blobbi source status; added the commit-pin + fetch-only-upstream pinning discipline, cross-ref ch. 15; flagged NostrHub's shared license-absence class with MKStack.
Confidence:
- High — scaffold anatomy, dependency versions, and the AGENTS.md/
.mcp.json/opencode.jsonagent-compatibility findings (all fetched directly from the live repo). Also high for mkstack-nsp's AGPLv3 license (full text fetched). - Medium-High — the mkstack template's license absence: well-evidenced (four 404s, no SPDX identifier, no GitLab-detected license, silent toolbox page), but Soapbox wasn't asked directly.
- Medium — Cursor/Windsurf compatibility: no dedicated rules file found for either; plausible but unconfirmed.
- Low — MKStack's exact public launch date: no dedicated announcement post found; repo-creation date used as proxy.
What It Is
MKStack is Soapbox's Nostr client scaffold: a React 19 + Vite + Tailwind v4 template pre-wired with Nostrify, shadcn/ui, and roughly twenty documented NIPs, at gitlab.com/soapbox-pub/mkstack [2], marketed at soapbox.pub/mkstack [1], product domain mkstack.xyz [2][13]. It is a template you fork, not a standalone CLI — the CLI that scaffolds it is a separate product, Stacks (@getstacks/stacks on npm) [16][17]. Get the direction of that relationship right: Stacks 1.0 (released 2025-08-20) is the generic template-sharing platform — templates publish as kind 30717 events and anyone can publish one [17][18] — and MKStack is just one published stack on it: Soapbox's own flagship stack, not the platform itself. Shakespeare is one client of a shared protocol, not MKStack's only front door.
| Fact | Value | Source |
|---|---|---|
| Canonical repo | gitlab.com/soapbox-pub/mkstack | [2][10] |
| Product domain | mkstack.xyz | [2][13] |
| Repo created | 2025-04-17 | [10] |
| Commits on main / branches / tags | 509 / 20 / 0 | [10] |
| Last activity | 2026-07-09 — today | [10] |
| GitLab stars / forks | 2 / 2 | [10] |
| GitHub mirror | None found (404 on both mkstack and mkstack-nsp) | [20] |
| package.json name/version | "mkstack", "0.0.0", private: true | [4] |
Timeline: mkstack created 2025-04-17 → mkstack-nsp (the NSP backend, AGPLv3) created 2025-06-23 [12] → Shakespeare announced/launched 2025-07-10/14 (ch. 04) → Stacks 1.0 released 2025-08-20 [18] → Shakespeare Act 2, 2025-10-01 (ch. 04) → both repos still live today, mkstack committed this same day [10][21].
Is Shakespeare just a hosted UI over MKStack? Yes, precisely. mkstack-nsp's own service-discovery event hardcodes the flow identifier "n": "https://shakespeare.diy/#nsp" [13] — direct evidence Shakespeare and MKStack's reference NSP share one wire protocol. Shakespeare, the Stacks CLI's agent command, and (in principle) any other client can all target the same NSP.
License — Flagged Loudly
This manual's ch. 01/07 call Soapbox frameworks "permissive," grouped with Nostrify (confirmed MIT). That does not hold for MKStack's own template repo — it ships no license file at all. Four filename variants 404 [11]; package.json has no license field [4]; GitLab's API detects none [10]; the toolbox page states none [19]. The README's only statement on the subject:
"📄 License: Open source - build amazing Nostr applications and help grow the decentralized web!" [3]
That is marketing language, not an SPDX grant.
| Component | License | Evidence |
|---|---|---|
| mkstack (the template you fork) | No LICENSE file found | 4× 404 on LICENSE/.md/.txt/COPYING [11]; no package.json field [4]; no GitLab-detected license [10] |
| mkstack-nsp (the AI-generation backend / NSP) | AGPLv3 — confirmed, full text present | Direct fetch: "GNU AFFERO GENERAL PUBLIC LICENSE, Version 3" verbatim [15] |
Nostrify (@nostrify/nostrify, a dependency) | MIT (established ch. 01) | Cross-reference; consistent with its use as a normal npm import [4] |
Practical read, not legal advice: forking the scaffold to start one project mirrors the unenforced norm around tools like create-vite — nobody expects a boilerplate's license silence to bind the app built from it, and ch. 04 already found Shakespeare-generated code carries no imposed license either.
Same license-absence class, same fix in flight: NostrHub. nostrhub.io's repo (gitlab.com/soapbox-pub/nostrhub) was created 2026-06-11 — a one-month-old "2.0" codebase — and shows the identical pattern: no license detected on direct fetch, 0 stars/forks per the GitLab-API sweep this manual's ch. 01 already ran [29][30]. This isn't a separate problem needing a separate email: the pending Soapbox license outreach (ADR 0001, still unsent as of this pass per the maintainers' companion production audit) already covers both mkstack and NostrHub in one ask — send one email, not two [30].
The sharper exposure is the NSP, not the template. Self-hosting a modified mkstack-nsp — e.g. wiring a Dojo Gateway adapter in place of its OpenAI/OpenRouter adapters [14] — trips AGPLv3's network-use clause: you'd owe that modified server's source to whoever uses it. Verify template-reuse terms with Soapbox before a commercial closed fork; treat any self-hosted NSP as AGPL-bound from day one.
Confidence: Medium-High — the absence is well-triangulated across four independent checks, but "no license file" was not confirmed by Soapbox saying so, only by its repeated absence.
Scaffold Anatomy
The live dependency manifest [4] contradicts the template's own README — a sharper version of ch. 09's secondary-source caution, except here it's the repo's own marketing copy that's stale against its own package.json and AGENTS.md:
README: "MKStack is an AI-powered framework for building Nostr applications with React 18.x, TailwindCSS 3.x, Vite, shadcn/ui, and Nostrify." [3]
AGENTS.md: "This project is a Nostr client application built with React 19.x, TailwindCSS 4.x, Vite, shadcn/ui, and Nostrify." [5]
AGENTS.md and the live manifest agree; the README does not. Trust the manifest.
| Layer | Package | Live version | Note |
|---|---|---|---|
| Framework | react / react-dom | 19.2.5 | README says "18.x" — stale [3][4] |
| Build | vite | 8.0.10 (devDep) | matches ch. 09 |
| Styling | tailwindcss / @tailwindcss/vite | 4.2.4 | README says "3.x" — stale |
| Components | shadcn/ui, unified radix-ui pkg | 48+ | not per-package @radix-ui/react-* [5] |
| Nostr | @nostrify/nostrify / react / nostr-tools | 0.52.2 / 0.6.2 / 2.23.3 | Nostrify is MIT |
| Data / routing | @tanstack/react-query / react-router-dom | 5.100.5 / 7.14.2 | catch-all /:nip19 route |
| Forms / charts | react-hook-form+zod / recharts | 7.74/4.3.6 / 3.8.1 | recharts reconfirms ch. 09 |
| Testing / types | vitest+RTL+jsdom / typescript+eslint | see [4] | gated in the test script |
The test script is a compound gate — tsc --noEmit && eslint --cache && vitest run … && vite build [4] — mirroring AGENTS.md's validate-before-commit order exactly [5].
NIP support out of the box — "50+ NIPs" is the marketing claim [1]; independently traced to a hook, component, or rule:
| NIP | Feature | Where in scaffold |
|---|---|---|
| 01/02/05 | Profiles, follows, verified IDs | useAuthor, NostrMetadata [3][5] |
| 07/46 | Extension signing / remote signer | LoginArea, AuthDialog, NostrLoginProvider [3][5] |
| 19 | npub/note/nevent/naddr/nsec | catch-all /:nip19 route, nip19-routing skill [5] |
| 17/44/18/25/23/28/29 | DMs, encryption, reposts, reactions, articles, chat | listed in README + skills [3][5] |
| 31 | alt tag on custom kinds | AGENTS.md rule, mandatory [5] |
| 47/57/60/61 | NWC / zaps / Cashu / nutzaps | useZaps hook, nwc skill [3][8] |
| 65 | Relay list / outbox | AppProvider+NostrSync, auto-publishes kind 10002 [5] |
| 94 + Blossom | File metadata + media storage | useUploadFile, file-uploads skill [5][8] |
The Agent Contract: AGENTS.md, Not NIP.md
Correction to this manual's own working assumption (ch. 07: "MKStack's NIP.md agent contract"): the file that actually governs what a coding agent does inside MKStack is AGENTS.md, not NIP.md. NIP.md is real, fetched in full below [13] — but it lives in a different repo (mkstack-nsp) and documents a wire protocol, not coding conventions. AGENTS.md, 348 lines, sits at the template repo's root [5] and names its own role directly:
"The assistant's behavior is defined by this file (AGENTS.md). Edit it directly to change guidelines — updates take effect the next session." [5]
| File | Lives in | What it is | Status |
|---|---|---|---|
AGENTS.md (348 ln) | mkstack template | Real system prompt: stack, structure, kind/tag rules, XSS-first security, validate-then-commit | DOCUMENTED, native to all [5] |
.mcp.json | mkstack template | Claude Code's own MCP config — wires nostr + js-dev servers | DOCUMENTED for Claude Code [6] |
opencode.json | mkstack template | OpenCode's own config, same nostr server | DOCUMENTED for OpenCode [7] |
.agents/skills/*/SKILL.md ×19 | mkstack template | Topic skills, YAML frontmatter | Claude-Skill-shaped format [8] |
NIP.md (1,085 ln) | mkstack-nsp — different repo | "Nostr SPA Builder" wire protocol, thin-client ↔ NSP RPC (kind 31999/25742/25743) | Not a coding contract [13] |
NIP.md (per-project) | generated in your fork | Documents custom kinds your app defines | Per AGENTS.md's own rule [5] |
.cursor/rules, .windsurfrules | — | Neither found in the tree | NOT independently confirmed |
.mcp.json resolved concretely (2026-07-10): the file wires exactly two stdio MCP servers, both launched via npx:
| Server | Package | Tools |
|---|---|---|
nostr | @nostrbook/mcp@latest | read_nip, read_kind, read_tag, read_protocol, read_nips_index — live-confirmed byte-identical to the tool set documented at nostrbook.dev/mcp [28] |
js-dev | @soapbox.pub/js-dev-mcp@latest | Not independently enumerated this pass |
Practical read: a Claude Code session in a fresh MKStack clone can query live NIP/kind/tag/protocol docs and the full NIPs index without leaving the editor — the same reference material this manual's own research draws on, available to any agent session directly.
NIP.md (mkstack-nsp) opens with:
"This document defines a Nostr Service Provider that generates, deploys, and makes updates to single-page web applications (SPAs) using AI." [13]
It specifies discovery (kind 31999), encrypted RPC (start_project, send_project_message, get_models… over kind 25742), and notifications (25743) [13] — relevant only if you're building your own NSP or thin client; irrelevant to a Claude Code session editing files locally.
Nineteen skills live under .agents/skills/ — testing, theming, encryption, relay-pools, security, nip19-routing, file-uploads, NWC, and eleven more [8] — each a SKILL.md with YAML frontmatter (name, description), structurally identical to Claude Code's own Skill format. AGENTS.md defers to them by name ("load the nostr-security skill") rather than inlining every detail [5]. This chapter fetched and confirmed only one skill file in full (testing/SKILL.md [8]) — ch. 12 (new) enumerates all 19 as the curriculum's definition-of-done checklist; treat that chapter, not this "eleven more," as the authoritative skill directory going forward.
Confidence: High for Claude Code and OpenCode (direct config files, unambiguous). Medium for the broader "any AGENTS.md-aware agent" claim — the convention exists and is used correctly here, but per-tool adoption wasn't verified.
Provider-Agnostic Lanes
The operator's question — can Claude Code, Cursor, Windsurf, or a Dojo-driven orchestrator build MKStack apps directly, on any model provider — resolves cleanly once the AGENTS.md/NIP.md split is clear. Four lanes exist; only two touch a Soapbox-controlled model roster.
| Lane | Driver | Model source | Deploy path | License exposure |
|---|---|---|---|---|
| Hosted Shakespeare | Dork, via Shakespeare's SPA-Builder client | Free Gemini / BYOK / local / paid ("shakespeare"/"tybalt" aliases) [13] | 1-click *.shakespeare.wtf, or export (ch. 04) | None on generated code |
| Stacks CLI + Dork | stacks agent, terminal | stacks configure: OpenRouter, Routstr, PayPerQ [3][17] | npm run deploy → Blossom + NostrDeploy.com — ⚠ tripwire, see Deploy + Distribution | No LICENSE file (§2) |
| Self-hosted NSP | Your server, CLONE_URL-pointed at any template | AI_PROVIDER=openai|openrouter today; 3rd adapter buildable [14] | Whatever your NSP implements | AGPLv3 — must publish server changes to users [15] |
| Local IDE/CLI agent | git clone + Claude Code / OpenCode / Cursor / Windsurf, reading AGENTS.md + .mcp.json | 100% yours — Dojo Gateway, Anthropic, OpenRouter, local Ollama, anything | GitHub Pages (shipped CI), Vercel/Netlify, or nsite | Same as Stacks lane; none from tooling |
INFERRED, practical read: the fourth lane is where Dojo orchestration slots in natively — Claude Code already speaks this repo's .mcp.json out of the box, and "any provider" is trivially true because MKStack imposes none on a local session. Only the third lane needs new code: mkstack-nsp documents exactly two adapters today (OpenAI, OpenRouter) [14]; a Dojo Gateway adapter would need to match one of those shapes, or be added as a third — unattempted by anyone found this pass.
Confidence: High for lanes 1, 2, and 4 (each directly documented). Medium for lane 3's Dojo-adapter feasibility — the extension point is real and documented, but untested by anyone found in this research pass.
Deploy + Distribution
| Path | Mechanism | Target | Evidence |
|---|---|---|---|
| Stacks / NostrDeploy | npm run deploy — script is added by the Stacks CLI scaffold; absent from a raw clone's package.json [4] | Blossom upload + relay-published pointer event, listed on NostrDeploy.com — ⚠ domain dead as of 2026-07-10, see tripwire below | [3][16][26] |
| GitHub Pages (shipped CI) | .github/workflows/deploy.yml, triggers on push to main | actions/deploy-pages, standard dist/ build | [9] |
| Anywhere static | Plain npm run build → dist/ | Vercel, Netlify, Cloudflare Pages, or nsite/Blossom manually (ch. 04 precedent) | [4][21] |
| Listing | NostrHub 31990 app-listing kind (ch. 05) + t:mkstack tag browse | nostrhub.io/apps/t/mkstack/ | [3] |
⚠ Tripwire (2026-07-10):
nostrdeploy.comdoes not resolve. A direct check hit DNS failure (ENOTFOUND) — the domain doesn't resolve at all, which reads as dead-or-broken domain, not a server outage [26]. Every mention of thenpm run deploy→ NostrDeploy lane in this manual (this table, the lanes table above, and ch. 04's decentralized-deploy route) is therefore unverifiable and possibly dead. Until the domain resolves again: don't promise this lane to a client, don't build a runbook on it, and re-check DNS before any workflow that assumes it. The GitHub Pages CI and plain-static paths are unaffected. One untested nuance: the Blossom+relay substrate underneath is content-addressed and may still work even if the NostrDeploy.com index/gateway is gone — that distinction has not been exercised.
A raw git clone gets a normal Vite project with no deploy script at all — npm run deploy is injected by the Stacks CLI during scaffolding, not shipped in the template's git history. A local agent lane adds its own step or rides the shipped GitHub Pages workflow. Custom-domain mechanics repeat ch. 04's open question — asserted [16] but no runbook found; verify live before promising a client one.
Extensibility for the Mod Plan
| Addition | Scaffold conflict? | Evidence |
|---|---|---|
| i18next (ch. 08's Agora-pattern fork) | None found — zero i18n deps today, no LocaleProvider in App.tsx's provider stack, AGENTS.md silent on locale | [4][5] — independently corroborates ch. 08 |
| Recharts | Already present, 3.8.1 | [4] — reconfirms ch. 09 |
| MapLibre GL / PMTiles | None found — zero map deps today; one flagged CSS gotcha (isolate + negative z-index, called out generically in the theming skill) applies to any full-bleed canvas | [4][5] |
Nothing in AGENTS.md's provider stack [5] structurally fights either addition — both are standard Vite/React npm installs. The one warning is procedural, not technical: "Never write over App.tsx, AppRouter.tsx, or NostrProvider without first reading their contents" [5] — read before wiring in a LocaleProvider or a map canvas.
Maturity + Community
| Signal | Value | Source |
|---|---|---|
| GitLab stars / forks | 2 / 2 | [10] |
| Commits / branches / tags | 509 / 20 / 0 | [10] |
| Last activity | 2026-07-09 — same day as this research | [10] |
| GitHub mirror | None (404) | [20] |
| Real apps built on it | Chorus (community organizing + Cashu wallet for activists), Blobbi (persistent virtual pet), Treasures (decentralized geocaching) | [3][24] |
| One-prompt demos (README) | Group chat app, "Bookstr" (Goodreads-alike via OpenLibrary API), chess with NIP-64 | [3] |
| Docs surface | 237-line README + 348-line AGENTS.md + 19 skill files + dedicated tutorial post | [3][5][8][16] |
Star counts read small by GitHub norms, but Soapbox operates GitLab-first and Nostr-native by design (ch. 01) — commit velocity and shipped apps are the more honest signal, and both are strong: 500+ commits in fifteen months, one landing again today. Zero tags means no formal release process — a rolling-main template, consistent with the "ship fast, trust-based" culture ch. 01 and ch. 04 already established.
Toybox vs. "real apps built on it," disambiguated (2026-07-10): the table row above names three MKStack-built apps from the README, but only Treasures is actually a Toybox entry. Soapbox's Toybox page (soapbox.pub/toybox) catalogues 17 experiments, live-reconfirmed this pass: Treasures, Birdstar, Surveil, Espy, Blobbi Island, Lief, Nests, Nostrdamus, Polaroids, Plektos, ZapTrax, Podstr, Color Slide, Relaying Earth, Clawstr, Zappix, Bookstr [27]. Chorus is a flagship product positioned outside Toybox entirely — it doesn't appear on the Toybox page at all [27] — and its source-code status, like Blobbi's, remains unconfirmed (no public repo located in this manual's research to date). Treasures is confirmed public source, and better than expected: it's third-party-built ("Built by Chad," gitlab.com/chad.curtis/treasures [27]) — an independent developer's real MKStack app, arguably a stronger teardown candidate than a Soapbox-authored one for proving the scaffold works for outside builders, not just its creators.
Pinning discipline, reconfirmed 2026-07-10: zero tags (still true, live-rechecked) means there is no version to pin to in the conventional sense — the house answer is a commit-SHA pin + a fetch-only upstream remote (push disabled), which makes git diff <pin> upstream/main a real, runnable command rather than a note nobody checks. This is now configured on trespies-stack; ch. 15 (new) covers the full per-component exit-playbook this discipline feeds, including when to re-pin.
Open Questions
- Whether Soapbox would explicitly license the mkstack template if asked directly — the absence may be oversight, not decision; no dedicated "Introducing MKStack" post was found either, unlike Shakespeare's.
- The exact runbook for a fully custom domain on either deploy path — mirrors ch. 04's identical open question.
- Whether Cursor or Windsurf have been used against this scaffold in practice — plausible, not documented the way Claude Code and OpenCode are.
- Whether anyone has built a third
AI_PROVIDERadapter for mkstack-nsp (Dojo-Gateway-shaped or otherwise) — not found this pass. - Whether the
"shakespeare"/"tybalt"aliases inget_modelsstill map to the Claude-Sonnet-4/free-Gemini tiers ch. 04 documented — not re-confirmed. - Whether NostrDeploy.com's DNS death (ENOTFOUND, 2026-07-10 [26]) is a transient misconfiguration or a discontinued service — and whether the Blossom+pointer-event substrate under
npm run deploystill works without the NostrDeploy.com gateway. Re-check before relying on the Stacks deploy lane. - Whether Chorus or Blobbi Island have public repos — not located this pass; if found, both become teardown candidates alongside Treasures.
- The full tool list for the
js-devMCP server (@soapbox.pub/js-dev-mcp@latest) — not independently enumerated this pass, unlike thenostrserver's five tools.
Sources
- MKStack — AI-Powered Nostr App Framework — product marketing page: "AI-first" framing, 50+ NIPs claim, Dork branding. Accessed 2026-07-09.
- gitlab.com/soapbox-pub/mkstack — repo homepage, description confirming
mkstack.xyzas the product domain. Accessed 2026-07-09. - gitlab.com/soapbox-pub/mkstack — README.md (raw) — full 237-line README: quick start, tech stack (stale versions), NIP list, real-world examples, License section. Accessed 2026-07-09.
- gitlab.com/soapbox-pub/mkstack — package.json (raw) — live dependency manifest: React 19.2.5, Vite 8.0.10, Tailwind 4.2.4, Recharts 3.8.1, Nostrify, TanStack Query, no i18n packages, no
licensefield, nodeployscript. Accessed 2026-07-09. - gitlab.com/soapbox-pub/mkstack — AGENTS.md (raw) — full 348-line agent contract: stack (accurate versions), project structure, Nostr kind/tag rules, security model, validate-then-commit workflow, skill references. Accessed 2026-07-09.
- gitlab.com/soapbox-pub/mkstack — .mcp.json (raw) — Claude Code project-scoped MCP config, wiring
nostrandjs-devservers. Accessed 2026-07-09. - gitlab.com/soapbox-pub/mkstack — opencode.json (raw) — OpenCode agent config, wiring the same
nostrMCP server. Accessed 2026-07-09. - gitlab.com/soapbox-pub/mkstack — .agents/skills/ tree + testing/SKILL.md (raw) — 19-skill directory listing (via GitLab API) plus full content of one skill file confirming
SKILL.mdYAML-frontmatter format. Accessed 2026-07-09. - gitlab.com/soapbox-pub/mkstack — .github/workflows/deploy.yml (raw) — GitHub Pages deploy workflow, triggers on push to main. Accessed 2026-07-09.
- GitLab API project metadata —
https://gitlab.com/api/v4/projects/soapbox-pub%2Fmkstackand its/repository/treeendpoint — createdat, star/fork counts, commit/branch/tag counts, lastactivity_at, absence of alicensefield, full root file tree. Accessed 2026-07-09. - LICENSE / LICENSE.md / LICENSE.txt / COPYING at
gitlab.com/soapbox-pub/mkstack/-/raw/main/— all four return HTTP 404, confirmed via direct request. Accessed 2026-07-09. - gitlab.com/soapbox-pub/mkstack-nsp + GitLab API metadata — repo description ("Nostr Service Provider for generating Nostr web clients with AI"), created 2025-06-23, last activity 2025-09-02. Accessed 2026-07-09.
- gitlab.com/soapbox-pub/mkstack-nsp — NIP.md (raw, full 1,085 lines) — the "Nostr SPA Builder" wire-protocol spec: service discovery (kind 31999), encrypted RPC (kind 25742, incl.
get_modelsreturning"shakespeare"/"tybalt"model aliases), notifications (kind 25743), security considerations. Accessed 2026-07-09. - gitlab.com/soapbox-pub/mkstack-nsp — README.md (raw) — Deno/TypeScript NSP reference implementation:
AI_PROVIDER=openai|openrouteradapter config,CLONE_URL-configurable base template, Stripe/Lightning credit system. Accessed 2026-07-09. - gitlab.com/soapbox-pub/mkstack-nsp — LICENSE (raw) — full GNU Affero General Public License v3 text, confirmed present (HTTP 200) and verbatim. Accessed 2026-07-09.
- MKStack: Vibe Coding for Everyone — Soapbox Blog — step-by-step Stacks CLI workflow (
npm install -g @getstacks/stacks,stacks mkstack,stacks agent,npm run deploy), cost-discipline warning, NostrDeploy.com mechanics. Accessed 2026-07-09. - Stacks — AI-First Development Platform — Stacks CLI product page:
stack.json/agent.json/CONTEXT.md, Dork provider list (OpenRouter, PayPerQ, Routstr), kind-30717 template publishing. Accessed 2026-07-09. - Stacks 1.0 Released — Soapbox Blog — published 2025-08-20; multi-provider support, "your own custom models," MKStack framed as "one such stack." Accessed 2026-07-09.
- Soapbox Toolbox — full tool inventory; MKStack/Stacks/Nostrify one-line descriptions, no license stated for any. Accessed 2026-07-09.
github.com/soapbox-pub/mkstackandgithub.com/soapbox-pub/mkstack-nsp— both return HTTP 404, confirming no GitHub mirror exists; Soapbox is GitLab-only for these repos. Accessed 2026-07-09.- This manual, ch. 04 — Shakespeare — cross-referenced for Shakespeare's launch timeline, the "MKStack + Claude Sonnet 4" paid-NSP finding, and the AGPLv3-tool/no-license-on-output distinction reused here.
- This manual, ch. 08 — i18n Integration — cross-referenced; its citation of mkstack-nsp's
NIP.md(independently re-fetched in full for this chapter) and its zero-i18n-dependency finding on MKStack, independently reconfirmed here via livepackage.json. - This manual, ch. 09 — Visualization Integration — cross-referenced; its prior live
package.jsonfetch (React 19.2.5/Vite 8.0.10/Tailwind 4.2.4/Recharts 3.8.1) matches this chapter's independent fetch exactly, and its stale-secondary-source caution is echoed here against the template's own README. - Web search aggregate (Soapbox blog index, NostrHub app listings, general web) — corroborating descriptions of Chorus (community organizing + Cashu wallet), Blobbi (persistent virtual pet), and Treasures (geocaching) as real MKStack-built apps named in the README. Accessed 2026-07-09.
mkstack.xyz— canonical product domain, referenced throughout the repo description andNIP.mdasset URLs; page itself did not render in this pass (TLS handshake failure on direct fetch) — cited as the documented canonical domain, not independently rendered live.nostrdeploy.com— direct resolution/fetch attempt: DNS lookup fails (ENOTFOUND); the domain does not resolve at all. Checked 2026-07-10. Supports: the deploy-lane tripwire — NostrDeploy.com unreachable, service possibly dead.- soapbox.pub/toybox — accessed 2026-07-10 — live re-fetch: full 17-item Toybox roster reconfirmed; Treasures' public source link (
gitlab.com/chad.curtis/treasures, third-party-built) found on-page; Chorus does not appear on this page. - nostrbook.dev/mcp — accessed 2026-07-10 — live tool-list confirmation for the
nostrMCP server:read_nip,read_kind,read_tag,read_protocol,read_nips_index, matching.mcp.json's@nostrbook/mcp@latestpackage exactly. - gitlab.com/soapbox-pub/nostrhub — accessed 2026-07-10 — repo created 2026-06-11, no license detected on direct fetch.
- Maintainers' internal production-secrets-longevity audit note (companion document, not part of this public chapter set) — pre-positioning checklist (commit-pin + fetch-only-upstream discipline, now configured on trespies-stack; ADR 0001 license-outreach status covering mkstack + NostrHub) and correction log (NostrHub license-absence finding, 0 stars/forks per the ch. 01 GitLab-API sweep). Accessed/authored 2026-07-10.
11 — Nostrify: The Framework Layer
The interface layer every MKStack app compiles against — one shape for storage (NStore) and relays (NRelay) means swapping in-memory for Postgres, or a browser extension for a bunker, is a config change, not a rewrite.
Verified: 2026-07-10
Confidence:
- High —
NStore/NRelay/NostrSigner/NPolicy/NSchema/NUploaderinterface shapes and the 9-package map (fetched directly from nostrify.dev and the live GitLab repo/API). Also high for the bus-factor figure — independently reproduced via the GitHub API, matching the maintainers' own same-day companion audit to the decimal. - Medium —
NPool'sreq()-vs-query()EOSE/timeout split: documented clearly on one page, not cross-checked against the TypeScript source itself. - Medium —
@nostrify/react's full hook surface: two hooks confirmed (useNostr,useNostrLogin); page content came back tool-summarized rather than read as raw markdown. - Low — testing-utility completeness:
MockRelay/ErrorRelay/TestRelayServerconfirmed present in the live repo tree; a JSONL-fixture utility referenced in this manual's source research could not be independently relocated this pass.
What It Is
Nostrify is Soapbox's "Framework for Nostr on Deno and web" [1] — the library every MKStack-scaffolded app imports for storage, relay access, signing, validation, moderation, and uploads. MIT-licensed, pnpm + Changesets monorepo [2][3], nine packages:
| Package | Ships |
|---|---|
@nostrify/nostrify (core) | NStore, NRelay, NRelay1, NPool, NostrSigner + NSecSigner/NConnectSigner/NBrowserSigner, NPolicy, NSchema, NUploader, test utilities |
@nostrify/react | React hooks + provider |
@nostrify/db | NPostgres, NDatabase (Kysely SQL) |
@nostrify/indexeddb | NIndexedDB (browser cache) |
@nostrify/strfry | policy bridge into strfry's write-policy plugin interface |
@nostrify/policies | 20 shipped NPolicy implementations (see below) |
@nostrify/ndk | interop with NDK (Nostr Dev Kit) |
@nostrify/seed | NPhraseSigner, NSeedSigner, NCustodial |
@nostrify/types | shared TypeScript types |
Package list confirmed live against the monorepo's packages/ directory [4]. @nostrify/ndk shows up only in nostrify.dev's integrations sidebar [5] and @nostrify/types only in the directory listing — neither package's own README was independently fetched this pass.
One Interface, Many Backends
Two interfaces, one shape. NStore — event(), query(), count(), remove() [6]. NRelay — req(), event(), query() — and it's explicitly built on NStore: "Relays on Nostrify are actually just another type of storage. They implement NRelay (which is based on NStore)." [7] That sentence is the whole chapter in miniature: a relay, an in-memory cache, a Postgres table, and a browser's IndexedDB store are all the same shape to your app code.
| Interface | Implementation | Backend | Package |
|---|---|---|---|
NStore | (in-memory) | process memory | core |
NStore | NDatabase | any SQL via Kysely — SQLite, MySQL, etc. | db |
NStore | NPostgres | Postgres, jsonb tags + NIP-50 full-text search, production-tested | db |
NStore | NIndexedDB | browser IndexedDB, persistent client cache; degrades to a silent no-op under iOS Lockdown Mode | indexeddb |
NRelay | NRelay1 | one relay connection, auto-reconnecting | core |
NRelay | NPool | many relays, outbox-model routing | core |
NIndexedDB additionally handles replaceable/addressable-event supersession and NIP-09 deletions, with configurable tag indexing [8]. NPostgres is named the production-tested adapter; NDatabase trades raw performance for broader SQL-engine compatibility [9]. "Storages can be used interchangeably with relays, allowing you to switch between in-memory, SQL databases, and more without changing your code" [6] — the same query() call an app makes against a live NPool in development can run against NPostgres in a worker, or NIndexedDB in an offline-first PWA, with zero call-site changes.
NPool: Outbox Routing — and Its Timeout Trap
NPool is the NRelay implementation "designed with the Outbox model in mind" [10]. Its constructor takes two routing functions: a reqRouter mapping each relay URL to the filters it should serve (keyed on filter.authors), and an eventRouter mapping each outgoing event's pubkey to the relay URLs it should publish to. Feed it a user's NIP-65 relay list (kind 10002 — separate read/write sets) and this is outbox-model routing: the routing is the config, not a hardcoded relay array. A hardcoded relay list is the single most common reason "my app can't find this user's events" — it's routing to the wrong set, not a broken query.
EOSE behavior splits two ways depending which method you call, and this is the part that bites in production:
pool.req()— the streaming subscription generator — "will only emit an EOSE when all relays in its set have emitted an EOSE" [10], and there is no timeout option on it at all. One dead or silent relay in the route means that subscription's EOSE signal never fires. Events from the healthy relays still stream through; the "stored events are exhausted" signal just never arrives.pool.query()— the one-shot, promise-returning path most application code actually calls — is safer by default: it "will wait up to 1 second after the first relay sends EOSE before canceling slow relays" [10], via aneoseTimeoutoption (default1000ms; set0to force wait-for-all).
req() does accept an AbortSignal [7] — that's the documented escape hatch, but NPool does not wire one up for you.
Verdict: don't trust NPool's defaults for anything long-running. If your app calls req() directly for a live subscription — chat, a live feed, notifications — you own the timeout: wrap it in AbortSignal.timeout(N) or your own watchdog, because the default is "wait forever for the slowest relay in the set." query()'s 1-second default is workable for a UI-blocking fetch, but measure it against a cold NPostgres-backed relay before trusting it in a critical path.
Signers: One Interface, Six Implementations
NostrSigner needs getPublicKey() and signEvent(), plus nip04 and nip44 sub-objects — each with encrypt()/decrypt() — built into the interface itself [11]. Every implementation gets encryption for free; no separate library, no separate call convention. The interface's whole reason for existing: "Signers from Nostrify are all drop-in replacements for window.nostr!" [11] — app code written against the browser-extension API swaps to any of the six with no call-site change.
| Class | Package | Backs onto |
|---|---|---|
NSecSigner | core | raw nsec, held in-process |
NBrowserSigner | core | wraps the browser's own window.nostr (NIP-07) |
NConnectSigner | core | NIP-46 remote signer / bunker |
NPhraseSigner | seed | BIP-39 mnemonic phrase |
NSeedSigner | seed | raw HD seed |
NCustodial | seed | custodial, server-held key |
Class names confirmed directly against the live repo file tree [12][13]; interface shape and the drop-in claim from the docs [11].
NSecSigner and NCustodial both hold key material in-process — the named catastrophic-failure shape the maintainers' companion audit has already flagged elsewhere (raw nsec in localStorage or a server env var) [24]. NConnectSigner is the one that keeps a brand or team key out of the app entirely; prefer it for anything beyond a disposable per-session identity.
NSchema: Validate, Don't Assert
Zod-based; imported as import { NSchema as n } from '@nostrify/nostrify' — the n alias mirrors Zod's own z [14]. n.event().parse(data) validates a raw object into a well-formed event; chain .refine(verifyEvent) (from nostr-tools) to also check the signature. n.filter(), n.relayMsg(), n.clientMsg(), and n.bech32('npub') cover the rest of the wire format. Both .parse() (throws) and .safeParse() (returns a result object) ship on every schema [14] — the difference between "crash on bad relay data" and "handle it," and untrusted relay input is the normal case on this protocol, not the edge case.
NPolicy: 20 Shipped, Not 19
NPolicy is one method — call(event: NostrEvent): Promise<NostrRelayOK>, false means reject [15]. The live repo tree (2026-07-10) ships 20 policy implementation files, each with a matching test file [16]; nostrify.dev/policy/ currently documents 18 of them by name — AuthorPolicy and ReplyBotPolicy are shipped but not yet listed on the live docs page [15][16]. Same class of drift ch. 10 already caught between MKStack's README and its package.json [17] — trust the repo tree over the docs page.
| Policy | Does |
|---|---|
WoTPolicy | Web-of-trust filtering — reject pubkeys outside a trust radius |
OpenAIPolicy | AI-based content moderation via OpenAI's moderation API |
PowPolicy | Requires NIP-13 proof-of-work difficulty — a spam deterrent |
PipePolicy | Chains multiple policies into one pipeline |
HellthreadPolicy | Caps mass-mention ("hellthread") events |
The same NPolicy object runs in two places unmodified: in a client, to reject-before-render, or piped into a relay via @nostrify/strfry, which "adapts Nostrify policies for use in strfry policy plugins" and "hooks up to stdin/stdout and runs the policy on strfry input" [18]. Write the moderation pipeline once; run it client-side today, relay-side later.
Uploaders
NUploader is one method: upload(file: File): Promise<[['url', string], ...string[][]]> — the return is a NIP-94 tag array, first tag guaranteed to be url [19]. Two shipped implementations: BlossomUploader (one or more Blossom servers) and NostrBuildUploader (nostr.build or a compatible server) [19]. Output tags — url, m (MIME), x (sha256), size, dim, blurhash — drop directly into a kind 1063 (NIP-94) or inline (NIP-92) event with no reshaping.
@nostrify/react
NostrProvider wraps the app in relay context; useNostr() returns the pool from any component; useNostrLogin() exposes login/logout state [20][21]. Under the hood the provider constructs an NPool over NRelay1 connections [20] — this is the hook layer MKStack's own scaffold is built on (ch. 10 [17]). Pair useNostr()'s .query() calls with TanStack Query for caching; the docs' own example does exactly that [20].
Testing Utilities — Repo-Only, Undocumented
The core package's test/ directory ships MockRelay.ts, ErrorRelay.ts, and TestRelayServer.ts [12] — confirmed directly against the live file tree. None of the three appear anywhere on nostrify.dev; there is no dedicated testing page in its documented section list [1][12]. An app team that wants to write tests against NPool or NRelay today has to read source, not docs, to find them. A JSONL-fixture utility was flagged in this manual's source research but could not be independently relocated this pass — treat that specific claim as unconfirmed, not disproven.
Longevity Note
Measured live via the GitHub API, 2026-07-10: of 1,043 total contributions to soapbox-pub/nostrify, alexgleason accounts for 889 — 85.2% [22][23]. The next-largest contributor holds 141 (13.5%); five more combine for the remaining ~1.3% [23]. This reproduces, to the decimal, the figure the maintainers' own companion production-secrets-longevity audit measured earlier the same day [24] — two independent pulls, same number.
Nostrify is the single most load-bearing library in the stack — every MKStack app, and everything downstream of it, imports it directly. Sitting that on one person is a real risk, not a hypothetical one. The mitigation is available and cheap precisely because the license is permissive: MIT, confirmed on the root repo and every sub-package checked this pass [2][8][9][18]. Vendoring doesn't require anyone's permission. The maintainers' standing recommendation, unchanged by this pass: vendor Nostrify source locally, and have one engineer read NStore and NRelay end-to-end — so the interface contracts aren't being learned for the first time during an incident [24].
Verdict
Nostrify earns the "framework" label the rest of this manual has used loosely. The interface discipline is real and live-verified: NStore/NRelay share one shape, every NostrSigner is a window.nostr drop-in, and one NPolicy object runs client-side or relay-side unchanged. Build against the interfaces, not a specific backend, and the "swap not rewrite" promise holds. Two traps: NPool.req() has no default timeout and will hang a subscription's EOSE forever on one bad relay — build the AbortSignal layer yourself, don't discover this live. And the whole framework sits at 85.2% single-author — MIT licensing makes vendoring cheap insurance; not vendoring it is the actual risk, not a hypothetical one.
Sources
- nostrify.dev — homepage: tagline, framework description, documented section list (Relays/Storages/Signers/Schemas/Moderation Policies/Uploaders/Integrations — no testing section). Accessed 2026-07-10.
- gitlab.com/soapbox-pub/nostrify — README.md (raw) — monorepo description, pnpm + Changesets, MIT license. Accessed 2026-07-10.
- GitLab API project metadata —
gitlab.com/api/v4/projects/soapbox-pub%2Fnostrify— created 2024-01-16, 8 stars / 6 forks, last activity 2026-07-08. Accessed 2026-07-10. - GitLab API repository tree —
packages/(depth 1) — confirms the 9-package list: db, indexeddb, ndk, nostrify, policies, react, seed, strfry, types. Accessed 2026-07-10. - nostrify.dev/integrations/ — integrations page: React, NDK, Zaps, MCP, Welshman listed; links to JSR API reference. Accessed 2026-07-10.
- nostrify.dev/store/ —
NStoreinterface (event/query/count/remove), implementation list, "interchangeable... without changing your code" line. Accessed 2026-07-10. - nostrify.dev/relay/ —
NRelay/NRelay1definitions, "based on NStore" quote,req()'sAbortSignalparameter. Accessed 2026-07-10. - gitlab.com/soapbox-pub/nostrify — packages/indexeddb/README.md (raw) —
NIndexedDB: filter support, supersession/NIP-09 deletion handling, Lockdown Mode degradation to a silent no-op. Accessed 2026-07-10. - gitlab.com/soapbox-pub/nostrify — packages/db/README.md (raw) —
NPostgres(jsonb + NIP-50 FTS, production-tested) vsNDatabase(Kysely, broad SQL compatibility). Accessed 2026-07-10. - nostrify.dev/relay/pool —
NPooloutbox routing (reqRouter/eventRouter), thepool.req()vspool.query()EOSE/timeout split,eoseTimeoutdefault and override. Accessed 2026-07-10. - nostrify.dev/sign/ —
NostrSignerinterface, built-innip04/nip44, "drop-in replacements forwindow.nostr" quote. Accessed 2026-07-10. - GitLab API repository tree —
packages/nostrify/(recursive) — confirmsNSecSigner.ts,NConnectSigner.ts,NBrowserSigner.ts,NRelay1.ts,RelayError.ts, andtest/MockRelay.ts/test/ErrorRelay.ts/test/TestRelayServer.ts. Accessed 2026-07-10. - GitLab API repository tree —
packages/seed/(recursive) — confirmsNCustodial.ts,NPhraseSigner.ts,NSeedSigner.ts; no root README present. Accessed 2026-07-10. - nostrify.dev/schema/ —
NSchema(NSchema as n), Zod basis,.parse()/.safeParse(), event/filter/relayMsg/bech32 examples. Accessed 2026-07-10. - nostrify.dev/policy/ —
NPolicyinterface (call()returningNostrRelayOK), 18 policies enumerated by name on this page. Accessed 2026-07-10. - GitLab API repository tree —
packages/policies/(recursive) — confirms 20 policy implementation files incl.AuthorPolicy.tsandReplyBotPolicy.ts, absent from the docs page. Accessed 2026-07-10. - This manual, ch. 10 — MKStack — cross-referenced for the README-vs-manifest drift pattern (reused here for docs-page-vs-repo-tree) and for
@nostrify/react's role as MKStack's hook layer. - gitlab.com/soapbox-pub/nostrify — packages/strfry/README.md (raw) — the strfry write-policy bridge, stdin/stdout adapter, MIT license reconfirmed. Accessed 2026-07-10.
- nostrify.dev/upload/ —
NUploaderinterface,BlossomUploader/NostrBuildUploader, NIP-94 tag output shape. Accessed 2026-07-10. - nostrify.dev/react/ —
NostrProvider,useNostr(), setup pattern, TanStack Query pairing example. Accessed 2026-07-10. - gitlab.com/soapbox-pub/nostrify — packages/react/README.md (raw) — cross-check:
NostrContext.Provider,NostrLoginProvider,useNostrLogin(). Accessed 2026-07-10. - github.com/soapbox-pub/nostrify — confirms a live GitHub mirror exists (unlike MKStack, ch. 10): 1,050 commits, 17 stars, MIT. Accessed 2026-07-10.
- GitHub API —
api.github.com/repos/soapbox-pub/nostrify/contributors— full contributor list: alexgleason 889, xyzshantaram 141, patrickReiis 6, hzrd149 2, sergey3bv 2, marykatefain 1, dentropy 1, Sjors 1 (1,043 total). Accessed 2026-07-10. - Maintainers' internal production-secrets-longevity audit note (companion document, not part of this public chapter set) — same-day bus-factor measurement (Nostrify 85.2% Gleason, independently cross-checked here) and the standing pre-positioning recommendation to vendor Nostrify source + read
NStore/NRelayend-to-end.
12 — Building Consumer Apps: The Curriculum and the Workflow
Ch. 10 established that MKStack is buildable outside Shakespeare, on any model provider. This chapter answers the next question: once you can build there, what counts as "done" — the 19-skill directory as the definition-of-done checklist, the three-rung workflow Soapbox's own team uses to decide what to build and how, and the five concepts that separate a working prototype from a Nostr-native one.
Verified: 2026-07-10
Confidence:
- High — all 19 skill files (fetched live, raw, this pass), the dev-ladder quotes and the 48h Agora/HRF data point (fetched live from the source post), the domain/NIP mapping (independently re-verified against the live marketing page — matches the earlier gap-analysis draft exactly).
- Medium — builder-criticality ratings (Core/Common/Situational) in the skill table below: this manual's own judgment call, applied consistently, not a ranking Soapbox publishes anywhere.
- Medium — the AI-team roster: three personas confirmed live (Dirk Rost, Quilly, Sheila), each announced independently with no roster page tying them together. Treat as a plurality snapshot, not a closed list, and re-verify currency before citing any one as "the" example.
- Low — whether the §9.0 lane mapping to the dev ladder (below) is a parallel Soapbox intends or one this manual draws by analogy; no source states the two frameworks correspond.
What This Chapter Is
Three chapters now stack on top of each other. Ch. 10 covers MKStack's scaffold anatomy, dependency versions, and the AGENTS.md/.mcp.json agent contract. Ch. 11 covers Nostrify, the interface layer every scaffolded app compiles against — NStore/NRelay, signers, policies, uploaders. Neither is repeated here. This chapter covers the layer above both: what a coding agent (or the human directing it) must still get right after the scaffold compiles clean and the interfaces are wired, and how Soapbox's own team decides what to build in the first place. Two threads, in order: the 19-skill curriculum as the spec for "done," and the workflow culture that produces Soapbox's own apps 48 hours at a time.
The Soapbox Dev Ladder
Soapbox doesn't build consumer Nostr apps with one tool — it picks a rung based on how well the shape of the thing is already known [1]:
| Rung | Tool | When | What it optimizes for |
|---|---|---|---|
| 1 | Shakespeare | "where ideas start" | fastest path from a thought to a working Nostr app — ideation and quick prototyping [1] |
| 2 | OpenCode | "once an idea is worth building for real" | agentic AI coding, fine control over every file — going deep [1] |
| 3 | Clone MKStack | "when we already know exactly what we're making" | known shape; the template is the starting point, not a re-derivation [1] |
The ladder isn't strictly one-way — a team that already knows the shape can start straight at rung 3. Ch. 07 §9.0 codifies TresPies's own parallel structure: four lanes (Enterprise / TresPies-hosted / IDE agents / Local-routed) picked by who's driving and which model provider, not by how well-known the shape is — a different axis on the same instinct: match the tool to how much is already decided before you start. Read loosely rather than as a Soapbox-stated equivalence, the two frameworks line up:
| Dev-ladder rung | Nearest §9.0 lane | Shared logic |
|---|---|---|
| Shakespeare (ideation) | Local / routed — cost-floor mockup volume | Cheapest, fastest, most disposable output; nothing here is meant to survive |
| OpenCode (deep work) | IDE agents — operator-in-the-loop editing | Fine-grained control, a human steering every file, not a one-shot generation |
| Clone MKStack (known shape) | TresPies-hosted / Enterprise — house builds, contract work | The shape is already decided; the remaining choice is execution, not exploration |
The culture behind the ladder is stated twice in the same post. First, the producer framing:
"In music, the producer doesn't play every instrument; they bring taste, direction, and the judgment to know when it's done. That's the role." [1] "The AI writes the code; the human owns the vision and the standard." [1]
Second, prompting itself is named as a craft with a learning curve, not a shortcut around one:
"Prompting is a skill. Learning how to speak to an AI, how to frame a problem, how much context to give, when to steer and when to get out of the way, how to recognize why a model went sideways and correct it, is a real, earned craft." [1] "The best people on our team aren't the ones with the fanciest setup; they're the ones who've put in the reps." [1]
The data point behind the claim: Agora — ch. 03's non-custodial BTC fundraising product — was built in 48 hours at the HRF hackathon, using both Shakespeare and OpenCode, and "Venezuelan activists were on it within hours" of shipping [1]. That is the ladder under real time pressure, not a marketing abstraction.
The 19-Skill Curriculum: The Definition of Done
Ch. 10 established that .agents/skills/*/SKILL.md is a Claude-Skill-shaped directory an agent loads by name from AGENTS.md. What ch. 10 didn't do is open all 19 files — this chapter does, live, and treats the resulting list as the operational definition of "done": a shipped app that never touched a skill's territory hasn't finished that dimension, whether or not an agent wrote code near it.
Legend: Core = load-bearing for nearly any consumer app on this stack. Common = frequent, but scoped to a feature category. Situational = specific product shape only. Ratings are this manual's judgment — no Soapbox-published ranking exists.
| Skill | Teaches | Criticality |
|---|---|---|
nostr-security | XSS threat model for a client holding nsec in localStorage: CSP, URL/CSS sanitization, author-filtering on trust-sensitive queries [6] | Core |
testing | Vitest + RTL unit tests via the TestApp wrapper and pre-mocked browser APIs; run the full suite, not just one file [7] | Core |
nip19-routing | One root /:nip19 route decodes npub/nprofile/note/nevent/naddr; decode before filtering, scope addressable queries by author, never render a decoded nsec [9] | Core |
nostr-relay-pools | Target specific relays or curated groups via nostr.relay()/nostr.group(); read write-relays from NIP-65 metadata, never hardcode a relay list [5] | Core |
nostr-infinite-scroll | useInfiniteQuery + intersection observer, paginated by until timestamp, deduplicated by event id [19] | Core |
note-content | The NoteContent renderer: linkifies URLs/hashtags/mentions, embeds media/invoices/quoted notes, sanitizes all of it; guard recursive embeds against unbounded nesting [20] | Core |
theming | Fonts via @fontsource, light/dark CSS variables, shadcn/ui-consistent styling; the isolate + negative-z-index stacking-context gotcha [8] | Common |
file-uploads | useUploadFile posts to a Blossom server, returns NIP-94 imeta tags; requires a signed-in signer, prefer mutateAsync before publishing the referencing event [10] | Common |
nostr-encryption | NIP-44 (legacy NIP-04 fallback) encrypt/decrypt through the logged-in user's signer only; never derive a shared secret yourself [12] | Common |
edit-profile | Drop-in kind-0 profile form (name/about/picture/banner/nip05), reusing useUploadFile for images [15] | Common |
nostr-comments | Threaded comments (NIP-22 kind 1111) attachable to events, URLs, hashtags, or NIP-73 identifiers; NIP-09 deletion isn't guaranteed network-wide [18] | Common |
relay-management | User-facing NIP-65 relay-list editor, publishes kind 10002 — the write-side companion to the outbox model's login-time auto-sync [23] | Common |
nwc | Nostr Wallet Connect (NIP-47) + WebLN + NIP-57 zap requests; NWC connection strings are secrets, store like passwords [11] | Situational |
onchain-bitcoin | Derives a Taproot BTC address straight from the Nostr pubkey (same secp256k1 key), PSBT signing across all three signer types, kind-8333 onchain zaps; verify amounts on-chain, the amount tag is self-reported [21] | Situational |
capacitor | Wraps the web app as native iOS/Android — haptics, Keychain/KeyStore, deep links, no Swift/Kotlin; bootstrapNative() must run before React mounts [14] | Situational |
lockdown-mode | Maps what Apple Lockdown Mode blocks (IndexedDB, Service Workers, WASM, WebGL, WebRTC) and the fallback per API; no official detection API exists [16] | Situational |
nip85-stats | Pulls pre-computed engagement stats from a trusted NIP-85 assertion provider instead of scanning raw events; always author-filtered, not a source of truth for per-user interactivity [17] | Situational |
ai-chat | useShakespeare hook wires Nostr-authenticated chat completions to the Shakespeare API, streaming plus dynamic model discovery [13] | Situational |
plausible-analytics | Privacy-friendly analytics via @plausible-analytics/tracker, wired through AppConfig and VITE_PLAUSIBLE_* env vars; guard init() with a ref, it only runs once [22] | Situational |
Scope warning: nostr-security covers XSS/CSP/content-sanitization only. It documents the consequence of nsec living in localStorage — "Nostr private keys (nsec) are stored in plaintext in localStorage" [6] — not how to secure that storage, and it says nothing about .env/VITE_* discipline. Other skills in this table hand an agent raw secrets to wire up with no shared skill governing where those values may live or how they stay out of a client bundle — plausible-analytics's VITE_PLAUSIBLE_* vars [22], nwc's connection strings [11]. This is not hypothetical: a same-day production audit of this stack found VITE_ vars get "baked into the public bundle as literal strings" and that "default gitleaks has zero Nostr rules — a committed nsec1… passes CI today" [29]. Key-custody architecture and env-var discipline are specced for ch. 15 (forthcoming, already scoped) [29]. A clean nostr-security pass is not a security audit of either.
Hook and Component Inventory, by Consumer Domain
The marketing page organizes its "50+ NIPs" claim [2] around four consumer domains; independently re-verified against the live page this pass, the mapping matches the earlier gap-analysis draft exactly:
| Domain | NIPs | Skills / components that implement it |
|---|---|---|
| Social | 01, 02, 18, 25 (profiles, follows, reposts, reactions) | edit-profile [15], useAuthor, LoginArea [2] |
| Messaging | 17, 28, 29, 44 (DMs, public chat, groups, encryption) | nostr-encryption [12]; gift-wrap DMs and groups — see Scaffold vs. Product, below |
| Payments | 47, 57, 60, 61 (NWC, zaps, Cashu, nutzaps) | nwc [11]; onchain-bitcoin [21] as the kind-8333 alternative rail |
| Content | 23, 52, 53, 94 (long-form, calendar, live events, file metadata) | file-uploads [10], note-content [20], nostr-comments [18], nostr-infinite-scroll [19] |
Eight named hooks and components recur across the marketing page, the skill files, and ch. 10's scaffold anatomy — the primitives worth knowing by name before reading any app's source:
| Name | Role |
|---|---|
useNostr | Base hook: the active relay pool, underneath every query and publish [2] |
useNostrPublish | Signs and broadcasts an event through the current user's signer [2] |
useAuthor | Resolves a pubkey to kind-0 profile metadata, cached [2] |
useCurrentUser | The logged-in identity plus signer; gates any action requiring auth (uploads, publishes) [2][10] |
useUploadFile | Blossom upload, returns NIP-94-ready metadata [2][10] |
LoginArea | Account-switching UI, the NIP-07/NIP-46 login entry point [2][10] |
RelaySelector | User-facing relay picker [2] |
NoteContent | Sanitized, embed-aware note renderer — see the note-content skill row, above [2][20] |
Scaffold vs. Product: An Opportunity, Not a Contradiction
Ch. 10's NIP-support table, read against ch. 02's Ditto findings, produces a fact worth stating plainly rather than leaving as a footnote: the MKStack scaffold documents support for NIP-17 (gift-wrap DMs) and NIP-29 (relay-based groups) that Ditto — Soapbox's own flagship community server — does not have. Ch. 02 confirms the Ditto side directly: NIP-17/NIP-04 is "absent from Ditto's own official NIP reference list," and NIP-29 groups are "Flotilla's model, not Ditto's" [24]. Ch. 10 confirms the scaffold side: the template's README lists NIP-17 and NIP-29 among the roughly twenty NIPs it ships wired [26].
Read as a contradiction, this looks like the manual disagreeing with itself. It isn't. Ditto is a product — one opinionated deployment of the stack, scoped to what its own team chose to ship. MKStack is the scaffold every product forks from, and it carries broader protocol coverage than any single downstream app needs or exposes. A builder who wants private DMs or relay-based groups doesn't wait for Ditto to add them — the scaffold already has the pieces (nostr-encryption [12] for the crypto, the README's NIP-17/29 wiring for the event shapes). Ditto's gap is a product-scoping decision, not a stack ceiling. Carry that distinction into every future "does the stack support X" question: check the scaffold, not just the flagship app built on it.
Five Load-Bearing Concepts
Five ideas recur across all 19 skills and both chapters this one draws from. Miss one and an agent-built app still compiles, still passes tsc/eslint/vitest (ch. 10's validate-then-commit gate) — and still breaks in ways that gate never catches.
| Concept | Statement | What breaks if ignored |
|---|---|---|
| One interface, many backends | Storage is a swap, not a rewrite: ch. 11 documents NStore (event/query/count/remove) and NRelay as one shape shared by an in-memory store, NPostgres, NIndexedDB, a single relay, or a full NPool [28]. Every skill in this table is written against that shape, not a specific relay | Locking a component to one relay implementation forecloses the in-memory/IndexedDB/Postgres swaps the framework exists to allow |
| Signer discipline across three standards | NIP-07 (extension) and NIP-46 (remote/bunker) are detailed in ch. 06 [25]; NIP-55 (Android signer intents, e.g. Amber) is the third standard MKStack's own contract names, not yet covered there. Ch. 11 confirms the shape underneath all three: six NostrSigner implementations, each a window.nostr drop-in with nip04/nip44 built in [28]. Every skill above that touches a key routes through that interface, never a raw nsec | nostr-encryption [12], nostr-security [6], and onchain-bitcoin [21] each independently name the same failure mode: a raw nsec reachable by client-side JS turns any XSS into full account — and, with onchain-bitcoin installed, wallet — compromise |
| Outbox is routing, not config | NIP-65 read/write relay sets, auto-synced on login (ch. 10's NostrSync) and user-editable via relay-management [23]. nostr-relay-pools states the rule directly: "Don't hard-code user-facing relay lists" [5]; ch. 11 names the failure mode in the same words this chapter's skills point at: "a hardcoded relay list is the single most common reason 'my app can't find this user's events'" [28] | Hardcoded relay URLs are this stack's most common "can't find events" report — an event published to a user's write relays never reaches a reader whose client only queries a fixed default |
| Local-first, not CRUD-with-a-backend | Frontends are disposable; data lives on relays plus a local cache. lockdown-mode [16] makes the dependency legible in reverse: strip IndexedDB, which Apple Lockdown Mode does outright, and the caching layer most apps quietly lean on is simply gone. Ch. 11 confirms the same weak point from the framework side — NIndexedDB "degrades to a silent no-op under iOS Lockdown Mode" [28] | Builders arriving from a typical web-app background reach for a backend database by reflex; there isn't one here, and code assuming persistent server-side state has no home in this architecture |
| Skills-as-spec | The 19-skill directory (table, above) is the audit checklist for "done," not a menu an agent copies boilerplate from | Shipping a skill's happy path without its warnings — e.g. file-uploads without the useCurrentUser guard [10] — ships the documented failure mode still live |
The three chapters compose rather than compete: ch. 11 supplies the interface guarantee (a relay and a Postgres table look the same to your code), this chapter supplies the feature-by-feature checklist for using that guarantee correctly, and ch. 10 supplies the agent contract that loads both by name. An app that only reads one of the three will compile — tsc/eslint/vitest don't check for a hardcoded relay list or a signer bypass — but won't hold up past the first relay outage or the first security review.
The AI-Team Pattern
Soapbox's own build culture is not purely human. At least three named AI personas do production work, each announced independently, with no roster page connecting them — a plurality snapshot, not a closed team list; re-verify currency before citing any one as "the" example [1][3][4].
| Persona | Role | Runs on | Announced |
|---|---|---|---|
| Dirk Rost | Code review, merge-request approval, "a bird's-eye view across every project" [1] | Open-source infra; personality "defined in plain markdown anyone can read" [1] | Referenced in the ships-fast post; no dedicated announcement found this pass |
| Quilly | Docs, Nostr community engagement, GitLab contributions (blog posts, team-page updates), real-world Shakespeare testing, team coordination via Signal [3] | OpenClaw (open-source agent framework) in an LXC container; personality split across SOUL.md/AGENTS.md/USER.md/TOOLS.md [3] | 2026-01-26 [3] |
| Sheila | Full contractor-payment lifecycle — invoices, ACH/wire/Bitcoin payouts, bookkeeping, expense tracking, OpenCollective submissions [4] | OpenCode, human-supervised ("I see what she's doing before it goes out" [4]); 50+ single-purpose TypeScript scripts under a 600-line AGENTS.md [4] | 2026-03-06 [4] |
The pattern holds across all three: markdown-defined personality on infrastructure Soapbox controls, not a closed-source assistant product. Quilly's own SOUL.md states the operating principle plainly: "Be genuinely helpful, not performatively helpful. Have opinions. Be resourceful before asking." [3] The same open-infra, plain-markdown-personality shape that governs AGENTS.md's relationship to a coding agent (ch. 10) governs Soapbox's relationship to its own AI teammates — one contract format, applied to both code-writing and operational work.
Open Questions
- Whether the Core/Common/Situational criticality ratings in the skill table would look different if Soapbox published its own ranking — no such ranking exists in any source found this pass; treat these as this manual's judgment, re-derivable, not authoritative.
- Whether
nostr-security's XSS-only scope is a deliberate boundary or a gap Soapbox hasn't yet closed — the same-day production audit [29] reads it as the latter; Soapbox has not stated a position either way. - Whether Cursor or Windsurf read
.agents/skills/*/SKILL.mdthe same way Claude Code and OpenCode do — ch. 10 flagged this as unconfirmed, and it remains unconfirmed here; no dedicated rules file for either was found this pass. - Whether a fourth AI persona exists beyond Dirk Rost, Quilly, and Sheila — no roster page surfaced in this pass either; given the cadence of the three found (Jan, then Mar 2026), the count may already be stale by the time this is read.
Sources
- How Soapbox Ships Fast — Soapbox Blog — the Shakespeare/OpenCode/clone-MKStack ladder, producer-role quotes, prompting-as-craft quotes, the 48h Agora/HRF hackathon data point, Dirk Rost's role. Accessed 2026-07-10.
- MKStack — AI-Powered Nostr App Framework — marketing page: named hooks/components, NIP-to-domain mapping, "50+ NIPs" / "8 minutes" claims. Accessed 2026-07-10.
- Meet Quilly — Soapbox Blog — Quilly's role, OpenClaw infrastructure, SOUL.md quote. Published 2026-01-26. Accessed 2026-07-10.
- Announcing Sheila — Soapbox Blog — Sheila's role, OpenCode operation, human-oversight model. Published 2026-03-06. Accessed 2026-07-10.
- mkstack — .agents/skills/nostr-relay-pools/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/nostr-security/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/testing/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/theming/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/nip19-routing/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/file-uploads/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/nwc/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/nostr-encryption/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/ai-chat/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/capacitor/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/edit-profile/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/lockdown-mode/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/nip85-stats/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/nostr-comments/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/nostr-infinite-scroll/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/note-content/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/onchain-bitcoin/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/plausible-analytics/SKILL.md (raw) — accessed 2026-07-10.
- mkstack — .agents/skills/relay-management/SKILL.md (raw) — accessed 2026-07-10.
- This manual, ch. 02 — Community Ops — cross-referenced for Ditto's documented absence of NIP-17/NIP-04 DMs and NIP-29 groups.
- This manual, ch. 06 — Nostr Foundations — cross-referenced for NIP-07/NIP-46 signer mechanics; does not yet document NIP-55.
- This manual, ch. 10 — MKStack — cross-referenced for scaffold anatomy, the AGENTS.md agent contract, and the README-derived NIP-support table; not duplicated here.
- This manual, ch. 15 (forthcoming) — planned home for
.env/VITE_*discipline and key-custody architecture, outsidenostr-security's XSS/CSP scope. - This manual, ch. 11 — Nostrify — cross-referenced for
NStore/NRelay's shared interface shape,NPooloutbox routing and its hardcoded-relay-list finding, the sixNostrSignerimplementations, andNIndexedDB's Lockdown Mode degradation; not duplicated here. - Maintainers' internal production-secrets-longevity audit note (companion document, not part of this public chapter set) — same-day audit grounding the ch. 15 spec; source of the
VITE_public-bundle finding and the zero-Nostr-rules gitleaks finding cited in this chapter's scope warning.
13 — Relay Operations for an App Team
When an app team should run its own Nostr relay, which implementation to run, how to bridge one moderation policy across backends, and the archive/monitoring/backup/spam disciplines none of these ship with by default.
Verified: 2026-07-10
Confidence overview: High for strfry, khatru, and ditto-relay mechanics — all fetched directly from their own GitLab/GitHub READMEs this pass. High for the arXiv economics findings — the paper was fetched directly rather than trusted secondhand, and one correction is logged below: the companion audit's "median zap income ≈$67/6mo" framing understates the problem; the paper's actual figure is a 90th-percentile number among the minority of relays that receive zaps at all. High for NIP-13, NIP-66, and NIP-77 (spec text fetched directly). High for the @nostrify/strfry bridge mechanism (fetched directly); Medium for "the same policy runs on Ditto directly," which is architectural inference (Ditto is Nostrify-native — ch. 11) rather than a demonstrated example. Medium for ditto-relay's CAUTION verdict — real product, real docs, but "no known external production use" is an absence-of-evidence finding. Medium-Low for PlebOne's self-reported "zero spam" claim — no independent audit found. Medium for backup guidance, which is generic infrastructure discipline applied into a confirmed documentation gap, not a Nostr-specific practice.
Why Run Your Own Relay At All
The short version: because on the public network, nobody else is contractually keeping your users' data.
Wei and Tyson's empirical study of the Nostr relay ecosystem [1], measuring 712 relays over a six-month window (2023-07-01 to 2023-12-31), fetched directly for this chapter:
- 95% of free-to-use relays cannot cover their operating costs from zap income alone [1].
- The zap-income picture is worse than a median framing suggests. 64% of relays received zero zap transactions in the entire window. Among the roughly one-third that received any zaps, the 90th percentile totaled only ~150,000 sats (~$67) over six months [1]. There is effectively no meaningful "median zap income" to quote — most relays' zap income is zero, and even the strong performers among the rest are not making a living.
- Most paid relays in the 50-1,000-user "medium" tier can cover their operating costs [1] — the inverse finding, and the basis for the budget note near the end of this chapter.
- No relay or Blossom media host anywhere offers a contractual SLA. This is not from the paper — it is an exhaustive-search finding from the maintainers' companion production-audit notes (§2 fact 1): zero found, searched exhaustively, across every relay and Blossom host list.
Put together: public relays are a cache, not a database. They are mostly run by enthusiasts, funded by donations that mostly never arrive, with no contractual promise to keep an event past today. The NIP-65 outbox model (ch. 06) already assumes multiple write relays for redundancy — but redundancy across relays that all share the same economic exposure is not durability, it is the same failure mode running in parallel.
The rule this chapter follows: if your app has any user whose data needs to outlive the current session, run one relay whose only job is to be the source of truth. Every public relay your app also writes to per NIP-65 is then correctly understood as reach and redundancy — not as the place you depend on for durability.
Archive relay now; public-facing community relay later. These are two different decisions with two different risk profiles, and this chapter is scoped to the first one. An archive relay that only pulls copies of your own app's events and never accepts public writes carries the durability benefit above with almost none of the downside — it isn't "mere hosting" for anyone else's content. A public-facing relay that accepts writes from strangers is a heavier legal class: Section 230 does not cover federal criminal CSAM liability, and EU DSA liability attaches on actual notice plus failure to act (ch. 15). Stand up the archive relay now; defer opening it to public writes until a moderation posture — report queues, a takedown process, a human in the loop — actually exists.
Implementation Menu
| Relay | Stack | Best for | Maturity / adoption | Operational notes |
|---|---|---|---|---|
| strfry | C++ / LMDB, no external DB | Your archive relay; raw throughput | Mature — runs on ~23% of reachable relays, the most-deployed single implementation after nostr-rs-relay [16] | Originated the negentropy sync protocol now used ecosystem-wide [4]; strfry export --fried / strfry import moves JSONL; strfry sync <url> (up/down/both) reconciles against a remote relay; first-party Prometheus /metrics [4] |
| khatru | Go framework, not a turnkey relay | Bespoke relay logic — custom accept/reject policies, custom AUTH, custom storage | Framework-maturity, judged by what's built on it — GRASP's reference implementation ngit-relay is a khatru relay [6] | Hook-based: StoreEvent / QueryEvents / DeleteEvent / RejectEvent callback arrays [5]; pairs with the eventstore module for SQLite/Postgres/LMDB-backed storage [5] |
Ditto's built-in /relay | Deno + PostgreSQL | Teams already self-hosting Ditto for community ops — the relay ships free inside the server | Mature, ~2 years of public Ditto releases (ch. 02) | Recommended VPS: 4 cores / 8GB RAM / 100GB disk [13]; NIP-50 full-text search via Postgres; full self-host runbook already lives in ch. 02 — not repeated here |
| ditto-relay (separate product) | Bun runtime + OpenSearch | Apps that need NIP-50 search with language: / sentiment: / media: filters plus a trending engine feeding NIP-85 signed assertions | CAUTION — born 2026-02-08, ~5 months old; 99.3% single-author; no known external production deployment | NIPs: 01/09/11/40/42/45/50/62/70/77/85 [2]; worker pool keeps signature verification, language detection, sentiment analysis, and media detection off the hot path [2]; stateless, scales horizontally behind a load balancer [2]; Prometheus /metrics [2]; NOSTR_NSEC is a required plain env var — flag this now, full treatment in ch. 15 (secrets) |
The @nostrify/strfry Policy Bridge
Nostrify's moderation model is NPolicy — a class with one method that looks at an event and returns accept or reject (ch. 11). Nineteen ship in @nostrify/policies, from WoTPolicy to PowPolicy to an OpenAI-backed content-scoring policy. The problem this section solves: strfry doesn't speak TypeScript, so how does a Deno/TS moderation policy reach a C++ relay?
@nostrify/strfry is the bridge. strfry's own plugin interface reads accept/reject decisions over stdin/stdout; the package wraps that protocol so any composed NPolicy can sit behind it [3]. Its own example composes six policies through PipePolicy — FiltersPolicy (kind allow-listing), KeywordPolicy (banned phrases, e.g. Telegram-link spam), RegexPolicy, PubkeyBanPolicy, HellthreadPolicy (reply-storm limiting), and AntiDuplicationPolicy (Deno KV-backed) — then a single call connects it to strfry [3]. In shape:
const policy = new PipePolicy([
new FiltersPolicy([/* allowed kinds */]),
new KeywordPolicy([/* banned phrases */]),
new PubkeyBanPolicy([/* banned pubkeys */]),
// WoTPolicy / PowPolicy from the same @nostrify/policies library slot in the same way
]);
await strfry(policy); // wires the composed policy to strfry's stdin/stdout plugin interface
The same policy object also runs directly inside Ditto, since Ditto is Nostrify-native (ch. 11) — write the moderation set once, point it at either backend. That is the whole value of the bridge: abuse and spam rules stop being a strfry-specific plugin script or a Ditto-specific admin toggle and become one portable TypeScript module.
The Archive Pattern
Your archive relay's job is to hold a durable copy of every event your app or its users have written — not to serve production traffic.
Three moving parts:
- Sync in.
strfry sync <relay-url> --dir downpulls everything from each relay your app writes to, using negentropy set-reconciliation so only the delta transfers [4]. Run this against every relay in your app's NIP-65 write set, on a schedule. - Export out.
strfry export --friedwrites JSONL with precomputedfriedelements to stdout; cron it to cold storage — object store or offsite disk — on whatever cadence matches your data's value [4]. - The negentropy caveat. NIP-77, the spec negentropy implements, is merged into the NIPs repo as draft, optional [15]. "Merged" describes the spec's repo status, not relay adoption — most public relays you sync from will not support it,
strfry syncfalls back to slower reconciliation against those, and your schedule should assume the slow path is the common path, not the exception.
Minimum viable archive setup:
- Provision a small VPS — strfry's LMDB footprint is modest, and there is no separate database process to run [4].
- Install strfry, point its config at local storage, start it as a service [4].
- For each relay in your app's NIP-65 write set, run
strfry sync <relay-url> --dir downonce to seed the archive from history [4]. - Cron the same sync command going forward — hourly or daily depending on write volume.
- Cron
strfry export --fried > backup-$(date +%F).jsonland ship the file to cold storage [4]. - Wire
/metricsinto whatever Prometheus instance you already run (Monitoring, below) [4]. - Do not open the relay to public writes — see the archive-vs-community-relay distinction above.
Monitoring
| Surface | What it gives you | Coverage | Notes |
|---|---|---|---|
| NIP-66 / nostr.watch | Liveness + capability observations published as ordinary Nostr events — kind 30166 discovery events, kind 10166 monitor announcements [7] | 8 automated monitoring stations across 6 continents [8] | Decentralized by design — anyone can run a competing monitor; no single authority owns the data [8] |
| First-party Prometheus | /metrics scraped directly off your own relay process | Ships in strfry [4], nostr-rs-relay [14], and ditto-relay [2]; Ditto (Deno+Postgres) documents metrics too [12] | No first-party alerting rules ship with any of them |
| Community Grafana dashboard | Pre-built panels for Ditto's Prometheus output | Dashboard ID 21844 [11]; a second, independent community project (Ditto-Dash) also exists | Neither is a Soapbox first-party artifact |
No alerting norms exist anywhere in this stack — no shipped Alertmanager rules, no documented paging thresholds, nothing Nostr-specific. Inherit generic Prometheus/Alertmanager discipline (disk-full, process-down, sync-lag, queue-depth) the same way you would for any other self-hosted service; nothing about Nostr changes what a relay alert should watch for.
Backup
| Component | Backup mechanism | Status |
|---|---|---|
| Ditto (Postgres) | Generic pg_dump / pgBackRest discipline | No first-party Ditto backup runbook exists — confirmed absent across two research passes; you own this |
| strfry (LMDB) | strfry export --fried to JSONL, already covered under Archive Pattern above | Doubles as both your sync mechanism and your backup/restore mechanism |
| ditto-relay (OpenSearch) | OpenSearch's own snapshot/restore API | Generic OpenSearch discipline — the product itself is too young to have documented backup guidance |
The pattern across this whole stack: newer, thinner products document search and moderation in detail and say nothing about disaster recovery. Treat that absence as the default, not the exception, and provision backup discipline yourself regardless of which relay you run.
Spam and Abuse Controls
| Control | Mechanism | Precedent |
|---|---|---|
| Proof of work | NIP-13: difficulty = leading zero bits of the event id; a relay advertises its floor via NIP-11's min_pow_difficulty and rejects anything below it [9] | PowPolicy ships in @nostrify/policies (ch. 11) — same bridge as above |
| Relay-level auth | NIP-42: gate REQ/EVENT behind an authenticated pubkey before accepting writes or serving reads (ch. 06 — stable, gates paid and DM-sensitive traffic) | khatru exposes custom AUTH handlers [5]; Ditto and strfry both support NIP-42 |
| Web-of-trust filtering | Accept events only from pubkeys within N hops of a trusted set | PlebOne (relay.pleb.one): a free, public WoT relay self-reporting zero spam — no payment required, the trust graph does the filtering instead [10] |
WoTPolicy ships alongside PowPolicy in the same @nostrify/policies library (ch. 11) — both the PlebOne pattern and the PoW pattern above are one PipePolicy line away, on either backend.
Budget
The economics in the first section converge on one number: at least one relay in your budget should be paid. Free relays fail the durability test 95% of the time; medium-scale paid relays (50-1,000 users) mostly clear their costs [1]. This does not require a commercial paid relay service specifically — a VPS you pay for and control is functionally a paid relay in the paper's own terms: money in, durability out. What it rules out is treating an entirely free relay stack as your durability plan. Line-item this before scoping the app budget, not after.
TresPies Hooks
What follows is the maintainers' own integration notes, published as a worked example of where relay-ops decisions intersect a real firm's other priorities — adapt the shape, swap the specifics for your own.
Two hooks worth flagging, not resolving here. First: ditto-relay's NIP-50 language:<code> search filter [2] means the maintainers' own bilingual thesis is not only a UI/content decision — it is a data-layer one. A relay or search index that filters and ranks language:es results natively is infrastructure for their Spanish-language product surface, not a nice-to-have bolted on later — swap in whatever locale your own product targets. Second, a concrete note from the maintainers' own backlog: a relay-provisioning task is already queued on their operator side — this chapter does not act on it; the general lesson is to run any queued infrastructure task through your own team's intake process before touching it.
Open Questions
- Whether
@nostrify/strfry'sPipePolicycomposition runs byte-identical inside Ditto's own moderation config, or needs a thin Ditto-specific wrapper — asserted as architectural inference from Ditto being Nostrify-native (ch. 11), not confirmed against a working Ditto deployment this pass. - Whether ditto-relay's trending engine (followers/engagers/comment-count scoring feeding signed NIP-85 assertions [2]) has been independently verified to produce sane rankings at any real scale — the README documents the mechanism, not a production trace.
- The exact
strfry syncbehavior against a relay that only partially supports negentropy (advertises it but times out) is not documented in the fetched README [4] — worth a smoke test before depending on the archive pattern's schedule. - PlebOne's "zero spam" claim [10] is the relay operator's own framing; no independent measurement of its web-of-trust filter's false-negative rate was found.
Sources
- Yiluo Wei and Gareth Tyson, "An Empirical Analysis of the Nostr Social Network: Decentralization, Availability, and Replication Overhead," arXiv:2402.05709v2. Accessed 2026-07-10. Supports: 95% free-relay cost-coverage failure, 64% zero-zap relays, 90th-percentile zap income (~150k sats / ~$67 over 6 months), medium-scale paid-relay cost coverage.
- gitlab.com/soapbox-pub/ditto-relay/-/raw/main/README.md. Accessed 2026-07-10. Supports: ditto-relay stack (Bun + OpenSearch), NIP list, NIP-50
language:/sentiment:/media:filters, trending engine feeding NIP-85 publishing, worker-pool/stateless/horizontal-scaling architecture, Prometheus/metrics,NOSTR_NSECenv var. - gitlab.com/soapbox-pub/nostrify/-/raw/main/packages/strfry/README.md. Accessed 2026-07-10. Supports:
@nostrify/strfrybridge mechanism, stdin/stdout connection to strfry's plugin interface,PipePolicycomposition example. - github.com/hoytech/strfry (README). Accessed 2026-07-10. Supports: strfry stack (C++/LMDB), negentropy sync origin,
export --fried/importJSONL,strfry synccommand, Prometheus/metrics. - github.com/fiatjaf/khatru (README). Accessed 2026-07-10. Supports: khatru as a Go framework (not turnkey), hook-based architecture (
StoreEvent/QueryEvents/DeleteEvent/RejectEvent),eventstore-backed storage options, custom AUTH handlers. - ngit.dev/grasp/ — "Grasp: Git Repositories Authorized via Signed-Nostr Proofs." Accessed 2026-07-10. Supports: GRASP's reference implementation
ngit-relayruns on khatru. - nips.nostr.com/66 — NIP-66, Relay Discovery and Liveness Monitoring. Accessed 2026-07-10. Supports: kind
30166(discovery) /10166(monitor announcement) event definitions. - Search-index results on github.com/sandwichfarm/nostr-watch and nostr.watch. Accessed 2026-07-10. Supports: nostr.watch's 8 automated monitoring stations across 6 continents, decentralized-monitor design.
- nips.nostr.com/13 — NIP-13, Proof of Work. Accessed 2026-07-10. Supports: difficulty = leading zero bits of event id; PoW as spam deterrence; delegated PoW for constrained devices.
- github.com/PlebOne/relay.pleb.one. Accessed 2026-07-10. Supports: free public web-of-trust relay self-reporting zero spam without payment.
- grafana.com/grafana/dashboards/21844-ditto/. Accessed 2026-07-10. Supports: community Grafana dashboard ID 21844 for Ditto's Prometheus metrics.
docs.soapbox.pub/ditto/metrics(301 redirect toabout.ditto.pub). Accessed 2026-07-10. Supports: Ditto's own metrics documentation is mid-migration (consistent with ch. 02's finding); Ditto exposes Prometheus metrics.- Nostr note (nevent), recommended Ditto VPS specs, surfaced via search index on nostr.com. Accessed 2026-07-10. Supports: 4 cores / 8GB RAM / 100GB disk recommended for self-hosting Ditto.
- nostr-rs-relay community references (nutcroft.mataroa.blog; github.com/scsibug/nostr-rs-relay), via search index. Accessed 2026-07-10. Supports: nostr-rs-relay exposes a first-party
/metricsendpoint for Prometheus. - nips.nostr.com/77 — NIP-77, Negentropy Syncing. Accessed 2026-07-10. Supports: range-based set reconciliation; spec status "draft, optional" — merged into the NIPs repo, not a relay-adoption guarantee.
- nostr.watch relay-software statistics, via search index. Accessed 2026-07-10. Supports: strfry runs on 283 of 1,341 reachable relays (~23.1%), the software-share figure used in the Implementation Menu.
14 — Distribution and Native for Consumer Nostr Apps
How a consumer Nostr app actually reaches a phone: Zapstore's permissionless Android store, a Capacitor wrap for native features without touching Swift or Kotlin, what Apple's Lockdown Mode silently breaks, and the standards gap — no Nostr NIP for push — that a PWA install papers over but doesn't solve.
Verified: 2026-07-10
Confidence: High for Zapstore's publishing/verification mechanics (its own FAQ, publish guide, and OpenSats grant page, fetched directly) and for the absence of a ratified Nostr push NIP (the full NIP index scanned directly, plus the one serious prior attempt read in full). High for NIP-55's same-device signing model (spec fetched directly) and for the capacitor/lockdown-mode skill content (both SKILL.md files fetched directly). Medium for Ditto's current iOS distribution — its own install guide and a live App Store search result disagree, and this pass didn't reconcile them. Medium for the two Apple 2026 policy facts (each confirmed against a primary Apple page, but that's two targeted checks, not a guideline audit). Low for "no Nostr-specific App Store friction" as a general claim — Apple's Bitcoin/crypto-payment provisions (Guideline 3.1.5) weren't examined this pass.
1. The channel map
A consumer app built on MKStack ends up choosing among four channels: ship as a PWA (zero review, zero native code, but a background-push hole under one condition below), or wrap it in Capacitor for Google Play, Zapstore, and/or the App Store. They aren't mutually exclusive — one Capacitor-wrapped codebase produces both Android artifacts, and most teams ship the PWA on day one regardless of native plans.
2. Zapstore — Android's permissionless catalog
Zapstore is not a Soapbox product. It's an independently OpenSats-funded project (10th Wave of Nostr Grants, announced 2025-03-27 [1]), founded by Franzap and incubated by And Other Stuff, with OpenSats and HRF named as donors [2]. Its own tagline states the design goal plainly: "You shouldn't need permission to use apps" [2]. Today it's Android-only — "Available for Android and more platforms soon," with iOS on the roadmap for Q3 2026 [2] — running 3,000+ listed apps and roughly 4,000 active users [2]. Ditto ships through it alongside Google Play and the web, as one Android option among several [2][5] — but it isn't Soapbox's channel; Soapbox is a customer of it, same as any other Android Nostr app (Agora is also Zapstore-only on Android, no iOS listing found — ch. 03).
Publishing is free, permissionless, and keypair-keyed [1][4]:
| Step | Mechanism |
|---|---|
| Install | zsp CLI (go install github.com/zapstore/zsp@latest) [4] |
| Configure | zsp publish --wizard walks source selection, metadata, signing, and writes zapstore.yaml to the repo — committed, not gitignored [4] |
| Sign | SIGN_WITH env var: nsec1..., a hex key, a bunker:// NIP-46 URL, or browser (NIP-07) — bunker URLs as CI secrets are the recommended non-interactive path [4] |
| Link identity to build | First publish requires the actual Android signing keystore (.jks/.p12/.pem) to cryptographically link the APK's signing certificate to the Nostr pubkey — this is NIP-C1, certificate linking [4] |
| Get whitelisted | Wizard-written zapstore.yaml commits the pubkey to the repo; the relay fetches and verifies the match on first publish — or skip repo verification entirely if the npub already carries social reputation, via the Vertex system [4] |
No listing fee, no revenue share, no review queue — updates ship "as often as you like" [3]. Verification at install time stacks two independent checks, not one: a Nostr-layer check (signature on the catalog event, publisher identity) and an Android-layer check (the APK's actual signing certificate compared against the NIP-C1 declaration, enforced by Android's own OS-level cert-pinning at install time) [3]. The Nostr key signs metadata — explicitly not a substitute for the APK's own Android signing certificate [3].
Ditto's own distribution is where this chapter corrects the manual's working assumption. Soapbox's install guide confirms Android ships as a real native app via Google Play ("the standard way") and Zapstore, with the Android build carrying "full push notifications, secure credential storage, native file downloads" [5]. iOS, per that same guide, is web-app-only today — Safari's Share sheet → "Add to Home Screen" → a full-screen installed PWA with working push — with a native app explicitly framed as "in development," not shipped [5]. A live App Store search independently surfaced a listing, "Ditto: Fun Social Media" under Soapbox Technology LLC [6] — but the fetched page returned stale version metadata predating this year's relaunch, so it's unclear whether that's a dormant earlier build, a soft-launched beta, or a caching artifact. Treat iOS-native Ditto distribution as unconfirmed; verify live before promising a client an App Store listing exists.
3. Zapstore's neighbor: the App Store, mid-2026
No Nostr-specific App Store friction turned up in this pass — no rejection pattern, no guideline naming Nostr, zaps, or decentralized identity as a special case. What did turn up are two generic 2026 rules that apply to any MKStack-derived native build:
| Rule | Since | What it means for a Capacitor-wrapped MKStack app |
|---|---|---|
| iOS 26 SDK / Xcode 26 mandatory | 2026-04-28, already in effect [7] | Every new submission and every update to an existing app must be built with Xcode 26 against an iOS/iPadOS/tvOS/visionOS/watchOS 26 SDK — deployment target (oldest device supported) is unaffected, only the build toolchain. Apps built against the 26 SDK also inherit Apple's Liquid Glass UI treatment on native chrome by default [7]. |
| Guideline 5.1.2(i): third-party AI data sharing | Added 2025-11, still current | Verbatim: "You must clearly disclose where personal data will be shared with third parties, including with third-party AI, and obtain explicit permission before doing so" [8]. Any MKStack app wiring up the ai-chat skill (ch. 12, forthcoming) and sending user messages to an external LLM needs an explicit opt-in screen before that first request — a privacy-policy mention alone doesn't satisfy this. |
Confidence: Medium — both rules confirmed against Apple's own pages, but this is two targeted lookups, not a guideline audit; Bitcoin/crypto-specific provisions weren't checked.
4. Capacitor — native features without Swift or Kotlin
The capacitor MKStack skill wraps a web app into iOS and Android binaries, exposing native capability through helper functions the app calls unconditionally — on web they no-op or fall back, on native they hit the real API [9]:
| Web behavior | Capacitor-native behavior |
|---|---|
| Basic vibration | Haptics — taptic engine (iOS) / Android haptics [9] |
| Browser download | Write directly to the app's Documents directory [9] |
<a href> to an external URL | Native share sheet [9] |
localStorage | iOS Keychain / Android KeyStore, with automatic migration of any plaintext values already stored [9] |
| Client-side route change only | OS deep-link (app-open) events routed through React Router [9] |
| CSS-only notch handling | Safe-area Tailwind utilities for the actual notch/gesture-bar geometry [9] |
The design rule is explicit in the skill itself: helpers are "SSR-safe and web-safe — import and call unconditionally from shared components" [9] — there's no if (native) branching scattered through the app. Setup is mechanical: install the Capacitor runtime, plugins, and CLI; copy eight utility files plus one config file into the project; register the safe-area Tailwind plugin; call bootstrapNative() before React mounts; render <DeepLinkHandler /> inside the router; then npx cap add ios / npx cap add android and build [9]. It's opt-in — "not included in the project by default" [9]. Agora and Armada's Android builds already run on Capacitor (ch. 02, ch. 03); this pass didn't independently confirm Ditto's own native build uses the same skill rather than a separate codebase.
5. Lockdown Mode — the sandbox gets smaller
Apple's Lockdown Mode (opt-in, iOS/iPadOS 16+, macOS Ventura+, watchOS 10+) restricts the exact WKWebView shell a Capacitor app — and any PWA — runs inside [10]. The restrictions aren't cosmetic:
| Blocked entirely | Still works |
|---|---|
| IndexedDB, Service Workers, Cache API, WebAssembly, Web Locks, WebRTC (critical/high) [10] | localStorage/sessionStorage, fetch/XHR, WebCrypto (crypto.subtle), Credential Management, cookies, inline PDF [10] |
WebGL, FileReader, OPFS, SharedArrayBuffer (medium) [10] | File constructor, URL.createObjectURL() [10] |
| Notifications API, WebCodecs, Gamepad, Web Share, Speech Synthesis (low) [10] | — |
The skill's own recommendations: localStorage as the primary store, no hard IndexedDB dependency (in-memory fallback instead), pure-JS crypto (@noble/secp256k1) rather than WASM builds, WebGL treated as progressive enhancement only, and native Capacitor equivalents (@capacitor/share, @capacitor/local-notifications, @capacitor/filesystem) standing in for the blocked web APIs [10]. There is no official detection API for Lockdown Mode — the only way to know is to probe the disabled surfaces directly, and testing on a real Lockdown-enabled device beats trusting feature-detection alone [10].
This is exactly where MKStack's NIndexedDB — the persistent browser cache on the same NStore interface, covered in ch. 11 (Nostrify, forthcoming) — earns its keep: it's built directly on the API this table strips out, and it's designed to degrade gracefully when IndexedDB isn't there. The localStorage-first discipline recommended above is that degradation path, not a separate concern.
6. PWA install and push — a real gap, not a documentation gap
Installing a Nostr PWA is unremarkable: Safari's Share → "Add to Home Screen" produces a full-screen app with working push, confirmed directly on Ditto's own install guide [5]. The mechanism underneath is entirely generic — the Push API, the Notification API, and a Service Worker to receive events in the background. No Nostr-specific push standard exists, and none of MKStack's 19 skills covers it — confirmed by scanning the full NIP index directly: nothing numbered, lettered, or hex-coded addresses push or notification delivery [11].
One serious attempt exists, and it's instructive in exactly the way ch. 08's language-tag story is instructive:
| Proposal | Author | Status | What it would have added |
|---|---|---|---|
| "Push Notification — Event Watcher API" (PR #1528) | vitorpamplona (Amethyst) | Closed, unmerged, 2025-07-31 | A NIP-96-shaped registration spec for push-relay servers: POST interface, multi-account registration in one event, wrapped encryption to hide the destination pubkey, multi-server/relay/token support [12] |
It wasn't a paper proposal — it had "been running on Amethyst for the last year or so" before the PR was even opened [12]. It still didn't merge; the author's own closing note: "after seeing how well Pokey has been doing, I am not sure if this idea is worth pursuing anymore" [12]. Read plainly: this is an ecosystem gap, and it's contribution-shaped — the same class of opening as the es-i18n upstream play in ch. 08. A working reference implementation already ran in production; what's missing is someone carrying it (or a redesign) back through ratification, or an MKStack skill that at least standardizes the non-Nostr fallback so app teams stop re-solving it per app.
The Lockdown Mode interaction from §5 matters here directly: Web Push requires an active Service Worker to receive events in the background, and Service Workers sit in Lockdown Mode's blocked-entirely tier [10]. So the fallback isn't "PWA push degrades" — under Lockdown Mode it's absent, full stop, regardless of the Notification API's own lower severity rating there. The only remaining path for a Lockdown Mode user who needs real background push is a Capacitor-native build wired to @capacitor/push-notifications (APNs/FCM) — not @capacitor/local-notifications, which only schedules device-local notifications and doesn't solve remote delivery at all. Don't conflate the two when scoping a build.
7. Signer distribution for consumer onboarding
Ch. 06 already covers NIP-07 (browser extension) and NIP-46 (relay-based remote signing/bunker) as two of the three standards Soapbox names for signing without ever handling a raw nsec. NIP-55 is the third, and it's new to this manual. It solves a narrower problem than NIP-46: 2-way communication between an Android signer app and a Nostr client on the same device, via Android Intents (a visible approval popup, one per request) or a Content Resolver (silent, background, only for previously "remember my choice" grants) [13]. The spec is explicit about why you'd pick the other standard instead: "Consider using NIP-46 for web applications. With the approach here the web client can't call the signer in the background, so the user sees a popup for every request" [13] — NIP-55 is same-device-only and relay-free; NIP-46 is cross-device and relay-based. The spec text names no reference implementation, but the ecosystem's is unambiguous: Amber, whose author is widely credited with having written NIP-55 itself [14].
diVine (divine.video) — Vine's 6-second-loop format rebuilt on Nostr [15], independent of Soapbox, which has only written about it, not built it [17] — takes a third onboarding path worth contrasting: Keycast, a managed key-custody service offering OAuth2 login with NIP-46 remote signing underneath, keys encrypted server-side rather than held in a device app [16]. That's a materially different trust model from Amber's on-device custody — closer to "sign in with Google" UX at the cost of a custodian in the loop — and a legitimate pattern for a consumer app whose users won't install a second app just to hold a key.
| Standard | Custody | Device model | Best fit |
|---|---|---|---|
| NIP-07 | Browser extension storage | Single machine | Daily web use — ch. 06 |
| NIP-46 | Signer app/daemon/service | Cross-device, relay-based | Multi-device, team accounts, OAuth-style services like Keycast |
| NIP-55 | Signer app (e.g. Amber) | Same Android device only | Mobile-first, no relay dependency, visible per-request consent |
8. Nostr-native docs, dogfooded
Stacks' own onboarding practices what it sells: its Getting Started flow has a new user run stacks add naddr1qvzqqqrhl5pzqprpljlvcnpnw3pejvkkhrc3y6wvmd7vjuad0fg2ud3dky66gaxaqqy8qeedwfjkcctehjfz9x to pull in a starter template, then stacks list and stacks mkstack [18] — the first thing the platform teaches a new user is the exact kind-30717 template-publishing mechanism it runs on. The intended companion piece — NostrDeploy.com as the index/gateway for the nsite+Blossom deploy lane — is DNS-dead as of this manual's 2026-07-10 delta pass; full tripwire detail lives in ch. 10 and ch. 04 and isn't re-litigated here.
Open Questions
- Whether Ditto ships a native iOS app today or the App Store listing found is stale/dormant — the primary install guide and a live App Store search disagree; re-check before any client commitment.
- Whether Ditto's own native Android build actually uses the
capacitorMKStack skill or a separate codebase — the feature list matches closely but wasn't independently confirmed. - Whether PR #1528's design (or a successor) gets picked up by anyone in the MKStack/Soapbox orbit specifically — no roadmap signal found either way.
- Apple's Bitcoin/Lightning/zap-specific App Store provisions (Guideline 3.1.5 family) — out of scope this pass, worth a dedicated check before an app with native zaps ships to the App Store.
Sources
- OpenSats — Zapstore — 10th Wave of Nostr Grants (announced 2025-03-27), mission statement, ecosystem scope (relays, indexer, signing tools,
zapstore-cli). Accessed 2026-07-10. - zapstore.dev — homepage: tagline, founder (Franzap), incubator (And Other Stuff), donors (OpenSats, HRF), Android-only today / iOS Q3 2026 roadmap, 3,000+ apps / ~4,000 active users. Accessed 2026-07-10.
- zapstore.dev/docs/faq — publishing model (free, permissionless, no fees/review queue), dual Nostr+Android verification model. Accessed 2026-07-10.
- zapstore.dev/docs/publish and zapstore.dev/docs/quickstart —
zspCLI,zapstore.yaml,SIGN_WITHsigning methods, NIP-C1 certificate linking, whitelisting/Vertex-reputation bypass. Quickstart returned mostly navigational content; mechanism detail sourced from the publish guide. Accessed 2026-07-10. - soapbox.pub/blog/how-to-install-ditto-mobile — Android via Google Play + Zapstore with native-app feature list; iOS as PWA-only today with native "in development"; web at ditto.pub. Accessed 2026-07-10.
- Ditto: Fun Social Media — App Store — live listing under Soapbox Technology LLC, but returned stale version metadata (2.23.4, dated 2024) inconsistent with the 2026 relaunch timeline; conflict flagged, not resolved. Accessed 2026-07-10.
- Apple Developer — Upcoming Requirements — Xcode 26 / iOS 26 (+ iPadOS/tvOS/visionOS/watchOS 26) SDK requirement for all App Store Connect uploads, in effect since 2026-04-28; applies to build toolchain, not deployment target. Accessed 2026-07-10.
- TechCrunch — Apple's new App Review Guidelines clamp down on apps sharing personal data with 'third-party AI' — Guideline 5.1.2(i) verbatim text, third-party-AI-specific disclosure/consent requirement. Accessed 2026-07-10.
- gitlab.com/soapbox-pub/mkstack —
.agents/skills/capacitor/SKILL.md(raw) — full skill content: haptics, native downloads, share sheet, Keychain/KeyStore storage, deep linking, safe areas, setup steps, SSR/web-safe design rule. Accessed 2026-07-10. - gitlab.com/soapbox-pub/mkstack —
.agents/skills/lockdown-mode/SKILL.md(raw) — full skill content: platform availability, blocked/available API tables, storage/crypto/media recommendations, native-alternative mapping, no official detection API. Accessed 2026-07-10. - github.com/nostr-protocol/nips — README (NIP index) — full index scanned directly; no NIP addresses push notifications or web/mobile push delivery. Accessed 2026-07-10.
- github.com/nostr-protocol/nips — PR #1528, "Push Notification - Event Watcher API" — author vitorpamplona, closed unmerged 2025-07-31, in production on Amethyst for roughly a year before submission, verbatim closing quote. Accessed 2026-07-10.
- github.com/nostr-protocol/nips — 55.md — full NIP-55 spec text: Intent/Content-Resolver mechanism, same-device scope, verbatim comparison to NIP-46. Accessed 2026-07-10.
- github.com/greenart7c3/Amber — Android NIP-55 signer app, corroborating community sources (reviews, client lists) crediting its author with authoring NIP-55. Accessed 2026-07-10 (also cited in ch. 06 as source 13).
- divine.video and about.divine.video/faqs — diVine product description (Nostr-native short-form video), independence from Soapbox. Accessed 2026-07-10.
- github.com/divinevideo/keycast — Keycast managed key-custody service: OAuth2 login, server-encrypted keys, NIP-46 bunker / HTTP RPC signing. Accessed 2026-07-10.
- soapbox.pub/blog/what-is-nostr-divine — Soapbox explainer post about diVine, confirming an editorial rather than ownership relationship. Accessed 2026-07-10.
- getstacks.dev — Getting Started flow (
stacks add naddr1...→stacks list→stacks mkstack); homepage is a client-rendered SPA (confirmed via direct fetch of an empty#rootshell), so the exact command was recovered via a search-index cache rather than a direct page render. Accessed 2026-07-10.
15 — Production: Durability, Secrets, Law, Longevity
This is the chapter that decides whether "we run on Nostr" survives contact with a lost key, a deleted file, a subpoena, or a regulator's letter. Chapters 01-13 answer "how does this work." This one answers "what breaks, who is liable when it does, and what do we do before it happens" — the audit rendered as teaching. Grounded in a same-day, three-agent production audit (durability economics, secrets/custody, longevity) plus live re-verification of every load-bearing citation below.
Verified: 2026-07-10
Confidence
| # | Section | Confidence | Why |
|---|---|---|---|
| 1 | Data-durability economics | High | Direct fetch of the source paper's own numbered sections; two figures corrected from this chapter's source audit (below) |
| 2 | Media persistence | High | Direct ToS + spec fetch; the media-cap figure is corrected from the source audit |
| 3 | Deletion vs. privacy law | Medium — inference-flagged | NIP text is High; "no ruling exists" is a documented absence, not a citable presence; the defensible-posture recommendation is explicitly SYNTHESIZED, not documented industry practice |
| 4 | Operator legal exposure | Medium | Statute/regulation text is High; applying it to a Ditto instance's classification is reasoned analysis, not an actual ruling; the nearest precedent is fediverse (Mastodon), not Nostr-specific |
| 5 | Degradation engineering | High | Direct fetch of Nostrify and nostr-ux.com docs; corrects this chapter's own source audit's premise (below) |
| 6 | Monitoring + backup | High for spec/tooling; Medium for the Ditto-runbook absence (a negative claim) | NIP-66/Prometheus/Grafana directly confirmed live |
| 7 | Secrets + custody | High | Verified against the maintainers' own configuration files, not just vendor docs |
| 8 | Longevity | Medium-High | Bus-factor/funding figures are this session's own audit measurement, not independently re-run here; pinning/remote evidence is directly verified against the maintainers' own repository |
| 9 | Incident catalog | Medium | The Black Hat paper is High; two of the source audit's named incidents could not be verified live — substituted with verified adjacent evidence, flagged below |
1. Data Durability: The Economics of "Free"
A 2025 measurement study of the live network — Wei and Tyson, "An Empirical Analysis of the Nostr Social Network: Decentralization, Availability, and Replication Overhead" (arXiv:2402.05709v2, accepted CoNEXT '25) [1] — puts a number on what operators feel anecdotally: free relay infrastructure does not pay for itself. Its own finding: "our estimates show 95% of the free-to-use relays cannot cover their operational cost" from donations/zaps alone [1]. Not a governance failure — the base economics of the model.
Correcting this chapter's source audit: the audit cited "median zap ≈$67." The paper reports no median — it reports a 90th percentile: relays that receive zaps at all see a 90th-percentile total of 150,000 sats (≈$67) [1]. That is a more damning number, not the same one — if the top 10% of zap-earning relays top out near $67, the true median sits lower. Separately, 64% of all relays receive zero zaps in the observed window [1]. The paper describes medium-scale paid relays (50-1k users) only qualitatively as "most... able to cover their operational costs" [1] — no exact percentage is given, so this manual does not repeat a "64% of medium paid relays" figure; that 64% belongs to a different statistic (relays earning nothing at all).
What the paper measures precisely is replication and waste: the average post lands on 34.6 relays across 12.4 distinct Autonomous Systems [1] — real redundancy — while 98.2% of download/retrieval traffic network-wide is duplicate, an estimated 144 TiB wasted [1]. The network already over-replicates data it then fails to fund.
The zero-SLA finding. No relay or Blossom media host surfaced in this or the source audit's research publishes a contractual uptime or durability guarantee — free or paid. Public infra is a cache. An owned archive relay, plus BUD-04 media mirroring (§2), is storage.
The practical answer: own your archive relay. NIP-65 already establishes that a user's write relays are the outbox clients SHOULD check first [2]. The corollary for an operator: run a self-hosted strfry as one of your write relays, treat it as source of truth, and let every public relay you also write to be exactly what §1's economics say it is — a cache with no promises attached.
The negentropy caveat. NIP-77 wraps the Negentropy set-reconciliation protocol so an archive relay can catch up with peers without re-transferring events it already holds [3]; it carries draft optional status as of this verification [3]. Merged into the spec is not the same claim as deployed across the relay population — most public relays do not yet support it. Budget for targeted REQ backfill from each write relay as the default, with negentropy as an accelerant once a peer confirms support, not a dependency.
2. Media Persistence: Blobs Are Not Backups
nostr.build's free-tier Terms of Service state that for free-service media, nostr.build "has the unrestricted right to delete any uploaded content" [4], and more broadly that it "may ban any user, delete any Media, or terminate any account for any reason or no reason, at their sole discretion, without notice or liability" [4]. Paid plans carry one floor free accounts don't: media stays online for a stated period after account expiration [4] — implying no equivalent floor below that tier.
Correcting this chapter's source audit: the audit cited a "100MB cap." Live-checked against nostr.build's own plan and README pages, the actual free-tier ceiling is far lower — 5MB per file on the free service, roughly 20MB as a hard limit on unauthenticated/free uploads; the entry paid tier raises that to 450MB per file. Treat "100MB" as a figure this manual cannot stand behind; re-check the live plan page at time of adoption, since these numbers move.
The resilience mechanism is BUD-04, not hope. Blossom's server-to-server mirroring spec defines a PUT /mirror endpoint: server B fetches a blob from a URL on server A, verifies the downloaded content's hash against the authorization token, and stores its own copy [5]. Paired with BUD-03 (a user's published server list, per ch06 §5), mirroring is what makes a single host's ToS survivable — if nostr.build deletes a file, a client can still resolve it from another server in the uploader's list, provided a mirror was actually made first. Mirroring is opt-in and per-upload; nothing forces it to happen. Any media this organization treats as important — logos, policy-post attachments, anything cited from a long-form NIP-23 post — needs an explicit mirror-on-upload step to a server you control, not an assumption that Blossom's redundancy story covers you by default.
3. Deletion vs. Privacy Law
Two Nostr-native deletion primitives exist, and they are not the same strength. NIP-09 is advisory: relays "SHOULD" honor a kind-5 deletion request [6]. NIP-62 ("Request to Vanish") is the strong version, merged into the spec on 2025-02-19 [7] — missing from this manual's ch06 NIP table, which currently jumps 61 to 65; add it there. Its language is MUST-level: "Relays MUST fully delete any events from the .pubkey if their service URL is tagged in the event," relays "MUST ensure the deleted events cannot be re-broadcasted," and even "paid relays or relays that restrict who can post MUST also follow the request to vanish regardless of the user's status" [7]. Merged status is not the same claim as universal implementation — adoption is thin, and this manual found no data quantifying it.
No Nostr-specific privacy ruling exists anywhere. This chapter searched for a GDPR or CCPA enforcement action, regulatory guidance, or court decision naming Nostr specifically and found none. The nearest general analog: the European Data Protection Board finalized blockchain-specific GDPR guidance on 2026-07-08 — two days before this verification — ruling that encrypted or hashed on-chain data remains personal data, that immutability and encryption are not technical exemptions from GDPR obligations, and that the right to erasure applies "even when blockchain technology makes deleting records technically challenging" [8]. On the US side, commentary on decentralized social platforms generally (Mastodon-class, not Nostr-specific) describes the same structural tension: fragmented server operators and unclear liability assignment under CCPA's comply clock, which assumes a single accountable party exists to receive the request [9]. Neither source rules on Nostr; both describe the exact problem Nostr shares with any federated or append-only architecture — there may be no single entity who can legally "control" a user's data once it has propagated to relays outside your operation, and "technically hard to delete" is not a recognized excuse.
The defensible posture — [SYNTHESIZED INFERENCE, not documented industry practice; no source found states this as a standard]:
- Scope every deletion promise to "systems we control" — your own relays, your own Blossom mirrors, your own database exports. Do not promise deletion from relays or clients you do not operate; NIP-62 [7] is a best-effort ask once content has left your infrastructure, not a guarantee.
- Issue a NIP-62 request as standard practice on any verified deletion ask, understood as best-effort beyond your own systems.
- For content that must never be recoverable even from your own cold storage, prefer NIP-17/NIP-59 gift-wrapped encryption [10] at time of creation over after-the-fact deletion — destroying the decryption context ("crypto-shredding") is something you can actually guarantee for content you hold the keys to, in a way "delete the plaintext everywhere" is not.
4. Operator Legal Exposure
Two liability regimes matter, and both turn on the same mechanism: knowledge plus inaction, not mere hosting.
United States — Section 230 has a criminal-law hole. 47 U.S.C. §230(e)(1): "Nothing in this section shall be construed to impair the enforcement of section 223 or 231 of this title, chapter 71 (relating to obscenity) or 110 (relating to sexual exploitation of children) of title 18, United States Code, or any other Federal criminal statute" [11]. Section 230's civil immunity was never a shield against federal criminal prosecution, CSAM statutes included. Separately, 18 U.S.C. §2258A obligates a provider that obtains actual knowledge of apparent CSAM to report it to NCMEC's CyberTipline "as soon as reasonably possible" [12] — most recently amended 2025-12-18 (Pub. L. 119-60) [12]. This duty attaches to knowledge, not to platform size or architecture.
European Union — actual-knowledge-plus-inaction, with size-tiered duties layered on top. Article 6(1) of Regulation (EU) 2022/2065 exempts a host from liability for stored content only if it "(a) does not have actual knowledge of illegal activity or illegal content... or (b) upon obtaining such knowledge or awareness, acts expeditiously to remove or to disable access to the illegal content" [13]. Correcting this chapter's source audit: small and micro enterprises are exempted from Chapter III Section 3 (Articles 19-28 — internal complaint handling, trusted flaggers, most transparency reporting) except Article 24(3)'s active-user-count reporting, which still applies [14]. Independent-audit duties sit at Article 37, not 42, and apply only to designated Very Large Online Platforms/Search Engines in the first place — small/micro operators were never in that scope, so this isn't something they're "exempted" from so much as categorically outside of [14]. What size never exempts anyone from: Article 16's notice-and-action mechanism, in Section 2, applying to every hosting provider regardless of size [14] — a visible, working way to flag illegal content is baseline, not a large-platform-only duty.
Enforcement is not staying at the top of the size ladder. National Digital Services Coordinators, not only the European Commission, are "the primary point of contact for users and the enforcers of the DSA's obligations on smaller platforms" [15] — enforcement structurally reaches non-VLOP platforms through this national layer, even though public attention has focused on designated giants.
Product shape sets the liability tier — [reasoned application of the DSA's structure, not a ruling that has classified either product]. A bare relay — a WebSocket server storing and forwarding events with no public-facing dissemination surface — reads as mere hosting. A Ditto instance bundles a Mastodon-API-compatible web UI that disseminates content to the public at a recipient's request, the DSA's own definitional shape for "online platform" — a heavier class carrying Article 16's duty regardless of size, plus Section 3's obligations once large enough to matter. Treat a Ditto-based community instance as if it is an "online platform," not a bare relay, when scoping compliance work.
The nearest real precedent is the fediverse, not Nostr. The Stanford Internet Observatory's "Child Safety on Federated Social Media" (Thiel & DiResta, 2023-07-24) scanned the 25 most popular Mastodon instances over two days and found 112 PhotoDNA hash matches for known CSAM, plus nearly 2,000 posts using the top 20 hashtags associated with CSAM-trading communities [16] — all reported to NCMEC. Mastodon is federated, not relay-based, but it is the closest architecturally-similar network anyone has actually audited for this failure mode, and the finding generalizes uncomfortably well: decentralized, low-moderation-friction networks accumulate this content measurably, not hypothetically, once they reach any scale.
What mitigation looks like in practice: a visible, working notice-and-action mechanism; a real NCMEC relationship established before it's needed, not after; human-reviewed report queues. NIP-56 itself warns against pure automation — "reports are easily manipulated," with "trusted moderator judgment" as the intended backstop [17] — which happens to match what both US and EU law expect: a human acting on knowledge, not a bot acting on volume.
5. Degradation Engineering
The hang risk is real but method-specific — not the blanket claim this chapter's source audit assumed. Nostrify's NPool exposes two request shapes [18]. pool.req() waits for an EOSE from every relay in the pool with no documented timeout at all — one unresponsive relay can hang the whole call indefinitely. pool.query(), by contrast, ships a documented default: 1000ms after the first relay's EOSE before giving up on the rest (eoseTimeout, configurable, settable to 0 to force wait-for-all) [18]. Correction: "no documented default timeout" only holds for pool.req(). Use pool.query() for anything user-facing; treat pool.req() as the specific call that needs an operator-added timeout wrapper if used at all.
Ship the pattern, don't invent it. nostr-ux.com's core-interaction patterns give implementable numbers: a 5-second timeout on the primary publish flow (timeout: 5000, "Pattern A: Reliable Post Publishing") [19]; treat an action as successful once at least 3 relays confirm (successful.length >= 3) [19]; render partial success explicitly — "[!] Posted (2/4 relays) [Retry]" [19]; retry stragglers in the background with exponential backoff, never blocking the user on them [19]. This is shared infrastructure every consumer-facing app needs and no framework in this stack provides by default.
The replaceable-event wipeout class is real and documented, though not at the specific citation this chapter's source audit named. Live re-verification could not locate the "franzap" stacker.news post the audit cited. What is directly verifiable: nostr-protocol/nips issue #261 documents the identical failure class — kind-0 (profile) and kind-3 (contact list) events are fully replaceable, so "for each combination of pubkey and kind, only the latest event must be stored," and a client publishing its own partial view of a profile (missing a field it doesn't understand, e.g. a lightning address) silently erases that field for every other client, because the relay has no mechanism to merge two partial updates [20]. Never publish a replaceable event kind from a partial local model of it — merge against the latest known state first, or you become the client that wipes someone's profile.
6. Monitoring + Backup
Relay health has a NIP. NIP-66 ("Relay Liveness Monitoring") defines kind 30166 (a monitor's probe results — RTT for open/read/write, auth requirements, DNS resolution, geolocation) and kind 10166 (a monitor announcing itself) [21]. Its own text builds in graceful degradation: "Clients MUST NOT require 30166 events to function. Absence of monitoring data MUST NOT prevent relay connections" [21] — a signal layered on top of §5's degradation patterns, not something they can depend on. nostr.watch is the ecosystem's most visible public implementation of relay monitoring, useful for spot-checking a relay's general reputation before depending on it — a third-party view, not your own instrumentation.
Metrics are first-party on the relay software itself. strfry ships built-in Prometheus metrics: "Metrics are exposed via HTTP at the /metrics endpoint on the same port as the relay WebSocket server" [22]. nostr-rs-relay carries the same shape internally — a create_metrics() Prometheus registry wired into its server code, exposed at its own /metrics endpoint [23]. Either pairs directly with Grafana dashboard 21844, a community-maintained Ditto dashboard published on Grafana Labs [24] — the fastest path to a working operational view if Ditto is any part of the stack.
Ditto's backup story is a gap, not an oversight you missed. This chapter's source audit searched twice for a first-party Ditto backup runbook and found none either time [25]. Ditto's storage is Postgres — which makes the discipline here entirely conventional, not Nostr-specific: pg_dump on a schedule at minimum, pgBackRest (or an equivalent WAL-archiving tool) for anything called production. Pair this with §1's archive-strfry backup story — event data and Ditto's relational state are two different backup problems, and Ditto ships a runbook for neither.
7. Secrets + Custody
The mental model every other stack teaches does not transfer here. There is no "rotate the key" button.
No rotation mechanism exists, full stop. NIP-41 (key migration) has been an open pull request since October 2023 — three approvals, over 100 comments, still unmerged as of its most recent activity (2025-11-25) [26], blocked on an unresolved fight over automatic migration's griefing window ("stolen recovery kits enable permanent griefing... you will always worry whether you're losing your social graph to scammers," per one maintainer's objection) [26]. NIP-26 (delegated signing), once floated as a workaround, is deprecated [27]. If an nsec leaks, there is no protocol-level undo — every mitigation below is blast-radius reduction, not a rotation story.
FROSTR is the closest exception, and it is early. FROSTR implements FROST threshold signatures over Nostr relays: split an nsec into t-of-n shares across signing devices, and a threshold of shares signs without ever reconstituting the full key [28]. Rotation, in FROSTR's model, means destroying the old shares and issuing a new set — the npub never changes [28]. Its browser-extension component is explicitly documented as "under heavy development" [29]; its core library (Bifrost) is further along — v2.0.2, released 2026-01-25, with a documented threat model [28] — but nothing in the ecosystem carries a completed third-party audit as of this verification. Revisit for brand-key custody once it exits that phase.
Keycast is the team-custody answer, with its own caveat attached. Keycast targets teams specifically (versus nsec.app/Amber for individuals, ch06 §1), with row-level AES-256 encryption, file- or AWS-KMS-backed key storage, and policy-gated NIP-46 remote signing [30]. Its own README states the caveat this manual adopts: "Keycast is an early-stage project. A May 2026 audit found and fixed several auth, permission, data integrity, and dependency issues, but this should still get a deployment review before it is trusted with real team keys on the public internet" [30]. Read that as audited, not yet trust-worn.
Structurally, there is no HSM/KMS integration and no keyless-signing analog anywhere in this ecosystem. Every custody tool above is a software wallet with better or worse blast-radius properties — none hands signing authority to a hardware security module or a short-lived, identity-bound credential the way mainstream software-supply-chain tooling (Sigstore/Fulcio, cloud KMS) does. This is a gap in the protocol's tooling, not any one product's oversight.
The stack's own defaults point the wrong way. Both products in this manual that document their server-side signing pattern do it the same way — a raw nsec in an environment variable. Ditto (Deno + Postgres) documents DITTO_NSEC; ditto-relay (Bun + OpenSearch) documents NOSTR_NSEC [31] — do not conflate the two; they are different products with different variable names.
Secrets architecture (the maintainers' own adopted pattern — a reusable template for your own Nostr work)
| Secret type | Storage | Rotation story | Detection |
|---|---|---|---|
| Brand nsec | Keycast (self-hosted, deployment-reviewed) or nsec.app interim [30] | None — grant revocation on teammate departure, not rotation; revisit FROSTR once it exits heavy development [28][29] | gitleaks custom rule (below) |
| Per-service nsec (relay identity, bots, publishers) | Injected from a secret store at deploy only — never in repo, Dockerfile, or wrangler.toml | None — one npub per service keeps blast radius small and audit trails attributable via authors queries | gitleaks custom rule (below) |
| Human-touchable backups | ncryptsec (NIP-49) [32], passphrase in its own secret-store entry | Re-encrypt on passphrase rotation only | gitleaks custom rule (below) |
| CI signing key | Encrypted blob + passphrase from secret store (same shape as GPG signing), own npub per pipeline | Passphrase rotation only | gitleaks custom rule (below) |
VITE_* build-time config | Public by construction — baked into the client bundle as literal strings | N/A — never put a secret here | Treat as already public; anything sensitive goes in a Worker secret (wrangler secret put) |
gitleaks has zero Nostr coverage by default, and that is now fixed in the maintainers' own workspace. The default ruleset ships roughly 190+ rules covering AWS/Azure/GCP, Stripe/PayPal, Slack/Discord, GitHub/GitLab, and more [33] — and not one pattern for nsec1…, ncryptsec1…, or a bunker:// secret. A committed raw nsec would pass CI today on the default config alone. The maintainers' own .gitleaks.toml now extends the default set with three custom rules — nostr-nsec (nsec1[a-z0-9]{58}), nostr-ncryptsec (ncryptsec1[a-z0-9]{80,}), and nostr-bunker-secret (an embedded secret= parameter in a bunker:// URI) — landed the same day as this audit [34]. If you fork this pattern into your own repo, copy that file before the first Nostr credential exists there, not after.
The nostr-security skill shipped with this stack does not cover any of the above, and should not be mistaken for doing so. Read directly, its threat model is entirely XSS/CSP: localStorage key theft via script injection, URL and CSS sanitization for untrusted event data, authors filtering for trust-sensitive queries [35]. It never mentions .env discipline, VITE_ exposure, key rotation, or custody. That is a correct and useful scope for what it covers — the risk is an operator reading "we have a nostr-security skill" and assuming secrets sit inside its perimeter. They do not; extend this fork's AGENTS.md with a dedicated secrets section rather than growing this skill past its stated scope.
8. Longevity: Funding, Bus Factor, Exit Playbook
Funding is one demonstrated line. This chapter's source audit measured the funding picture directly: OpenSats is the one confirmed, self-reported funding relationship; the Human Rights Foundation, previously assumed a grantor, reclassifies on closer reading to a prize/event partner — no 2026 grant naming Soapbox, Ditto, or Agora was found anywhere [25]. MIT/AGPL licensing makes forking every component in this stack legal. It does not make forking cheap once a maintainer disappears — that is a bus-factor problem, and pre-positioning against it now is inexpensive; doing it after a maintainer goes dark is not.
Bus factor, measured this session via each repo's contributor history [25]:
| Repo | Primary-author share | Read |
|---|---|---|
| Nostrify | 85.2% (Gleason) | High-risk — the framework every MKStack app compiles against has one author |
| mkstack | 71.9% (Gleason) | High-risk |
| ditto-relay | 99.3% (Gleason) | Highest concentration measured |
| Ditto | 47.4% (Gleason) | Healthiest of the four — the only repo with real secondary maintainers |
Two cheap moves, one already partly done here. First: add a fetch-only upstream remote to any fork of an upstream Soapbox/MKStack repo, so git diff <pin> upstream/main is a live command instead of a dead note. The maintainers' own trespies-stack fork already has one — git remote -v shows upstream → gitlab.com/soapbox-pub/mkstack.git (fetch), push disabled [verified directly, 2026-07-10] — extend the same pattern to any other forked component before treating a commit pin as more than documentation. Second: schedule recurring re-verification of this manual's highest-churn claims (tags, licenses, DNS, funder pages, bus factor) — every number in the table above has a shelf life.
Per-component exit playbook
| Component | Exit posture | Why |
|---|---|---|
| Ditto | YES — fork and self-maintain | AGPL; 47.4% bus factor is the healthiest measured; Postgres backend is boring and portable |
| Nostrify | CONDITIONAL — vendor the source | 85.2% bus factor on the one library every MKStack app depends on; vendoring plus one engineer who has read NStore/NRelay end-to-end is the insurance |
| MKStack | CONDITIONAL — license ask outstanding | A license-clarification ask to Soapbox is drafted but unsent; confirm before treating forked output as unencumbered |
| Shakespeare | YES — export and go | AGPL; output is a static/portable site — the tool disappearing doesn't strand what it already built |
| NostrHub | CONDITIONAL — license: None, repo one month old | Created 2026-06-11, zero stars/forks at last check, no license file at all — treat anything built against it as provisional until that changes |
| ditto-relay | CAUTION | 99.3% bus factor, roughly five months old, no known external production deployment found beyond Soapbox's own — use behind an exit hatch, not as a dependency without a workaround |
Pre-positioning checklist (ranked by cost)
- Send the outstanding MKStack + NostrHub license outreach. The ask is drafted; NostrHub's
license: Nonegap is a new finding this closes at the same time. One email; the window closes permanently if the maintainer goes dark first. - Add fetch-only
upstreamremotes to every forked component that doesn't have one yet (one already does — see above). - Schedule recurring re-verification of this manual's high-churn claims via your own scheduling tooling.
- Vendor Nostrify's source locally, and have one engineer read
NStore/NRelayend-to-end — insurance on the single highest-leverage bus-factor risk in the stack. - Stand up the archive strfry from §1, syncing from every relay this organization writes to, with a cron export to cold storage.
- Open a light relationship with Ditto's non-Gleason maintainers — low-touch, before it's needed.
- Keep at least one paid relay in the relay budget, on the economics established in §1.
9. Incident Catalog
Black Hat USA 2025 — "Not Sealed: Practical Attacks on Nostr, a Decentralized Censorship-Resistant Protocol." Presented 2025-08-06 by Hayato Kimura, Ryoma Ito, Kazuhiko Minematsu, Shogo Shiraki, and Takanori Isobe [36]; the underlying paper (preprint title "Not in The Prophecies: Practical Attacks on Nostr," eprint.iacr.org/2025/1459) [37] describes four attack classes — plaintext recovery, DM forgery, profile forgery, and Bitcoin-payment hijacking — rooted in flawed client-side signature verification compounded by CBC-mode encryption weaknesses and a link-preview mechanism that leaks a recipient's IP address and message-open timing via external HTTP fetches [36][37]. The research affected past versions of four named clients: Damus (iOS), Iris (iOS), FreeFrom (iOS/Android), and Plebstr [36]. No CVE identifiers were found assigned to any of these findings in available sources. This is the concrete answer to "why migrate off NIP-04 at all": the researchers' own recommendation matches this manual's ch06 guidance exactly — move to NIP-44-based encryption and NIP-17+NIP-59 gift-wrapped DMs, disable automatic link previews, and prefer clients that verify every event signature [36].
The replaceable-event wipeout class (mechanism in §5) is documented directly in the protocol's own issue tracker, nostr-protocol/nips issue #261 [20] — real, citable, still open. This chapter's source audit also named a specific incident — a "franzap" post on stacker.news — that could not be independently re-verified live; the citation did not resolve to retrievable content this session. What live research surfaced instead: two real, narrower Damus bugs — issue #1994, "Profile is wiped (Balaji's bug)," a local profile-edit bug from 2024-02 [38], and issue #2264, "bookmarks wiped on purple expiration," a local bug from 2024-05 [39] — neither of which is the "relay-upgrade wipe" the source audit described, though both confirm the same underlying lesson: client-side state and relay-side state disagreeing silently is a recurring Nostr bug class, not a one-off. Nostr's own explainer material states the broader durability risk plainly: "If all the relays that you have used in the past go offline, all your posts will be unretrievable" [40].
DVM decay as protocol-churn precedent. NIP-90 (Data Vending Machines) — the base spec for AI-job marketplaces relevant to DojoGenesis-style agent dispatch — is itself now marked unrecommended, its own text conceding "this got totally out of control, prefer use-case-specific microstandards" [41]. Governance moved to an informally-maintained sister kind-registry repo rather than the spec dying outright [41]. The lesson for anything built on this manual's NIP table: merged into the spec is a snapshot, not a guarantee. Re-verify currency verdicts on the cadence recommended in §8, not once at project start.
Disclosure note. ditto-relay — the Bun-based relay/search product covered in ch13 — runs on a runtime Anthropic acquired on 2025-12-02 [42][43]. This manual is Claude-authored, researching a stack whose relay layer partly depends on an Anthropic-owned runtime. Bun remains open-source and MIT-licensed under the new ownership [42], so this changes no technical recommendation above — but it's the kind of overlap a trustworthy manual states rather than leaves for a reader to notice first.
Sources
- Wei & Tyson — "An Empirical Analysis of the Nostr Social Network: Decentralization, Availability, and Replication Overhead" (full text: arxiv.org/html/2402.05709v2), accepted CoNEXT '25 — accessed 2026-07-10 — free-relay cost coverage (95%), zap-income percentile ($67/90th), replication factor (34.6 relays/12.4 ASes), wasted bandwidth (98.2%/144 TiB).
- NIP-65: Relay List Metadata — accessed 2026-07-10 — outbox model.
- NIP-77: Negentropy Syncing — accessed 2026-07-10 — draft/optional status, bandwidth-efficient reconciliation.
- nostr.build Terms of Service (nostr.build/tos/ redirects here) — accessed 2026-07-10 — free-tier unrestricted deletion right; plan limits cross-checked against account.nostr.build/plans and the FOSS README.
- BUD-04: Mirroring Blobs — accessed 2026-07-10 — server-to-server mirroring mechanism.
- NIP-09: Event Deletion Request — accessed 2026-07-10 — advisory SHOULD-level deletion.
- NIP-62: Request to Vanish; merge commit 619e3be, "Right to Vanish (#1256)" — accessed 2026-07-10 — MUST-level deletion, merged 2025-02-19.
- EDPB — Guidelines on processing of personal data through blockchain technologies, finalized 2026-07-08 — accessed 2026-07-10 — no technical exemption from GDPR obligations; right to erasure applies despite technical difficulty.
- Decentralized Social Media and CCPA Compliance — accessed 2026-07-10 — fragmented-liability framing for federated platforms under CCPA.
- NIP-17: Private Direct Messages / NIP-59: Gift Wrap — accessed 2026-07-10 — encryption mechanics underlying the crypto-shredding inference.
- 47 U.S.C. §230 — accessed 2026-07-10 — §230(e)(1), no effect on federal criminal law.
- 18 U.S.C. §2258A — accessed 2026-07-10 — NCMEC CyberTipline reporting duty, current through Pub. L. 119-60 (2025-12-18).
- Regulation (EU) 2022/2065 (Digital Services Act), Article 6 — eur-lex.europa.eu/eli/reg/2022/2065 — accessed 2026-07-10 — actual-knowledge-plus-inaction hosting liability standard.
- DSA Articles 16, 19, 24, and 37 — eu-digital-services-act.com/Digital_Services_Act_Articles.html — accessed 2026-07-10 — small/micro exemption scope (Section 3 minus Art. 24(3)), notice-and-action baseline (Art. 16, all sizes), audit obligation (Art. 37, VLOP/VLOSE-only).
- The Dawn of DSA Enforcement — Lessons from the Digital Services Coordinators' First Annual Reports, Platform Law Blog, 2025-10-06 — accessed 2026-07-10 — DSC enforcement reaching smaller platforms.
- Thiel & DiResta — "Child Safety on Federated Social Media", Stanford Internet Observatory, 2023-07-24 (see also fsi.stanford.edu coverage) — accessed 2026-07-10 — 112 PhotoDNA CSAM matches, ~2,000 hashtag-flagged posts across 25 Mastodon instances.
- NIP-56: Reporting — accessed 2026-07-10 — warning against automated moderation on report volume.
- Nostrify — NPool — accessed 2026-07-10 —
pool.req()vspool.query()EOSE/timeout behavior,eoseTimeoutdefault. - nostr-ux.com — Core Interaction Loops — accessed 2026-07-10 — 5s publish timeout, 3-relay confirm threshold, partial-success UI, background backoff retry.
- nostr-protocol/nips issue #261 — "Metadata. Kind:0 Profiles." — accessed 2026-07-10 — replaceable-event field-overwrite failure class.
- NIP-66: Relay Liveness Monitoring — accessed 2026-07-10 — kind 30166/10166, graceful-degradation clause.
- strfry README — Prometheus metrics — accessed 2026-07-10 — built-in
/metricsendpoint. - scsibug/nostr-rs-relay (see server.rs) — accessed 2026-07-10 — built-in
create_metrics()Prometheus registry. - Grafana dashboard 21844 — Ditto — accessed 2026-07-10 — community-maintained Ditto operational dashboard.
- Maintainers' internal production-secrets-longevity audit note (companion document, not part of this public chapter set; same-session production audit) — internal source, 2026-07-10 — Ditto backup-runbook absence (twice-confirmed), funding verdict, bus-factor measurements.
- NIP-41 pull request #829 — accessed 2026-07-10 — unmerged since October 2023, griefing-risk objection.
- NIP-26: Delegated Event Signing — accessed 2026-07-10 (also ch06) — deprecated.
- FROSTR-ORG/bifrost — accessed 2026-07-10 — FROST threshold-signing library, v2.0.2 (2026-01-25), documented threat model.
- FROSTR-ORG/frost2x — accessed 2026-07-10 — browser-extension signer, "under heavy development."
- Keycast — accessed 2026-07-10 — team key management, May 2026 audit caveat (quoted verbatim from README).
- This manual, ch06 §3 correction log —
DITTO_NSEC(Ditto, Deno+Postgres) vsNOSTR_NSEC(ditto-relay, Bun+OpenSearch); see ch06. - NIP-49: Private Key Encryption — accessed 2026-07-10 (also ch06) —
ncryptsecbackup format. - gitleaks default config — accessed 2026-07-10 — ~190+ default rules, zero Nostr coverage.
- The maintainers'
.gitleaks.toml(their own workspace root) — read directly, 2026-07-10 — three custom Nostr credential rules, landed same-day as the source audit. trespies-stack/.agents/skills/nostr-security/SKILL.md(the maintainers' own MKStack fork) — read directly, 2026-07-10 — confirmed XSS/CSP/injection-only scope; no custody or.envcoverage.- Black Hat USA 2025 — "Not Sealed: Practical Attacks on Nostr, a Decentralized Censorship-Resistant Protocol" (project page: crypto-sec-n.github.io) — presented 2025-08-06 — accessed 2026-07-10 — named affected clients, attack classes, mitigation recommendations.
- eprint.iacr.org/2025/1459 — "Not in The Prophecies: Practical Attacks on Nostr" — accessed 2026-07-10 — abstract, attack-class taxonomy.
- damus-io/damus issue #1994 — "Profile is wiped (Balaji's bug)" — accessed 2026-07-10 — local profile-edit data-loss bug, 2024-02.
- damus-io/damus issue #2264 — "bookmarks wiped on purple expiration" — accessed 2026-07-10 — local data-loss bug, 2024-05.
- nostr.how — relays explainer — accessed 2026-07-10 — "if all the relays... go offline, all your posts will be unretrievable."
- NIP-90: Data Vending Machines (also ch06) — accessed 2026-07-10 — deprecation notice, successor governance.
- Anthropic — "Anthropic acquires Bun as Claude Code reaches $1B milestone" — accessed 2026-07-10 — acquisition announcement, 2025-12-02; Bun remains MIT-licensed.
- Bun — "Bun is joining Anthropic" — accessed 2026-07-10 — corroborating first-party announcement.