Port 465 requires implicit TLS, not SSL. Changed SMTP_SSL to SMTP_TLS
with value 'always' and added SMTP_AUTH: always.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add EMAIL_FROM to arty.yml environment defaults
- Configure asciinema to use EMAIL_FROM for MAIL_FROM_ADDRESS
- Set SMTP_SSL to true for IONOS SMTP on port 465
- Set SIGN_UP_DISABLED default to false (will enable after admin creation)
- Follow netdata compose.yaml pattern for Traefik labels
- Add proper HTTP to HTTPS redirect middlewares
- Configure compression and security headers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed API key reference from ${ANTHROPIC_API_KEY} to
os.environ/ANTHROPIC_API_KEY to match LiteLLM's documented syntax.
The os.environ/ prefix tells LiteLLM to use os.getenv() to retrieve
the environment variable at runtime, which is the correct way to
reference environment variables in LiteLLM config files.
Reference: https://docs.litellm.ai/docs/proxy/deploy🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed custom Dockerfile and SFTP function integration in favor of
the simpler REST API approach (webui-export.py).
Changes:
- Restored webui service to use official Open WebUI image
- Removed custom Dockerfile.webui (paramiko build)
- Removed ai/functions/save_to_disk.py SFTP function
- Removed SSH key and functions volume mounts
The REST API export script (webui-export.py) is a simpler and more
flexible solution that doesn't require Docker modifications.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added Python script to extract and save code blocks from Open WebUI
chat conversations to local disk using the REST API.
Features:
- Export code blocks from specific chats or all chats
- Automatic language detection and proper file extensions
- Organizes files by chat title with metadata
- No Docker modifications needed
- Remote access support via SSH tunnel or public URL
Usage:
python3 ai/webui-export.py --all --output-dir ./exports
python3 ai/webui-export.py --chat-id <id> --output-dir ./code
This replaces the complex SFTP integration with a simple API-based
approach that's easier to maintain and use.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added custom Open WebUI function for SSH/SFTP file operations:
**New Function: save_to_disk.py**
- save_file(): Write generated code to local filesystem via SFTP
- read_file(): Read files from local disk
- list_files(): List directory contents
- Configurable via Valves (host, port, username, paths)
**Custom Dockerfile (Dockerfile.webui)**
- Based on ghcr.io/open-webui/open-webui:main
- Installs paramiko library for SSH/SFTP support
- Creates .ssh directory for key storage
**Configuration Updates**
- Mount SSH private key from host (/root/.ssh/id_rsa)
- Mount functions directory for custom tools
- Build custom image with SFTP capabilities
**Usage in Open WebUI**
Claude can now use these tools to:
- Generate code and save it directly to your local disk
- Read existing files for context
- List project directories
- Create new files in any project
Default base path: /home/valknar/Projects
Authentication: SSH key-based (passwordless)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added LiteLLM as an OpenAI-compatible proxy for Anthropic's API to
enable Claude models in Open WebUI.
**New Service: litellm**
- Image: ghcr.io/berriai/litellm:main-latest
- Internal proxy on port 4000
- Converts Anthropic API to OpenAI-compatible format
- Health check with 30s intervals
- Not exposed via Traefik (internal only)
**LiteLLM Configuration (litellm-config.yaml)**
- Claude Sonnet 4 (claude-sonnet-4-20250514)
- Claude Sonnet 4.5 (claude-sonnet-4-5-20250929)
- Claude 3.5 Sonnet (claude-3-5-sonnet-20241022)
- Claude 3 Opus (claude-3-opus-20240229)
- Claude 3 Haiku (claude-3-haiku-20240307)
**Open WebUI Configuration Updates**
- Changed OPENAI_API_BASE_URLS to point to LiteLLM proxy
- URL: http://litellm:4000/v1
- Added litellm as dependency for webui service
- Dummy API key for proxy authentication
**Why LiteLLM?**
Anthropic's API uses different endpoint structure and authentication
headers compared to OpenAI. LiteLLM acts as a translation layer,
allowing Open WebUI to use Claude models through its OpenAI-compatible
interface.
**Available Models in Open WebUI**
- claude-sonnet-4 (latest Claude Sonnet 4)
- claude-sonnet-4.5 (Claude Sonnet 4.5)
- claude-3-5-sonnet
- claude-3-opus
- claude-3-haiku
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated README.md with AI intelligence system:
**Core Systems Table**:
- Added AI system entry with ai.pivoine.art access point
**Infrastructure Section**:
- Added AI Intelligence Core with PostgreSQL 16 + pgvector
- Listed Open WebUI, Crawl4AI, and document embeddings
**Navigation Commands**:
- New AI Operations section with usage instructions
- How to configure Claude API in Open WebUI
- Steps: create account, add API connection, select model, upload docs
- Internal services documentation (Crawl4AI, PostgreSQL)
- n8n workflow integration examples
**Ship Architecture**:
- Added AI Intelligence category with 3 services
- PostgreSQL+pgvector for vector database
- Open WebUI for Claude interface
- Crawl4AI for web scraping (internal)
**Storage Volumes**:
- ai_postgres_data: AI vector database
- ai_webui_data: Open WebUI application data
- ai_crawl4ai_data: Web scraping cache
**Backup Protocol**:
- Updated backup window to 2-10 AM
- Updated count from 11 to 16 backup plans
All AI services accessible at https://ai.pivoine.art with Claude
integration, RAG support, and web search capabilities.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed service name from 'postgres' to 'ai_postgres' to avoid naming
conflict with the core PostgreSQL service in Docker Compose include.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created complete AI infrastructure stack at ai.pivoine.art:
**New Services:**
- **Open WebUI** (ai.pivoine.art)
- ChatGPT-like interface for AI models
- Multi-user chat with authentication
- RAG (Retrieval-Augmented Generation) support
- Document upload and processing
- Claude API integration via Anthropic
- **PostgreSQL with pgvector** (dedicated AI database)
- Vector similarity search for RAG
- Separate from production databases
- Stores embeddings and documents
- **Crawl4AI** (internal API service)
- Web scraping optimized for LLMs
- Converts websites to clean Markdown
- Called by n8n workflows
- No public exposure (internal only)
**Configuration:**
- Added 18 AI environment variables to arty.yml
- Configured email notifications via IONOS SMTP
- OpenAI API compatibility for Claude integration
- Traefik SSL termination and compression
**Backup:**
- Added 3 AI volumes to Restic backup
- Daily backup at 3 AM
- Retention: 7 daily, 4 weekly, 6 monthly, 2 yearly
**Integration:**
- Shares falcon_network with existing services
- Ready for n8n workflow automation
- Mattermost notifications support
- Watchtower auto-updates enabled
Ready for Phase 2: GPU server integration with Ollama, Whisper, and
Stable Diffusion when IONOS A10 server is provisioned.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed PostgreSQL image from postgres:16-alpine to
pgvector/pgvector:pg16-alpine to enable vector similarity search
capabilities for AI/RAG applications.
This is required for Open WebUI's RAG functionality to store and
query document embeddings.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Tandoor's internal Nginx listens on port 80, not 8080. This was causing
502 Bad Gateway errors because Traefik was trying to connect to the wrong port.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed VPN backup configuration since the VPN service is not currently
included in the compose.yaml and the vpn_etc_wireguard volume doesn't exist.
Kept Netdata backup as those volumes exist and the service is running.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added Tandoor Recipes as a comprehensive recipe management solution:
**Tandoor Stack** (tandoor.pivoine.art):
- Modern recipe manager with smart scaling and collaboration
- PostgreSQL backend for recipe persistence
- Email notifications via IONOS SMTP
- Static and media file storage in dedicated volumes
- User signups disabled (admin-only access)
**Features:**
- Smart recipe scaling (auto-adjust ingredients for servings)
- Spaces for collaboration (family/roommate recipe sharing)
- Meal planning and shopping lists
- Recipe import from URLs
- Mobile app support (Kitshn app)
- Nutritional information and pricing
**Infrastructure updates:**
- Added tandoor database to PostgreSQL init script
- Added environment variables to arty.yml
- Updated compose.yaml include list
- Added Tandoor volumes (staticfiles, mediafiles) to Restic backup
- Configured email notifications for invitations and notifications
**Tech stack:**
- Django/Python backend
- Vue.js frontend
- PostgreSQL database (shared core instance)
- Gunicorn WSGI server
Tandoor provides superior UX compared to Mealie with better recipe
scaling, collaboration features, and mobile app experience.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated documentation to reflect current infrastructure:
**Added:**
- Mattermost team collaboration platform
- Team chat, file sharing, integrations
- Email notifications via IONOS SMTP
- Incoming webhooks for infrastructure alerts
- PostgreSQL backend
- Netdata real-time monitoring
- System and service monitoring
- PostgreSQL and Docker monitoring
- Restic backup repository monitoring
- Email and Mattermost alerts
- HTTP Basic Auth protection
**Removed:**
- Gotify notification server (replaced by Mattermost)
**Updated:**
- Database initialization: Added mattermost database
- Backup configuration: Added Mattermost volumes
- Environment variables: Added MATTERMOST_WEBHOOK_URL and WATCHTOWER_NOTIFICATION_URL
- Volume management: Added Mattermost, Joplin, and Jellyfin volumes
- Service list in compose include pattern
All documentation now reflects the current state of the infrastructure
with Mattermost as the central notification and collaboration hub.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added Mattermost volumes to backup configuration:
- mattermost_config: Server configuration and settings
- mattermost_data: User data, posts, files, and attachments
- mattermost_plugins: Installed plugins
These volumes contain critical Mattermost data and should be
backed up regularly to ensure team chat history and configurations
can be restored if needed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed Gotify completely from infrastructure, replaced by Mattermost:
- Removed gotify/compose.yaml stack
- Removed Gotify environment variables from arty.yml
- Removed Gotify from compose.yaml include list
- Removed Gotify volume backup from Restic configuration
Gotify has been fully replaced by Mattermost for:
- Infrastructure notifications (Netdata, Watchtower, Restic)
- n8n workflow notifications
- Team collaboration and chat
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added MATTERMOST_WEBHOOK_URL environment variable to n8n:
- Available as environment variable in workflow expressions
- Can be used in HTTP Request nodes to send notifications
- Enables easy integration with Mattermost from any workflow
Usage in n8n workflows:
- Use {{ $env.MATTERMOST_WEBHOOK_URL }} in expressions
- Send POST requests with JSON payload to notify Mattermost
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added Mattermost webhook notifications for infrastructure services:
- **Netdata**: Health monitoring alerts sent to Mattermost
- Configured via MATTERMOST_WEBHOOK_URL environment variable
- All alert roles route to Mattermost notifications channel
- **Restic/Backrest**: Backup status notifications
- Webhook URL passed as environment variable
- Backrest web UI can be configured to use the webhook
- **Watchtower**: Container update notifications
- Uses Shoutrrr mattermost:// URL format
- Sends notifications when containers are updated
Configuration:
- Webhook URL stored securely in .env file
- All services reference ${MATTERMOST_WEBHOOK_URL} variable
- Watchtower uses specialized Shoutrrr format URL
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added Mattermost stack to the docker-compose infrastructure:
- **Mattermost stack** (mattermost.pivoine.art):
- Team collaboration and chat platform
- PostgreSQL backend for message persistence
- Email notifications via IONOS SMTP
- Support for channels, direct messages, and integrations
- Mobile and desktop app support
- Full Traefik integration with SSL termination
Infrastructure updates:
- Updated PostgreSQL init script to create mattermost database
- Added environment variables to arty.yml for Mattermost configuration
- Updated compose.yaml include list
- Configured email settings for notifications and invitations
This will replace Gotify as the primary notification and messaging platform.
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>
Netdata container is Debian-based, not Alpine. Updated package
manager from apk to apt-get for installing msmtp packages.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created custom Dockerfile for Netdata container that includes msmtp
and msmtp-mta packages for sending email notifications.
Changes:
- Added netdata/Dockerfile extending netdata/netdata:latest
- Installed msmtp, msmtp-mta, and ca-certificates via apk
- Set proper permissions (600) for msmtprc config file
- Updated compose.yaml to build from custom Dockerfile
This enables Netdata to send health alarm notifications via email
using the IONOS SMTP configuration in msmtprc.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive email configuration to enable notifications for
password resets, user invitations, system alerts, and backup failures.
**Services configured:**
- **Vaultwarden** (vault/compose.yaml):
- SMTP settings for password resets, 2FA, emergency access emails
- Uses IONOS SMTP with force_tls security
- Sender: hi@pivoine.art
- **Linkwarden** (links/compose.yaml):
- Email server configuration for user invitations
- Password reset functionality via email
- Uses smtp:// connection string format
- **n8n** (n8n/compose.yaml):
- SMTP mode enabled for workflow notifications
- User invitation emails
- Password reset support
- SSL-secured connection
- **Netdata** (netdata/compose.yaml, health_alarm_notify.conf, msmtprc):
- Health alarm notifications via email
- MSMTP configuration for sending alerts
- Notifications sent to valknar@pivoine.art
- Alerts for system issues, resource exhaustion, service failures
**Common SMTP settings** (from .env):
- Provider: IONOS (smtp.ionos.de:465)
- From address: hi@pivoine.art
- Transport: SMTP with TLS/SSL
- Admin email: valknar@pivoine.art
**Backrest notifications:**
- Configured via web UI at restic.pivoine.art
- Supports webhooks to Gotify for push notifications
- Email notifications can be added through UI settings
All services now have proper email notification capabilities for
improved monitoring, user management, and security features.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed from mounting entire go.d directory to mounting specific
config files (postgres.conf, filecheck.conf) to avoid conflicts
with Netdata's initialization script which needs to write to the
go.d directory during startup.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added HTTP Basic Authentication to secure the Netdata monitoring dashboard:
- Added basicauth middleware using shared AUTH_USERS credentials
- Protects sensitive infrastructure metrics from unauthorized access
- Uses same credentials as Scrapy and other protected services
- Maintains SSL/TLS encryption via Traefik
Security improvements:
- Dashboard now requires username/password
- Prevents public access to server metrics
- Infrastructure monitoring data protected
- Follows security best practices from Netdata documentation
Access requires credentials stored in AUTH_USERS environment variable.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added Netdata real-time monitoring system at netdata.pivoine.art:
- Real-time infrastructure and container monitoring
- Auto-discovers all Docker containers
- Tracks CPU, memory, disk, network usage per service
- Low overhead monitoring (~1-3% CPU)
- Self-hosted with web dashboard on port 19999
Configuration:
- Created netdata/compose.yaml with full Traefik integration
- Added to main compose.yaml include list
- Added environment variables to arty.yml
- Mounted Docker socket for container metrics
- Mounted system directories for host metrics (/proc, /sys, /var/log)
- Three persistent volumes: config, lib, cache
- Required capabilities: SYS_PTRACE, SYS_ADMIN for system monitoring
- Watchtower enabled for automatic updates
Benefits for infrastructure:
- Monitor 20+ running services in real-time
- Track PostgreSQL, Redis, Traefik performance
- Watch backup processes (Backrest/Restic)
- Monitor Jellyfin transcoding load
- Alert on resource issues before they become critical
- Historical data for capacity planning
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added Excalidraw collaborative drawing tool to the Kit toolkit at draw.kit.pivoine.art:
- Virtual whiteboard for sketching hand-drawn diagrams
- Infinite canvas with collaborative features
- Image: excalidraw/excalidraw:latest
- Exposed on port 80 via Traefik with SSL
Configuration:
- Added draw service to kit/compose.yaml
- Added KIT_DRAW_IMAGE and KIT_DRAW_TRAEFIK_HOST to arty.yml
- Health check disabled to prevent false negatives
- Follows same pattern as other Kit tools
- Watchtower enabled for automatic updates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Disabled health check for Units converter service as it was failing
due to IPv6 connection attempts. The service is running correctly
but the health check was trying to connect to [::1]:80 instead of
127.0.0.1:80, causing false negatives.
Similar to Pastel services, disabling the built-in health check.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed port configuration for Units converter service:
- Changed loadbalancer.server.port from 3000 to 80
- Matches the actual nginx port in the units-ui container
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added Units converter to the Kit toolkit at units.kit.pivoine.art:
- Unit conversion tool for length, weight, temperature, etc.
- Image: ghcr.io/valknarness/units-ui:latest
- Exposed on port 3000 via Traefik with SSL
Configuration:
- Added units service to kit/compose.yaml
- Added KIT_UNITS_IMAGE and KIT_UNITS_TRAEFIK_HOST to arty.yml
- Follows same pattern as other Kit tools (Vert, Paint, Pastel, Stirling)
- Watchtower enabled for automatic updates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>