Configuration Guide
Tenrankai is highly configurable, allowing you to tailor it to your specific needs. This guide covers all configuration options including role-based permissions, metadata storage, image protection features, and more.
Configuration File
Tenrankai uses TOML format for configuration. By default, it looks for config.toml in the current directory, but you can specify a different file using the --config flag.
Server Configuration
The [server] section controls how Tenrankai listens for connections:
[server]
host = "127.0.0.1" # IP address to bind to
port = 3000 # Port number
- host: Use
"127.0.0.1"for local-only access,"0.0.0.0"to listen on all interfaces - port: Any available port number (default: 3000)
Application Configuration
The [app] section contains general application settings:
[app]
name = "My Gallery"
log_level = "info" # Options: trace, debug, info, warn, error
cookie_secret = "change-me-in-production-use-long-random-string"
base_url = "https://yourdomain.com"
# Optional: Enable authentication
# user_database = "users.toml"
- name: Display name for your gallery
- log_level: Logging verbosity (trace, debug, info, warn, error)
- cookie_secret: Secret key for signing session cookies (change in production!)
- base_url: Full URL of your site (used for OpenGraph meta tags and login emails)
- user_database: Optional path to user database file for authentication
Template and Static Files Configuration
The [templates] and [static_files] sections support cascading directories for easy customization:
[templates]
# Single directory (backward compatible)
directory = "templates"
# Or cascading directories (files in first directory override later ones)
directories = ["templates-custom", "templates"]
[static_files]
# Single directory (backward compatible)
directory = "static"
# Or cascading directories for customization
directories = ["static-custom", "static"]
- templates.directory/directories: Path(s) to your Liquid template files
- static_files.directory/directories: Path(s) to static assets (CSS, JavaScript, fonts, images)
- Files in earlier directories take precedence over files in later directories
- Perfect for customizing themes without modifying core files
Gallery Configuration
Tenrankai supports multiple independent galleries. Each gallery is configured in a [[galleries]] section.
Basic Gallery Settings
[[galleries]]
name = "main" # Unique identifier
url_prefix = "/gallery" # URL path prefix
source_directory = "/path/to/photos" # Where your photos are stored
cache_directory = "cache/main" # Where to store processed images
images_per_page = 50 # Pagination setting
new_threshold_days = 7 # Mark images as "NEW" if modified within X days
cache_refresh_interval_minutes = 60 # Auto-refresh interval
jpeg_quality = 85 # JPEG compression (1-100)
webp_quality = 85.0 # WebP compression (0.0-100.0)
copyright_holder = "Your Name" # Per-gallery copyright watermark
image_indexing = "filename" # URL format: "filename", "sequence", or "unique_id"
# Custom templates (optional)
gallery_template = "modules/gallery.html.liquid"
image_detail_template = "modules/image_detail.html.liquid"
# Pre-generation configuration (optional)
# [galleries.pregenerate]
# formats = { jpeg = true, webp = true, avif = false }
# sizes = { thumbnail = true, gallery = true, medium = true, large = false }
# tiles = false
# Tile configuration for enhanced zoom (optional)
# [galleries.tiles]
# tile_size = 1024
Role-Based Permissions (RBAC)
Tenrankai uses a role-based permission system that provides fine-grained control over gallery access.
Understanding the Permission System
The permission system is configured within each gallery’s [galleries.permissions] section:
[galleries.permissions]
# Role assigned to unauthenticated visitors
public_role = "viewer"
# Role assigned to authenticated users by default
default_authenticated_role = "member"
# Define custom roles with specific permissions
[galleries.permissions.roles.viewer]
name = "Viewer"
permissions = { can_view = true }
[galleries.permissions.roles.member]
name = "Member"
permissions = {
can_view = true,
can_see_exact_dates = true,
can_see_location = true
}
# Assign specific users to custom roles
[[galleries.permissions.user_roles]]
username = "photographer"
roles = ["admin"]
[[galleries.permissions.user_roles]]
username = "client"
roles = ["member"]
Available Permissions
Viewing Permissions:
- can_view - View images in the gallery (includes thumbnail and gallery size downloads)
Privacy Permissions:
- can_see_exact_dates - See exact capture dates (vs approximate month/year)
- can_see_location - See GPS coordinates and maps
- can_see_technical_details - See camera, lens, and EXIF data
Download Permissions:
- can_download_medium - Download medium resolution images
- can_download_large - Download large resolution images
- can_download_original - Download original files
Note: Thumbnail and gallery size downloads are automatically included with the can_view permission.
Interactive Permissions:
- can_use_zoom - Basic click-to-zoom loupe (uses medium image at 1.8x scale)
- can_use_tile_zoom - Enhanced high-resolution zoom using tiles (requires tiles config)
- can_read_metadata - Read user-generated content (comments, picks, tags)
Content Management:
- can_add_comments - Add comments to images
- can_edit_own_comments - Edit your own comments
- can_delete_own_comments - Delete your own comments
- can_set_picks - Mark images as picks/favorites
- can_add_tags - Add tags to images
Moderation:
- can_edit_any_comments - Edit any user’s comments (admin)
- can_delete_any_comments - Delete any user’s comments (admin)
Special:
- owner_access - Full access to everything (overrides all other permissions)
Permission Examples
Example 1: Public Portfolio
[[galleries]]
name = "portfolio"
copyright_holder = "Professional Photographer Inc."
[galleries.permissions]
public_role = "viewer"
default_authenticated_role = "viewer"
[galleries.permissions.roles.viewer]
name = "Viewer"
# Show camera info for credibility
# Allow detailed image viewing
# No location or exact dates for privacy
# No high-res downloads without permission
permissions = {
can_view = true,
can_see_technical_details = true,
can_use_zoom = true
}
Example 2: Family Gallery with Privacy
[[galleries]]
name = "family"
copyright_holder = "The Smith Family"
[galleries.permissions]
public_role = "limited"
default_authenticated_role = "limited"
# Public sees limited info
[galleries.permissions.roles.limited]
name = "Limited Viewer"
# Approximate dates only (shows "October 2026" instead of exact date)
permissions = {
can_view = true
}
# Family members see everything
[galleries.permissions.roles.family_member]
name = "Family Member"
permissions = {
can_view = true,
can_see_exact_dates = true,
can_see_location = true,
can_see_technical_details = true,
can_download_medium = true,
can_download_large = true,
can_download_original = true,
can_use_zoom = true,
can_read_metadata = true,
can_add_comments = true,
can_edit_own_comments = true,
can_delete_own_comments = true,
can_set_picks = true,
can_add_tags = true
}
# Assign family members to their role
[[galleries.permissions.user_roles]]
username = "parent"
roles = ["family_member"]
Metadata Storage System
When users have appropriate permissions, they can add metadata (comments, picks, highlights, tags) to images. This metadata is stored in .toml sidecar files alongside the images:
# Example: IMG_1234.jpg.toml (created automatically)
[picks]
"user@example.com" = true
[highlights]
"user@example.com" = true
[tags]
"user@example.com" = ["sunset", "landscape", "california"]
[[comments]]
user = "user@example.com"
comment = "Beautiful sunset!"
timestamp = "2026-01-09T10:30:00Z"
# Comments can include area selections
[[comments]]
user = "photographer@example.com"
comment = "Great detail in the clouds here"
timestamp = "2026-01-09T11:45:00Z"
[comments.image_area]
x = 25.5 # Percentage from left
y = 10.2 # Percentage from top
width = 30.0 # Percentage width
height = 25.0 # Percentage height
The gallery view shows badges for images with metadata:
- ✓ (checkmark) - Image marked as pick
- ⭐ (star) - Image highlighted
- 💬 (speech bubble) - Has comments
- 🏷️ (tag) - Has tags
Image Area Comments
Users can now select specific areas of an image when adding comments:
- Visual Selection: Click and drag to select rectangular areas
- Touch Support: Works on mobile devices with touch gestures
- Edit Support: Areas can be added, changed, or removed when editing comments
- Visual Feedback: Selected areas are highlighted when viewing comments
- Percentage-Based: Areas use percentage coordinates for responsive display
This feature is perfect for:
- Client Feedback: Point out specific details needing adjustment
- Collaborative Review: Discuss particular elements in an image
- Education: Highlight technical aspects or composition elements
- Quality Control: Mark areas with issues or exceptional quality
Gallery Filter Bar
Users with can_read_metadata permission can filter galleries by metadata type:
- Picks (✓) - Show only images marked as picks
- Rejects (✗) - Show images marked as rejects
- Highlights (⭐) - Show highlighted images
- Comments (💬) - Show images with comments
Filter selections persist in the URL for easy sharing: /gallery?filter=picks,comments
Folder-Level Permissions
You can override gallery permissions for specific folders by creating a _folder.md file within that folder:
Example: Hide a folder from public view
Create photos/private/_folder.md:
+++
title = "Private Photos"
[permissions]
# Remove all public access to this folder
public_role = "none"
# Only specific users can access
default_authenticated_role = "family"
[permissions.roles.family]
name = "Family"
permissions = {
can_view = true,
can_see_exact_dates = true,
can_download_original = true
}
+++
These photos are only visible to family members.
Example: Limited access folder
Create photos/preview/_folder.md:
+++
title = "Client Preview"
[permissions]
public_role = "preview_only"
[permissions.roles.preview_only]
name = "Preview Only"
# No downloads or folder browsing allowed
permissions = { can_view = true }
+++
Preview images for client approval.
Image Size Configuration
Each gallery can define custom image sizes:
[galleries.thumbnail]
width = 300
height = 300
[galleries.gallery_size]
width = 800
height = 800
[galleries.medium]
width = 1200
height = 1200
[galleries.large]
width = 1600
height = 1600
Gallery Preview Configuration
Control how gallery previews are generated:
[galleries.preview]
max_images = 6 # Number of images to show
max_depth = 3 # How deep to traverse folders
max_per_folder = 2 # Max images from each folder
Authentication Configuration
Email Provider Configuration
Tenrankai supports multiple email providers for authentication:
[email]
provider = "ses" # Options: "ses", "null"
from_address = "noreply@yourdomain.com"
from_name = "Your Gallery" # Optional
reply_to = "support@yourdomain.com" # Optional
# Amazon SES Configuration (for production)
# The AWS SDK uses a credential provider chain:
# 1. Explicit credentials in config (access_key_id, secret_access_key)
# 2. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
# 3. AWS credentials file (~/.aws/credentials)
# 4. IAM role credentials (EC2, ECS, Lambda)
region = "us-east-1" # Optional, defaults to AWS SDK default region
# access_key_id = "your-key" # Optional - use IAM roles instead
# secret_access_key = "your-secret" # Optional - use IAM roles instead
# OR Null Provider (for development - logs emails instead of sending)
# provider = "null"
WebAuthn/Passkey Configuration
WebAuthn/Passkey support is automatically enabled when authentication is configured. No separate configuration is needed:
[app]
name = "My Photo Gallery" # Used as the WebAuthn RP name
base_url = "https://yourdomain.com" # Required - hostname used as RP ID
user_database = "users.db" # Enables authentication and WebAuthn
# That's it! WebAuthn is now enabled with:
# - RP ID: yourdomain.com (from base_url hostname)
# - RP Name: "My Photo Gallery" (from app.name)
# - Origin: https://yourdomain.com (from base_url)
User Management
Create and manage users with the CLI:
# Add a new user
tenrankai user add john.doe@example.com --display-name "John Doe"
# List all users
tenrankai user list
# Update user display name
tenrankai user update john.doe@example.com --display-name "John Smith"
# Remove a user
tenrankai user remove john.doe@example.com
Users can:
- Manage their passkeys at
/_login/profile - Add multiple passkeys for different devices
- Use passwordless WebAuthn authentication
Posts Configuration
Configure multiple blog/documentation systems:
[[posts]]
name = "blog"
source_directory = "posts/blog"
url_prefix = "/blog"
posts_per_page = 10
refresh_interval_minutes = 30
# Custom templates (optional)
index_template = "modules/posts_index.html.liquid"
post_template = "modules/post_detail.html.liquid"
[[posts]]
name = "docs"
source_directory = "posts/docs"
url_prefix = "/docs"
posts_per_page = 20
refresh_interval_minutes = 60
Advanced Features
Enhanced Metadata Sources
Tenrankai reads metadata from multiple sources with the following priority:
-
Markdown files (
IMAGE.jpg.mdorIMAGE.md)+++ title = "Sunset at Big Sur" description = "Golden hour magic" tags = ["sunset", "landscape"] # Astronomy-specific fields telescope = "Celestron NexStar 8SE" mount = "EQ6-R Pro" filters = "Ha, OIII, SII" total_exposure_hours = 12.5 ra = "05:34:31.94" dec = "+22:00:52.2" additional_details = "7nm narrowband filters, Bortle 3 site" +++ Extended description in markdown format... -
XMP sidecar files (
IMAGE.jpg.xmp)- Automatically parsed for standard metadata
- Supports Adobe Lightroom and other XMP-compatible tools
-
EXIF data (embedded in image)
- Camera settings, GPS, timestamps
- Lowest priority, can be overridden
This allows you to:
- Override incorrect EXIF data
- Add rich descriptions and context
- Support specialized workflows (e.g., astrophotography)
- Maintain metadata separate from image files
Image Indexing Modes
Control how images are referenced in URLs:
-
filename (default):
/gallery/image/vacation/IMG_1234.jpg- SEO-friendly, predictable URLs
- Exposes file naming patterns
-
sequence:
/gallery/image/vacation/1- Clean, ordered URLs
- Good for portfolios and stories
-
unique_id:
/gallery/image/vacation/a3k2x- Maximum privacy, prevents URL guessing
- Best for client galleries
Zoom Features
Tenrankai offers different zoom experiences for desktop and mobile, controlled by permissions:
Desktop: Click-to-Zoom Loupe (can_use_zoom):
- Click and hold to activate magnifying loupe
- Uses medium-sized image at 1.8x scale
- Custom cursor indicates zoom availability
- Image protection via CSS background-image
Desktop: Tile-Based Loupe (can_use_tile_zoom):
- Enhanced loupe using high-resolution tiles
- Full detail inspection for large images
- Requires
[galleries.tiles]configuration
Mobile: Pinch-to-Zoom (can_use_zoom):
- Native pinch gesture to zoom in/out
- Double-tap to quick zoom at tap location
- Pan gesture to navigate when zoomed
- Fullscreen modal with zoom level indicator
- Automatically loads high-res tiles when zoom > 1.5x
- Swipe navigation disabled while zoomed
Both platforms feature smooth animations and image protection.
Hide Technical Details
Control visibility of camera and technical information per folder:
# In _folder.md
+++
title = "Client Portfolio"
hide_technical_details = true # Hides camera, lens, EXIF data
+++
When enabled:
- Removes camera make/model, lens information
- Hides technical settings (ISO, aperture, shutter speed)
- Removes GPS coordinates
- Keeps title, description, and navigation
- Perfect for professional portfolios
Cache Pre-Generation
Configure which image variants to pre-generate on server startup:
[galleries.pregenerate]
# Which formats to pre-generate
formats = { jpeg = true, webp = true, avif = false }
# Which sizes to pre-generate
sizes = { thumbnail = true, gallery = true, medium = true, large = false }
# Whether to pre-generate tiles (requires tiles config)
tiles = false
Pre-generation benefits:
- Parallel processing: Uses all CPU cores for faster generation
- Memory-safe: Limits concurrent operations to prevent memory exhaustion
- Incremental: Only generates missing cache entries
- Cancellation support: Graceful shutdown on Ctrl+C
- Progress logging: See detailed progress in server logs
Tile-Based High-Resolution Zoom
Enable high-resolution tiled zoom for detailed image exploration:
[galleries.tiles]
tile_size = 1024 # Size of each tile in pixels
[galleries.permissions.roles.contributor]
permissions = {
can_use_zoom = true, # Basic zoom (medium image)
can_use_tile_zoom = true # Enhanced tile-based zoom
}
To pre-generate tiles, enable in the pregenerate section:
[galleries.pregenerate]
tiles = true # Requires [galleries.tiles] to be configured
Cache Management CLI
Tenrankai provides CLI commands for cache management:
# Generate a format coverage report for a gallery
tenrankai cache report -g photos
# Clean up outdated cache files
tenrankai cache cleanup -g photos
The cache report shows which image sizes and formats have been generated, helping you verify pre-generation coverage.
Cascading Directories
Override default assets without modifying core files:
[static_files]
# Files in first directory override later ones
directories = ["static-custom", "static-theme", "static"]
[templates]
directories = ["templates-brand", "templates"]
Migration Guide
From Old Permission System
Old configuration:
approximate_dates_for_public = true
hide_location_from_public = true
hide_technical_details = true # In _folder.md
New configuration:
[galleries.permissions]
public_role = "viewer"
[galleries.permissions.roles.viewer]
name = "Viewer"
# Note: No can_see_exact_dates, can_see_location, or can_see_technical_details
permissions = {
can_view = true
}
Copyright Holder
Old: Global in [app] section
New: Per-gallery in each [[galleries]] section
[[galleries]]
name = "main"
copyright_holder = "Your Name" # Now here instead of [app]
Complete Example
Here’s a comprehensive configuration showing all features:
[server]
host = "0.0.0.0"
port = 8080
[app]
name = "Professional Photography"
log_level = "info"
cookie_secret = "use-openssl-rand-base64-32-output-here"
base_url = "https://photos.example.com"
user_database = "users.toml"
[templates]
directories = ["templates-custom", "templates"]
[static_files]
directories = ["static-custom", "static"]
[email]
provider = "ses" # Use Amazon SES for production
from_address = "noreply@photos.example.com"
from_name = "Photo Gallery"
region = "us-east-1"
# Uses AWS credential chain for authentication
# WebAuthn is automatically configured based on app settings
# Public portfolio
[[galleries]]
name = "portfolio"
url_prefix = "/"
source_directory = "/srv/photos/portfolio"
cache_directory = "/srv/cache/portfolio"
copyright_holder = "Jane Photographer"
image_indexing = "sequence"
pregenerate_cache = true
[galleries.permissions]
public_role = "viewer"
default_authenticated_role = "viewer"
[galleries.permissions.roles.viewer]
name = "Viewer"
permissions = {
can_view = true,
can_see_technical_details = true,
can_use_zoom = true
}
# Client gallery with restricted access
[[galleries]]
name = "clients"
url_prefix = "/clients"
source_directory = "/srv/photos/clients"
cache_directory = "/srv/cache/clients"
copyright_holder = "Studio Name"
image_indexing = "unique_id" # Privacy
[galleries.permissions]
public_role = "preview"
default_authenticated_role = "preview"
# Very limited public access
[galleries.permissions.roles.preview]
name = "Preview"
permissions = { can_view = true }
# Client role with download rights
[galleries.permissions.roles.client]
name = "Client"
permissions = {
can_view = true,
can_download_medium = true,
can_download_original = true,
can_use_zoom = true,
can_read_metadata = true, # See comments and feedback
can_add_comments = true # Leave feedback on images
}
# Assign specific clients
[[galleries.permissions.user_roles]]
username = "client"
roles = ["client"]
# Note: Folder-specific permissions are configured via _folder.md files
# within the gallery directories, not in config.toml
Best Practices
-
Security:
- Use strong, random cookie secrets
- Enable HTTPS in production
- Use
unique_idindexing for sensitive galleries - Carefully consider each permission
-
Privacy:
- Omit
can_see_exact_datesfor public family photos - Omit
can_see_locationto protect home addresses - Use folder-level permissions to hide sensitive content
- Enable
hide_technical_detailsfor client portfolios
- Omit
-
Performance:
- Enable
pregenerate_cachefor stable galleries - Use appropriate JPEG/WebP quality settings
- Set appropriate cache refresh intervals
- React frontend builds automatically, no configuration needed
- Enable
-
User Experience:
- Enable WebAuthn for easy passwordless login
- Use role inheritance to avoid repetition
- Provide appropriate download sizes for each audience
- Enable
can_use_zoomfor detailed image exploration - Use metadata features for collaborative workflows
Troubleshooting
Common issues:
- “Permission denied” errors: Check role assignments and inheritance
- Missing images: Verify folder permissions allow
can_view - Dates showing incorrectly: Check
can_see_exact_datespermission - Downloads failing: Ensure appropriate download permissions are set
Next Steps
- Deployment Guide - Production deployment best practices
- Template Customization - Customize appearance
- API Documentation - REST API reference