Chad Curtis 935c121bab Implement Campaign kind 33863
Replaces every kind 30223 surface with kind 33863 -- the self-authored
fundraising campaign with a single `w` Bitcoin wallet endpoint. Hard
cutoff: no migration, no dual-read, no legacy support.

Schema (src/lib/campaign.ts):
- `CAMPAIGN_KIND` constant bumped 30223 -> 33863.
- New `CampaignWallet` type with `onchain` (`bc1q`/`bc1p`) and `sp`
  (`sp1`) modes, prefix-disambiguated. Bitcoin-mainnet only;
  testnet/regtest/lightning prefixes are rejected at parse time.
- New `parseCampaignWallet()` validates bech32 via bitcoinjs-lib for
  on-chain addresses and shape-checks silent-payment codes.
- `ParsedCampaign` drops `recipients`, `category`, `tags`,
  `location`, `archived`, `image` (-> `banner`), `goalSats` (->
  `goalUsd`). Adds `wallet` and `bannerImeta` (parsed NIP-92).
- `CAMPAIGN_CATEGORIES`, `CampaignCategory`,
  `LEGACY_CAMPAIGN_CATEGORY_ALIASES`, `getCampaignPrimaryTagLabel`,
  `splitDonation`, `minDonationForSplit`, `DonationSplit`,
  `CampaignRecipient` removed.

Publishing (useDonateCampaign):
- Single-output PSBT paying `campaign.wallet.value`.
- Kind 8333 receipt has NO `p` tags -- campaigns are not Nostr-identity
  recipients. `i`, `amount`, `a`, `K`, `alt` only.
- SP campaigns are refused with a clear error directing donors to
  external BIP-352-capable wallets via the on-page QR/copy panel.

Verification (useOnchainZaps.verifyOnchainZap):
- Two modes: identity-recipient (existing `p`-tag derivation) and
  campaign-wallet (match outputs against `campaign.w`). The branch is
  selected by whether the receipt has an `a` tag pointing at a kind
  33863 campaign. SP-targeted receipts are rejected.

Querying (useCampaigns, useAllCampaigns, useCampaignDonations):
- Drop `category`, `recipientPubkeys`, `includeArchived` options.
- `useCampaignDonations` now takes a `ParsedCampaign` and verifies
  every receipt on-chain against the campaign's `w` address before
  counting it. SP campaigns short-circuit to zeros.
- Search drops `location` and `t`-tag branches; title/summary/story
  only.

UI:
- `CampaignCard`, `HeroCampaignSpotlight`, `CampaignsPage`: drop
  recipient counts, archived/category badges, `location`; use
  `banner`. Silent-payment campaigns render a "Private -- totals not
  public" notice instead of progress.
- `CampaignDetailPage`: archive flow replaced with NIP-09 kind 5
  deletion. Drops the multi-beneficiary recipient column. Donate column
  shows the in-app PSBT donate button (on-chain) plus the always-
  available external-wallet QR/copy panel. SP campaigns show the panel
  only -- no in-app donate.
- `CreateCampaignPage`: drops Beneficiaries section, tag input, and
  USD-to-sats conversion. Adds a Wallet field with mode-aware
  validation hint. Goal is integer USD. Banner upload captures NIP-94
  tags and converts to NIP-92 `imeta` at publish.
- `DonateDialog`: collapses ~1200 LOC of split logic into a single-
  output flow. Form -> Confirm -> Success. Logged-out and signer-
  unsupported users are pointed at the external-wallet panel.
- New `CampaignWalletDonatePanel` component (replaces the
  pubkey-derived `BeneficiaryDonateDialog`). QR + copy + open-in-wallet
  for any `bc1`/`sp1` endpoint, with mode-appropriate privacy notice.

Removed:
- `useArchiveCampaign` hook (closure via NIP-09 deletion only).
- `ClaimPage` and its `/claim` route (claim-for-someone-else flow no
  longer applies -- campaigns are self-authored).
- `BeneficiaryDonateDialog.tsx` (replaced by
  `CampaignWalletDonatePanel.tsx`).
