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.
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.
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.
Exact lat/long live inside AES-GCM ciphertext. The server indexes 12-character geohashes for snapshots and never accepts plaintext coordinates on writes.
Create groups, share invite links or QR codes, and deliver HPKE welcome envelopes so new members receive group keys securely.
Production requests use Ed25519 headers (X-WMC-Pubkey, X-WMC-Signature, X-WMC-Timestamp). Development mode can skip signing for local Docker.
Location updates can enqueue Nostr outbox events; flush to relays or publish client-signed events. Mock publish is enabled until you configure real relays.
Clients poll GET /groups/{id}/snapshot with cursor pagination and ETag / 304 support — no hard member cap.
MapLibre GL JS with OpenFreeMap vector tiles, continuous sharing toggle, decrypt markers, and opt-in HPKE attributes stored as opaque ciphertext.
Designed for pseudonymous groups: the host sees coarse geohash cells and ciphertext blobs, not cleartext identity fields unless users opt in with HPKE.
Retry-After). Operational guardrails, not a metering or payment model.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
OpenAPI draft in the project repo. All /v1/* routes require signed auth in production.
Start with health check (no auth).
| Method | Path | Purpose |
|---|---|---|
| GET | /health | Service status (no auth) |
| GET | /v1/me | Caller identity |
| PUT | /v1/me/attributes | Store HPKE ciphertext (opt-in PII) |
| POST | /v1/groups | Create a group |
| POST | /v1/groups/{id}/locations | Publish encrypted fix (geohash-12 + ciphertext) |
| GET | /v1/groups/{id}/snapshot | Paginated member locations + ETag |
| POST | /v1/groups/{id}/invites | Invite link / QR |
| POST | /v1/groups/{id}/invites/{id}/redeem | Join group + welcome envelope |
| POST | /v1/nostr/flush | Publish pending Nostr outbox |
| POST | /v1/groups/{id}/nostr/publish | Queue 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"}'
The reference client runs in your browser: create a group, publish GPS (encrypted), invite friends via QR, and refresh the shared map.