itchan

module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 26, 2026 License: MIT

README

Itchan Imageboard

Go CI PostgreSQL

A modern, high-performance imageboard built with Go, featuring a clean three-layer architecture, advanced authentication, and comprehensive rate limiting. Designed for privacy-conscious communities with domain-based board access control.

Table of Contents

Features

Core Functionality
  • Boards & Threads: Create boards with custom names, organize discussions in threads
  • Messages & Replies: Post messages with cross-thread reply support
  • File Attachments: Upload images (JPEG, PNG, GIF) and videos (MP4, WebM, OGG) with automatic thumbnail generation
  • Attachments-Only Messages: Post messages with only attachments (text is optional)
  • Markdown Support: Rich text formatting using a custom markdown parser
  • Thread Bumping: Automatic bump-to-top with configurable bump limits
  • Pinned Threads: Pin important threads to the top of boards
  • Pagination: Efficient board browsing with configurable threads per page
  • Disable Media Mode: Cookie-based toggle to replace all images and videos with text placeholders
Authentication & Authorization
  • User Registration: Email-based registration with confirmation codes
  • Invite System: Registered users can generate invite codes to bring in friends without corporate email
  • JWT Authentication: Secure, stateless authentication using JWT tokens
  • Dual Auth Methods: Cookie-based (browsers) and Bearer token (API/mobile clients) support
  • Admin Roles: Special privileges for board and content moderation
  • User Blacklist: Admin-managed user blacklist with cached validation for banned users
  • Board Permissions: Domain-based access control (restrict boards to specific email domains)
  • CSRF Protection: Token-based CSRF protection for state-changing requests
Performance & Security
  • Nginx Reverse Proxy: TLS termination, HTTP/2, connection limits, slowloris protection
  • Two-Layer Rate Limiting: Nginx (per-IP, connection limits) + Go (per-user, token bucket)
  • Media Sanitization: Automatic EXIF/metadata stripping from images and videos using ffmpeg
  • Email Encryption: AES-256-GCM encryption of email addresses at the application level
  • Database Partitioning: Automatic table partitioning by board for optimal performance
  • Materialized Views: Fast board previews with configurable refresh intervals
  • Security Headers: HSTS, CSP, X-Frame-Options, X-Content-Type-Options
  • Gzip Compression: Automatic response compression
  • CORS Support: Configured for secure cross-origin requests
  • Graceful Shutdown: Proper cleanup and connection handling
Developer Experience
  • Clean Architecture: Three-layer backend (Storage → Service → Handler)
  • Comprehensive Testing: Unit and integration tests for all layers
  • Docker Deployment: Full containerization with Docker Compose
  • Type Safety: Strong domain types and validation
  • Middleware Chain: Composable authentication, authorization, and rate limiting

Architecture

    Web Browsers   Mobile Apps    API Clients
    (Cookies)      (Bearer)       (Bearer)
        │              │              │
        └──────────────┼──────────────┘
                       ▼
┌─────────────────────────────────────────────────────────────┐
│                      Docker Compose                          │
├──────────────────────────────────────────────────────────────┤
│   Nginx (Reverse Proxy)                                      │
│   TLS termination, rate limiting, HTTP/2                     │
│   Ports 80/443                                               │
├──────────────┬──────────────────────┬──────────────────────┤
│   Frontend   │   Backend API        │   PostgreSQL 17.6    │
│   (Go HTML)  │   (REST Service)     │   (Partitioned DB)   │
│   Port 8081  │   Port 8080          │      Port 5432       │
│   (internal) │   (internal)         │      (internal)      │
└──────────────┴──────────────────────┴──────────────────────┘

Backend Layers:
  Handler    ← HTTP routing, JSON I/O, validation
     ↓
  Service    ← Business logic, authentication, file processing
     ↓
  Storage    ← PostgreSQL operations, partitioning, queries

Shared Modules:
  • domain/       - Domain types and models
  • middleware/   - Auth, rate limiting, board access control
  • config/       - Configuration management
  • jwt/          - JWT token handling
  • validation/   - Input validation and file handling

