Getting Started
Install and run Pumpkin Hub in a development environment.
Prerequisites
| Tool | Minimum Version | Usage |
|---|---|---|
| Docker + Docker Compose | 24.x / v2 | Service orchestration |
| Rust | Edition 2021 (1.70+) | API compilation |
| Node.js | 20.x | Frontend build |
| Git | 2.x | Version control |
Installation
Clone the repository and navigate to the directory:
git clone https://github.com/FabLrc/pumpkin-hub.git
cd pumpkin-hub
Environment Setup
Pumpkin Hub uses two separate .env files depending on how you run the project.
Understanding which one to fill is the most common source of confusion.
| File | Used by | When |
|---|---|---|
.env (project root) |
Docker Compose | When running docker compose up. Compose reads this file for variable substitution (${GITHUB_CLIENT_ID}, etc.). |
api/.env |
Rust API directly | When running cargo run inside api/ without Docker. Loaded by the dotenvy crate at startup. |
.env is needed for Docker Compose.
If GITHUB_CLIENT_ID is set in api/.env but the root .env is missing or empty,
Docker Compose will inject an empty string into the container — causing GitHub OAuth to return a 404.
Minimal root .env (Docker Compose)
Create this file at the project root (same directory as docker-compose.yml):
# Required — create at https://github.com/settings/developers
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
# Optional — enables Google / Discord login
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
# JWT secret (change in production)
JWT_SECRET=dev_jwt_secret_change_me_in_production
# Optional — enables GitHub App features (repo linking, auto-publish, publish-from-GitHub)
# Create at https://github.com/settings/apps
GITHUB_APP_ID=
GITHUB_APP_PRIVATE_KEY= # PEM content with literal \n between lines
GITHUB_APP_WEBHOOK_SECRET=
All other variables (database URL, Meilisearch, MinIO, SMTP) are already hardcoded
in docker-compose.yml for local development and do not need to appear in the root .env.
Launch with Docker Compose
The recommended method for development. Docker Compose orchestrates 6 services:
frontend-dev— Next.js with hot-reload (port 3000)api-dev— Rust API with cargo-watch (port 8080)db-dev— PostgreSQL 16 Alpine (port 5432)meilisearch-dev— Meilisearch v1.7 (port 7700)minio-dev— MinIO S3-compatible object storage (ports 9000/9001)mailpit-dev— Local email testing server (SMTP 1025, web UI 8025)
# Build and start all services
docker compose up --build
# Or in the background
docker compose up --build -d
# View logs in real-time
docker compose logs -f api-dev
healthcheck configured in the compose file.
Services and Ports
| Service | Local URL | Description |
|---|---|---|
| Frontend | http://localhost:3000 | Next.js interface |
| API | http://localhost:8080 | Axum backend |
| PostgreSQL | localhost:5432 | Database |
| Meilisearch | http://localhost:7700 | Search engine |
| MinIO | http://localhost:9000 | Object storage (binary artifacts) |
| MinIO Console | http://localhost:9001 | MinIO admin console |
| Mailpit | http://localhost:8025 | Email testing web UI |
Local Launch (without Docker)
To develop a service individually, it can be run outside Docker:
Rust API
cd api
# Check compilation
cargo check
# Run in development mode
cargo run
# With hot-reload (requires cargo-watch)
cargo install cargo-watch
cargo watch -x run
Next.js Frontend
cd frontend
# Install dependencies
npm install
# Start development server
npm run dev
# Check linting
npm run lint
# Production build
npm run build
Environment Variables Reference
Full reference of all variables accepted by the API.
In Docker mode, most are already set by docker-compose.yml — only
the secrets listed in the Environment Setup section
need to be provided via the root .env.
When running with cargo run directly, set all required variables in api/.env.
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL | Yes | — | PostgreSQL connection URL |
MEILISEARCH_URL | Yes | — | Meilisearch server URL |
MEILISEARCH_KEY | Yes | — | Meilisearch master key |
SERVER_HOST | No | 0.0.0.0 | Bind IP address |
SERVER_PORT | No | 8080 | Listening port |
ALLOWED_ORIGINS | No | http://localhost:3000 | CORS origins (comma-separated) |
RUST_LOG | No | pumpkin_hub_api=info | Log verbosity level |
NEXT_PUBLIC_API_URL | No | — | API URL for the frontend (browser-side) |
NEXT_INTERNAL_API_URL | No | — | API URL for server-side proxy routes (Docker internal network) |
S3_ENDPOINT_URL | Yes | — | Internal S3-compatible endpoint used by the API for uploads & deletes (e.g. http://minio-dev:9000 inside Docker) |
S3_PUBLIC_URL | Strongly recommended | = S3_ENDPOINT_URL | Browser-reachable URL used to sign presigned download URLs. Must be reachable by the end-user. See S3 configuration notes. |
S3_BUCKET | Yes | — | S3 bucket name for binary storage |
S3_ACCESS_KEY_ID | Yes | — | S3 access key ID |
S3_SECRET_ACCESS_KEY | Yes | — | S3 secret access key |
S3_REGION | No | us-east-1 | S3 region. MinIO ignores it; use auto for Cloudflare R2 |
S3_FORCE_PATH_STYLE | No | false | Set to true for MinIO (path-style addressing). Must be false for Cloudflare R2 |
S3_USE_DIRECT_URLS | No | false | Serve files via direct public URLs (no presigning). Use for public R2 buckets. |
Security & Cookies
| Variable | Required | Default | Description |
|---|---|---|---|
COOKIE_SECURE | No | Auto-detected | Force Secure flag on auth cookies. Auto-enabled when any ALLOWED_ORIGINS uses HTTPS. |
COOKIE_DOMAIN | No | — | Shared cookie domain for cross-subdomain auth (e.g., .pumpkinhub.org for pumpkinhub.org + api.pumpkinhub.org). |
Authentication & JWT
| Variable | Required | Default | Description |
|---|---|---|---|
JWT_SECRET | Yes | — | Secret key for signing JWT tokens |
JWT_TTL_SECONDS | No | 86400 | JWT token lifetime (default: 24 hours) |
API_PUBLIC_URL | No | http://localhost:8080 | Publicly reachable API base URL (used in email links) |
GITHUB_CLIENT_ID | Yes | — | GitHub OAuth App client ID |
GITHUB_CLIENT_SECRET | Yes | — | GitHub OAuth App client secret |
GITHUB_REDIRECT_URI | No | …/auth/github/callback | GitHub OAuth redirect URI |
GOOGLE_CLIENT_ID | No | — | Google OAuth client ID (enables Google login) |
GOOGLE_CLIENT_SECRET | No | — | Google OAuth client secret |
GOOGLE_REDIRECT_URI | No | …/auth/google/callback | Google OAuth redirect URI |
DISCORD_CLIENT_ID | No | — | Discord OAuth client ID (enables Discord login) |
DISCORD_CLIENT_SECRET | No | — | Discord OAuth client secret |
DISCORD_REDIRECT_URI | No | …/auth/discord/callback | Discord OAuth redirect URI |
GitHub App
The GitHub App variables are optional. When configured they unlock repository linking, webhook-based auto-publishing, and the Publish from GitHub flow. Create an App at github.com/settings/apps.
| Variable | Required | Default | Description |
|---|---|---|---|
GITHUB_APP_ID | No* | — | GitHub App numeric ID. Enables App features when set. |
GITHUB_APP_PRIVATE_KEY | If APP_ID set | — | RSA private key (PEM). Use literal \n between lines in .env files. |
GITHUB_APP_WEBHOOK_SECRET | If APP_ID set | — | Webhook secret for HMAC-SHA256 signature verification. |
SMTP (Email)
SMTP is optional. When configured, the API sends email verification and password recovery emails. If omitted, email features degrade gracefully (new accounts are auto-verified).
| Variable | Required | Default | Description |
|---|---|---|---|
SMTP_HOST | No* | — | SMTP server hostname (e.g. localhost, smtp.gmail.com). Enables email features when set. |
SMTP_PORT | No | 587 | SMTP server port |
SMTP_USERNAME | If SMTP_HOST set | — | SMTP authentication username |
SMTP_PASSWORD | If SMTP_HOST set | — | SMTP authentication password |
SMTP_FROM_ADDRESS | If SMTP_HOST set | — | Sender email address (e.g. noreply@pumpkinhub.dev) |
Rate Limiting
| Variable | Required | Default | Description |
|---|---|---|---|
RATE_LIMIT_GENERAL_PER_SECOND | No | 1 | Token replenish interval (seconds) for general routes |
RATE_LIMIT_GENERAL_BURST_SIZE | No | 30 | Max burst size for general routes |
RATE_LIMIT_AUTH_PER_SECOND | No | 4 | Token replenish interval (seconds) for auth routes (stricter) |
RATE_LIMIT_AUTH_BURST_SIZE | No | 5 | Max burst size for auth routes (stricter) |
Example api/.env (local development without Docker)
DATABASE_URL=postgres://pumpkin_user:dev_password@localhost:5432/pumpkin_hub_dev
MEILISEARCH_URL=http://localhost:7700
MEILISEARCH_KEY=dev_master_key
SERVER_PORT=8080
ALLOWED_ORIGINS=http://localhost:3000
RUST_LOG=pumpkin_hub_api=debug,tower_http=debug
# Authentication
JWT_SECRET=your-super-secret-key-change-in-prod
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
# GitHub App (optional — enables repo linking & auto-publish)
# GITHUB_APP_ID=
# GITHUB_APP_PRIVATE_KEY=
# GITHUB_APP_WEBHOOK_SECRET=
# Object storage (MinIO — local outside Docker)
S3_ENDPOINT_URL=http://localhost:9000
S3_PUBLIC_URL=http://localhost:9000
S3_BUCKET=pumpkin-hub-binaries
S3_ACCESS_KEY_ID=minioadmin
S3_SECRET_ACCESS_KEY=minioadmin
S3_FORCE_PATH_STYLE=true
# SMTP (optional — uses Mailpit in Docker)
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_USERNAME=pumpkin
SMTP_PASSWORD=pumpkin
SMTP_FROM_ADDRESS=noreply@pumpkinhub.dev
S3 / R2 Configuration Guide
Pumpkin Hub uses an S3-compatible object store for binary artifacts. Two separate S3 clients are instantiated internally: one for server-side operations (upload, delete) using the internal endpoint, and one for generating presigned download URLs using the public endpoint.
Host header in the request signature. If a presigned URL
is generated for host minio-dev:9000 (Docker-internal) but opened by a browser
at localhost:9000, the store returns a SignatureDoesNotMatch
error. S3_PUBLIC_URL tells the API which host to use when signing
download URLs, ensuring the signed host matches the host the browser will contact.
Local Development (Docker + MinIO)
Inside Docker, the API reaches MinIO at http://minio-dev:9000 while the
browser reaches it at http://localhost:9000. Set both variables to
different values:
S3_ENDPOINT_URL=http://minio-dev:9000 # used by the API container
S3_PUBLIC_URL=http://localhost:9000 # used for presigned URL signing
S3_FORCE_PATH_STYLE=true # required for MinIO
Production (Cloudflare R2)
With Cloudflare R2, upload traffic goes through the private R2 endpoint while
download links are signed for your public R2 subdomain (or custom domain).
Note that R2 requires S3_REGION=auto and virtual-hosted style addressing
(S3_FORCE_PATH_STYLE=false).
S3_ENDPOINT_URL=https://<ACCOUNT_ID>.r2.cloudflarestorage.com
S3_PUBLIC_URL=https://pub-<HASH>.r2.dev # or your custom domain
S3_BUCKET=pumpkin-hub-binaries
S3_ACCESS_KEY_ID=<R2_TOKEN_ID>
S3_SECRET_ACCESS_KEY=<R2_TOKEN_SECRET>
S3_REGION=auto
S3_FORCE_PATH_STYLE=false
Email Testing (Mailpit)
In the Docker development environment, a Mailpit instance captures all outgoing emails. No real email is ever sent during development.
- SMTP port:
1025(API sends mail here) - Web UI: http://localhost:8025 — browse captured emails
When you register a new account or trigger a password reset, the email will appear instantly in the Mailpit web interface. Click the verification/reset link from there to complete the flow.
Installation Verification
Once the services are running, verify that the API responds:
curl http://localhost:8080/api/v1/health
Expected response:
{
"status": "ok",
"service": "pumpkin-hub-api",
"version": "0.1.0",
"database": "connected"
}
cargo fmt + cargo clippy +
cargo test for the API.