Files
caravel/backend/spec/api.md
T
2026-03-25 16:50:44 -07:00

4.3 KiB

pub struct Api

Api manages the HTTP interface for the application

Members:

  • host: String - the hostname of the service for checking NIP 98 auth, from HOST
  • port: u16 - a port to run the server on from PORT
  • admins: Vec<String> - a list of admin pubkeys from ADMINS
  • origins: Vec<String> - to be used in CORS headers, from ALLOW_ORIGINS
  • repo: Repo

Notes:

  • Authentication is done using NIP 98 comparing u to self.host, not the incoming request
  • Each route is responsible for authorization using self.is_admin(pubkey) or self.is_tenant(authorized_pubkey, tenant_pubkey)
  • Successful API responses should be of the form {data, code: "ok"} with an appropriate http status code.
  • Unsuccessful API responses should be of the form {error, code} with an appropriate http status code. code is a short error code (e.g. duplicate-subdomain) and error is a human-readable error message.

pub fn new() -> Self

  • Reads environment and populates members

pub fn serve(&self) -> Result<()>

  • Initializes an axum::Router
  • Adds CORS middleware based on origins
  • Calls axum::serve with a listener

--- Tenant routes

async fn list_tenants(...) -> Response

  • Serves GET /tenants
  • Authorizes admin only
  • Return data is a list of tenant structs from repo.list_tenants

async fn get_tenant(...) -> Response

  • Serves GET /tenants/:pubkey
  • Authorizes admin or matching tenant
  • Return data is a single tenant struct from repo.get_tenant

async fn create_tenant(...) -> Response

  • Serves POST /tenants
  • Authorizes anyone, but must be authorized
  • Creates a new tenant using repo.create_tenant based on the authorized pubkey
  • If tenant is a duplicate, return a 422 with code=pubkey-exists
  • Return data is a single tenant struct. Use HTTP 201.

--- Relay routes

async fn list_relays(...) -> Response

  • Serves GET /relays?tenant=<pubkey>
  • Authorizes admin or existing tenants
  • If user is admin, tenant query parameter is optional
  • If user is a tenant, tenant query parameter is not ok; authenticated pubkey is used
  • Return data is a list of relay structs from repo.list_relays

async fn get_relay(...) -> Response

  • Serves GET /relays/:id
  • Authorizes admin or relay owner
  • Return data is a single relay struct from repo.get_relay

async fn create_relay(...) -> Response

  • Serves POST /relays
  • Authorizes admin or matching tenant pubkey in request body
  • Validates/prepares the relay data to be saved using prepare_relay
  • Creates a new relay using repo.create_relay
  • If relay is a duplicate by subdomain, return a 422 with code=subdomain-exists
  • Return data is a single relay struct. Use HTTP 201.

async fn update_relay(...) -> Response

  • Serves PUT /relays/:id
  • Authorizes admin or relay owner
  • Validates/prepares the relay data to be saved using prepare_relay
  • Updates the given relay using repo.update_relay
  • If relay is a duplicate by subdomain, return a 422 with code=subdomain-exists
  • Return data is a single relay struct.

async fn deactivate_relay(...) -> Response

  • Serves POST /relays/:id/deactivate
  • Authorizes admin or relay owner
  • Deactivates relay using repo.deactivate_relay
  • Return data is empty

--- Billing routes

async fn list_invoices(...) -> Response

  • Serves GET /invoices?tenant=<pubkey>
  • Authorizes admin or existing tenants
  • If user is admin, tenant query parameter is optional
  • If user is a tenant, tenant query parameter is not ok; authenticated pubkey is used
  • Return data is a list of invoice structs from repo.list_invoices

Utility functions

extract_auth_pubkey(headers: &HeaderMap, method: &Method, uri: &Uri) -> Result<String>

  • Parses Authorization header
  • Validates event kind and signature using nostr_sdk
  • Validates event u and method tags against parameters
  • Returns pubkey if header is valid

Refer to https://github.com/nostr-protocol/nips/blob/master/98.md for details. Use nostr_sdk functionality where possible.

prepare_relay(relay: Relay) -> anyhow::Result<Relay>

  • Validate subdomain
  • If plan is free and blossom is enabled, return premium-feature
  • If plan is free and livekit is enabled, return premium-feature
  • Populate schema if not already set
  • Populate missing fields using reasonable defaults