Files
picpeak/SIMPLE_SETUP.md
T
Paul Nothaft ccab9024d4 fix(security): bound inbound-mail resources, redact secrets from logs (stable) (#965)
* 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. (stable)

* 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
(cherry picked from commit 9a54b6f0231c3285df4c4865eb846e63e1ed0dda)

---------

Co-authored-by: Paul Nothaft <paul@MacStudio-von-Paul.local>
2026-08-02 21:18:49 +02:00

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

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:

  1. Installation method (Docker or Native)
  2. Admin email and password
  3. Domain configuration (optional)
  4. Email server settings (optional)
  5. 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 application
  • picpeak-workers - Background workers
  • caddy - 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:

  1. Open http://your-server:3000/admin — you'll land on a setup screen.
  2. 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):
    docker compose exec backend cat /app/data/SETUP_TOKEN
    
    Only if that write fails does the backend log the token instead.
  3. 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 frontend
  • https://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

Via Admin Panel

  1. Login to admin panel at /admin
  2. Click "Create New Event"
  3. Configure settings (name, date, password, customer email)
  4. Upload photos via drag & drop in the Photos tab
  5. 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.

<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

  1. Enable 2-Factor Authentication
  2. Generate App Password
  3. 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

  1. Sign up at sendgrid.com (100 emails/day free)
  2. Create API key
  3. 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

  1. Check firewall:
# Ubuntu/Debian
sudo ufw allow 3001

# RHEL/CentOS
sudo firewall-cmd --add-port=3001/tcp --permanent
sudo firewall-cmd --reload
  1. 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

  1. Check logs:

    • Docker: docker compose logs -f
    • Native: sudo journalctl -u picpeak-backend -f
    • Installation: /tmp/picpeak-setup-*.log
  2. Documentation:

  3. Support:

    • GitHub Issues
    • Include: Error messages, system info (uname -a), installation method

🔒 Security Best Practices

Essential Security

  1. Change default admin password immediately
  2. Use HTTPS for production (Let's Encrypt included)
  3. Configure firewall (only open necessary ports)
  4. Regular updates (system and PicPeak)
  5. 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