Tech Stack

  • Backend: Go 1.24+ (frontend-agnostic JSON REST API)
  • Frontend: Go templates (html/template) - replaceable with any client
  • Database: PostgreSQL 17.6 with partitioning
  • Reverse Proxy: Nginx (TLS termination, rate limiting, HTTP/2)
  • Router: Chi (go-chi/chi)
  • Auth: JWT (golang-jwt/jwt) with dual-mode support (Cookie + Bearer)
  • TLS: Let's Encrypt (automatic renewal via certbot)
  • Markdown: Custom parser
  • Media Processing: ffmpeg (for metadata sanitization)
  • Monitoring: Prometheus + Grafana (optional)
  • Container: Docker & Docker Compose
  • CI/CD: GitHub Actions
  • Email: SMTP support for confirmations

Getting Started

Prerequisites
  • Docker 20.10+ and Docker Compose 2.0+
  • Go 1.24+ (for local development)
  • PostgreSQL 17.6+ (if running without Docker)
  • ffmpeg (for media sanitization - automatically included in Docker image)
Production Deployment

See SETUP.md for full production setup guide with HTTPS and nginx.

git clone https://github.com/itchan-dev/itchan.git
cd itchan
chmod +x scripts/*.sh
./scripts/setup.sh yourdomain.com your-email@example.com
# Configure email in config/private.yaml
make deploy
Quick Start (Development)

For local development without nginx/HTTPS:

git clone https://github.com/itchan-dev/itchan.git
cd itchan
docker-compose up --build

# Frontend UI: http://localhost:8081
# Backend API: http://localhost:8080/v1

The database will be automatically initialized with the schema from backend/internal/storage/pg/migrations/init.sql.

Local Development

For development without Docker:

# 1. Start PostgreSQL (adjust connection settings in config/private.yaml)
# Make sure PostgreSQL is running on localhost:5432

# 2. Run database migrations
psql -U itchan -d itchan -f backend/internal/storage/pg/migrations/init.sql

# 3. Configure your environment
cp config/private.yaml.example config/private.yaml
# Edit config/private.yaml with your database and email settings

# 4. Start the backend API
cd backend
go run cmd/itchan-api/main.go -config_folder ../config

# 5. In a new terminal, start the frontend
cd frontend
go run cmd/frontend/main.go -config_folder ../config

Project Structure

itchan/
├── backend/                    # Backend API service
│   ├── cmd/
│   │   ├── itchan-api/        # Main entry point
│   │   │   └── main.go
│   │   └── tools/             # CLI utilities
│   │       └── generate-encryption-key/  # Generate AES-256 encryption key
│   ├── internal/
│   │   ├── handler/           # HTTP handlers (REST endpoints)
│   │   │   ├── auth.go        # Register, login, logout
│   │   │   ├── blacklist.go   # User blacklist management
│   │   │   ├── board.go       # Board CRUD operations
│   │   │   ├── handler.go     # Handler dependencies
│   │   │   ├── health.go      # Health and readiness probes
│   │   │   ├── helpers.go     # Shared handler helpers
│   │   │   ├── invite.go      # Invite code management
│   │   │   ├── message.go     # Message posting and retrieval
│   │   │   ├── thread.go      # Thread operations
│   │   │   └── user_activity.go # User activity tracking
│   │   ├── service/           # Business logic layer
│   │   │   ├── auth.go        # Authentication logic (incl. invites)
│   │   │   ├── board.go       # Board management
│   │   │   ├── gc.go          # Orphaned media cleanup
│   │   │   ├── media_storage.go # File storage service
│   │   │   ├── message.go     # Message processing
│   │   │   ├── thread.go      # Thread management
│   │   │   ├── user_activity.go # User activity tracking
│   │   │   └── utils/         # Service utilities
│   │   │       └── sanitize.go # Media sanitization (EXIF/metadata stripping)
│   │   ├── storage/           # Data access layer
│   │   │   ├── pg/            # PostgreSQL implementation
│   │   │   │   ├── auth.go    # User storage operations
│   │   │   │   ├── blacklist.go # User blacklist operations
│   │   │   │   ├── board.go   # Board storage
│   │   │   │   ├── board_enrichment.go # Board data enrichment
│   │   │   │   ├── board_view.go  # Materialized view management
│   │   │   │   ├── message.go # Message storage
│   │   │   │   ├── message_enrichment.go # Message data enrichment
│   │   │   │   ├── pg.go      # DB connection & partitioning
│   │   │   │   ├── thread.go  # Thread storage
│   │   │   │   ├── user_activity.go # User activity storage
│   │   │   │   ├── migrations/init.sql  # Database schema
│   │   │   │   └── templates/ # SQL templates for partitioning & views
│   │   │   └── fs/            # File system storage
│   │   │       └── fs.go      # File upload/download
│   │   ├── utils/             # Backend utilities
│   │   │   ├── utils.go       # Helper functions
│   │   │   └── email/         # Email sending service
│   │   ├── router/            # Route configuration
│   │   │   └── router.go      # All API routes and middleware
│   │   └── setup/             # Dependency injection
│   │       └── setup.go
│   └── Dockerfile
│
├── frontend/                   # Frontend UI service
│   ├── cmd/frontend/          # Main entry point
│   │   └── main.go
│   ├── internal/
│   │   ├── handler/           # HTTP handlers for pages
│   │   │   ├── account.go     # Account management page
│   │   │   ├── auth.go        # Login/register pages
│   │   │   ├── blacklist.go   # Blacklist management page
│   │   │   ├── board.go       # Board view
│   │   │   ├── faq.go         # FAQ page
│   │   │   ├── handler.go     # Handler dependencies
│   │   │   ├── helpers.go     # Shared handler helpers
│   │   │   ├── index.go       # Homepage
│   │   │   ├── invites.go     # Invite management page
│   │   │   ├── legal.go       # Legal pages (terms, privacy, contacts)
│   │   │   ├── message.go     # Message actions
│   │   │   ├── render.go      # Template rendering
│   │   │   ├── settings.go    # User settings
│   │   │   └── thread.go      # Thread view
│   │   ├── apiclient/         # Backend API client
│   │   │   ├── apiclient.go
│   │   │   ├── auth.go
│   │   │   ├── blacklist.go
│   │   │   ├── board.go
│   │   │   ├── invites.go
│   │   │   ├── message.go
│   │   │   ├── thread.go
│   │   │   └── user_activity.go
│   │   ├── domain/            # Frontend domain models
│   │   │   ├── board.go
│   │   │   ├── common.go      # Common template data
│   │   │   ├── message.go
│   │   │   ├── pages.go       # Page-specific data structs
│   │   │   ├── partials.go    # Partial-specific data structs
│   │   │   └── thread.go
│   │   ├── markdown/          # Markdown processing
│   │   │   ├── parser.go      # Custom parser with block/inline rules
│   │   │   └── trie.go        # Trie for fast marker matching
│   │   ├── middleware/        # Frontend middleware
│   │   │   ├── auth.go        # Authentication forwarding
│   │   │   └── csrf.go        # CSRF token handling
│   │   ├── router/            # Route configuration
│   │   │   └── router.go
│   │   └── setup/             # Dependency setup
│   │       └── setup.go
│   ├── templates/             # HTML templates
│   │   ├── base.html          # Base layout
│   │   ├── index.html         # Board list
│   │   ├── board.html         # Board view
│   │   ├── thread.html        # Thread view
│   │   ├── login.html         # Login page
│   │   ├── register.html      # Registration page
│   │   ├── register_invite.html # Invite-based registration
│   │   ├── check_confirmation_code.html # Email confirmation
│   │   ├── account.html       # Account management
│   │   ├── admin.html         # Admin panel
│   │   ├── invites.html       # Invite management
│   │   ├── faq.html           # FAQ and rules
│   │   ├── about.html         # About page
│   │   ├── contacts.html      # Contact information
│   │   ├── privacy.html       # Privacy policy
│   │   ├── terms.html         # Terms of service
│   │   └── partials.html      # Reusable components
│   ├── static/                # Static assets
│   │   ├── css/style.css
│   │   ├── js/main.js
│   │   └── favicon.ico
│   └── Dockerfile
│
├── shared/                     # Shared packages
│   ├── api/                   # API request/response types
│   ├── blacklist/             # Blacklist caching
│   ├── config/                # Configuration management
│   ├── crypto/                # Email encryption (AES-256-GCM)
│   ├── csrf/                  # CSRF token protection
│   ├── domain/                # Domain models
│   │   ├── attachments.go     # File attachment models
│   │   ├── auth.go            # User, credentials
│   │   ├── board.go           # Board models
│   │   ├── helpers.go         # Domain helper functions
│   │   ├── message.go         # Message models
│   │   ├── thread.go          # Thread models
│   │   └── types.go           # Custom types and validation
│   ├── errors/                # Error types
│   ├── jwt/                   # JWT utilities
│   ├── logger/                # Structured logging
│   ├── middleware/            # HTTP middleware
│   │   ├── auth.go            # JWT authentication
│   │   ├── security_headers.go # Security headers (HSTS, CSP, etc.)
│   │   ├── board_access/      # Board permission check
│   │   ├── metrics/           # Prometheus metrics
│   │   └── ratelimiter/       # Rate limiting
│   ├── storage/               # Storage interfaces
│   ├── utils/                 # Utility functions
│   │   ├── crypto.go          # Hashing utilities
│   │   └── web.go             # HTTP helpers
│   └── validation/            # Input validation
│       ├── attachment.go      # File validation
│       ├── multipart.go       # Multipart form handling
│       └── errors.go
│
├── nginx/                      # Reverse proxy
│   ├── nginx.conf             # Nginx configuration
│   ├── nginx.conf.explained   # Line-by-line explanation
│   ├── certs/                 # SSL certificates (generated)
│   └── certbot/               # ACME challenge files
│
├── monitoring/                 # Prometheus + Grafana
│   ├── prometheus.yml         # Prometheus configuration
│   └── grafana/               # Grafana dashboards
│
├── scripts/                    # Setup and maintenance
│   ├── setup.sh               # Full initial setup (configs + SSL + cron)
│   ├── gen-configs.sh         # Generate configuration files
│   └── renew-ssl.sh           # SSL certificate renewal (called by cron)
│
├── config/                     # Configuration files
│   ├── public.yaml            # Public configuration (shared with frontend)
│   └── private.yaml           # Private configuration (backend only, generated)
│
├── docker-compose.yml         # Docker orchestration (production)
├── docker-compose.dev.yml     # Docker orchestration (development)
├── docker-compose.monitoring.yml # Monitoring stack
├── Makefile                   # Build and deployment commands
├── SETUP.md                   # Production setup guide
├── CONTRIBUTING.md            # Contribution guidelines
├── SECURITY.md                # Security policy
├── CODE_OF_CONDUCT.md         # Code of conduct
├── go.mod                     # Go module definition
├── go.sum                     # Go module checksums
└── README.md

Backend Architecture

Itchan follows a strict three-layer architecture for maintainability and testability:

1. Handler Layer (internal/handler/)

Responsibility: HTTP request/response handling

  • Parse HTTP requests and extract parameters
  • Validate input format and types
  • Call appropriate service methods
  • Convert service responses to HTTP responses
  • Handle HTTP-specific errors (404, 401, etc.)

Example: handler/board.go

func (h *Handler) CreateBoard(w http.ResponseWriter, r *http.Request) {
    // Parse request body
    // Validate input
    // Call service.Board.Create()
    // Return JSON response
}
2. Service Layer (internal/service/)

Responsibility: Business logic and orchestration

  • Enforce business rules (bump limits, thread counts, etc.)
  • Coordinate between multiple storage operations
  • Handle file uploads and processing
  • Manage transactions
  • Authentication and authorization logic

Example: service/thread.go

func (s *Thread) Create(ctx context.Context, data domain.ThreadCreationData) error {
    // Check thread count limits
    // Validate board exists
    // Create thread in storage
    // Trigger GC if needed
}
3. Storage Layer (internal/storage/pg/)

Responsibility: Data persistence

  • Execute SQL queries
  • Manage database connections
  • Handle table partitioning
  • Manage materialized views
  • Map between database rows and domain models

Example: storage/pg/board.go

func (s *Storage) CreateBoard(ctx context.Context, board *domain.Board) error {
    // Execute INSERT statement
    // Create partition for new board
    // Return database errors
}

Database Schema

The database uses PostgreSQL's table partitioning for scalability. Each board gets its own partition.

Key Tables
  • users: User accounts with encrypted email and password hash
  • user_blacklist: Blacklisted users with reason and admin reference (cached for JWT validation)
  • confirmation_data: Email confirmation codes for registration
  • invite_codes: User-generated invite codes for registration
  • boards: Board metadata (name, short_name, activity timestamps)
  • board_permissions: Domain-based access control (email domain allowlist per board)
  • threads: Thread metadata (title, message count, bump time, pinned flag) - partitioned by board
  • messages: Individual posts (text, author, timestamps, ordinal) - partitioned by board
  • attachments: Links messages to uploaded files - partitioned by board
  • files: File metadata with both original and sanitized filenames/MIME types (path, size, dimensions, thumbnail path)
  • message_replies: Cross-reference table for reply relationships - partitioned by board
Materialized Views
  • board_previews: Pre-computed board views with last N messages per thread
    • Refreshed on configurable intervals (board_preview_refresh_interval)
    • Dramatically improves board page load times
Indexes

Optimized indexes for common queries:

  • Thread lookup by board and bump time
  • Message lookup by thread
  • Reply lookup by receiver message
  • Author lookup for moderation

Configuration

Configuration is split into two files:

config/public.yaml (Shared between backend and frontend)
jwt_ttl: 24h                           # JWT token lifetime
threads_per_page: 15                   # Pagination
messages_per_thread_page: 1000         # Messages per thread page (0 = all)
max_thread_count: 100                  # Max threads per board (null = unlimited)
n_last_msg: 3                          # Messages shown in board preview
bump_limit: 500                        # Messages before thread stops bumping
board_preview_refresh_internval: 30s   # Materialized view refresh rate
board_activity_window: 3m              # How far back to check for board activity
blacklist_cache_interval: 300          # Blacklist cache refresh interval (seconds)

# Auth
confirmation_code_ttl: 10m             # Email confirmation code expiration

# Logging
log_level: info                        # debug, info, warn, error
log_format: text                       # text or json

# Security settings
secure_cookies: false                  # Set to true for HTTPS
csrf_enabled: true                     # Enable CSRF protection

# Text length limits (defaults shown, all optional)
board_name_max_len: 10
board_short_name_max_len: 3
thread_title_max_len: 50
message_text_max_len: 10000
message_text_min_len: 1
password_min_len: 8
max_replies_per_message: 50            # Max >>reply links per message

# File upload limits
max_attachments_per_message: 4
max_attachment_size_bytes: 10485760    # 10 MB per file
max_total_attachment_size: 20971520    # 20 MB total
allowed_image_mime_types:
  - image/jpeg
  - image/png
  - image/gif
allowed_video_mime_types:
  - video/mp4
  - video/webm
  - video/ogg

# Invite system
invite_enabled: false
invite_code_length: 12
invite_code_ttl: 720h                  # 30 days
max_invites_per_user: 5                # 0 = unlimited
min_account_age_for_invites: 720h      # 30 days

# User activity page
user_messages_page_limit: 50           # Messages shown on account page

# Caching
static_cache_max_age: 240h             # Static file cache (CSS, JS)
media_cache_max_age: 168h              # User-uploaded media cache

# Media processing
media:
  thumbnail_max_size: 225              # Max dimension (px) for thumbnails
  thumbnail_display_op: 225            # Display size for OP thumbnails
  thumbnail_display_reply: 150         # Display size for reply thumbnails
  jpeg_quality_main: 85               # JPEG quality for main images (0-100)
  jpeg_quality_thumbnail: 75          # JPEG quality for thumbnails (0-100)

# Registration restrictions
allowed_registration_domains: []       # Empty = allow all domains. Example: ["gmail.com", "company.com"]
config/private.yaml (Backend only - never commit to git!)
jwt_key: "<your-secret-key>"           # JWT signing key (generate random 512+ bit string)
encryption_key: "<your-encryption-key>" # AES-256-GCM key for email encryption (use backend/cmd/tools/generate-encryption-key)

pg:
  host: localhost
  port: 5432
  user: itchan
  password: itchan
  dbname: itchan

email:
  smtp_server: smtp.gmail.com
  smtp_port: 587                          # 587 = STARTTLS, 465 = implicit TLS (Yandex)
  username: your-email@gmail.com
  password: your-app-password
  sender_name: "Itchan Imageboard"
  timeout: 10

API Endpoints

Authentication Methods

The API supports two authentication methods:

  1. Cookie-based (automatic for browsers):

    • Login sets an HTTP-only cookie with the JWT
    • Subsequent requests include the cookie automatically
    • Best for browser-based clients
  2. Bearer Token (for API/mobile clients):

    • Login returns access_token in response body
    • Include token in requests: Authorization: Bearer <token>
    • Best for mobile apps, CLI tools, third-party integrations
Authentication Endpoints
POST   /v1/auth/register                 - Register new user (rate limited: 1/s per email & IP)
POST   /v1/auth/check_confirmation_code  - Verify email confirmation code (5 attempts per 10 min per email)
POST   /v1/auth/login                    - Login with credentials (returns access_token)
POST   /v1/auth/register_with_invite     - Register with an invite code (rate limited: 1/s per IP)
POST   /v1/auth/logout                   - Logout (clear cookies)

Login Response:

{
  "message": "You logged in",
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Boards (Authenticated)
GET    /v1/boards                        - List all accessible boards
GET    /v1/{board}                       - Get board with threads (paginated)
GET    /v1/{board}/last_modified         - Get board's last modification timestamp

Example with Bearer Token:

curl -H "Authorization: Bearer <your_token>" \
  http://localhost:8080/v1/boards
Threads (Authenticated)
POST   /v1/{board}                       - Create new thread (rate limited: 1/minute per user)
GET    /v1/{board}/{thread}              - Get thread with all messages
GET    /v1/{board}/{thread}/last_modified - Get thread's last modification timestamp
Messages (Authenticated)
POST   /v1/{board}/{thread}              - Post message to thread (rate limited: 1/second per user)
GET    /v1/{board}/{thread}/{message}    - Get single message
Invites (Authenticated)
GET    /v1/invites/                      - Get user's invite codes
POST   /v1/invites/                      - Generate new invite code (rate limited: 1/minute per user)
DELETE /v1/invites/{codeHash}            - Revoke an invite code
User (Authenticated)
GET    /v1/users/me/activity             - Get current user's activity data
GET    /v1/public_config                 - Get public configuration
Admin (Admin only)
POST   /v1/admin/boards                  - Create new board
DELETE /v1/admin/{board}                 - Delete board
DELETE /v1/admin/{board}/{thread}        - Delete thread
POST   /v1/admin/{board}/{thread}/pin    - Toggle thread pinned status
DELETE /v1/admin/{board}/{thread}/{message} - Delete message
POST   /v1/admin/users/{userId}/blacklist - Blacklist user (with optional reason)
DELETE /v1/admin/users/{userId}/blacklist - Remove user from blacklist
GET    /v1/admin/blacklist               - Get all blacklisted users with details
POST   /v1/admin/blacklist/refresh       - Manually refresh blacklist cache
Health & Monitoring (No auth)
GET    /health                           - Liveness probe (200 if running)
GET    /ready                            - Readiness probe (200 if DB connected)
GET    /metrics                          - Prometheus metrics
Rate Limits
  • Registration: 1/s per email, 1/s per IP, 100 global RPS
  • Confirmation Code: 5 attempts per 10 minutes per email, 1/s per IP, 100 global RPS
  • Login: 1/s per IP, 1000 global RPS
  • Invite Registration: 1/s per IP, 100 global RPS
  • Create Thread: 1 per minute per user
  • Post Message: 1 per second per user
  • Generate Invite: 1 per minute per user
  • Get Board: 10 RPS per user
  • General authenticated: 100 RPS per user
  • Admin: No rate limits
Example Usage

Browser/Frontend (Cookie-based):

// Login - cookie set automatically
await fetch('http://localhost:8080/v1/auth/login', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'user@example.com', password: 'pass' })
});

// Subsequent requests - cookie sent automatically
await fetch('http://localhost:8080/v1/boards', { credentials: 'include' });

API/Mobile Clients (Bearer Token):

# Login and extract token
TOKEN=$(curl -X POST http://localhost:8080/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"pass"}' \
  | jq -r '.access_token')

# Use token in requests
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/v1/boards

Python:

import requests

# Login
response = requests.post('http://localhost:8080/v1/auth/login',
    json={'email': 'user@example.com', 'password': 'pass'})
token = response.json()['access_token']

# Use token
headers = {'Authorization': f'Bearer {token}'}
boards = requests.get('http://localhost:8080/v1/boards', headers=headers)

See BEARER_AUTH_EXAMPLE.md for more examples (React Native, Flutter, Node.js, etc.)

Frontend

The frontend is a server-rendered Go application using html/template.

Templates
  • base.html: Common layout (header, footer, navigation)
  • index.html: Board listing and creation form
  • board.html: Thread list with pagination
  • thread.html: Full thread view with all messages and reply form
  • login.html / register.html / register_invite.html: Authentication pages
  • check_confirmation_code.html: Email confirmation page
  • account.html: Account management
  • admin.html: Admin panel
  • invites.html: Invite code management
  • faq.html / about.html / contacts.html: Info pages
  • privacy.html / terms.html: Legal pages
  • partials.html: Reusable components (message rendering, forms)
Markdown Support

The frontend includes a lightweight custom markdown parser optimized for imageboard use:

  • Code blocks (fenced with ```)
  • Inline code, bold, italic, strikethrough
  • Greentext (lines starting with >)
  • Message links (>>threadId#msgId) with hover previews
Interactive Features
  • Popup Reply System: Dynamic popup forms for quick replies with intelligent positioning
  • Message Preview System: Hover over message links to see inline previews with caching (500 message cache)
  • Preview Chains: Navigate through reply chains with automatic cleanup and smart timeout handling
  • File Upload Manager: Real-time file preview with thumbnails, size validation, and individual file removal
  • Hash-based Reply Links: Support for #reply-{id} URLs to auto-populate reply forms
  • Disable Media Mode: Toggle in header to hide all images/videos, showing only filenames and metadata as text links (useful for slow connections, persists via cookie, works without JS)
Static Assets
  • CSS: Minimal, responsive stylesheet
  • JavaScript: Vanilla JS for dynamic reply forms, message previews, and file upload management
  • Favicon: Custom imageboard icon

Testing

Comprehensive test coverage across all layers:

# Backend tests
cd backend

# Unit tests (service layer)
go test ./internal/service/...

# Integration tests (storage layer - requires running PostgreSQL)
go test ./internal/storage/pg/...

# Handler tests
go test ./internal/handler/...

# Frontend tests
cd ../frontend
go test ./internal/handler/...

# Run all tests with coverage
go test -v -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
Test Structure
  • Unit tests: Business logic validation (service layer)
  • Integration tests: Database operations (storage layer with test DB)
  • Handler tests: HTTP endpoint behavior (mocked services)

Integration tests use a separate test database and clean up after execution.

Security Features

Authentication & Authorization
  • JWT-based authentication with configurable TTL
  • Dual authentication support: Cookie-based (browsers) and Bearer token (API clients)
  • Invite system: Users can generate invite codes for registration without corporate email
  • Blacklist cache validation: Automatic JWT rejection for blacklisted users with cached validation
  • Secure cookie storage with HttpOnly and Secure flags (when enabled)
  • Password hashing using bcrypt
  • Email encryption: AES-256-GCM encryption of email addresses at the application level
  • CSRF protection: Token-based CSRF protection for state-changing requests
  • Email confirmation required for registration
  • Registration domain restrictions: Optional email domain allowlist for registration
  • Admin role for moderation capabilities
Rate Limiting
  • Multi-tier rate limiting: Per-user, per-IP, and global limits
  • Token bucket algorithm with configurable refill rates
  • Admin exemption: Admin users bypass rate limits
  • Fingerprinting fallback: Uses request fingerprint if IP unavailable
Board Access Control
  • Domain-based permissions: Restrict boards to specific email domains
  • Middleware enforcement: Board access checked on every request
  • Automatic filtering: Users only see boards they can access
Input Validation & Media Processing
  • File type validation: Strict MIME type checking for uploads
  • File size limits: Per-file and total attachment size limits
  • Media sanitization: Automatic EXIF/metadata stripping from images (decode/encode) and videos (ffmpeg)
  • Format normalization: GIF/WebP images converted to JPEG, PNG preserved
  • Image dimension extraction: Metadata validation
  • SQL injection prevention: Parameterized queries throughout
  • XSS prevention: Template auto-escaping, markdown sanitization
Infrastructure
  • Nginx reverse proxy: TLS termination, multi-tier rate limiting, connection limits, slowloris protection
  • Let's Encrypt: Automatic SSL certificate provisioning and renewal
  • Security headers: HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy
  • CORS configuration: Controlled cross-origin access
  • Gzip compression: Automatic response compression (nginx)
  • Graceful shutdown: Proper connection cleanup
  • Database partitioning: Isolation between boards

Deployment

Development
make dev  # or: docker compose up --build
Production

Full setup guide: SETUP.md

make deploy          # Build and start all services (nginx, frontend, api, postgres)
docker compose ps    # View running services
make logs            # View all logs
make logs-api        # View API logs
make logs-frontend   # View frontend logs

Monitoring

Optional Prometheus + Grafana stack for metrics and dashboards.

Enable Monitoring
make deploy-monitoring

Access:

Disable Monitoring
make deploy  # Redeploy without monitoring compose file
docker compose stop prometheus grafana  # Stop monitoring services
Available Metrics
  • http_requests_total{method, path, status} - Request count
  • http_request_duration_seconds{method, path} - Latency histogram
  • http_requests_in_flight - Concurrent requests gauge
  • Go runtime metrics (goroutines, memory, GC)
Health Endpoints
  • GET /health - Liveness probe (200 if running)
  • GET /ready - Readiness probe (200 if DB connected)
  • GET /metrics - Prometheus metrics (backend only)

CI/CD

Automated testing and deployment via GitHub Actions.

Workflow
  1. On Pull Request: Run tests
  2. On Push to main: Run tests → Deploy to VPS
Setup

Add these secrets in GitHub (Settings → Secrets → Actions):

  • VPS_HOST - Your server IP/hostname
  • VPS_USER - SSH username
  • VPS_SSH_KEY - Private SSH key content
  • VPS_PROJECT_PATH - Path to project on VPS (e.g., /home/user/itchan)

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Write tests for your changes
  4. Ensure all tests pass (go test ./...)
  5. Format your code (go fmt ./...)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request
Code Style
  • Follow standard Go conventions (gofmt, golint)
  • Write descriptive commit messages
  • Add comments for complex logic
  • Keep functions focused and testable
  • Maintain the three-layer architecture separation

License

MIT License - see LICENSE for details.


Built with ❤️ using Go

Directories

Path Synopsis
backend
cmd/itchan-api command
internal/storage/fs
in storage/localfs/localfs.go
in storage/localfs/localfs.go
internal/storage/pg
Package pg implements the storage layer for the application using a PostgreSQL database.
Package pg implements the storage layer for the application using a PostgreSQL database.
frontend
cmd/frontend command
shared
api
Package api defines the HTTP request and response types for the backend REST API.
Package api defines the HTTP request and response types for the backend REST API.
jwt
middleware/metrics
Package metrics provides Prometheus HTTP metrics middleware.
Package metrics provides Prometheus HTTP metrics middleware.
storage/pg
Package pg provides core PostgreSQL database primitives for storage layers.
Package pg provides core PostgreSQL database primitives for storage layers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL