Group location sharing that keeps exact coordinates off the wire

Where's My Crowd is an open, API-first prototype for friends, crews, and events who need a live map without handing plaintext GPS to a central server. Clients encrypt locations; the API stores geohash-12 indexes and group ciphertext only. The public endpoints run on the operator's Cloudflare stack — not a priced API product yet.

What you get

A v2 prototype on Cloudflare: Hono Worker, D1 database, and a MapLibre reference client. HTTPS REST is live at api.crowdapi.app; Nostr kind 31990 transport is implemented but optional and not fully configured in production. Hosting is the operator's Cloudflare bill — there is no public pricing, billing, or paid API plan yet; abuse-oriented rate limits are in place while a quota or payment strategy is still being designed.

Encrypted locations

Exact lat/long live inside AES-GCM ciphertext. The server indexes 12-character geohashes for snapshots and never accepts plaintext coordinates on writes.

Groups & invites

Create groups, share invite links or QR codes, and deliver HPKE welcome envelopes so new members receive group keys securely.

Signed API access

Production requests use Ed25519 headers (X-WMC-Pubkey, X-WMC-Signature, X-WMC-Timestamp). Development mode can skip signing for local Docker.

Nostr bridge

Location updates can enqueue Nostr outbox events; flush to relays or publish client-signed events. Mock publish is enabled until you configure real relays.

Paginated snapshots

Clients poll GET /groups/{id}/snapshot with cursor pagination and ETag / 304 support — no hard member cap.

Reference map UI

MapLibre GL JS with OpenFreeMap vector tiles, continuous sharing toggle, decrypt markers, and opt-in HPKE attributes stored as opaque ciphertext.

Privacy model

Designed for pseudonymous groups: the host sees coarse geohash cells and ciphertext blobs, not cleartext identity fields unless users opt in with HPKE.

Architecture

Monorepo packages: @wmc/api-worker, @wmc/crypto, @wmc/nostr-bridge, @wmc/sample-app.

┌─────────────────────┐     HTTPS /v1      ┌──────────────────────────┐
│  MapLibre sample    │ ─────────────────► │  wmc-api (Hono + D1)     │
│  crowdapi.app/app   │   Ed25519 signed   │  api.crowdapi.app        │
└──────────┬──────────┘                    └────────────┬─────────────┘
           │  @wmc/crypto encrypt                       │ optional
           └──────────────────────────────────────────►│ @wmc/nostr-bridge
                                                        │ outbox + relays

HTTP API (v1)

OpenAPI draft in the project repo. All /v1/* routes require signed auth in production. Start with health check (no auth).

Method Path Purpose
GET/healthService status (no auth)
GET/v1/meCaller identity
PUT/v1/me/attributesStore HPKE ciphertext (opt-in PII)
POST/v1/groupsCreate a group
POST/v1/groups/{id}/locationsPublish encrypted fix (geohash-12 + ciphertext)
GET/v1/groups/{id}/snapshotPaginated member locations + ETag
POST/v1/groups/{id}/invitesInvite link / QR
POST/v1/groups/{id}/invites/{id}/redeemJoin group + welcome envelope
POST/v1/nostr/flushPublish pending Nostr outbox
POST/v1/groups/{id}/nostr/publishQueue client-signed Nostr event

Example signed create (body hash = SHA-256 hex of raw JSON):

curl -sS -X POST https://api.crowdapi.app/v1/groups \
  -H 'Content-Type: application/json' \
  -H 'X-WMC-Pubkey: <64-hex-ed25519-pubkey>' \
  -H 'X-WMC-Signature: <hex-sig>' \
  -H 'X-WMC-Timestamp: <unix-seconds>' \
  -d '{"groupId":"my-crew"}'

Try it

The reference client runs in your browser: create a group, publish GPS (encrypted), invite friends via QR, and refresh the shared map.