Fixed typo in Stirling PDF service naming:
- Renamed service from sterling to stirling
- Updated all variable names: KIT_STERLING_* to KIT_STIRLING_*
- Updated domain: sterling.kit.pivoine.art to stirling.kit.pivoine.art
- Updated container name: kit_sterling to kit_stirling
This matches the correct spelling of Stirling-PDF.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added Stirling PDF (S-PDF) to the Kit toolkit at sterling.kit.pivoine.art:
- PDF manipulation tool with 100+ features
- Merge, split, convert, OCR, compress PDFs
- Privacy-focused: local processing, no data collection
- Image: frooodle/s-pdf:latest
- Exposed on port 8080 via Traefik with SSL
Configuration:
- Added sterling service to kit/compose.yaml
- Added KIT_STERLING_IMAGE and KIT_STERLING_TRAEFIK_HOST to arty.yml
- Follows same pattern as other Kit tools (Vert, Paint, Pastel)
- Watchtower enabled for automatic updates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed all Traefik labels from pastel_api service since the API
is now only accessed internally by pastel_ui via Docker network.
Changes:
- Removed traefik.enable and all HTTP/HTTPS router configurations
- Removed path prefix routing (/api)
- Kept only Watchtower label for auto-updates
- API now accessible only at http://pastel_api:3000 internally
This simplifies the configuration and improves security by not
exposing the API endpoint externally.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed Pastel UI environment variable to use internal Docker
network communication instead of external HTTPS:
- Changed from NEXT_PUBLIC_API_URL/NEXT_PUBLIC_APP_URL to PASTEL_API_URL
- Use internal container name: http://pastel_api:3000
- Removes dependency on external domain resolution
- Improves performance by avoiding Traefik proxy for internal calls
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed custom Dockerfile and switched to using the official
machines/filestash image from Docker Hub:
- Removed stash/Dockerfile (custom build with ffmpeg)
- Updated compose.yaml to use ${STASH_IMAGE} variable
- Defaults to machines/filestash:latest
This simplifies maintenance and ensures we use the officially
maintained image with proper updates via Watchtower.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed Pastel color palette generator services in Kit stack:
- Disabled health checks for pastel_api (curl not available in image)
- Disabled health checks for pastel_ui (unnecessary)
- Added NEXT_PUBLIC_API_URL environment variable to pastel_ui
- Added NEXT_PUBLIC_APP_URL environment variable to pastel_ui
Health checks were showing containers as unhealthy even though services
were running correctly. The API health check used curl which isn't
installed in the image, causing false negatives.
Environment variables configure the Next.js frontend to correctly
connect to the API endpoint.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed authentication middleware from Vert and Paint services:
- Removed basicauth middleware labels from vert service
- Removed basicauth middleware labels from paint service
- Updated middleware chains to exclude auth
- Updated CLAUDE.md to reflect public access
All Kit stack services (landing, vert, paint) are now publicly
accessible without authentication as they are client-side tools
that don't require protection.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added kit-ui landing page as the main entry point for the toolkit:
- Image: ghcr.io/valknarness/kit-ui:latest
- URL: https://kit.pivoine.art
- Provides links to Vert (file converter) and Paint (image editor)
Changes:
- Added landing service to kit/compose.yaml
- Updated arty.yml with KIT_TRAEFIK_HOST and KIT_LANDING_IMAGE
- Updated CLAUDE.md with landing page documentation
- Updated README.md with landing page reference
Toolkit structure:
- Landing: kit.pivoine.art (main hub)
- Vert: vert.kit.pivoine.art (file converter)
- Paint: paint.kit.pivoine.art (image editor)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Moved Dockerfile from paint/ to kit/ directory and updated
build context to use local directory instead of ../paint.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created new kit.pivoine.art stack with path-based routing:
- /vert: VERT file format converter (250+ formats)
- /paint: miniPaint image editor
Changes:
- Created kit/compose.yaml with both services
- Removed Sablier scale-to-zero from Vert (no longer needed)
- Deleted old vert/ and paint/ stack directories
- Updated compose.yaml includes (removed vert, paint; added kit)
- Updated arty.yml with KIT_* environment variables
- Updated CLAUDE.md documentation with new Kit section
- Updated README.md with consolidated toolkit entry
Benefits:
- Single domain for related utilities
- Simplified service management
- Maintained HTTP Basic Auth protection
- No Sablier dependency for Vert
Access:
- File converter: https://kit.pivoine.art/vert
- Image editor: https://kit.pivoine.art/paint🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added documentation for three new services:
- **DROP (PairDrop)**: Peer-to-peer file sharing with WebRTC
- Direct device-to-device transfers (no server storage)
- STUN-enabled cross-network support
- Automatic device discovery
- Share files, text, and clipboard content
- **JELLY (Jellyfin)**: Media streaming server
- HiDrive photo and video streaming
- Hardware transcoding support
- Multi-device playback
- **PAINT (miniPaint)**: Web-based image editor
- Layer support and filters
- Client-side processing
- PNG, JPG, GIF, WebP support
Updated:
- Core systems table with new services
- Ship architecture diagram
- Storage volumes list
- Navigation commands with usage instructions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enabled WebSocket server and fallback to help with device discovery
when devices appear to have the same public IP through the proxy.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Documented the PairDrop service with WebRTC STUN configuration:
- Service description and features
- RTC_CONFIG configuration with Google STUN servers
- Usage instructions for cross-network file sharing
- Technical details about WebRTC and NAT traversal
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed from ./drop/rtc_config.json to ./rtc_config.json since the
compose file is already in the drop/ directory. The previous path
caused Docker to create an empty directory instead of mounting the file.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed mount path from /config/rtc_config.json to /rtc_config.json
to resolve EISDIR error where container was trying to read a directory
instead of the file.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added RTC configuration with Google's public STUN servers to enable
peer-to-peer connections across different networks (e.g., WiFi to
cellular data).
Changes:
- Created drop/rtc_config.json with 5 Google STUN servers
- Updated drop/compose.yaml to mount RTC config file
- Added RTC_CONFIG environment variable pointing to config file
This should resolve connectivity issues when devices are on different
networks or behind NAT.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added PairDrop stack for peer-to-peer file sharing:
- WebRTC-based direct file transfers between devices
- No file size limits or server storage
- End-to-end encrypted transfers
- Local network auto-discovery
- Cross-platform support (desktop, mobile, tablets)
- Progressive Web App installable on mobile
- Rate limiting enabled for security
PairDrop provides secure, private file sharing without uploading
files to any server - all transfers happen directly between devices.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added jellyfin-backup plan to Backrest configuration:
- Backs up /volumes/jelly_config daily at 9 AM
- Retention: 7 daily, 4 weekly, 6 monthly, 2 yearly
- Added jelly_config volume mount to restic/compose.yaml
- Updated documentation in CLAUDE.md
This ensures Jellyfin configuration, library metadata, and user
settings are backed up to HiDrive.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added Jellyfin stack for streaming photos and videos from HiDrive:
- Maps /mnt/hidrive/users/valknar/Pictures to /media/pictures (read-only)
- Maps /mnt/hidrive/users/valknar/Videos to /media/videos (read-only)
- Hardware transcoding support for optimal video playback
- Multi-device streaming (web, mobile, TV apps)
- Automatic media organization with metadata fetching
Jellyfin provides superior video playback compared to Filestash's
transcoding plugin, which has compatibility issues with named pipes
in containerized environments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Video transcoding was failing with 'Failed to open segment pipe:out000.ts'
because ffmpeg couldn't create named pipes. Added tmpfs mount with exec
permissions to /tmp to allow ffmpeg to create temporary files and pipes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added custom Dockerfile to build Filestash with ffmpeg and ffprobe installed,
enabling the built-in video transcoding plugin for seamless video playback.
Changes:
- Created stash/Dockerfile extending machines/filestash:latest
- Installed ffmpeg package with apt-get
- Updated stash/compose.yaml to build from Dockerfile
- Video transcoding plugin will automatically detect ffmpeg presence
This enables Filestash to transcode mov, mkv, avi, mpeg, and other video
formats for in-browser playback without manual conversion.
Note: Enable the video transcoding feature in Filestash admin panel at
https://stash.pivoine.art/admin after deployment.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Protected paint.pivoine.art with HTTP Basic Auth using shared AUTH_USERS
credentials, matching the security setup of scrapy and other protected services.
Changes:
- Added basicauth middleware with AUTH_USERS variable
- Added compression middleware
- Updated router middlewares chain to include auth, compress, and security headers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added new paint service stack to the docker-compose infrastructure:
- **Paint stack** (paint.pivoine.art):
- miniPaint: Web-based image editor built from GitHub
- Multi-stage Docker build clones from https://github.com/viliusle/miniPaint
- Features: layers, filters, drawing tools, text, shapes support
- Client-side processing with no server uploads
- Stateless architecture (no backups needed)
Infrastructure updates:
- Created paint/compose.yaml with Traefik routing and SSL
- Created paint/Dockerfile with Node.js build stage and nginx serve
- Added PAINT environment variables to arty.yml
- Updated compose.yaml include list
- Updated CLAUDE.md documentation
All services integrated with Traefik for SSL termination and include
Watchtower auto-update labels.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Properly configured AUTH_USERS environment variable for Traefik container
to use in the vert-sablier.yaml dynamic configuration via Go templating.
Changes:
- Added AUTH_USERS environment variable to proxy compose file
- Updated vert-auth middleware to use {{ env AUTH_USERS }} template
- Fixed environment syntax to use map format instead of list format
This keeps credentials secure in the .env file (not tracked in git) while
the tracked vert-sablier.yaml file only contains the template reference.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed AUTH_USERS environment variable from Traefik container as Docker
Compose was incorrectly expanding the $ signs in the htpasswd hash.
Instead, hardcoded the credentials directly in the vert-sablier.yaml dynamic
configuration file (which is not tracked in git, so changes remain local).
The AUTH_USERS variable with $$ escaping continues to work correctly in
Docker labels for other services.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed "middleware does not exist" error for VERT by defining all middlewares
in the file provider configuration instead of relying on Docker provider
middlewares from a stopped container (Sablier scale-to-zero).
Changes:
- Added vert-auth middleware using AUTH_USERS environment variable
- Added vert-compress middleware for gzip compression
- Passed AUTH_USERS to Traefik container via environment section
- Removed @docker suffixes from middleware references
This ensures middlewares are always available, even when the VERT container
is stopped by Sablier's dynamic scaling feature.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed "the service 'vert-web-secure@docker' does not exist" error by defining
the service directly in the vert-sablier.yaml file provider configuration.
Previously, the router was defined in the file provider but tried to reference
a service from the Docker provider (@docker suffix), which caused a mismatch.
Now both the router and service are defined in the same file provider, while
still using Docker-based middlewares (auth, compression).
Changes:
- Added services.vert-web-secure with loadBalancer pointing to vert_app:80
- Changed router service reference from "vert-web-secure@docker" to "vert-web-secure"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed the global `--entrypoints.web-secure.http.tls.options=default@file`
configuration from proxy/compose.yaml that was causing "unknown TLS options"
errors during Traefik startup.
The issue occurred because Traefik attempted to apply TLS options to all
routers before the file provider finished loading security.yaml, creating
a race condition. Services now use Let's Encrypt certificates without
explicit TLS options at the entrypoint level.
The TLS security settings (minimum TLS 1.2, strong cipher suites, etc.)
remain defined in proxy/dynamic/security.yaml and can be applied to
individual services via their router configurations if needed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Router is now fully defined in proxy/dynamic/vert-sablier.yaml.
Docker labels now only define:
- Service (loadbalancer port)
- Middlewares (auth, compress)
- Sablier labels (enable, group)
This prevents conflicts between Docker label routers and dynamic file routers.
Moved Sablier middleware from Docker labels to Traefik dynamic configuration
to fix "unknown plugin type: sablier" error. Traefik plugins can only be
used in dynamic file configuration, not in Docker labels.
Changes:
- Removed sablier-vert@file reference from Docker label middleware chain
- Added complete router definition to proxy/dynamic/vert-sablier.yaml
- Router includes full middleware chain: sablier-vert → vert-auth → compress
- Kept sablier.enable and sablier.group labels for Sablier service discovery
This allows Sablier to properly scale the container to zero while maintaining
the middleware chain order through file-based configuration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added VERT universal file format converter deployed at vert.pivoine.art:
**Vert stack** (vert.pivoine.art):
- vert: WebAssembly-based file converter
- Supports 250+ file formats (images, audio, documents, video)
- Client-side processing for privacy
- No file size limits
- No persistent data storage (stateless)
Infrastructure updates:
- Created vert/compose.yaml with Vert configuration
- Added VERT_* environment variables to arty.yml
- Updated compose.yaml to include vert stack
- Updated README.md and CLAUDE.md documentation
- No backup needed (stateless service)
All services integrated with Traefik for SSL termination and include
Watchtower auto-update labels.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added email configuration to Joplin Server using existing SMTP settings from .env:
- MAILER_ENABLED: 1
- MAILER_HOST, MAILER_PORT from EMAIL_SMTP_* variables
- MAILER_AUTH_USER and MAILER_AUTH_PASSWORD from .env
- MAILER_NOREPLY_EMAIL using EMAIL_FROM
This enables password reset emails and user notifications.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added Joplin Server deployment at joplin.pivoine.art:
**Joplin stack** (joplin.pivoine.art):
- joplin: Note-taking server with multi-device sync
- PostgreSQL backend for data persistence
- End-to-end encryption support
- Compatible with official Joplin clients (desktop, mobile, CLI)
- Markdown-based notes with attachments
Infrastructure updates:
- Added joplin database to PostgreSQL init script
- Updated compose.yaml to include joplin stack
- Added JOPLIN_* environment variables to arty.yml
- Added joplin-backup plan to restic (13th backup plan)
- Updated restic/compose.yaml with joplin_data volume mount
- Updated README.md and CLAUDE.md documentation
All services integrated with Traefik for SSL termination and include
Watchtower auto-update labels. Daily backups scheduled for 2 AM with
7 daily, 4 weekly, 6 monthly, and 2 yearly retention.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>