Core Concepts
Before diving into detailed configuration, it’s important to understand how Tenrankai works. This guide covers the fundamental concepts that make Tenrankai unique.
File-Based Philosophy
Tenrankai operates entirely on files and folders - no database required. This means:
- Drop files to publish: Add images to a folder, they appear in your gallery
- Edit files to update: Change a markdown file, your content updates
- Delete files to remove: Remove an image, it’s gone from the gallery
- Version control friendly: Track all changes with git
- Easy backups: Just copy your folders
This approach works perfectly with file synchronization tools like SyncThing, allowing you to edit content on any device and have it automatically deployed.
Directory Structure
A typical Tenrankai site looks like this:
my-gallery/
├── config.toml # Bootstrap configuration (server, email)
├── config.d/ # Site configuration (ConfigStorage)
│ └── sites/
│ └── default/
│ ├── site.toml # Site settings
│ ├── permissions.toml # Roles and access control
│ ├── galleries/
│ │ └── main.toml # Gallery configurations
│ └── posts/
│ ├── blog.toml # Blog configuration
│ └── docs.toml # Documentation configuration
├── users.toml # User database (if authentication enabled)
├── templates/ # Liquid templates
│ ├── pages/ # Full page templates
│ ├── modules/ # Reusable components
│ └── partials/ # Shared fragments
├── static/ # CSS, JavaScript, fonts, images
├── photos/ # Your gallery images
│ ├── 2026/
│ │ ├── vacation/
│ │ │ ├── IMG_001.jpg
│ │ │ ├── IMG_001.jpg.md # Optional metadata
│ │ │ └── _folder.md # Folder description
│ │ └── portraits/
│ └── _folder.md
├── posts/ # Markdown content
│ ├── blog/ # Blog posts
│ └── docs/ # Documentation
└── cache/ # Generated images (auto-created)
Two-Tier Configuration
Tenrankai uses a two-tier configuration system:
- Bootstrap config (
config.toml): Server settings, email, OpenAI - static settings that rarely change - ConfigStorage (
config.d/): Site-specific configuration (galleries, posts, permissions) that can be managed via CLI or Admin UI (recommended for all new installations)
# Use default config.toml
tenrankai serve
# Use a specific config file
tenrankai serve --config production.toml
Quick Setup with CLI
The easiest way to get started is using the CLI:
# Initialize ConfigStorage directory with a default site
tenrankai config init config.d
# Add galleries and posts to your site
tenrankai config add-gallery photos --site default --source photos --url-prefix /gallery
tenrankai config add-posts blog --site default --source posts/blog --url-prefix /blog
Bootstrap Configuration (config.toml)
The bootstrap config contains server-level settings:
[server]
host = "127.0.0.1"
port = 3000
[app]
name = "My Gallery"
cookie_secret = "CHANGE_ME_generate_with_openssl_rand_base64_32"
config_storage = "config.d" # Path to ConfigStorage directory
# Optional: Email for authentication
# [email]
# provider = "null"
# from_address = "noreply@example.com"
Site Configuration (ConfigStorage)
Site-specific configuration lives in config.d/sites/default/:
site.toml - Site settings:
hostnames = ["localhost", "photos.example.com"]
templates = ["templates"]
static_files = ["static"]
base_url = "https://photos.example.com"
# user_database = "users.toml" # Enables authentication
galleries/main.toml - Gallery configuration:
name = "main"
url_prefix = "/gallery"
source_directory = "photos"
cache_directory = "cache/main"
permissions.toml - Roles and access control:
public_role = "viewer"
[roles.viewer]
permissions = { can_view = true }
Server Configuration
The [server] section controls network settings:
[server]
host = "127.0.0.1" # IP address to bind to
port = 3000 # Port number
| Setting | Description | Common Values |
|---|---|---|
host | IP address to listen on | 127.0.0.1 (local only), 0.0.0.0 (all interfaces) |
port | Port number | 3000 (dev), 8080 (behind proxy) |
For production, bind to 127.0.0.1 and use a reverse proxy (nginx, Caddy) for HTTPS.
Application Configuration
The [app] section in the bootstrap config contains global settings:
[app]
name = "My Photo Gallery"
cookie_secret = "CHANGE_ME_generate_with_openssl_rand_base64_32"
config_storage = "config.d" # Path to ConfigStorage directory
| Setting | Required | Description |
|---|---|---|
name | Yes | Site name (appears in titles, emails) |
cookie_secret | Yes | Session signing secret (generate with openssl rand -base64 32) |
log_level | No | trace, debug, info, warn, error (default: info) |
config_storage | No | Path to ConfigStorage directory (recommended) |
Site-specific settings like base_url and user_database are configured in config.d/sites/default/site.toml.
Generating a Cookie Secret
# Generate a secure random secret
openssl rand -base64 32
CLI Configuration Commands
Manage your configuration without editing files:
# Initialize ConfigStorage
tenrankai config init config.d
# List and manage sites
tenrankai config list-sites
tenrankai config add-site photos --hostname photos.example.com
# Manage galleries
tenrankai config list-galleries default
tenrankai config add-gallery portfolio --site default --source portfolio --url-prefix /portfolio
# Manage posts
tenrankai config list-posts default
tenrankai config add-posts blog --site default --source posts/blog --url-prefix /blog
Templates and Static Files
Templates and static files support cascading directories, configured in site.toml:
# config.d/sites/default/site.toml
templates = ["templates-custom", "templates"]
static_files = ["static-custom", "static"]
How cascading works:
- Tenrankai looks for each file in directories left-to-right
- First match wins
- Override specific files without copying everything
This lets you customize individual templates or CSS files while keeping the defaults for everything else.
Galleries
Galleries are the core of Tenrankai. Each gallery is an independent collection of images with its own URL, settings, and permissions. Each gallery is configured as a separate TOML file in config.d/sites/<site>/galleries/:
# config.d/sites/default/galleries/main.toml
name = "main" # Unique identifier
url_prefix = "/gallery" # URL path
source_directory = "photos" # Where images are stored
cache_directory = "cache/main" # Where processed images go
Add multiple galleries by creating additional TOML files:
# Via CLI
tenrankai config add-gallery portfolio --site default --source portfolio --url-prefix /
tenrankai config add-gallery family --site default --source family-photos --url-prefix /family
Or create the files directly:
# config.d/sites/default/galleries/portfolio.toml
name = "portfolio"
url_prefix = "/"
source_directory = "portfolio"
cache_directory = "cache/portfolio"
See Gallery Setup for complete gallery configuration.
Posts (Blog/Documentation)
Posts are markdown files that become pages on your site. Each posts collection is configured as a TOML file in config.d/sites/<site>/posts/:
# config.d/sites/default/posts/blog.toml
name = "blog"
source_directory = "posts/blog"
url_prefix = "/blog"
posts_per_page = 10
refresh_interval_minutes = 30
# config.d/sites/default/posts/docs.toml
name = "docs"
source_directory = "posts/docs"
url_prefix = "/docs"
posts_per_page = 20
Post Format
Posts use TOML frontmatter:
+++
title = "My First Post"
summary = "A brief description"
date = "2026-01-15"
categories = ["Travel", "Photo Gear"] # optional
hero_image = "gallery:main:vacation/beach.jpg" # optional
+++
# My First Post
Content goes here in **markdown** format.
Required fields are title, summary, and date (either YYYY-MM-DD or full
RFC 3339). Posts are sorted by date (newest first) and automatically paginated.
Categories
The optional categories list assigns one or more labels to a post.
Categories appear as clickable chips on the index and on each post, and the
index page grows a filter bar with per-category post counts. Each category
gets its own index page at a URL-safe slug of the label
(/blog/category/photo-gear), and pagination preserves the active filter.
Category pages for publicly visible posts are included in the sitemap.
Category Options
Categories work with zero configuration, but an optional _categories.md
file at the posts source root can declare per-category options (keyed by the
category’s slug):
+++
[categories.archive]
name = "Archive"
description = "Older posts kept for reference"
archive = true
[categories.travel]
weight = 1
+++
name— canonical display label, overriding whatever spelling appears in post frontmatter. Used on chips, filter notes, and feed titles.description— shown under the category page heading, and used as the category feed’s description and the page’s meta description.archive— posts carrying this category are hidden from the unfiltered index and the main RSS feed. They still list on every category page they belong to (the archive category’s page acts as the archive view), remain reachable at their permalinks, and stay in the sitemap. Archiving a post is just adding the label.weight— chip ordering in the filter bar: lower weights sort first, undeclared categories follow alphabetically, and archive categories always render last after a visual separator.
Undeclared categories keep the defaults, so the file only needs entries for categories you want to customize.
RSS Feeds
Every posts system serves RSS 2.0 feeds automatically:
/blog/feed.xml— the 20 most recent posts/blog/category/photo-gear/feed.xml— the same, filtered to one category
Feeds include the full post content, honor the posts permission system (a
restricted post only appears when the feed is fetched with an authorized
session), and are advertised via <link rel="alternate"> tags and a
subscribe link at the bottom of the posts index.
Post pages also emit article Open Graph metadata for rich link previews:
article:published_time, article:modified_time, article:author,
article:section (the first category), and one article:tag per category.
Hero Images
The optional hero_image sets the image shown on the post’s index card and
used for social media previews (og:image). It accepts either a plain URL or
a gallery reference in the form gallery:<gallery-name>:<path/to/image.jpg>.
When omitted, the first image found in the post content is used automatically.
A gallery-reference hero is clickable on the post page, linking through to
the image’s gallery detail page.
Embedding Gallery Images
Post content can embed images from any configured gallery using an image tag
whose alt text is a gallery reference and whose URL is a size hint
(thumbnail, gallery, medium, or large), optionally followed by
comma-separated options:


