Publish metadata on startup

This commit is contained in:
Jon Staab
2026-02-25 14:08:43 -08:00
parent 90831a4237
commit 1df7b1b37c
4 changed files with 87 additions and 1 deletions
+10 -1
View File
@@ -37,6 +37,9 @@ Environment variables:
| `RELAY_DOMAIN` | Relay base domain for subdomains | `spaces.coracle.social` |
| `NWC_URL` | Platform NWC URL for invoice generation | _required for billing_ |
| `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_MESSAGING_RELAYS` | Comma-separated relays published in kind `10050` | _optional_ |
The database directory is created automatically if it doesnt exist.
@@ -82,6 +85,13 @@ 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
On startup, the backend publishes:
- Kind `0` metadata (name/description)
- Kind `10050` relay list for DMs
These are published to the relays listed in `NOSTR_INDEXER_RELAYS`.
## API Routes
Tenant routes (all require NIP-98 auth; pubkey is inferred from the token):
@@ -108,4 +118,3 @@ Admin routes (all require NIP-98 auth; pubkey must be in `HOSTING_ADMIN_PUBKEYS`
## Next Steps
- Add invoice generation and billing jobs
- On start, publish kind 0, 10002, 10050 to indexer relays based on env vars
+14
View File
@@ -11,6 +11,9 @@ pub struct Config {
pub relay_domain: String,
pub platform_nwc_url: String,
pub indexer_relays: Vec<String>,
pub platform_name: String,
pub platform_description: String,
pub platform_messaging_relays: Vec<String>,
}
impl Config {
@@ -40,6 +43,14 @@ impl Config {
.map(|v| v.trim().to_string())
.filter(|v| !v.is_empty())
.collect::<Vec<_>>();
let platform_name = env::var("PLATFORM_NAME").unwrap_or_default();
let platform_description = env::var("PLATFORM_DESCRIPTION").unwrap_or_default();
let platform_messaging_relays = env::var("PLATFORM_MESSAGING_RELAYS")
.unwrap_or_default()
.split(',')
.map(|v| v.trim().to_string())
.filter(|v| !v.is_empty())
.collect::<Vec<_>>();
Self {
database_url,
@@ -51,6 +62,9 @@ impl Config {
relay_domain,
platform_nwc_url,
indexer_relays,
platform_name,
platform_description,
platform_messaging_relays,
}
}
}
+10
View File
@@ -5,6 +5,7 @@ mod config;
mod db;
mod models;
mod notifications;
mod platform;
mod provisioning;
mod repo;
@@ -20,6 +21,7 @@ use crate::config::Config;
use crate::db::init_pool;
use crate::billing::BillingService;
use crate::notifications::Nip17Notifier;
use crate::platform::publish_platform_identity;
use crate::provisioning::Provisioner;
use crate::repo::Repo;
@@ -35,6 +37,14 @@ async fn main() -> Result<()> {
let pool = init_pool(&config.database_url).await?;
let repo = Repo::new(pool);
publish_platform_identity(
&config.platform_secret,
&config.indexer_relays,
&config.platform_name,
&config.platform_description,
&config.platform_messaging_relays,
)
.await?;
let notifier = Nip17Notifier::new(config.platform_secret.clone(), config.indexer_relays.clone()).await?;
let billing = BillingService::new(
repo.clone(),
+53
View File
@@ -0,0 +1,53 @@
use anyhow::{anyhow, Result};
use nostr_sdk::prelude::*;
pub async fn publish_platform_identity(
platform_secret: &str,
indexer_relays: &[String],
name: &str,
description: &str,
messaging_relays: &[String],
) -> Result<()> {
if indexer_relays.is_empty() {
return Ok(());
}
if platform_secret.trim().is_empty() {
return Err(anyhow!("PLATFORM_SECRET is required for platform identity"));
}
let keys = Keys::parse(platform_secret)?;
let client = Client::new(keys);
for relay in indexer_relays {
client.add_relay(relay).await?;
}
client.connect().await;
let mut metadata = Metadata::new();
if !name.is_empty() {
metadata = metadata.name(name);
}
if !description.is_empty() {
metadata = metadata.about(description);
}
let metadata_builder = EventBuilder::metadata(&metadata);
client.send_event_builder(metadata_builder).await?;
if messaging_relays.is_empty() {
return Ok(());
}
let mut tags = Vec::new();
for relay in messaging_relays {
let tag = Tag::parse(["relay", relay.as_str()])?;
tags.push(tag);
}
let relay_builder = EventBuilder::new(Kind::Custom(10050), "").tags(tags);
client.send_event_builder(relay_builder).await?;
Ok(())
}