Files
caravel/backend/spec/infra.md
T
Jon Staab b4af2f3866
Docker / build-and-push-image (backend, backend, coracle/caravel-backend) (push) Failing after 0s
Docker / build-and-push-image (frontend, frontend, coracle/caravel-frontend) (push) Failing after 0s
Update spec and readme
2026-05-22 10:15:52 -07:00

3.3 KiB

pub struct Infra

Infra is a background worker that listens for activity and synchronizes relay configuration to a remote zooid instance.

Members:

  • env: Env - configuration; supplies zooid_api_url, relay_domain, the BLOSSOM_S3_* and LIVEKIT_* settings, and the robot key used to sign requests
  • query: Query
  • command: Command

pub fn new(query: Query, command: Command, env: &Env) -> Self

  • Stores query, command, and a clone of env

pub async fn start(self)

  • Subscribes to command.notify
  • Runs reconcile_relay_state("startup") before entering the loop
  • Loops on rx.recv(), calling handle_activity for each Activity
  • On Lagged, runs reconcile_relay_state("lagged"); on Closed, exits

async fn handle_activity(&self, activity: &Activity)

  • Ignores anything that isn't a relay resource with activity type create_relay, update_relay, activate_relay, deactivate_relay, or fail_relay_sync
  • For fail_relay_sync, schedules a delayed retry via schedule_relay_sync_retry
  • Otherwise resolves the relay (skip if gone) and calls sync_relay

async fn reconcile_relay_state(&self, source: &str)

  • Lists relays still pending sync (query.list_relays_pending_sync)
  • For each: sync_relay immediately if its sync_error is empty, otherwise schedule_relay_sync_retry

async fn schedule_relay_sync_retry(&self, relay_id: &str, source: &str)

  • Counts the relay's consecutive trailing fail_relay_sync activities to derive the attempt number
  • Computes an exponential backoff (base 30s, doubling, capped at 15 minutes); gives up after RELAY_SYNC_RETRY_MAX_ATTEMPTS (6) to avoid infinite retry loops
  • Spawns a task that sleeps for the delay, then re-reads the relay and sync_relays it (no-op if the relay is gone)

async fn sync_relay(&self, relay: &Relay)

  • Calls try_sync_relay; on success command.complete_relay_sync, on failure command.fail_relay_sync with the error

async fn try_sync_relay(&self, relay: &Relay)

  • A relay is "new" only if it has never completed a sync (synced != 1 and no complete_relay_sync activity exists). New relays are created with POST /relay/:id; existing relays are updated with PATCH /relay/:id.
  • A freshly generated secret is included only for creation (POST), so updates don't rotate relay identity and we never store the secret.
  • The body carries relay configuration: host (= subdomain.relay_domain), schema, inactive (true when status is inactive or delinquent), info (name/icon/description/pubkey), policy, groups, management, blossom, livekit, push, and hard-coded roles.
  • When blossom_enabled, the blossom section uses adapter: "s3" with the BLOSSOM_S3_* settings and s3.key_prefix set to the relay's schema; otherwise it sends { "enabled": false }.
  • When livekit_enabled, the livekit section carries the LIVEKIT_* settings; otherwise { "enabled": false }.

pub async fn list_relay_members(&self, relay_id: &str) -> Result<Vec<String>>

  • GET /relay/:id/members from zooid; returns the members array

async fn request(&self, method, path, body)

  • Sends an authenticated request to the zooid API at path (relative to env.zooid_api_url), with a 5s timeout
  • Authenticates each request with a NIP-98 header via env.make_auth
  • Returns the response on 2xx; bails with the status and body text otherwise