Skip to content

Latest commit

 

History

History
418 lines (311 loc) · 10.8 KB

File metadata and controls

418 lines (311 loc) · 10.8 KB

Open Source Village

A mobile-first web application for Open Source Village event (Jan 26 - Feb 6, 2026) that facilitates networking through NFC badges, token economy, and open space workshop coordination.

Features

  • NFC Badge System: Attendees scan badges to claim profiles and send tokens
  • Token Economy: ERC20 tokens on Gnosis Chain for workshop RSVPs and offers
  • Workshop Coordination: Create and RSVP to community-led workshops
  • Marketplace: Browse and claim generic offers from other attendees
  • Offline-First: NOSTR protocol for event propagation with blockchain settlement
  • Calendar Integration: Google Calendar sync for official event schedule

Tech Stack

  • Frontend: Next.js 14, React 18, TypeScript, Tailwind CSS
  • Backend: Next.js API routes (Node.js)
  • Package Manager: Bun
  • Storage: File-based (JSON/JSONL)
  • Blockchain: Gnosis Chain (ERC20 tokens via token-factory)
  • Protocol: NOSTR for offline-first event propagation

Getting Started

Prerequisites

  • Bun installed
  • Node.js 18+ (for Next.js)

Installation

# Clone the repository
git clone https://github.com/commonshub/app.opensourcevillage.org.git
cd app.opensourcevillage.org

# Install dependencies
bun install

# Create data directories
mkdir -p data/badges data/usernames data/logs

# Start development server
bun dev

Visit http://localhost:3000 to see the app.

Project Structure

├── src/
│   ├── app/              # Next.js App Router pages and API routes
│   │   ├── api/          # API endpoints
│   │   ├── page.tsx      # Home page
│   │   └── layout.tsx    # Root layout
│   ├── lib/              # Utility functions
│   │   └── storage.ts    # File-based storage layer
│   └── types/            # TypeScript type definitions
├── specs/                # Project specifications
│   ├── TECHNICAL_SPEC.md
│   └── screens/prototype/ # HTML prototypes (UI reference)
├── data/                 # Runtime data (gitignored)
│   ├── badges/           # User profiles and queues
│   ├── usernames/        # Username -> badge symlinks
│   └── logs/             # Application logs
└── @AGENT.md             # Build and development instructions

Development

# Run development server
bun dev

# Build for production
bun run build

# Start production server
bun start

# Run tests
bun test

# Run tests with coverage
bun run test:coverage

API Endpoints

POST /api/claim

Claim an NFC badge and create user profile.

Request:

{
  "username": "alice",
  "serialNumber": "ABC123",
  "npub": "npub1..."
}

Response:

{
  "success": true,
  "profile": {
    "npub": "npub1...",
    "username": "alice",
    "createdAt": "2026-01-20T...",
    "updatedAt": "2026-01-20T..."
  }
}

Architecture

NFC Badge Flow

  1. User scans NFC tag: app.opensourcevillage.org/badge#{serialNumber}
  2. Serial number extracted from URL fragment (client-side only)
  3. User sets username and password/PIN
  4. Client derives NOSTR keypair from serialNumber + password
  5. Profile created, 50 tokens minted automatically

Token Economics

  • Creating an offer: 1 token
  • RSVP to workshop: 1 token (refunded if cancelled)
  • Claiming an offer: 1 token
  • Workshop organizer receives: 1 token per attendee

Storage

File-based storage with eventual consistency:

  • data/badges/{serialNumber}/profile.json - User profile
  • data/badges/{serialNumber}/queue.jsonl - Blockchain operations
  • data/badges/{serialNumber}/nostr_log.jsonl - NOSTR events
  • data/usernames/{username} - Symlink to badge directory

Production Deployment

Quick Setup

Run the automated setup script to set up everything (nginx, SSL, systemd services, cron jobs):

# Run the setup script (will prompt for domain)
curl -sSL https://raw.githubusercontent.com/commonshub/app.opensourcevillage.org/main/scripts/setup-services.sh | sudo bash

Or with custom configuration (non-interactive):

# Set all options via environment variables
curl -sSL https://raw.githubusercontent.com/commonshub/app.opensourcevillage.org/main/scripts/setup-services.sh | \
  sudo DOMAIN=myapp.example.com APP_DIR=/var/www/myapp DOMAIN_SET=1 bash

The script will:

  • Install nginx and certbot
  • Clone the repository and install dependencies
  • Build the application
  • Create systemd services for the main app, payment processor, and NOSTR recorder
  • Set up a cron job for calendar sync (every 5 minutes)
  • Configure nginx as a reverse proxy
  • Obtain SSL certificate with Let's Encrypt
  • Start all services

