forked from quic-issues/427e7578-d7bf-49c8-aee9-2dd999e25316
87 lines
2.6 KiB
Markdown
87 lines
2.6 KiB
Markdown
---
|
|
status: complete
|
|
created: '2026-03-16'
|
|
tags:
|
|
- server
|
|
- infra
|
|
priority: high
|
|
created_at: '2026-03-16T07:57:36.544Z'
|
|
depends_on:
|
|
- 001-project-setup
|
|
updated_at: '2026-03-16T10:01:29.167Z'
|
|
completed_at: '2026-03-16T10:01:29.167Z'
|
|
completed: '2026-03-16'
|
|
transitions:
|
|
- status: complete
|
|
at: '2026-03-16T10:01:29.167Z'
|
|
---
|
|
|
|
# Poll Snapshot Server
|
|
|
|
> **Status**: ✅ Complete · **Priority**: High · **Created**: 2026-03-16 · **Tags**: server, infra
|
|
|
|
## Overview
|
|
|
|
A minimal server that stores poll snapshots for public/offline viewing. This is the only server-side component — intentionally thin. No user accounts, no directory, no auth beyond signature verification.
|
|
|
|
## Design
|
|
|
|
### Tech Stack
|
|
|
|
- **Runtime**: Cloudflare Workers (or a simple Bun server if self-hosting)
|
|
- **Storage**: Cloudflare KV (or SQLite for self-hosted)
|
|
- **Auth**: Snapshot writes are signed with the poll owner's Ed25519 private key
|
|
|
|
### API Endpoints
|
|
|
|
- `PUT /api/polls/:id/snapshot` — Store/update snapshot (signed by poll owner)
|
|
- Body: `{ pollId, ownerId, ownerPeerId, title, description, options[], voteCounts, status, signature }`
|
|
- `GET /api/polls/:id/snapshot` — Fetch snapshot (public, no auth)
|
|
|
|
### Security
|
|
|
|
- Write operations require a valid Ed25519 signature
|
|
- Verify signature against `ownerId` in the request body; bind `pollId → ownerId` on first write; reject future writes if `ownerId` changes
|
|
- No sessions, no cookies, no passwords
|
|
- Rate limiting on writes to prevent abuse
|
|
|
|
### Data Model
|
|
|
|
```typescript
|
|
// KV key: poll:{pollId}:snapshot → PollSnapshot
|
|
|
|
interface PollSnapshot {
|
|
pollId: string;
|
|
ownerId: string;
|
|
ownerPeerId: string;
|
|
title: string;
|
|
description: string;
|
|
options: { id: string; text: string }[];
|
|
voteCounts: Record<string, number>;
|
|
status: 'draft' | 'open' | 'closed';
|
|
updatedAt: number;
|
|
}
|
|
```
|
|
|
|
## Plan
|
|
|
|
- [ ] Set up Cloudflare Workers project (or simple Bun server)
|
|
- [ ] Implement Ed25519 signature verification middleware
|
|
- [ ] Implement poll snapshot store/fetch endpoints
|
|
- [ ] Add rate limiting
|
|
- [ ] Deploy
|
|
|
|
## Test
|
|
|
|
- [ ] Snapshot store with valid signature succeeds
|
|
- [ ] Snapshot store with invalid signature is rejected
|
|
- [ ] Snapshot fetch returns stored data (public, no auth)
|
|
- [ ] Second write from different ownerId is rejected
|
|
- [ ] Rate limiting works
|
|
|
|
## Notes
|
|
|
|
- The P2P app works without this server — it just loses public sharing and offline snapshot viewing
|
|
- Future: user directory endpoints can be added here when peer discovery is implemented (see archived spec 007)
|
|
- Consider a TTL on snapshots (e.g., auto-expire 90 days after last update)
|