Update readme
This commit is contained in:
@@ -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 `<subdomain>.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
|
||||
|
||||
+11
-4
@@ -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
|
||||
|
||||
@@ -13,6 +13,7 @@ pub struct Config {
|
||||
pub indexer_relays: Vec<String>,
|
||||
pub platform_name: String,
|
||||
pub platform_description: String,
|
||||
pub platform_picture: String,
|
||||
pub platform_messaging_relays: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -45,6 +46,7 @@ impl Config {
|
||||
.collect::<Vec<_>>();
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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?;
|
||||
|
||||
Reference in New Issue
Block a user