Prerequisites

  • A Linux server with systemd
  • Bun installed globally (curl -fsSL https://bun.sh/install | bash)
  • Git configured with SSH access to the repository
  • Sudo access for the deployment user (for systemctl restart)

Environment Setup

  1. Copy .env.example to .env.local and configure all required variables:
cp .env.example .env.local
nano .env.local
  1. Generate a webhook secret:
openssl rand -hex 32

Add this to both your .env.local (as WEBHOOK_SECRET) and your GitHub repository webhook settings.

Systemd Service (Manual Setup)

Note: If you used the quick setup script above, these services are already configured. This section is for reference or manual setup.

Create a systemd service to run the Next.js app and background processes.

  1. Create the service file /etc/systemd/system/osv.service:
[Unit]
Description=Open Source Village App
After=network.target

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/app.opensourcevillage.org
Environment=NODE_ENV=production
ExecStart=/usr/local/bin/bun run start
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
  1. Create a service for the payment processor /etc/systemd/system/osv-payment-processor.service:
[Unit]
Description=OSV Payment Processor
After=network.target

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/app.opensourcevillage.org
Environment=NODE_ENV=production
ExecStart=/usr/local/bin/bun run scripts/payment-processor.ts
Restart=on-failure
RestartSec=30

[Install]
WantedBy=multi-user.target
  1. Create a service for the NOSTR event recorder /etc/systemd/system/osv-nostr-recorder.service:
[Unit]
Description=OSV NOSTR Event Recorder
After=network.target

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/app.opensourcevillage.org
Environment=NODE_ENV=production
ExecStart=/usr/local/bin/bun run scripts/record-nostr-events.ts
Restart=on-failure
RestartSec=30

[Install]
WantedBy=multi-user.target
  1. Enable and start all services:
sudo systemctl daemon-reload
sudo systemctl enable osv osv-payment-processor osv-nostr-recorder
sudo systemctl start osv osv-payment-processor osv-nostr-recorder
  1. Check service status:
sudo systemctl status osv
sudo systemctl status osv-payment-processor
sudo systemctl status osv-nostr-recorder
  1. View logs:
sudo journalctl -u osv -f
sudo journalctl -u osv-payment-processor -f
sudo journalctl -u osv-nostr-recorder -f

Cron Job for Calendar Sync

Set up a cron job to sync local calendar proposals to Google Calendar.

  1. Edit the crontab for the www-data user:
sudo crontab -u www-data -e
  1. Add the following line to sync every 5 minutes:
*/5 * * * * cd /var/www/app.opensourcevillage.org && /usr/local/bin/bun run scripts/sync-calendars.ts >> /var/log/osv/calendar-sync.log 2>&1
  1. Create the log directory:
sudo mkdir -p /var/log/osv
sudo chown www-data:www-data /var/log/osv

GitHub Webhook for Auto-Deployment

  1. In your GitHub repository, go to Settings → Webhooks → Add webhook

  2. Configure the webhook:

    • Payload URL: https://app.opensourcevillage.org/api/webhook/github
    • Content type: application/json
    • Secret: Use the same value as WEBHOOK_SECRET in your .env.local
    • Events: Select "Just the push event"
  3. Allow the deployment user to restart services without password:

sudo visudo -f /etc/sudoers.d/osv-deploy

Add:

www-data ALL=(ALL) NOPASSWD: /bin/systemctl restart osv
www-data ALL=(ALL) NOPASSWD: /bin/systemctl restart osv-payment-processor
www-data ALL=(ALL) NOPASSWD: /bin/systemctl restart osv-nostr-recorder
  1. Test the webhook by pushing a commit to the main branch.

Nginx Configuration (Optional)

If using Nginx as a reverse proxy:

server {
    listen 80;
    server_name app.opensourcevillage.org;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name app.opensourcevillage.org;

    ssl_certificate /etc/letsencrypt/live/app.opensourcevillage.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.opensourcevillage.org/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

NOSTR Relay Setup (Pyramid)

The setup script automatically installs Pyramid, a lightweight NOSTR relay. After installation, you need to configure the allowed event kinds.

Required Event Kinds

The application uses the following NOSTR event kinds:

Kind Name Description
0 Profile User profile metadata (NIP-01)
1 Note Text notes, used for offers (NIP-01)
7 Reaction Reactions, used for RSVPs (NIP-25)
1734 Payment Request Token payment request (custom)
1735 Payment Receipt Token payment receipt (custom)
22242 Auth Client authentication (NIP-42)
31922 Calendar Event Calendar events (NIP-52)

Pyramid Configuration

Edit the Pyramid configuration file (usually at /etc/pyramid/config.toml or ~/.pyramid/config.toml):

# Allow specific event kinds
allowed_kinds = [0, 1, 7, 1734, 1735, 22242, 31922]

# Or allow all kinds (less secure, but simpler)
# allowed_kinds = []

Restart Pyramid after configuration:

sudo systemctl restart pyramid

Verify Relay Status

Check that the relay is running:

sudo systemctl status pyramid
sudo journalctl -u pyramid -f

The relay should be accessible at wss://your-domain.com (if configured with nginx/SSL) or ws://localhost:7777 locally.

Contributing

See @AGENT.md for build instructions and development standards.

License

MIT