The image links to its gallery detail page and is served in the requested
size with lazy loading. The details option adds a hover card showing the
image’s description and technical details — camera, lens, exposure, and the
astronomical fields (telescope, filters, integration time, RA/Dec) when
present.
Post authors with can_edit_content don’t need to type references by hand:
the web editor’s Browse… button (hero) and Gallery image toolbar
button (content) open a gallery browser and insert the reference, with size
and details options for embeds.
The Index Page and Sharing
The posts index renders as a flowing card grid: the newest post appears as a full-width featured card, and cards show hero images, category labels, dates, estimated reading time, and summaries. Each post page includes a share bar with native share support, Bluesky, X, Facebook, LinkedIn, Mastodon (your instance is remembered locally), email, and copy-link buttons, plus Open Graph and Twitter Card metadata for rich link previews.
Embedding Posts on Pages
Any static page template can embed a recent-posts summary block, in the same spirit as the embeddable gallery preview:
{% include "_posts_preview.html.liquid" posts_name: "blog", posts_url: "/blog", heading: "Latest Posts" %}
Optional parameters: count (default 3, max 12), category (a category
slug to filter by), and variant: "list" for a super-compact title + date
list instead of summary cards. The block fetches posts client-side so it
respects the viewer’s permissions, excludes archived posts (unless filtered
to an archive category), hides itself when there are no matching posts, and
always renders a “View All Posts” link that works without JavaScript.
The gallery preview partial accepts an optional count parameter as well
(default 6, max 20), and multiple preview blocks of either kind can share a
page.
Image Metadata
Tenrankai reads image metadata from multiple sources:
-
Markdown sidecar files (highest priority)
IMG_001.jpg.mdorIMG_001.md- TOML frontmatter + markdown description
-
XMP sidecar files
IMG_001.jpg.xmp- Adobe Lightroom compatible
-
EXIF data (lowest priority)
- Embedded in the image file
- Camera, lens, GPS, timestamps
Example markdown sidecar (vacation/beach.jpg.md):
+++
title = "Sunset at the Beach"
description = "Golden hour at Santa Monica"
tags = ["sunset", "beach", "california"]
+++
Extended description with **markdown** formatting.
Astrophotography Metadata and Sky Maps
Sidecar frontmatter can carry astronomical fields — telescope, mount,
filters, total_exposure_time (hours), ra, dec, and
additional_details. When an image has both ra and dec (e.g.
ra = "05h 34m 32s", dec = "+22° 00' 52\""), its detail page renders a
Sky Position card: an all-sky chart with the brightest stars and a
crosshair at the target, plus links opening Aladin Lite and SIMBAD at those
coordinates. Coordinates parse from sexagesimal hour or degree forms as well
as plain decimal degrees. Like the rest of the technical metadata, the card
is shown to viewers with can_see_technical_details.
Plate Solving and Object Overlays
With seiza data files configured in the
bootstrap config.toml, astronomical images are plate-solved on demand — an
exact WCS is fitted from the star field itself:
[astro]
star_data = "/var/lib/tenrankai/astro/stars-deep-gaia17.bin" # Required
blind_index = "/var/lib/tenrankai/astro/blind-gaia16.idx" # Blind solving
object_data = "/var/lib/tenrankai/astro/objects.bin" # Overlay objects
Images with ra/dec in their sidecar are solved from that hint; images in a
folder marked astro = true can be blind-solved without any coordinates. Solved
images gain an Objects toggle overlaying labeled deep-sky objects and named
stars, live supernova markers, and comets and asteroids placed at the image’s
capture time. Overlays follow into zoomed views, post embeds, and post heroes.
See the Astrophotography guide for the full setup: catalog files, transient refresh, overlay regeneration, and the API.
Folder Descriptions
Add _folder.md to any folder to provide a title and description:
+++
title = "Summer Vacation 2026"
hidden = false
+++
Photos from our trip to California.
| Setting | Description |
|---|---|
title | Display name for the folder |
hidden | If true, folder is accessible but not listed |
hide_technical_details | If true, hides camera/EXIF info |
[permissions] | Folder-specific access control |
Caching
Tenrankai automatically caches processed images (resized, watermarked, converted). The cache:
- Is created automatically in the configured
cache_directory - Persists across restarts
- Can be pre-generated on startup
- Supports both local filesystem and S3 storage
Cache Queue
Control background cache generation with the [cache_queue] section in config.toml:
[cache_queue]
enabled = false # Enable background cache processing (default: false)
buffer_size = 1000 # Number of items to buffer in the queue (default: 1000)
concurrency = 4 # Number of concurrent cache generation tasks (default: number of CPU cores)
When enabled, cache entries are generated in the background as images are requested, using the configured concurrency level to balance performance with system resources.
Cache CLI
You generally don’t need to manage the cache manually, but CLI commands are available:
# View cache coverage report
tenrankai cache report -g photos
# Clean outdated cache files
tenrankai cache cleanup -g photos
Admin UI
Tenrankai includes a built-in administration interface at /_admin/ for managing your site without editing configuration files.
Features:
- User Management: Create, delete, and invite users
- Gallery Viewer: See gallery configurations and permissions
- Role Viewer: View built-in roles and their permissions
- Site Configuration: Manage site settings via API
Access Requirements:
- Must be authenticated
- Must have
owner_accesspermission in any gallery
The Admin UI is a React-based single-page application that communicates with the Admin API.
Next Steps
Now that you understand the basics:
- Gallery Setup - Configure image processing, sizes, and formats
- Authentication - Set up user accounts and login
- Permissions - Control who can see what
- Advanced Features - Admin UI, S3 storage, multi-site hosting
- Astrophotography - Plate solving and object overlays