From c58b20ea5331ca22cdbd2d7a63b0a35c21c32203 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Wed, 25 Feb 2026 14:11:49 -0800 Subject: [PATCH] Update readme --- README.md | 14 +++++++++++--- backend/README.md | 15 +++++++++++---- backend/src/config.rs | 3 +++ backend/src/main.rs | 1 + backend/src/platform.rs | 4 ++++ 5 files changed, 30 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 67175e2..aef18d5 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Users of the platform. Identified by their Nostr public key. |---|---| | `pubkey` | Primary key — Nostr public key | | `status` | Account status (active, suspended, etc.) | +| `tenant_nwc_url` | Tenant-provided NWC URL for recurring billing | ### `relays` Virtual relays linked to a tenant. @@ -50,6 +51,7 @@ Virtual relays linked to a tenant. | `name` | Human-readable relay name | | `subdomain` | Subdomain slug (e.g. `my-relay` → `my-relay.spaces.coracle.social`) | | `schema` | zooid DB schema name (subdomain with dashes replaced by underscores; stored for reference only) | +| `icon` | Relay icon URL | | `description` | Relay description | | `plan` | Pricing tier: `free`, `basic`, or `growth` | | `status` | Relay status (pending, active, suspended, etc.) | @@ -106,7 +108,7 @@ Line items linking an invoice to a specific relay charge. - Async job runner for billing checks, invoice collection, and relay provisioning ### Relay Provisioning -When a relay is created, an async worker is spawned that sends the appropriate API request to zooid to set up the virtual relay. +When a relay is created, an async worker is spawned that sends the appropriate API request to zooid to set up the virtual relay. Status updates to `active` on success and `provisioning_failed` on error. ### Billing Logic - Billing is monthly. Invoices batch all of a tenant's relay charges into a single payment. @@ -120,6 +122,11 @@ When a relay is created, an async worker is spawned that sends the appropriate A | `HOSTING_ADMIN_PUBKEYS` | Comma-separated Nostr pubkeys with super admin access | | `PLATFORM_SECRET` | Nostr private key used by the platform to send NIP-17 DMs | | `NWC_URL` | Nostr Wallet Connect URL used by the platform to generate Lightning invoices | +| `NOSTR_INDEXER_RELAYS` | Relays used to fetch kind `10050` DM relay lists | +| `PLATFORM_NAME` | Platform display name (published as kind `0`) | +| `PLATFORM_DESCRIPTION` | Platform description (published as kind `0`) | +| `PLATFORM_PICTURE` | Platform picture URL (published as kind `0`) | +| `PLATFORM_MESSAGING_RELAYS` | Relays published in kind `10050` for DMs | --- @@ -151,6 +158,7 @@ When a relay is created, an async worker is spawned that sends the appropriate A - Fields: - Relay name - Subdomain (auto-generated from name, editable; displayed as `.spaces.coracle.social`) + - Icon URL - Description - Plan selection — shown as a comparison table (see Pricing Tiers) - On submit (if plan is not free): generates a Lightning invoice @@ -185,7 +193,7 @@ When a relay is created, an async worker is spawned that sends the appropriate A - Actions: edit or deactivate relay #### Relay Edit Form (`/admin/relays/{id}/edit`) -- Edit relay name, subdomain, description, and plan +- Edit relay name, subdomain, icon, description, and plan ### Relay Edit Form (`/relays/{id}/edit`) -- Edit relay name, subdomain, description, and plan +- Edit relay name, subdomain, icon, description, and plan diff --git a/backend/README.md b/backend/README.md index 77dcba7..78a579c 100644 --- a/backend/README.md +++ b/backend/README.md @@ -16,10 +16,14 @@ backend/ migrations/ # SQL migrations src/ auth.rs # NIP-98 verification helper + billing.rs # Billing loop + invoice generation config.rs # Env-based configuration db.rs # SQLite pool + migrations main.rs # Axum server entrypoint models.rs # Data models + notifications.rs # NIP-17 DM sender + relay discovery + platform.rs # Startup kind 0/10050 publishing + provisioning.rs # Zooid provisioning worker repo.rs # Data access layer ``` @@ -39,6 +43,7 @@ Environment variables: | `NOSTR_INDEXER_RELAYS` | Comma-separated relays to fetch kind `10050` DM relays | _required for notifications_ | | `PLATFORM_NAME` | Platform display name for kind `0` metadata | _optional_ | | `PLATFORM_DESCRIPTION` | Platform description for kind `0` metadata | _optional_ | +| `PLATFORM_PICTURE` | Platform picture URL for kind `0` metadata | _optional_ | | `PLATFORM_MESSAGING_RELAYS` | Comma-separated relays published in kind `10050` | _optional_ | The database directory is created automatically if it doesn’t exist. @@ -85,6 +90,12 @@ The backend runs an in-process billing loop that: - Sends NIP-17 DMs with invoices when recurring is off - Sends NIP-17 DMs on successful payment when recurring is on +NIP-17 relay discovery: + +- Uses `NOSTR_INDEXER_RELAYS` to fetch kind `10050` for each tenant +- Cached for a short period +- If no relays are found, no DM is sent + On startup, the backend publishes: - Kind `0` metadata (name/description) @@ -114,7 +125,3 @@ Admin routes (all require NIP-98 auth; pubkey must be in `HOSTING_ADMIN_PUBKEYS` - `GET /admin/relays/:id` — get relay - `PUT /admin/relays/:id` — update relay - `DELETE /admin/relays/:id` — deactivate relay - -## Next Steps - -- Add invoice generation and billing jobs diff --git a/backend/src/config.rs b/backend/src/config.rs index a0ad3d4..5141ebc 100644 --- a/backend/src/config.rs +++ b/backend/src/config.rs @@ -13,6 +13,7 @@ pub struct Config { pub indexer_relays: Vec, pub platform_name: String, pub platform_description: String, + pub platform_picture: String, pub platform_messaging_relays: Vec, } @@ -45,6 +46,7 @@ impl Config { .collect::>(); let platform_name = env::var("PLATFORM_NAME").unwrap_or_default(); let platform_description = env::var("PLATFORM_DESCRIPTION").unwrap_or_default(); + let platform_picture = env::var("PLATFORM_PICTURE").unwrap_or_default(); let platform_messaging_relays = env::var("PLATFORM_MESSAGING_RELAYS") .unwrap_or_default() .split(',') @@ -64,6 +66,7 @@ impl Config { indexer_relays, platform_name, platform_description, + platform_picture, platform_messaging_relays, } } diff --git a/backend/src/main.rs b/backend/src/main.rs index b8b4718..774a529 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -42,6 +42,7 @@ async fn main() -> Result<()> { &config.indexer_relays, &config.platform_name, &config.platform_description, + &config.platform_picture, &config.platform_messaging_relays, ) .await?; diff --git a/backend/src/platform.rs b/backend/src/platform.rs index 833d9d6..97497e4 100644 --- a/backend/src/platform.rs +++ b/backend/src/platform.rs @@ -6,6 +6,7 @@ pub async fn publish_platform_identity( indexer_relays: &[String], name: &str, description: &str, + picture: &str, messaging_relays: &[String], ) -> Result<()> { if indexer_relays.is_empty() { @@ -32,6 +33,9 @@ pub async fn publish_platform_identity( if !description.is_empty() { metadata = metadata.about(description); } + if !picture.is_empty() { + metadata = metadata.picture(Url::parse(picture)?); + } let metadata_builder = EventBuilder::metadata(&metadata); client.send_event_builder(metadata_builder).await?;