Files
427e7578-d7bf-49c8-aee9-2dd…/specs/004-poll-data-model/README.md
2026-03-16 23:03:27 +13:00

126 lines
4.3 KiB
Markdown

---
status: complete
created: '2026-03-16'
tags:
- data
- core
priority: high
created_at: '2026-03-16T07:51:48.793Z'
depends_on:
- 002-p2p-networking
- 003-user-identity-profiles
updated_at: '2026-03-16T10:01:27.181Z'
completed_at: '2026-03-16T10:01:27.181Z'
completed: '2026-03-16'
transitions:
- status: complete
at: '2026-03-16T10:01:27.181Z'
---
# Poll Data Model & Sync
> **Status**: ✅ Complete · **Priority**: High · **Created**: 2026-03-16 · **Tags**: data, core
## Overview
Define the core data structures for polls, options, votes, and their sync behavior across peers. Polls are owned by the creating user and stored on their device (and cached by participants).
## Design
### Poll Schema
```typescript
interface Poll {
id: string; // uuid
ownerId: string; // creator's public key
title: string;
description: string;
anonymous: boolean; // set at creation, immutable
status: 'draft' | 'open' | 'closed';
visibility: 'private' | 'link' | 'public'; // who can view via URL
createdAt: number;
closedAt?: number;
options: Option[];
votes: Vote[];
roles: RoleAssignment[];
}
interface Option {
id: string;
text: string;
addedBy: string; // user ID
addedAt: number;
}
interface Vote {
optionId: string;
voterId: string | null; // null if poll.anonymous === true
timestamp: number;
signature: string; // proves vote authenticity
}
interface RoleAssignment {
userId: string;
role: 'viewer' | 'participant' | 'moderator';
}
```
### Anonymity
- When `anonymous: true`, votes store `voterId: null`. The owner's device may transiently see the sender's peer ID during live submission, but the identity is **not persisted**. Anonymity means: hidden from other participants and public snapshots. It is not cryptographic anonymity from the poll owner.
- When `anonymous: false`, `voterId` is the voter's public key
- This flag is set at poll creation and **cannot be changed** after any votes are cast
### Visibility
- `private`: Only users with assigned roles can access. Poll link requires role assignment.
- `link`: Anyone with the poll link can view as a viewer. No directory listing.
- `public`: Poll snapshot is published to the server. Discoverable via direct link (no directory listing yet — can be added with future discovery feature).
### Roles & Permissions
| Action | Viewer | Participant | Moderator | Owner |
|---|---|---|---|---|
| View poll & results | ✅ | ✅ | ✅ | ✅ |
| Add options | ❌ | ✅ | ✅ | ✅ |
| Vote | ❌ | ✅ | ✅ | ✅ |
| Add/remove users | ❌ | ❌ | ✅ | ✅ |
| Start/stop poll | ❌ | ❌ | ✅ | ✅ |
| Delete poll | ❌ | ❌ | ❌ | ✅ |
- Owner is implicit (`poll.ownerId === userId`); not stored in `roles[]`
- `RoleAssignment` entries in `poll.roles[]` grant viewer, participant, or moderator access
- Users without a role assignment who connect via link get `viewer` by default
- Permission checks happen both client-side (UI gating) and owner-side on message receipt (owner validates before applying any mutation)
- Role changes are broadcast to all connected peers
### Sync Strategy
- Owner is the source of truth
- On connect: owner sends full poll snapshot
- Changes (new vote, new option, role change) are sent as incremental messages
- Participants cache poll locally for offline viewing
## Plan
- [ ] Define TypeScript interfaces for Poll, Option, Vote, RoleAssignment
- [ ] Create poll store (Svelte store + IndexedDB)
- [ ] Implement poll CRUD operations locally
- [ ] Implement sync: snapshot on connect, incremental updates
- [ ] Enforce anonymity invariant (voterId null when anonymous)
- [ ] Create permission check utility (`canVote()`, `canAddOption()`, `canModerate()`, etc.)
- [ ] Implement owner-side validation of incoming messages against roles
- [ ] Implement role change broadcast over PeerJS
## Test
- [ ] Poll can be created, read, updated locally
- [ ] Anonymous polls never store voter identity
- [ ] Poll state syncs correctly between two peers
- [ ] Incremental updates apply correctly to cached state
- [ ] Viewer cannot vote or add options
- [ ] Participant can vote and add options
- [ ] Moderator can add/remove users and start/stop poll
- [ ] Only owner can delete
- [ ] Unknown users connecting via link get viewer role