A decentralized community organizing tool that helps groups coordinate presentations and discussions through a two-stage process: interest gathering and scheduling.
RallyRound enables communities to:
- Stage 1: Create topics and gather interest from community members
- Stage 2: Schedule sessions once interest thresholds are met using Google Calendar integration
Built with a peer-to-peer architecture using GunDB SEA for decentralized data storage and cryptographic authentication, with Google OAuth for identity verification and calendar integration.
Stage One: Gathering Interest
- Presenters create topics with customizable parameters:
- Minimum and maximum participant thresholds
- Session duration
- One-time or recurring sessions
- Recurrence patterns (weekly, bi-weekly, monthly)
- Members express interest in topics
- Real-time interest tracking with visual progress indicators
- Automatic transition to Stage 2 when minimum threshold is met
Stage Two: Scheduling
- Integration with Google Calendar via OAuth
- Automatic availability checking for all interested participants
- Calendar event creation with all participants
- Support for recurring sessions
- Decentralized Architecture: GunDB for peer-to-peer data synchronization
- SEA Cryptographic Identity: GunDB SEA (Security, Encryption, Authorization) for authenticated writes
- Public-Read, User-Only-Write: Topics stored in user space, verified automatically by peers
- Real-time Updates: Live synchronization of topic changes across all clients
- Calendar Integration: Read availability and create events in participants' Google Calendars
RallyRound uses a hybrid architecture that combines traditional web authentication with decentralized data storage:
flowchart TB
subgraph Client["Browser (React SPA)"]
UI["User Interface"]
SEA["SEA Crypto Module"]
end
subgraph Server["Express Server"]
Auth["Authentication"]
Seed["Seed Generator"]
Calendar["Calendar Proxy"]
Relay["GunDB Relay"]
end
subgraph Google["Google Services"]
OAuth["OAuth 2.0"]
CalAPI["Calendar API"]
end
subgraph P2P["GunDB P2P Network"]
Topics["Topic Data"]
Sync["Real-time Sync"]
end
UI -->|Login| Auth
Auth -->|Verify identity| OAuth
Auth -->|Get seed| Seed
SEA -->|Derive keypair| Seed
UI -->|Check availability| Calendar
Calendar --> CalAPI
SEA <-->|Read/write topics| P2P
Relay <-->|Peer connection| P2P
How it works:
-
Identity: Users sign in with Google. The server generates a deterministic seed from their Google ID, which the browser uses to derive cryptographic keys. The same user always gets the same keys.
-
Data Storage: Topics are stored in GunDB, a decentralized peer-to-peer database. Each user's data is cryptographically signed, so only they can modify it, but anyone can read it.
-
Scheduling: Once enough people express interest in a topic, the app checks participants' Google Calendar availability and creates calendar events.
| Component | Technology | Purpose |
|---|---|---|
| Frontend | React + TypeScript + Vite | Single-page application with real-time updates |
| Backend | Node.js + Express | OAuth handling, seed generation, calendar proxy |
| Database | GunDB with SEA | Decentralized storage with cryptographic authentication |
| Identity | Google OAuth + GunDB SEA | Google verifies who you are; SEA keys let you write data |
- Google OAuth verifies user identity
- Deterministic keys ensure the same user always gets the same cryptographic identity
- Client-side key derivation means private keys never leave your browser
- Signed writes let peers verify that data came from its claimed author
- Public discovery allows browsing topics while protecting authorship
- Node.js (v18 or higher)
- npm
- Google Cloud Project with OAuth 2.0 credentials
- Google Calendar API enabled
git clone <repository-url>
cd RallyRound# Install server dependencies
npm install
# Install client dependencies
cd client && npm install && cd ..- Go to Google Cloud Console
- Create a new project or select an existing one
- Enable the following APIs:
- Google+ API (or People API)
- Google Calendar API
- Configure OAuth consent screen:
- Add your email as a test user
- Add scopes:
userinfo.profile,userinfo.email,calendar.readonly,calendar.events
- Create OAuth 2.0 credentials:
- Application type: Web application
- Authorized redirect URIs:
http://localhost:8765/auth/google/callback
- Copy the Client ID and Client Secret
Create a .env file in the root directory:
cp .env.example .envEdit .env and add your credentials:
PORT=8765
NODE_ENV=development
GOOGLE_CLIENT_ID=your_google_client_id_here
GOOGLE_CLIENT_SECRET=your_google_client_secret_here
GOOGLE_REDIRECT_URI=http://localhost:8765/auth/google/callback
SESSION_SECRET=generate_a_random_string_here
SEA_SECRET=generate_another_random_string_here
GUN_PEERS=http://localhost:8765/gun
APP_URL=http://localhost:8765Important: SEA_SECRET is used to generate deterministic seeds. Keep it secure!
npm run devThis starts both:
- Backend server on port 8765
- Vite dev server on port 3000 (with proxy to backend)
Open your browser and navigate to:
http://localhost:3000
RallyRound bridges Google identity to decentralized storage using a two-phase authentication:
sequenceDiagram
participant User
participant Browser
participant Server
participant Google
participant GunDB
Note over User,Google: Phase 1: Verify identity with Google
User->>Browser: Click "Sign in with Google"
Browser->>Server: Redirect to OAuth
Server->>Google: Request authorization
Google->>User: Show consent screen
User->>Google: Grant permission
Google->>Server: Return auth code
Server->>Google: Exchange for tokens
Google-->>Server: User info + tokens
Server-->>Browser: Session established
Note over Browser,GunDB: Phase 2: Establish decentralized identity
Browser->>Server: Request crypto seed
Server-->>Browser: Deterministic seed (from Google ID)
Browser->>Browser: Generate keypair from seed
Browser->>GunDB: Authenticate with keypair
GunDB-->>Browser: Ready to read/write
Why two phases?
- Phase 1 proves you are who you say you are (Google's job)
- Phase 2 gives you a cryptographic identity for the decentralized network
The seed is deterministic: the same Google account always produces the same keypair. This means you can log in from any device and access your data, while your private key never leaves your browser.
RallyRound/
├── server/
│ ├── index.js # Express server + GunDB relay peer
│ └── routes/
│ └── auth.js # OAuth, seed generation, calendar API
│
├── client/ # React SPA (Vite + TypeScript)
│ ├── src/
│ │ ├── components/ # UI: Header, Dashboard, TopicCard, etc.
│ │ ├── hooks/ # useAuth (login state), useTopics (CRUD)
│ │ ├── lib/ # gun.ts (DB client), sea-auth.ts (crypto)
│ │ └── types/ # TypeScript interfaces
│ └── vite.config.ts # Dev server with API proxy
│
├── .env.example # Environment template
└── package.json # Scripts: dev, build, start
GET /auth/google- Initiate Google OAuth flowGET /auth/google/callback- OAuth callback handlerGET /auth/user- Get current user infoGET /auth/sea-seed- Get deterministic SEA seed for keypair derivationPOST /auth/logout- Logout current user
GET /auth/calendar/availability- Get user's calendar availabilityPOST /auth/calendar/event- Create a calendar event
/gun- GunDB peer endpoint (WebSocket)
interface Topic {
id: string;
title: string;
description: string;
presenter: string;
presenterEmail: string;
presenterPub: string; // SEA public key
minParticipants: number;
maxParticipants?: number;
duration: number;
type: 'one-time' | 'recurring';
recurrence?: 'weekly' | 'biweekly' | 'monthly';
stage: 1 | 2 | 3;
createdAt: number;
scheduledTime?: number;
}interface PublicTopicRef {
id: string;
title: string;
presenter: string;
presenterPub: string;
minParticipants: number;
stage: 1 | 2 | 3;
interestCount: number;
createdAt: number;
}- SEA Authentication: GunDB's built-in cryptographic layer
- Deterministic Keys: Same user always gets same keypair
- Server-Mediated Seed: Server bridges Google identity to SEA identity
- Automatic Verification: Peers verify signatures without custom code
- All writes cryptographically signed
- Peers automatically reject invalid signatures
- User space protected by default (public-read, user-only-write)
- No custom certificate infrastructure needed
- Server Dependency: SEA seed generation requires server (for now)
- SEA_SECRET Compromise: Would allow impersonation of any user
- Google Account Compromise: Leads to GunDB identity compromise
- Use HTTPS: Critical for OAuth and session security
- Secure SEA_SECRET: Store in secure key management (AWS KMS, HashiCorp Vault)
- Rate Limiting: Prevent abuse of seed endpoint
- Audit Logging: Log all seed requests
The server's role is minimal and can be reduced further:
| Current | Future Options |
|---|---|
| Server generates seed from Google ID | Client derives seed from Google ID directly |
| DID/Verifiable Credentials | |
| Web3 wallet signatures as seed |
The SEA-based topic storage pattern remains unchanged regardless of how the seed is generated.
# Development (both server and client)
npm run dev
# Server only
npm run dev:server
# Client only
npm run dev:client
# Build client for production
npm run build
# Start production server
npm startContributions are welcome! Please see CONTRIBUTING.md for guidelines.
See LICENSE file for details.