This is a multi-tenant voting services API built with Hono and Bun that provides democratic voting capabilities for Farcaster communities. The API was extracted from a Next.js application to enable reuse across multiple applications and AI agents.
- Multi-tenant group management with configurable criteria
- Democratic voting system with 3-of-5 subgroup mechanics
- Multi-modal authentication (Frame SDK, Bearer tokens for services)
- Real-time participation tracking and analytics
- XMTP integration for chat-based interactions
- Comprehensive test coverage with Vitest
- Runtime: Bun (TypeScript)
- Framework: Hono (Web Standards API)
- Database: PostgreSQL with pgvector extension
- ORM: Drizzle ORM
- Validation: Zod schemas
- Testing: Vitest with coverage
- Deployment: Docker containers, Railway
-
VotingService (
src/db/services/voting-service.ts)- Handles proposal submission and voting logic
- Manages member eligibility and subgroup selection
- Tracks participation metrics and voting history
-
GroupsService (
src/db/services/groups-service.ts)- Multi-tenant group management
- Member assignment and removal
- Participation statistics and analytics
-
UsersService (
src/db/services/users-service.ts)- User management
- Address management
- XMTP integration
Located in src/middleware/auth.ts, supports Bearer token authentication:
- Frame SDK tokens: JWT tokens for Next.js Mini App users
- Service keys: Secret keys for Eliza agents, MCP servers, and external services
All authentication uses the standard Authorization: Bearer <token> header format.
The schema (src/db/voting-schema.ts) supports multi-tenancy with:
- groups: Group definitions with JSON criteria
- users: Address-based user identification
- members: Group membership with status tracking
- proposals: Add/remove member proposals
- votes: Individual vote records
- vote_groups: Voting assignments per group
- cached_usernames: Farcaster username lookups
- participation_snapshot: Voting participation analytics
groups -> members -> proposals -> votes
\-> vote_groups -> members
\-> participation_snapshot -> members
Previously the users table was anchored on fid but we want to prefer userId (uuid) for consistency with address and other contexts. Any time we spin up a route that takes in users we should refer to them with userId. In routes that return users we should return both userId and fid.
GET /health- Health check
GET /groups- List all groupsPOST /groups- Create new groupGET /groups/:id- Get group details (with stats/members options)PUT /groups/:id- Update groupDELETE /groups/:id- Delete groupGET /groups/name/:name- Get group by nameGET /groups/chat/:chatId- Get group by chat ID
GET /groups/:id/members- List group membersPOST /groups/:id/members- Add member to groupPUT /groups/:id/members/:memberId- Update member statusDELETE /groups/:id/members/:memberId- Remove memberPOST /groups/:id/verify-eligibility- Verify member eligibility
GET /groups/:id/stats- Group participation statisticsPOST /groups/validate-criteria- Validate group criteria
GET /proposals- List proposalsPOST /proposals- Submit proposalGET /proposals/:id- Proposal details
POST /votes- Cast voteGET /votes/current-user- User's voting history
GET /activity- Recent activity feed
# Development
bun run dev # Start dev server with hot reload
bun run test # Run test suite
bun run test:watch # Run tests in watch mode
bun run test:coverage # Generate coverage report
# Database
bun run db:up # Start PostgreSQL container
bun run db:down # Stop PostgreSQL container
bun run db:generate # Generate Drizzle migrations
bun run db:migrate # Run migrations
bun run db:studio # Open Drizzle Studio
# Testing Database
bun run db:test:up # Start test database
bun run db:test:setup # Run migrations on test DB
bun run db:test:down # Stop test database
# Production
bun run build # Build for production
bun run start # Start production server- Test Database: PostgreSQL on port 5556
- Test Runner: Vitest with sequential execution
- Coverage: v8 provider with HTML reports
- Setup: Automated database migrations and cleanup
- CI/CD: GitHub Actions workflow (
.github/workflows/test.yml) runs tests on PR and main commits
Groups define membership criteria as a string array saved as JSON in the criteria field:
["has 1000 followers", "is into trains", "can type 60 words per minute"]- Subgroup Size: 5 members per voting group
- Approval Threshold: 3 of 5 votes needed
- Voting Period: 16 hours
- Cooldown: 24 hours for rejected proposals
The participation_snapshot table tracks:
- Vote groups assigned vs. votes cast
- Response time analytics
- Activity-based member scoring
- Neynar API: Username/FID lookups
- Frame SDK: Authentication tokens
- Cast Analysis: Proposal extraction from messages
- Chat IDs: Groups can be linked to XMTP chats
- Address Verification: Links XMTP addresses to Farcaster identities
- Eliza Agents: AI agents can submit proposals via Bearer tokens
- MCP Servers: Claude Code integration for data access
- Alerts Service: Real-time notifications
- Unit Tests: Individual service methods
- Integration Tests: API endpoint functionality
- Database Tests: Schema and migration validation
- Mock Data: Consistent test fixtures
tests/setup.ts: Global test configurationtests/helpers/: Auth mocks and test fixturestests/services/: Service layer teststests/routes/: API endpoint tests
- Exclude migrations and generated files
- Focus on business logic and API endpoints
- Target comprehensive service method coverage
# Database
DATABASE_URL=postgresql://postgres:postgres@localhost:5555/postgres
# API Keys
NEYNAR_API_KEY=your-neynar-api-key
ELIZA_AGENT_SECRET=your-eliza-secret
MCP_SERVICE_SECRET=your-mcp-secret
# Features
DISABLE_NOTIFICATIONS=false
INCLUDE_ADMINS_IN_NOTIFICATIONS=true
ALERTS_URL=http://localhost:8080- Main DB: PostgreSQL with pgvector on port 5555
- Test DB: Separate PostgreSQL instance on port 5556
- Health Checks: Built-in container health monitoring
- Services: Business logic in
src/db/services/ - Routes: API handlers in
src/routes/ - Middleware: Authentication and validation
- Utils: Shared constants and helpers
- Use address-based user identification for multi-tenancy
- Implement proper foreign key constraints
- Add comprehensive indexes for performance
- Use enums for status fields
- RESTful endpoints with proper HTTP methods
- Zod validation for all request bodies
- Consistent error handling and responses
- Permission-based access control
- Environment: Node.js compatible (Bun runtime)
- Database: PostgreSQL with pgvector extension
- Scaling: Horizontal scaling with connection pooling
- Monitoring: Health checks and logging
- Railway (primary deployment)
- Docker containers
- Any Web Standards compatible platform
- Cloudflare Workers, AWS Lambda, Vercel
Admin FIDs are defined in src/utils/constants.ts for special permissions and notifications.
The codebase includes comprehensive migration support:
- Database schema migrations via Drizzle
- API versioning for backward compatibility
- Feature flags for gradual rollouts
- README.md: User-facing documentation
- API Documentation: In-code comments and schemas
- Test Documentation: Test-specific README in
/tests/
This API serves as a foundation for democratic community management across multiple Farcaster applications and AI agents.