Winchester Coin Club Forum - Technical Design Document
Architecture Overview
The Winchester Coin Club forum is a cryptographically-signed message board using ECDSA P-256 signatures for authentication and a web-of-trust model for authorization.
System Components
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Web Browser │◄───────►│ Flask API │◄───────►│ PostgreSQL DB │
│ (Static HTML) │ HTTPS │ (forum-api) │ │ │
│ │ │ │ │ │
│ • forum.html │ │ • /messages │ │ • forum_users │
│ • forum.js │ │ • /users │ │ • forum_messages│
│ • forum-crypto │ │ • /register-* │ │ • forum_reg_req │
└─────────────────┘ └──────────────────┘ └─────────────────┘
Technology Stack
Frontend:
- Pure HTML/CSS/JavaScript (no framework)
- Web Crypto API for ECDSA operations
- localStorage for key persistence
- Fetch API for HTTP requests
Backend:
- Python 3.x
- Flask web framework
- psycopg2 for PostgreSQL
- python-ecdsa for signature verification
Database:
- PostgreSQL 13+
- Three main tables:
forum_users, forum_messages, forum_registration_requests
Cryptographic Implementation
Key Generation (Client-Side)
Keys are generated using the Web Crypto API with ECDSA P-256 (secp256r1):
const keyPair = await crypto.subtle.generateKey(
{ name: 'ECDSA', namedCurve: 'P-256' },
true, // extractable
['sign', 'verify']
);
Private Key:
- Generated in browser, never transmitted
- Stored in localStorage as PKCS#8 PEM
- Used to sign all messages
Public Key:
- Exported as SPKI PEM format
- Transmitted to server during registration
- Stored in database for verification
Address:
- SHA-256 hash of the public key
- Serves as the user's unique identifier
- Format: Base64-encoded hash (44 characters)
Message Signing
Each message is signed with the following process:
- Construct message string:
messageToSign = address + nonce + body
- Sign with private key using ECDSA with SHA-256
- Send to server with signature as hex string
Signature Verification (Server-Side)
Server verifies each message signature before accepting using the python-ecdsa library.
Database Schema
forum_users
| Column |
Type |
Description |
| id |
SERIAL |
Primary key |
| address |
VARCHAR(255) |
SHA-256 hash of public key (unique) |
| public_key |
TEXT |
SPKI PEM format public key |
| display_name |
VARCHAR(100) |
Human-readable name |
| directus_user_id |
UUID |
Link to Directus CMS user (nullable) |
| is_approved |
BOOLEAN |
Whether user can post |
| vouched_by |
VARCHAR(255) |
Address of user who vouched |
| user_role |
VARCHAR(20) |
'member', 'friend', or 'bot' |
| created_at |
TIMESTAMP |
Registration timestamp |
forum_messages
| Column |
Type |
Description |
| id |
SERIAL |
Primary key |
| author_address |
VARCHAR(255) |
Foreign key to forum_users |
| parent_id |
INTEGER |
NULL for threads, message ID for replies |
| subject |
VARCHAR(255) |
Optional subject line |
| body |
TEXT |
Message content |
| signature |
TEXT |
Hex-encoded ECDSA signature |
| nonce |
VARCHAR(100) |
Timestamp + random (anti-replay) |
| created_at |
TIMESTAMP |
Post timestamp |
API Endpoints
Public Endpoints (No Authentication)
GET /messages - Returns all messages with author info
GET /users - Returns all approved users with message counts
GET /user/<address> - Get specific user info
GET /register-request/<address> - Get registration request details
Authenticated Endpoints
POST /register-member - Create forum account for club member (requires Directus token)
POST /register-request - Create pending registration (public)
POST /messages - Create message (requires valid signature)
Authentication Flow
For Club Members (Directus Users)
- User logs in to Directus → receives access token
- User clicks "Register for Forum" on homepage
- Browser generates key pair
- POST /register-member with Directus token + public key
- Server verifies token with Directus API
- Server creates forum_users record with is_approved=TRUE
- User can immediately post
For Public Users (Web of Trust)
- User clicks "Get Started"
- Browser generates key pair
- POST /register-request with public key + display name
- Server stores request, returns public_key_hash
- User shares hash with existing member
- Member verifies identity (out-of-band)
- Admin/member approves via Directus or manual DB update
- User's is_approved set to TRUE
- User can now post
Security Considerations
Protections
- ✅ Forged messages (signature verification)
- ✅ Message tampering (signature includes body)
- ✅ Replay attacks (nonce verification)
- ✅ Spam registration (IP rate limiting, vouching required)
- ✅ Password leaks (no passwords used)
- ✅ MITM attacks (HTTPS required)
Current Limitations
- ⚠️ Key loss = identity loss (no recovery mechanism)
- ⚠️ Browser localStorage can be cleared
- ⚠️ No key rotation mechanism
- ⚠️ Nonce reuse not currently checked (future enhancement)
Frontend Architecture
Three-Column Layout
┌──────┬────────────┬─────────────────────┐
│ Nav │ Threads │ Message Content │
│ 70px │ 350px │ Flexible │
│ │ │ │
│ 🏠 │ Thread 1 │ ┌─────────────────┐ │
│ 📝 │ Thread 2 │ │ Parent Message │ │
│ 👥 │ Thread 3 │ └─────────────────┘ │
│ │ │ ┌───────────────┐ │
│ 💬 │ │ │ Reply (30px) │ │
│ ❓ │ │ └───────────────┘ │
│ 🇺🇸 │ │ ┌─────────────┐│
│ │ │ │Reply (60px)││
└──────┴────────────┴─────┴─────────────┘┘
View Modes
- Threads View - Shows top-level messages only (parent_id = NULL)
- Users View - Shows all approved users with role badges and message counts
- My Posts View - Shows current user's messages with thread context
Threading Implementation
Messages support unlimited nesting depth through recursive rendering. Each reply is indented 30px from its parent, creating a visual hierarchy.
Thread Structure Example
Thread (parent_id = NULL)
├─ Reply A (parent_id = thread.id)
├─ Reply B (parent_id = thread.id)
│ ├─ Reply to B.1 (parent_id = B.id)
│ └─ Reply to B.2 (parent_id = B.id)
└─ Reply C (parent_id = thread.id)
Integration with Directus
User Role Mapping
Directus roles map to forum roles:
- Directus Role: Member →
user_role = 'member'
- Directus Role: Friend →
user_role = 'friend'
- No Directus account → Public user (requires vouching)
Comparison with Traditional Forums
| Feature |
Traditional Forum |
Cryptographic Forum |
| Authentication |
Username/password |
ECDSA key pair |
| User Registration |
Email verification |
Cryptographic identity |
| Authorization |
Admin/moderator approval |
Web of trust (vouching) |
| Password Reset |
Email link |
N/A (no passwords) |
| Identity Recovery |
Support ticket |
Export/import keys |
| Spam Prevention |
CAPTCHA, email blocks |
IP limiting, vouching |
| Message Integrity |
Database trust |
Cryptographic signatures |
| Centralization |
Admin-controlled |
Distributed trust |
Future Enhancements
Planned Features
- Key Export/Import UI
- Nonce Replay Prevention
- Message Threading UI (collapse/expand)
- Search Functionality
- @Mention Notifications
- WebSocket Live Updates
- Rate Limiting
- Message Editing (with re-signing)
Conclusion
The Winchester Coin Club forum demonstrates that cryptographic identity can replace traditional password-based authentication while enabling decentralized trust through a web-of-trust model. The system is secure, user-friendly for non-technical users, and resistant to common attack vectors.
By using ECDSA signatures for message authentication and a vouching system for authorization, the forum creates a self-moderating community without requiring centralized control.
Version: 2025-10-19
Author: Winchester Coin Club Development Team
Winchester Coin Club - Building community through trust