Live social-proof counter on the landing page, tied to the E2E
narrative. Formatted as understated mono footer, not hero brag.
Backend — new GET /api/public/stats (unauthed, 60s in-memory cache):
{
messagesRouted: SELECT COUNT(*) FROM mesh.message_queue,
meshesCreated: SELECT COUNT(*) FROM mesh.mesh WHERE archivedAt IS NULL,
peersActive: SELECT COUNT(*) FROM mesh.presence WHERE disconnectedAt IS NULL,
lastUpdated: ISO timestamp,
}
Aggregate counts only — no ids, no names, no ciphertext, no routing
metadata. Safe for public consumption. cache-control header sets
public/s-maxage=60 for edge caching. `x-cache: HIT|MISS` for debug.
Frontend — new MeshStats Server Component at
modules/marketing/home/mesh-stats.tsx. Reads the endpoint server-side
via the ~/lib/api/server client, renders monospace footer:
ciphertext routed → 4,217 messages · 12 meshes · 8 peers online
broker sees none of it
Graceful zero state: when messagesRouted === 0 shows
"ciphertext → ready to route" instead of embarrassing zeros. Tabular-
nums for the numeric spans so they don't jitter across renders.
Mounted between <CallToAction /> and <LatestNewsToaster />. Page-level
`export const revalidate = 60` so Next.js ISR refreshes the counter
every minute without a DB hit on every request (combined with the
API cache = two-layer 60s TTL, DB sees ~1 query/minute).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New /my/* Hono router scoped by session.user.id. User can only see meshes
they own OR have a non-revoked meshMember row for. All 7 endpoints guard
authz at the query level (ownerUserId = userId OR EXISTS membership).
- GET /my/meshes — paginated list with myRole, isOwner, memberCount
- POST /my/meshes — create mesh (slug collision check, returns id + slug)
- GET /my/meshes/:id — detail (mesh + members + invites)
- POST /my/meshes/:id/invites — generate ic://join/<base64url(JSON)> link.
Matches apps/cli/src/invite/parse.ts format exactly. mesh_root_key is a
deterministic sha256(mesh.id:slug) placeholder until Step 18 ed25519
signing lands.
- POST /my/meshes/:id/archive — owner-only
- POST /my/meshes/:id/leave — member self-removal (sets revokedAt)
- GET /my/invites — list invites this user has issued
Schemas live in packages/api/src/schema/mesh-user.ts. All enums mirror
the DB enums from packages/db/src/schema/mesh.ts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>