* fix(security): bound inbound-mail resources, redact secrets from logs (GHSA-2qf9, pgmp, r794) GHSA-2qf9 — emailIntakeService downloaded, parsed and persisted every message with no size, attachment-count or attachment-byte limit, reachable unauthenticated by anyone who can email the operator's mailbox: - fetch the envelope with `size` (same cheap pass) and refuse an oversized message BEFORE downloading its source; - cap attachment count and cumulative attachment bytes; - limits env-overridable, defaults generous for real supplier invoices. The teeth were in the dedup key. received_emails.message_id is varchar(512) UNIQUE, and the failure path wrote `err-<uid>-<Date.now()>`, which can never match the envelope-derived messageId the dedup pass compares against — so an oversized (or overlong-Message-ID) mail was re-downloaded every poll forever, and an OOM-kill/restart just resumed the loop. Size-skips are now recorded under the REAL message id, and overlong ids collapse to a stable sha256 key that always fits the column. GHSA-pgmp / r794 — new sanitizeForLog() util (key-name deny-set, recursive, cycle-safe) applied to the three request-body log sites in adminEvents/crud.js, plus sanitizeValidationErrors() because express-validator's errors.array() embeds the SUBMITTED value per field — a rejected plaintext password was still logged. Scope is wider than filed: the update path also logged client_password_hash and a LIVE client_share_token bearer credential. Also: the one-time setup token was logged at warn AND printed to stdout on every first boot, putting a live first-admin credential in combined.log, security.log and `docker logs`. It is now written to the 0600 token file and only surfaced when that write fails — the last-resort path it existed for. * fix(security): codex round 3 — repair the first-run token recovery flow (GHSA-r794) Two regressions from keeping the setup token out of the logs. 1. server.js decided whether to print the token by calling existsSync() on the candidate path. That answers a different question than "did the write succeed": a stale, read-only or directory-shaped SETUP_TOKEN reports as present, so the banner suppressed the live token and pointed the operator at content that is not it — leaving the current token only in combined.log under default production logging. setupService now records the path the write actually produced and exposes it via writtenSetupTokenFile(). 2. The setup screen, its EN/DE strings, README, SIMPLE_SETUP and .env.example all still told first-time users to run `docker compose logs backend | grep -i "setup token"`. On the normal path that command now returns a path banner and no credential, so the documented browser-first onboarding could not be completed. They now point at `docker compose exec backend cat /app/data/SETUP_TOKEN`, with the log fallback described as what it is — the failure path. Claude-Session: https://claude.ai/code/session_01F211U4dDbEj4zXiyKbi9me --------- Co-authored-by: Paul Nothaft <paul@MacStudio-von-Paul.local>
15 KiB
🚀 PicPeak Simple Setup Guide
This guide provides easy installation instructions for PicPeak on Linux servers with both Docker and non-Docker options.
📋 Quick Start
One-Line Installation
# Download and run the unified setup script
curl -fsSL https://raw.githubusercontent.com/PicPeak/picpeak/main/scripts/picpeak-setup.sh -o picpeak-setup.sh && \
chmod +x picpeak-setup.sh && \
sudo ./picpeak-setup.sh
The script will automatically detect your environment and recommend the best installation method.
🎯 Installation Methods
Method 1: Docker Installation (Recommended)
Best for: Most users, easy updates, isolated environment
sudo ./picpeak-setup.sh --docker
Pros:
- ✅ Easier installation and updates
- ✅ Better isolation from system
- ✅ Consistent environment across platforms
- ✅ Built-in PostgreSQL and Redis
Cons:
- ❌ Requires more resources (~4GB RAM recommended)
- ❌ Additional Docker overhead
Method 2: Native Installation
Best for: Resource-constrained systems, Raspberry Pi, direct control
sudo ./picpeak-setup.sh --native
Pros:
- ✅ Lower resource usage (~1GB RAM minimum)
- ✅ Direct system control
- ✅ No Docker overhead
- ✅ Better for ARM devices
Cons:
- ❌ More complex setup
- ❌ System dependencies required
- ❌ Manual update process
📋 System Requirements
Minimum Requirements
- OS: Ubuntu 20.04+, Debian 11+, Fedora 38+, RHEL/CentOS 8+, Raspberry Pi OS
- RAM:
- Docker: 2GB minimum (4GB recommended)
- Native: 1GB minimum (2GB recommended)
- Storage: 2GB for application + space for photos
- Network: Port 3001 (or 80/443 with proxy)
Supported Platforms
- ✅ Ubuntu 20.04, 22.04, 24.04
- ✅ Debian 11, 12
- ✅ Raspberry Pi OS (32-bit and 64-bit)
- ✅ Fedora 38, 39, 40
- ✅ RHEL/CentOS/Rocky/AlmaLinux 8, 9
🛠️ Installation Options
Interactive Mode (Default)
sudo ./picpeak-setup.sh
The script will prompt you to choose:
- Installation method (Docker or Native)
- Admin email and password
- Domain configuration (optional)
- Email server settings (optional)
- SSL/HTTPS setup (optional)
Unattended Installation
Docker with full configuration:
sudo ./picpeak-setup.sh --docker --unattended \
--domain photos.example.com \
--email admin@example.com \
--admin-password SecurePass123 \
--smtp-host smtp.gmail.com \
--smtp-port 587 \
--smtp-user your-email@gmail.com \
--smtp-pass your-app-password \
--enable-ssl
Native with minimal configuration:
sudo ./picpeak-setup.sh --native --unattended \
--email admin@example.com \
--admin-password SecurePass123
Command Line Options
| Option | Description | Example |
|---|---|---|
--docker |
Use Docker installation | --docker |
--native |
Use native installation | --native |
--unattended |
Run without prompts | --unattended |
--domain |
Domain for HTTPS setup | --domain photos.example.com |
--email |
Admin email address | --email admin@example.com |
--admin-password |
Set admin password | --admin-password MySecurePass |
--smtp-host |
SMTP server hostname | --smtp-host smtp.gmail.com |
--smtp-port |
SMTP server port | --smtp-port 587 |
--smtp-user |
SMTP username | --smtp-user user@gmail.com |
--smtp-pass |
SMTP password | --smtp-pass app-password |
--enable-ssl |
Enable HTTPS with Let's Encrypt | --enable-ssl |
--port |
Custom port (native only) | --port 8080 |
--update |
Update existing installation | --update |
--uninstall |
Remove installation | --uninstall |
--help |
Show help message | --help |
🏗️ What Gets Installed
Docker Installation
~/picpeak/ # Or custom directory
├── docker-compose.yml # Service definitions
├── .env # Configuration
├── storage/
│ └── events/ # Photo storage
│ ├── active/ # Current galleries
│ └── archived/ # Expired galleries
├── logs/ # Application logs
└── backup/ # Backup directory
Services:
- PicPeak Backend (Node.js application)
- PostgreSQL Database
- Redis Cache
- Nginx Reverse Proxy (optional)
- Background Workers
Native Installation
/opt/picpeak/ # Installation directory
├── backend/ # Application code
├── events/ # Photo storage
│ ├── active/ # Current galleries
│ └── archived/ # Expired galleries
├── logs/ # Application logs
└── config/ # Configuration files
Services (systemd):
picpeak-backend- Main applicationpicpeak-workers- Background workerscaddy- Web server (optional)
🔑 First Login — Create Your Admin
If you installed with picpeak-setup.sh and gave an --admin-password, your admin account already exists — log in at /admin with that email and password.
If you started PicPeak without setting ADMIN_PASSWORD (e.g. a plain docker compose up), there's no admin yet and you create it in the browser:
- Open
http://your-server:3000/admin— you'll land on a setup screen. - Read the one-time setup token from the 0600 file the backend writes it to
(it is not logged — that would leave a live credential in
docker logs):Only if that write fails does the backend log the token instead.docker compose exec backend cat /app/data/SETUP_TOKEN - Paste it, set your admin email + password. The token is single-use and the screen closes once an admin exists.
🌐 Access Methods
Direct Access (Simplest)
- Docker:
http://your-server:3000(frontend and admin at/admin) - Backend/API:
http://your-server:3001(API only; no UI routes)
For native installs, serve the built frontend (e.g., with nginx or Caddy) and access the admin at /admin on the frontend domain.
With Domain & HTTPS
If configured during setup:
https://your-domain.com- Gallery frontendhttps://your-domain.com/admin- Admin panel
Behind Existing Proxy
Add to your Nginx/Apache configuration (split frontend vs backend):
# Frontend (UI + /admin/*)
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
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;
}
# Backend API and protected resources
location /api {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
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;
client_max_body_size 100M;
}
location ~ ^/(photos|thumbnails|uploads) {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
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;
}
📁 Managing Galleries
Creating a Gallery
Via Admin Panel
- Login to admin panel at
/admin - Click "Create New Event"
- Configure settings (name, date, password, customer email)
- Upload photos via drag & drop in the Photos tab
- Publish the gallery when ready
Adding Photos via File System
Important: You must first create the event in the admin panel. The file watcher only detects new photos for events that already exist in the database. You cannot create a gallery by copying files alone.
Once an event exists, you can add photos by copying them into the event's folder. PicPeak's built-in file watcher will automatically detect the new files, create database records, and generate thumbnails.
# Docker installation — copy photos into an existing event's folder
cp /path/to/photos/*.jpg ~/picpeak/storage/events/active/<event-slug>/
# Native installation
sudo cp /path/to/photos/*.jpg /opt/picpeak/events/active/<event-slug>/
sudo chown -R picpeak:picpeak /opt/picpeak/events/active/<event-slug>
The event slug is visible in the admin panel URL or share link (e.g. wedding-smith-2024). Supported formats: .jpg, .jpeg, .png, .webp. The file watcher has a 2-second stability delay before processing new files.
Gallery Structure
<event-slug>/
├── collages/ # Group photos (optional subfolder)
├── individual/ # Individual photos (optional subfolder)
└── photo.jpg # Photos at root level also work
🔧 Service Management
Docker Installation
cd ~/picpeak
# Check status
docker compose ps
# View logs
docker compose logs -f
# Stop services
docker compose down
# Start services
docker compose up -d
# Restart services
docker compose restart
# Update PicPeak
docker compose pull
docker compose up -d
Native Installation
# Check status
sudo systemctl status picpeak-backend
sudo systemctl status picpeak-workers
# View logs
sudo journalctl -u picpeak-backend -f
sudo journalctl -u picpeak-workers -f
# Start services
sudo systemctl start picpeak-backend picpeak-workers
# Stop services
sudo systemctl stop picpeak-backend picpeak-workers
# Restart services
sudo systemctl restart picpeak-backend picpeak-workers
# Update PicPeak
# (reruns migrations to pick up schema fixes for native installs)
sudo ./picpeak-setup.sh --update
⚙️ Configuration
Docker Configuration
Edit ~/picpeak/.env:
nano ~/picpeak/.env
docker compose restart
Native Configuration
Edit /opt/picpeak/app/backend/.env:
sudo nano /opt/picpeak/app/backend/.env
sudo systemctl restart picpeak-backend
Key Settings
| Setting | Description | Default |
|---|---|---|
JWT_SECRET |
Token signing secret | Auto-generated |
ADMIN_EMAIL |
Admin email | admin@example.com |
ADMIN_PASSWORD |
Admin password | Auto-generated |
PHOTOS_DIR |
Photo storage path | Varies by method |
SMTP_ENABLED |
Email notifications | false |
DEFAULT_EXPIRY_DAYS |
Gallery expiration | 30 |
📧 Email Configuration
Gmail Setup
- Enable 2-Factor Authentication
- Generate App Password
- Configure:
SMTP_ENABLED=true
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
SMTP_FROM=noreply@yourdomain.com
SendGrid Setup
- Sign up at sendgrid.com (100 emails/day free)
- Create API key
- Configure:
SMTP_ENABLED=true
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASS=your-sendgrid-api-key
SMTP_FROM=verified-sender@yourdomain.com
🔄 Maintenance
Backups
Docker:
# Backup script included
cd ~/picpeak
./backup.sh
# Manual backup
docker exec picpeak-postgres pg_dump -U picpeak picpeak > backup.sql
tar -czf photos-backup.tar.gz storage/events/
Native:
# Database backup
sudo cp /opt/picpeak/app/backend/data/photo_sharing.db /backup/database-$(date +%Y%m%d).sqlite
# Photos backup
sudo tar -czf /backup/photos-$(date +%Y%m%d).tar.gz /opt/picpeak/events/
Updates
# Docker
cd ~/picpeak
docker compose pull
docker compose up -d
# Native
sudo ./picpeak-setup.sh --update
Uninstall
# Will prompt for confirmation and data removal options
sudo ./picpeak-setup.sh --uninstall
🐛 Troubleshooting
Common Issues
Service Won't Start
# Docker
docker compose logs backend
docker compose down && docker compose up -d
# Native
sudo journalctl -u picpeak-backend -n 50
sudo systemctl restart picpeak-backend
Can't Access Admin Panel
- Check firewall:
# Ubuntu/Debian
sudo ufw allow 3001
# RHEL/CentOS
sudo firewall-cmd --add-port=3001/tcp --permanent
sudo firewall-cmd --reload
- Verify service:
# Docker
curl http://localhost:3001/api/health
# Native
sudo systemctl is-active picpeak-backend
Photos Not Showing
# Check permissions (Native)
sudo chown -R picpeak:picpeak /opt/picpeak/events/
sudo chmod -R 755 /opt/picpeak/events/
# Check permissions (Docker)
ls -la ~/picpeak/storage/events/
Reset Admin Password
# Docker
docker exec picpeak-backend node scripts/reset-admin-password.js
# Native
cd /opt/picpeak/app/backend
sudo -u picpeak node scripts/reset-admin-password.js
Note: The new password will be displayed in the console output and saved to
ADMIN_PASSWORD_RESET.txt. Save it immediately!
Getting Help
-
Check logs:
- Docker:
docker compose logs -f - Native:
sudo journalctl -u picpeak-backend -f - Installation:
/tmp/picpeak-setup-*.log
- Docker:
-
Documentation:
-
Support:
- GitHub Issues
- Include: Error messages, system info (
uname -a), installation method
🔒 Security Best Practices
Essential Security
- Change default admin password immediately
- Use HTTPS for production (Let's Encrypt included)
- Configure firewall (only open necessary ports)
- Regular updates (system and PicPeak)
- Automated backups (configure in admin panel)
Advanced Security
- Use VPN for admin panel access
- Configure fail2ban for brute force protection
- Enable audit logging
- Regular security scans
- Implement IP whitelisting
📊 Performance Optimization
Docker Optimization
# Adjust in docker-compose.yml
services:
backend:
deploy:
resources:
limits:
cpus: '2'
memory: 2G
Native Optimization
# Increase Node.js memory
echo "NODE_OPTIONS=--max-old-space-size=2048" >> /opt/picpeak/app/backend/.env
sudo systemctl restart picpeak-backend
🎯 Quick Setup Examples
Home/Office Network
# Simple local setup without domain
sudo ./picpeak-setup.sh --native --email admin@local.com
Public Website with HTTPS
# Full production setup
sudo ./picpeak-setup.sh --docker \
--domain photos.company.com \
--email admin@company.com \
--enable-ssl
Raspberry Pi Setup
# Optimized for ARM devices
sudo ./picpeak-setup.sh --native \
--port 8080 \
--email pi@local.com
✅ Post-Installation Checklist
- Admin password changed
- Email configuration tested
- First test gallery created
- Backup schedule configured
- Firewall rules applied
- SSL certificate working (if applicable)
- Monitoring setup
- Documentation bookmarked
PicPeak Setup v1.0 | Documentation | Support