- Community-donate synthesis hack in `CommunityDetailPage` (no more
  fabricating a `ParsedCampaign` from community moderators).

NIP.md was updated separately to specify Kind 33863.
2026-05-21 19:04:41 -05:00
2026-04-17 12:15:51 +05:45
2026-05-21 19:04:41 -05:00
2026-02-16 16:54:01 -06:00
2026-04-20 18:14:35 +05:45
2026-05-11 14:01:54 -07:00
2026-05-13 18:35:03 -05:00
2026-04-20 18:27:38 +05:45
2026-04-20 18:09:28 +05:45
2026-05-15 19:37:23 -07:00
2026-02-26 21:20:15 +00:00
2026-04-20 18:09:28 +05:45
2026-02-22 16:35:53 -06:00
2026-05-18 09:49:31 -07:00
2026-05-18 09:49:31 -07:00

Agora

Power to the people.

Agora is a Nostr client focused on community ownership, expressive identity, and censorship resistance. This repository (agora-3) is the Agora-branded app built from the Ditto codebase.

agora.spot | Source

What This Repo Is

  • Agora product identity (name, theme, assets, native IDs)
  • Ditto-derived implementation with broad Nostr feature coverage
  • Configurable deployment defaults via agora.json

Features

  • Community-first social client: notes, articles, comments, reposts, reactions, and rich event rendering
  • Theming system: built-in presets + custom color/font/background themes that can be shared as events
  • Lightning support: zaps with Nostr Wallet Connect and WebLN
  • Private messaging: NIP-04 and NIP-17 direct messages
  • Mobile app shell: Capacitor-powered Android/iOS wrappers
  • Self-hostable: static web build + configurable relay and upload infrastructure

Getting Started

Prerequisites

Development

git clone https://gitlab.com/soapbox-pub/agora-3.git
cd agora-3
npm install
npm run dev

Development server: http://localhost:8080

Docker Getting Started

Use Docker Compose when you want the nginx reverse-proxy stack (necessary if you want decryptable media in messages - kind 15s of NIP 17):

git clone https://gitlab.com/soapbox-pub/agora-3.git
cd agora-3
cp .env.example .env
docker compose up --build

Proxy URL: http://localhost:8083

This starts:

  • vite service on the internal Docker network (vite:8080)
  • web service (nginx) on host port 8082, proxying to Vite with websocket support

Stop stack:

docker compose down

Production-style container build:

docker compose -f docker-compose.prod.yml up --build

Build

npm run build

Build output: dist/

Validate

npm test

This runs type-checking, linting, unit tests, and production build checks.

Configuration

Build-time config is read from agora.json (gitignored by default so each deployment can provide its own values).

{
  "theme": "dark",
  "relayMetadata": {
    "relays": [
      { "url": "wss://relay.ditto.pub", "read": true, "write": true },
      { "url": "wss://relay.primal.net", "read": true, "write": true },
      { "url": "wss://relay.damus.io", "read": true, "write": true }
    ]
  },
  "blossomServers": [
    "https://blossom.ditto.pub",
    "https://blossom.primal.net/"
  ]
}

Configuration priority (highest first):

  1. User settings (local storage)
  2. Build config (agora.json)
  3. Hardcoded app defaults

Use a custom config path:

CONFIG_FILE=./my-config.json npm run build

Deployment

Agora builds to static files and can be deployed to any static host.

  • GitLab/GitHub Pages
  • Netlify/Vercel
  • VPS or any web server with SPA routing fallback

For Android:

npm run build
npx cap sync
npx cap open android

Tech Stack

Layer Technology
Framework React 18
Build Vite
Language TypeScript
Styling TailwindCSS 3 + shadcn/ui
Routing React Router
Data TanStack Query
Nostr Nostrify + nostr-tools
Mobile Capacitor
Testing Vitest + React Testing Library

Contributing

Read CONTRIBUTING.md before opening a merge request.

License

AGPL-3.0

S
Description
Activist social network and fundraising platform on Nostr
Readme AGPL-3.0 43 MiB
Languages
TypeScript 95.7%
Java 1.5%
JavaScript 0.9%
Swift 0.9%
CSS 0.5%
Other 0.4%