# Cloudflare edge fabric

`curso.rs` is one Cloudflare Worker deployment with three responsibilities that stay semantically separate:

1. **Static assets** carry the cockpit, guides, immutable parts, and resource manifests.
2. **ResourceDirectory Durable Objects** provide a small SQLite index over public magnet, torrent, cursor, mailbox, and part descriptors.
3. **CurveMailbox Durable Objects** provide explicit-opt-in mailbox coordination, encrypted envelopes, signaling, receipts, settings, and hibernatable WebSockets.

There is no upstream origin server and no second mailbox Worker.

## Why this shape

Bulk bytes stay in static assets, torrent/webseed paths, browser cache, or peers. SQLite stores descriptors and coordination records, not movies or large immutable bodies.

```text
browser
  │
  ├─ static asset / webseed ───────────────► immutable bytes
  │
  ├─ /api/v1/resources/<kind>/<id>
  │        │
  │        ▼
  │   ResourceDirectory DO
  │        └─ SQLite descriptor index
  │
  └─ /api/v1/mailboxes/<id>/...
           │
           ▼
      CurveMailbox DO
           └─ SQLite envelopes / receipts / settings / signaling
```

This keeps the Cloudflare billable surface small and makes every layer inspectable.

## Public resource directory

The read-only resource API is:

```text
GET /api/v1/resources/magnet
GET /api/v1/resources/magnet/:id
GET /api/v1/resources/torrent
GET /api/v1/resources/torrent/:id
GET /api/v1/resources/cursor
GET /api/v1/resources/cursor/:id
GET /api/v1/resources/mailbox
GET /api/v1/resources/mailbox/:id
GET /api/v1/resources/part
GET /api/v1/resources/part/:id
```

Each resource kind maps to its own `ResourceDirectory` object via `getByName(kind)`. The object lazily loads the versioned static manifest under `/resources/<kind>.json` and mirrors the compact descriptors into SQLite. Public lookups are read-only. A descriptor does not grant authority merely because it is resolvable.

## Resource requests through mailboxes

Mailbox exchange does not need another queue protocol. A Cursors mailbox can carry typed envelope families such as:

```text
resource-request/v1
  kind
  id / cryptopointer
  requested view
  acceptable carriers
  capability refs[]
  reply mailbox
  expiry

resource-response/v1
  request id
  descriptor / cryptopointer
  available carriers[]
  integrity evidence
  receipt
```

These use the existing authenticated `/envelopes` route. Delivery is still separate from authorship, authority, decryption rights, and effect completion.

## Browser fallback order

For immutable parts the browser uses:

```text
WebRTC/WebTorrent peer
        ↓
same-origin browser part mailbox/cache
        ↓
curso.rs static HTTP part
        ↓
other declared HTTPS webseed
```

The static HTTP mapping is `/parts/<content-id>/<piece>.part`. A static Worker cannot synthesize a missing part from an arbitrary remote WebRTC peer without adding server-side transfer machinery, so the HTTP path only serves parts that were published with the deployment.

## Mailbox API

Remote mailboxes remain admission-gated:

```text
POST /api/v1/mailboxes/:id/enroll
GET|PUT /api/v1/mailboxes/:id/settings
GET|POST /api/v1/mailboxes/:id/envelopes
GET|POST /api/v1/mailboxes/:id/signals
GET /api/v1/mailboxes/:id/socket
GET /api/v1/mailboxes/:id/events
```

Every mailbox request uses the existing short-lived `Cursors-Envelope` proof. Enrollment additionally requires the configured offline admission root's public verification material. Private identity keys and admission-root private keys never belong in the Worker, page, URL, or chat.

## Cost boundary

The production Worker uses Workers Static Assets plus SQLite-backed Durable Objects. It deliberately does **not** require D1, KV, R2, Queues, Workers AI, Stream, or TURN for the basic fabric.

Current Cloudflare free-tier limits and pricing can change, so check the official Workers and Durable Objects pricing pages before changing retention or traffic assumptions. The application keeps hard per-mailbox quotas and a small CPU/subrequest ceiling even when the account plan allows more.

The guiding rule is: **SQLite for compact coordination and indexes; static/P2P paths for bytes.**

## Identity / authority boundary

Transport, lookup, storage, identity, and authority stay separate:

- a resource lookup proves only that the directory returned a descriptor;
- a static asset response proves only HTTP delivery plus whatever integrity evidence the caller verifies;
- a mailbox delivery receipt proves mailbox acceptance, not application execution;
- a WebRTC peer is not a Cursors identity;
- only verified signed grants carry Cursors authority.
