Tessera
Tessera is a command-line and web-based file tagging tool backed by SQLite. It lets you attach arbitrary tags — including typed key=value pairs — to any file on your filesystem, then query those tags to find, organise, or generate views of your collection.
What Tessera does
- Tag anything — images, videos, documents, archives, music, or any other file. Tags live in a small SQLite database (
.tessera/db.sqlite3) alongside your files, not in the files themselves. - Key=value tags — a tag is either a plain word (
genre) or a typed pair (year=2024,rating=5). Values support numeric comparisons in queries. - Query language — find files with boolean expressions:
genre/rock and year>=2020 and not live. - Symlink views — materialise a query result as a folder of symlinks, ready to hand to any other tool.
- Linked databases — split a large collection across multiple directories, each with its own database, and query them together.
- Web interface — a local web app with a file browser, tag editor, thumbnail previews, AI-assisted tagging, and semantic search.
- Cross-platform — macOS and Linux.
About this guide
This guide covers everyday use of Tessera: the CLI, the web interface, and the reference material you need to get the most out of the query language and settings.
Architecture and internal design are documented separately in the filetag-blueprint repository.
Installation
Prerequisites
- Rust toolchain — install via rustup (stable, edition 2024).
- SQLite — bundled; no system library needed.
- For the web interface: ffmpeg and ffprobe (for video thumbnails and trickplay sprites).
- Optional: ffsubsync on
$PATHfor automatic subtitle/audio synchronization. The container images include it.
Tessera searches $PATH, ~/.local/bin, ~/bin, Homebrew's standard
directories and the usual system binary directories. This also works when the
web server is started by a service manager with a minimal $PATH. Add custom
locations with the platform-separated TESSERA_TOOL_PATH environment variable.
From source
git clone <tessera repository URL>
cd tessera
# CLI only
cargo install --path tessera-cli
# Web interface
cargo install --path tessera-web
Both binaries end up in ~/.cargo/bin/. Make sure that directory is on your $PATH.
Verifying the installation
tessera --version
tessera-web --version
Updating
cd tessera
git pull
cargo install --path tessera-cli
cargo install --path tessera-web
Quick start
This page walks you through tagging your first files and finding them again in under five minutes.
1. Initialise a database
Go to the root of a directory tree you want to tag — your music folder, photos, or any other collection:
cd ~/Music
tessera init
This creates .tessera/db.sqlite3 in the current directory. Every file you tag must live somewhere inside this directory (or a subdirectory).
2. Tag some files
# Tag a single file
tessera tag song.flac -t lossless
# Tag with a key=value pair
tessera tag song.flac -t year=2024
# Multiple tags at once
tessera tag song.flac -t lossless,genre/rock,year=2024
# Tag a whole directory recursively
tessera tag -r Albums/Kraftwerk -t genre/electronic,year=1974
# Tag files piped from another command
fd -e flac | tessera tag -t lossless
3. List tags on a file
tessera tags song.flac
4. Find files by tag
# Simple tag query
tessera find lossless
# Combine tags
tessera find "lossless and genre/rock"
# Key=value comparison
tessera find "year>=2020 and genre/electronic"
Results are printed one path per line, ready for piping:
tessera find lossless | xargs -I{} cp {} ~/export/
5. Open the web interface
tessera-web ~/Music
Open http://localhost:4141 in your browser to browse, search, and tag files visually.
Next steps
- Tagging files — all tag operations in detail
- Query syntax — the full query language
- Linked databases — split large collections across multiple roots
Initialising a database
A tessera database is a single SQLite file stored at .tessera/db.sqlite3 inside the root directory of a collection. All file paths are stored relative to that root, so the database is portable: move the whole directory and everything still works.
Creating a database
cd /path/to/your/collection
tessera init
This creates .tessera/db.sqlite3 in the current directory. You only need one database per directory tree; files in subdirectories are automatically within scope.
Registering in the global registry
If you have several collections and want to query them all at once with --all-dbs, register the database:
tessera init --register
# or, after the fact:
tessera db register
The registry is stored at ~/.config/tessera/databases.json.
Nested collections
You can have a database inside a directory that already has a parent database. tessera will find the deepest database root for any given file and use that one. Queries on the parent automatically include child databases — see Linked databases for details.
The .tessera/ directory
Everything tessera writes lives here:
| Path | Contents |
|---|---|
.tessera/db.sqlite3 | The main database |
.tessera/cache/thumbs/ | Thumbnail cache (web interface) |
.tessera/cache/vthumbs/ | Trickplay sprite sheets (video) |
.tessera/cache/ai_sprites/ | AI analysis sprite sheets |
.tessera/cache/transcodes/ | Transcoded video for streaming |
.tessera/logs/tessera.log | Current tessera-web application log |
.tessera/logs/tessera.log.1 | Previous log after rotation |
tessera never writes outside .tessera/.
Tagging files
Basic tagging
tessera tag FILE... -t TAG[,TAG,...]
Tags are case-sensitive strings. A tag can be:
- A plain word:
lossless,favourite - A hierarchical path using
/as separator:genre/rock,status/todo - A key=value pair:
year=2024,rating=5,artist=Kraftwerk
Multiple tags can be given as a comma-separated list or by repeating -t:
tessera tag song.flac -t lossless,year=2024
tessera tag song.flac -t lossless -t year=2024
Recursive tagging
Tag all files in a directory (and its subdirectories):
tessera tag -r Albums/Kraftwerk -t genre/electronic
Tagging from stdin
tessera accepts file paths from standard input, one per line. This lets you combine it with find, fd, fzf, and similar tools:
fd -e flac | tessera tag -t lossless
find . -name "*.jpg" -newer reference.jpg | tessera tag -t new
Use -0 to read NUL-delimited paths (safer for filenames containing newlines or spaces):
fd -0 -e mp4 | tessera tag -0 -t video
Listing tags
# Tags on specific files
tessera tags FILE...
# All tags in the database
tessera tags
# JSON output
tessera tags --json FILE
The tags command is also available as tessera ls.
Removing tags
tessera untag FILE... -t TAG[,TAG,...]
Viewing full file info
tessera show FILE
Shows all tags, the file's database record, size, modification time, and file identity.
Renaming a tag
Renames the tag everywhere in the database:
tessera mv OLD_TAG NEW_TAG
Merging tags
Merge all occurrences of SOURCE into TARGET (destructive — prompts for confirmation):
tessera merge SOURCE TARGET
tessera merge SOURCE TARGET --force # skip confirmation
Global options
| Option | Description |
|---|---|
--json | JSON Lines output |
--color auto|always|never | Colour mode |
-q, --quiet | Suppress informational messages, and log only errors |
-v, --verbose | Extra detail, including debug-level log records |
--log-level SPEC | Log verbosity, optionally per target; beats TESSERA_LOG, -v and -q |
--db PATH | Override the database location |
--no-parents | Do not include ancestor databases in queries |
Log records go to stderr, never to stdout, so --json output stays pipeable.
The command writes no log file: it has no lifetime worth a forensic trail, and
tessera-web is the process that keeps one. The default level is warn, so an
ordinary invocation says nothing beyond its own output; --log-level accepts
the same per-target specs as the server, such as
info,tessera::embedding=error.
Querying and finding files
Basic usage
tessera find QUERY
Returns one file path per line. The command is also available as tessera f.
For the full query language reference, see Query syntax.
Examples
# Files with a single tag
tessera find lossless
# Boolean AND
tessera find "lossless and genre/rock"
# Boolean OR
tessera find "genre/jazz or genre/blues"
# Boolean NOT
tessera find "lossless and not live"
# Key=value exact match
tessera find "artist=Kraftwerk"
# Numeric comparison
tessera find "year>=2020"
tessera find "rating>3 and rating<=5"
# Glob on a tag namespace
tessera find "genre/*"
# File type filter
tessera find "type:image and landscape"
tessera find "type:video and year=2024"
# Combined
tessera find "(genre/rock or genre/metal) and year>=2000 and not live"
Output options
# With tags listed after each path
tessera find QUERY --with-tags
# Count only — no paths printed
tessera find QUERY --count
# NUL-delimited output (safe for xargs -0)
tessera find QUERY -0
# JSON Lines
tessera find QUERY --json
Scope options
By default, a query runs against the current database and any linked child databases and ancestor databases. To change this:
# Current database only
tessera find QUERY --isolated
tessera find QUERY -i
# All globally registered databases
tessera find QUERY --all-dbs
Piping results
# Copy matching files
tessera find "genre/rock" | xargs -I{} cp {} ~/export/
# Open in mpv
tessera find "type:video and year=2024" -0 | xargs -0 mpv
# Pass to another command safely
tessera find QUERY -0 | xargs -0 some-command
Symlink views
A view is a directory full of symlinks — one per matching file — generated from a query. Views make it easy to hand a tag-filtered selection to tools that don't know about tessera: media players, editors, import dialogs, etc.
Generating a view
# Default output: _.tags/ in the current directory
tessera view QUERY
# Custom output directory
tessera view QUERY -o /tmp/my-view
The symlinks use relative targets so the view directory is portable as long as it stays on the same filesystem.
Link naming
A file at Music/Rock/song.flac becomes a symlink named Music__Rock__song.flac (path separators replaced with __). This keeps all links flat in the output directory while preserving the original path as the name.
Long filenames are truncated at 255 bytes while preserving the file extension.
Keeping views clean
tessera automatically removes broken symlinks (pointing to files that no longer exist) and empty subdirectories from the output directory before generating new links.
Example workflow
# Build a playlist view for all lossless rock tracks
tessera view "lossless and genre/rock" -o ~/playlists/rock-lossless
# Open the whole view in a media player
mpv ~/playlists/rock-lossless/
# Refresh after tagging more files
tessera view "lossless and genre/rock" -o ~/playlists/rock-lossless
Database status and repair
Status
Show which files are missing (moved or deleted) and which are untagged:
tessera status
tessera status PATH # limit to a subdirectory
Output includes:
- Missing — files in the database whose path no longer exists on disk.
- Untagged — files on disk that are not in the database.
Repair
The repair command finds files that have been moved (or renamed) and updates the database records to match their new location. It uses the platform file identity (inode + device on Unix) and, as a fallback, name + size matching.
# Preview what would change
tessera repair --dry-run
# Apply changes
tessera repair
# Limit to a subdirectory
tessera repair path/to/subdir
How matching works
- File identity (inode) — exact match regardless of path. Works for simple moves within the same filesystem.
- Name + size fallback — if the inode is unavailable or has changed (e.g. copy + delete across filesystems), tessera matches by filename and file size.
When multiple candidates match a file, repair reports a conflict and skips that record. Use tessera show to investigate and tessera mv / manual SQL to resolve.
Linked databases
tessera supports multiple databases that know about each other. This is useful when a large collection is spread across several drives, or when you want independent databases per sub-collection but still want to query them together.
Concepts
- Root database — the database you run tessera commands from.
- Linked (child) database — a database registered as a child of the root.
- Ancestor database — a database whose root is a parent directory of the current root. tessera finds these automatically by walking up the directory tree.
By default, queries include the root database, all linked children, and all ancestors.
Managing linked databases
# List linked databases
tessera db ls
# Add a child database
tessera db add /path/to/child/collection
# Remove a child database
tessera db remove /path/to/child/collection
# Remove dead registrations (paths that no longer exist)
tessera db prune
Global registry
For collections that are not in parent/child directory relationships, use the global registry:
# Register the current database globally
tessera db register
# Unregister
tessera db unregister
# List all globally registered databases
tessera db registered
Query scope
# Default: current DB + children + ancestors
tessera find QUERY
# Current database only
tessera find QUERY --isolated
# All globally registered databases
tessera find QUERY --all-dbs
# Exclude ancestor databases
tessera find QUERY --no-parents
Pushing and pulling tags
Copy tag records between databases when files exist in both:
# Copy tags from root → child (for files the child knows about)
tessera db push /path/to/child --dry-run
tessera db push /path/to/child
# Copy tags from child → root
tessera db pull /path/to/child --dry-run
tessera db pull /path/to/child
Push/pull match files by their file identity (inode), so they work even if relative paths differ between the two databases.
Shell completions
tessera can generate completion scripts for Bash, Zsh, and Fish.
Bash
tessera completions bash > ~/.local/share/bash-completion/completions/tessera
Or for a system-wide install:
tessera completions bash | sudo tee /etc/bash_completion.d/tessera
Zsh
tessera completions zsh > ~/.zfunc/_tessera
Make sure ~/.zfunc is in your $fpath (add to ~/.zshrc if needed):
fpath=(~/.zfunc $fpath)
autoload -Uz compinit && compinit
Fish
tessera completions fish > ~/.config/fish/completions/tessera.fish
Getting started with the web interface
tessera-web is a local web application that provides a visual file browser, tag editor, search bar, and media previews on top of the same database that the CLI uses.
Starting the server
# Serve the collection in the current directory
tessera-web
# Serve a specific path
tessera-web ~/Photos
# Custom port and bind address
tessera-web --port 8080 ~/Photos
# Include parent databases in queries
tessera-web ~/Photos # parents are included by default
tessera-web --no-parents ~/Photos
Open http://localhost:4141 in your browser.
Authentication
When binding to localhost (the default), no password is required.
When binding to a non-loopback address (e.g. --bind 0.0.0.0), a random password is automatically generated and printed to stderr. You must supply it on the login page.
To set a specific password:
tessera-web --password mysecretpassword ~/Photos
# or via environment variable:
TESSERA_PASSWORD=mysecretpassword tessera-web ~/Photos
# or read from a file:
tessera-web --password-file /run/secrets/tessera ~/Photos
To generate a random password explicitly:
tessera-web -P ~/Photos # prints password to stderr
To disable authentication entirely (e.g. behind a reverse proxy that handles auth):
tessera-web --no-auth --bind 0.0.0.0 ~/Photos
Logging
Two independent settings decide what happens to a log record: how much is logged, and where it goes. The startup banner prints the resolved answer to both, so you never have to guess:
Logs: /home/you/Photos/.tessera/logs/tessera.log (info)
How much
--log-level sets the verbosity to off, error, warn, info, debug, or
trace; the default is info. TESSERA_LOG sets the same thing for
environments where a flag is awkward, and the flag wins when both are given.
Either accepts per-target overrides, so one noisy subsystem can be quietened without dimming the rest:
tessera-web --log-level info,tessera::embedding=error ~/Photos
A target matches on its module-path prefix, longest match first, so
tessera::embedding=error also covers tessera::embedding::batch.
Where
The server writes its application log to
<primary-root>/.tessera/logs/tessera.log. In a multi-database session this is
the single process-wide log; entries concerning another database include that
database's root path. The log rotates at 10 MiB and keeps one previous file as
tessera.log.1. Use --log-file <path> to write elsewhere, or --log-file none to write no file at all — useful in a container, where the root may be a
read-only mount and the collector wants the stream instead.
--log-stderr decides whether records are also streamed to the terminal:
| Value | Behaviour |
|---|---|
auto (default) | Stream only when stderr is not a terminal — a container, a systemd unit, a pipe. An interactive session stays quiet. |
always | Stream regardless, for watching a run as it happens. |
never | Never stream; the log file is the only sink. |
Records never go to stdout: that stream carries program output, and a stray log
line there would corrupt a --json pipeline.
A container therefore usually wants:
tessera-web --log-file none --log-level info ~/Photos # stderr only, collected by the runtime
and someone debugging locally wants:
tessera-web --log-stderr always --log-level debug ~/Photos
Panics and graceful shutdown signals (SIGINT, SIGTERM, SIGHUP, and
SIGQUIT) are recorded with their reason. SIGKILL cannot be caught by any
application. Tessera therefore keeps a run marker and reports an unclean prior
shutdown on the next start. For the exact cause of a SIGKILL—for example an
out-of-memory kill—consult the operating-system or service-manager logs.
See Authentication for more.
Multiple roots
Pass multiple paths to serve several collections at once:
tessera-web ~/Photos ~/Music ~/Documents
The browser shows a root selector at the top of the sidebar.
Browsing and tagging
The file browser
The main view shows the contents of the current directory as a grid of thumbnails (or a list). Click any thumbnail to open the detail panel on the right.
- Navigate — click a folder tile to enter it; use the breadcrumb at the top to go back.
- Switch view — use the grid/list toggle in the toolbar.
- Sort — click column headers in list view, or use the sort menu in grid view.
- Zoom — drag the slider in the toolbar to resize grid thumbnails.
The activity rail switches between Files, Chats, Documents, and—when enabled—Videolab. Management tools such as Tag Manager and Collections live below the content sections. A floating chat or text editor can remain open while you move between sections.
Collections
Collections are named working sets of documents; they do not move the source files or replace directory scopes. Add the current selection from its context menu, then open Collections from the activity rail to create, rename, or delete sets and remove individual members. A non-empty collection can be opened as one chat context or sent to a multi-document transformation project.
Split-pane view
Press F3 (or click the split-view button in the toolbar) to open a second browser pane side by side. Each pane browses independently — different directories, different searches, different roots.
- Active pane — click anywhere in a pane to make it active. The active pane has a coloured top border and a tinted header. The sidebar, search bar, and detail panel all operate on the active pane.
- Resize — drag the handle between the panes to adjust the split.
- Close — press F3 again or click the toolbar button to collapse back to a single pane.
The detail panel
Click any file to open its detail panel. From here you can:
- View and copy the file path.
- See all tags attached to the file.
- Add tags using the tag input field (type and press Enter, or pick from the autocomplete list).
- Remove tags by clicking the × on a tag chip.
- Preview the file (image, video, PDF, archive pages, etc.).
Adding tags
Type a tag into the input field in the detail panel and press Enter or click Add. The autocomplete list shows existing tags in the database — use it to avoid typos and keep tags consistent.
For key=value tags, type the whole expression: year=2024, rating=5.
Removing tags
Click the × button on any tag chip in the detail panel.
Tagging multiple files
Select multiple files by holding Shift or Cmd/Ctrl while clicking. When more than one file is selected, the detail panel switches to bulk-tag mode: you can add or remove a tag across all selected files at once.
File operations
Right-click any file or folder to open the context menu. Available operations:
| Operation | Description |
|---|---|
| Rename… | Rename the file or folder in place. |
| Rename selection… | Search and replace in several names at once, using plain text or a regular expression. This can include subdirectories and can be undone. |
| Move to… | Move to a different directory (within the same root or to another root). Tags are preserved. |
| Copy to… | Copy files or directories to a different directory. Tags are copied to the new files. |
| New folder from selection | Put selected items in a new folder. Tessera suggests a name and opens it directly for editing. |
| Compress | Create an archive from the selection, or create a separate archive for each selected directory. Format and compression preferences are configurable. |
| Extract | Extract one or more selected archives in list order. A containing directory is added unless the archive already has exactly one directory at its root. |
| Move to Trash | Move to the .tessera/trash/ folder inside the root. Recoverable from the Trash panel. |
| New folder… | Create a subfolder (directories only). |
| New text file… | Create a text file and open it in the standalone editor. |
To operate on multiple files at once, select them first (Shift/Cmd+click), then right-click for the multi-file menu.
Completed file operations can be undone and redone from the operation history. For reversible archive moves, undo restores the original items and removes the created archive or directory when nothing new has since been added to it.
Standalone text editor
Open a supported text file from its context menu, or create one with New text file… on a directory. The editor is a movable, resizable window independent of document-transformation projects. It provides local undo/redo and saves with a modification-time check so an external edit is never silently overwritten.
Saved edits are also recorded in filesystem history. Undo saved edit can restore the recovery copy after the editor has been closed or the page reloaded.
Trash
Trashed files are stored in .tessera/trash/ and can be restored at any time. Open the Trash panel from the toolbar to review, restore, or permanently delete trashed items.
The sidebar
The left sidebar has two tabs:
- Tags — shows all tags in the current root. Click a tag to add it to the current query; click an active tag to remove it. Existing manual query terms are preserved and simple added criteria are combined with AND. Clicking the ▶ arrow next to a hierarchical tag (e.g.
genre) expands it to show subtags. - Files — shows a navigable file tree of the current root. Click a directory to navigate the main panel to it; click a file to navigate to its parent directory and select the file.
Click the split-view button (⊞) in the sidebar header to show both tabs side by side.
File tree navigation
- Click a directory — navigate the main panel to it and use it as the sole direct search scope.
- Cmd/Ctrl+click or Shift+click a directory — toggle an additional scope. Multiple directories from different roots can be combined.
- Change a scope between only direct and with subfolders from its labelled control in the query composer.
- Click a file — navigate to the file's parent directory and open the file in the detail panel.
- Drag a file onto a tag in the Tags pane to apply that tag.
- Drag a file or directory onto a directory row to move it there. Hold Alt before or during the drag to copy instead of move.
Keyboard shortcuts
| Key | Action |
|---|---|
| F3 | Toggle split-pane view |
| ↑ ↓ ← → or h j k l | Move cursor through the grid or list |
| Enter | Open the focused item |
| Space | Select the focused item |
| Cmd/Ctrl+Space | Toggle the focused item in the selection |
| Shift+↑ ↓ ← → | Extend the selection from its anchor |
| Home / End | Move to the first / last item |
| Page Up / Page Down | Move by a visible page |
| u or Backspace | Go to parent directory |
| Cmd/Ctrl+A | Select all files in the current view |
| Alt+← / Alt+→ | Navigate back / forward |
| F4 | Toggle Follow Cursor, keeping the focused item selected and visible in the detail panel |
| Shift+F10 or Context Menu | Open the focused item's context menu |
| Escape | Cancel multi-selection or clear keyboard cursor |
When a query is active, Escape first clears its criteria and reloads the active directory. It does not remove the directory scope.
Searching
The search bar
The search bar at the top of the page accepts the same query language as tessera find on the command line.
genre/rock and year>=2020
(jazz or blues) and not live
type:image and landscape
artist=Kraftwerk
Press Enter to run the search. Results replace the current directory listing. The composer supports a block editor and a raw-text editor for the same query; switching editors does not change its meaning.
Broad search
A standalone search term such as horse performs a broad discovery search across
all available information:
- tags, subjects, and AI-generated tags;
- filenames and directory names;
- indexed document content;
- indexed image and video content.
Document, image, and video content is found through semantic embeddings. Configure and index embeddings under Settings → Embeddings to enable those matches. Filename and tag matches continue to work when embeddings are not configured.
Use explicit query syntax when you want precise filtering, for example
type:image and horse. Use name: for a file-path match, dirname: to return
matching directories themselves, or use ~ for semantic ranking.
The query composer below the toolbar keeps directory scopes separate from query criteria. Selecting a tag, subject, or person adds it to the current expression without replacing manually typed terms. Directory scopes can be switched between direct children and all subfolders without modifying the query text.
Click an and or or block in the composer to switch that operator. Criteria
can also be grouped, negated, reordered, or removed without rebuilding the
whole expression. Clear criteria preserves directory scopes; Clear all
removes both.
For the full query language reference, see Query syntax.
Semantic search
Prefix your query with ~ to switch to semantic search — a vector similarity search using embedding models rather than tag matching.
~ a red sports car at sunset
~ jazz piano solo
~ aerial landscape photography
Semantic search requires embedding models to be configured and files to be indexed. See Semantic search for setup instructions.
Clearing the search
Click Clear criteria, or press Escape outside an editor, to clear query criteria and return to the active directory listing. Escape does not remove the directory scope chip. Use the chip's remove button or Clear all when you also want to leave that scope.
Filesystem index and fallback
Filename discovery uses a disk-backed FTS index under each Tessera database. It is rebuilt and reconciled in the background, so untagged files participate without keeping every path in process memory. While an index is not ready, scoped searches fall back to a bounded live walk. Full walks run serially and do not follow directory symlinks, avoiding duplicate traversal through overlapping or cyclic trees.
dirname: always uses a bounded live directory walk because the filename index
contains files, not directory objects.
Scope
Clicking a directory makes it the sole direct scope. Cmd/Ctrl-click or Shift-click adds scopes; scopes may come from different roots. Multiple scopes are ORed together, then ANDed with the query criteria. Each chip can search only direct children or include every subfolder.
Without a directory scope, searches use the roots selected by the current web
context. Linked child and ancestor databases participate unless the server was
started with --no-parents.
Chats and document transformations
The Chats and Documents sections use the configured AI endpoint but serve different workflows. Chats are conversations with optional file context; Documents keeps a durable project and version history for text transformations.
Chats
Start a chat from the activity rail or from selected files in the browser. A conversation can include files from the active, unambiguous database root. Attached images, archive pages, video sprites, and supported documents expose a preview so you can verify the context before sending it.
Chats can be docked in their section or opened as a movable, resizable floating window while you browse. Saved conversations keep their existing session ID and autosave subsequent exchanges. A brand-new unsaved conversation is not silently turned into a saved one; save it explicitly when you want it in the session list.
If an attached file disappears, Tessera marks it unavailable rather than silently substituting a file with the same name from another root.
Transformation projects
Choose a supported document or text file in Files and start a transformation. The Documents section opens a project workspace containing:
- the original source;
- an instruction composer;
- a project list and version tree;
- the selected result editor;
- a project chat for follow-up changes.
The project, its jobs, chat messages, and derived versions are persistent. A follow-up transformation or manual edit creates a child version, so alternate branches remain visible. Panel widths are resizable and remembered locally.
Finished results can be edited, exported, renamed, or deleted. Deleting one derived version keeps its descendants by reconnecting them to the deleted version's parent. Deleting the project is a separate action that removes the whole project history.
Running versions remain read-only. Failed transformations can be resumed when completed chunks are available, or restarted as a fresh run.
The standalone file editor described in Browsing and tagging is intentionally separate: editing an ordinary file does not create a transformation project.
Tag Manager, subjects, and people
Open Tag Manager from the activity rail or the Tags sidebar. It is a full workspace section with separate Tags, Subjects, and People tabs.
Scope
The root selector at the top controls the manager's catalogue and every exact name mutation performed inside it. Choose one root when equal tag or subject names exist in several databases. All roots is useful for inspection, but identity-sensitive operations may require an explicit root.
The file browser sidebar remains a merged browsing catalogue; leaving Tag Manager restores that global view. Changes made in scoped Tag Manager menus do not silently switch the manager back to the global catalogue.
Tags
Select a tag to inspect assignments and values. Depending on the tag, the manager can rename or delete it, move a hierarchy, change the hierarchy separator, manage value synonyms, promote a tag to a subject property, prune unused tags, and edit its property contract.
Separator changes use a guarded confirmation. Press Escape while the guard is armed to cancel it.
Subjects
Subjects group properties that belong to the same entity rather than directly
to a file. Subject names and pure hierarchy prefixes can be renamed in the
manager. Subject synonyms affect only subject: queries; they never broaden a
plain tag query with the same text.
Autocomplete in tag and subject editors replaces only the segment at the caret,
up to the next / or =. Existing suffixes are preserved, which makes editing
long hierarchical names predictable.
People
The People tab combines named face identities with their file assignments. Because identities are database-owned, select an explicit root before making changes when several roots are loaded. See Face recognition for detection and assignment workflows.
Videolab
Videolab is an optional workspace for applying a configured restoration model
to video. Enable it under Settings → Features, then configure its worker in
the Videolab settings tab. The recommended setup imports the password-protected
connection document produced by tessera-worker show-connection; advanced
settings also accept the worker URL, token, and CA certificate separately.
Starting from a video
Right-click a video in Files and choose Open in Videolab. Select only a bounded interval for a preview, choose settings advertised by the connected worker, and start the preview job. A completed preview can be compared with the source and promoted to a full render.
Preview and full-render records are persistent. The history shows queued, running, completed, failed, and cancelled work. Completed or failed records can be cloned into a new draft without modifying the original settings record. Jobs continue to appear in the global jobs panel when you leave the workspace.
Privacy and recovery boundary
Tessera probes the local source, crops the requested interval, strips source metadata and non-video streams, segments the frames, and encrypts each bounded segment before transfer. Worker configuration, model identity, frame geometry, and other required technical fields cross the boundary; source filenames, paths, tags, and document metadata do not.
Cancellation is explicit and remains pending until remote cleanup and key destruction are acknowledged. Interrupted transfers resume only at persisted, authenticated chunk boundaries. A missing encryption key causes Tessera to cancel or clean up the old execution rather than assuming that encrypted work can still be resumed.
See the worker's own deployment guide for installation and network setup.
Semantic search
Semantic search lets you find files by describing what you're looking for in natural language, rather than by exact tag names. It works by comparing embedding vectors: a mathematical representation of the "meaning" of a file's content.
How it works
- An embedding model converts each file into a vector (a list of numbers that encodes meaning).
- Your search query is converted into a vector using the same model.
- Files whose vectors are closest to the query vector are returned as results.
tessera supports two modalities:
| Modality | Model type | Covers |
|---|---|---|
| Vision | Image/vision embedding (e.g. nomic-embed-vision) | Images, videos (via sprite sheet), comic archives (cover image) |
| Text | Text embedding (e.g. nomic-embed-text) | Text files, Markdown, EPUBs, ZIP entry listings |
Setup
1. Run an embedding server
You need an OpenAI-compatible embedding endpoint. Infinity is recommended:
pip install infinity-emb[all]
# Start a vision + text server
infinity_emb v2 \
--model-id nomic-ai/nomic-embed-vision-v1.5 \
--model-id nomic-ai/nomic-embed-text-v1.5 \
--port 7997
Or use Ollama with a compatible model:
ollama pull nomic-embed-text
ollama serve
2. Configure the endpoints in tessera-web
Open Settings → Embeddings and fill in:
| Field | Example value |
|---|---|
| Text endpoint | http://localhost:7997 |
| Text model | nomic-ai/nomic-embed-text-v1.5 |
| Vision endpoint | http://localhost:7997 |
| Vision model | nomic-ai/nomic-embed-vision-v1.5 |
If your server hosts both models at the same URL, set both endpoints to the same value.
3. Index your files
In Settings → Embeddings, click Index all files. A progress bar shows how many files have been processed. You can cancel and resume at any time — already-indexed files are skipped automatically.
Supported file types:
| Type | How it's embedded |
|---|---|
| Images (jpg, png, webp, …) | Thumbnail sent to vision model |
| Videos (mp4, mkv, avi, …) | Sprite sheet (contact sheet of frames) sent to vision model |
| Comic archives (cbz, cbr) | Cover image sent to vision model |
| Text files (txt, md, rst) | File content (up to 8 KB) sent to text model |
| EPUBs | Extracted text sent to text model |
| ZIP archives | Entry listing sent to text model |
Using semantic search
In the search bar, type ~ followed by your query. Quote a multi-word semantic
phrase when combining it with structured criteria:
~ sunset over the ocean
~ a person playing guitar on stage
~ invoice from 2023
~ technical documentation about networking
~"sunset over the ocean" and type:image
~"invoice from 2023" and dir:Documents/
The semantic term ranks the candidates selected by the rest of the expression.
It can therefore be combined with and, but not placed under or, not, or a
subject group. A badge appears in the search bar to confirm semantic ranking.
Results are sorted by similarity (most similar first). The score shown is cosine similarity — closer to 1.0 means more similar.
Finding similar files
In the detail panel (right side), click Find similar to search for files visually or textually similar to the file you are viewing. This uses the stored embedding vector rather than a text query.
Tips
- A semantic phrase does not implicitly mean tags:
~ jazz pianoranks by similar content. Add an explicit tag criterion when both are required. - For best results with images and videos, use a dedicated vision model (
nomic-embed-vision, CLIP variants). - For text documents, use a text model (
nomic-embed-text, BGE, E5, etc.). - The quality of results depends heavily on the model. Larger models generally give better results at the cost of speed.
- Indexing is one-time — only new or updated files need to be re-indexed.
AI analysis
tessera-web can use a vision language model (VLM) to automatically suggest tags for images, videos, and archives. The model looks at the file and returns a list of descriptive tags.
Setup
You need an OpenAI-compatible chat endpoint that supports vision (image input). Ollama with a model like llava, llava-phi3, or minicpm-v works well locally:
ollama pull llava-phi3
ollama serve
Or use any other OpenAI-compatible endpoint (OpenAI API, LM Studio, llama.cpp server, etc.).
Configuring the AI endpoint
Open Settings → Features and fill in:
| Field | Example |
|---|---|
| AI endpoint | http://localhost:11434 |
| AI model | llava-phi3 |
| Tag prefix | ai/ (optional — keeps AI-generated tags namespaced) |
| Collection description | My personal photo library (optional context for the model) |
Analysing a file
Open a file in the detail panel and click Analyse with AI. The model receives a (resized) JPEG of the file and returns a list of suggested tags. Review the suggestions, then click Apply to add them, or dismiss the panel to ignore them.
What the model receives
| File type | Input sent to model |
|---|---|
| Image | Resized JPEG (max 1024 px) |
| Video | Sprite sheet (contact sheet of frames) |
| Comic/archive | Bounded multi-page overview sampled across the archive |
| EPUB | Extracted plain text |
Batch analysis
In Settings → Features, click Analyse all files to run AI analysis on every unanalysed file in the database. A progress indicator shows the status.
Previously analysed files are skipped (their ai/* tags are already present). Use the Re-analyse option to force re-analysis.
Clearing AI tags
To remove all tags with the configured AI prefix from a file or from the entire database, use the Clear AI tags button in the settings or detail panel.
Customising the prompts
Advanced users can override the prompts used for image, video, and archive analysis in Settings → Features:
- Image prompt — instructions for single-image analysis
- Video prompt — instructions for sprite-sheet (video) analysis
- Archive prompt — instructions for archive (cover + listing) analysis
The model always responds in structured JSON regardless of the prompt; the prompt controls what aspects to focus on. Suggested tags may carry an explicit subject, which Tessera preserves as structured subject metadata rather than encoding it into the tag text.
Face recognition
tessera-web can detect faces in images and associate them with named subjects. Once a face is assigned a name, tessera can find all images containing that person.
Enabling face detection
Face detection requires an optional face detection model. Configure the endpoint in Settings → Features → Face detection.
Detecting faces
Open an image in the detail panel. If faces are detected, bounding boxes appear over the image. Each face has a label showing the assigned name (if known) or "Unknown".
Click a bounding box to open the face panel, where you can:
- Assign a name to the face.
- Confirm or reject a suggested match.
- Remove the detection.
Finding all images of a person
Once a subject has been named, use the tag sidebar or search to find all images containing them:
subject:Alice
Or in the web search bar:
subject:Alice and year=2024
Training and matching
tessera stores a face embedding (a vector representation of the face) for each detection. When you name a face, all future detections of similar faces are automatically suggested as the same person.
The quality of matching depends on image resolution and the detection model. Detections with low confidence scores may produce more false positives.
Previews and media
The detail panel in the web interface shows a preview of the selected file. The type of preview depends on the file format.
Images
JPEG, PNG, WebP, GIF, BMP, TIFF, and HEIC files are displayed directly. HEIC files are converted on the fly to JPEG for display in the browser.
RAW camera files (CR2, NEF, ARW, etc.) are extracted and displayed if a supported decoder is available; otherwise a placeholder is shown.
Double-clicking an image (or clicking the expand button in the detail panel) opens the media viewer. The viewer navigates through all viewable files currently shown in the grid—including cross-root search results—so the sequence reflects active filters and sort order rather than raw directory order.
The shared media toolbar supports page navigation, reading direction, spreads, continuous scrolling, zoom, fullscreen, pinning, and opening the viewer in a real same-origin browser window. Moving near the top edge reveals an unpinned toolbar; pin it when browser chrome makes edge access inconvenient. Cmd/Ctrl + mouse wheel zooms towards the pointer.
Video
Videos are streamed directly in the browser when the codec is browser-compatible (H.264 in MP4, AV1 in MP4/WebM). Other formats are transcoded on the fly to H.264/MP4 using ffmpeg.
Trickplay — hover over the video scrubber to see a sprite sheet of frames, letting you jump to any position without seeking.
Trickplay sprites are generated on first play and cached in .tessera/cache/vthumbs/.
The expanded video viewer uses the same toolbar lifecycle as the media viewer and adds a collapsible queue of videos from the current result set. Space owns one complete key press (down and up), so one press toggles playback once even when a media control has focus.
PDF files are rendered locally in the browser with PDF.js. Use the arrow buttons to navigate pages; the viewer also supports two-page spreads, right-to-left reading, continuous vertical or horizontal scrolling, and zoom/pan. In continuous mode only nearby pages are rasterised, so opening a large document does not render every page at once.
The PDF.js library and worker are bundled with Tessera and do not load code from a CDN. Server-side PDF tools remain useful for grid thumbnails, OCR, translation and AI analysis, but are not required for the reading surface.
Archives (ZIP, CBZ, CBR)
Archive contents are listed in grid or list form and have their own context menus. Image/comic pages load progressively: the active page is prioritised, then nearby previews and full-resolution pages are prefetched within bounded caches. The archive info action reports file, directory, image, compression, and solid-block metadata where the format exposes it.
Archive optimisation is available from the archive actions and settings. It can rebuild archives with configured compression and optionally replace the source; source metadata and files are preserved unless an explicit exclusion or replace option says otherwise. Solid 7z/RAR inputs are extracted sequentially rather than repeatedly decoding the same compression block.
Audio
Audio files (MP3, FLAC, OGG, WAV) are played via the browser's built-in audio player. Metadata (title, artist, album, duration) is shown if available.
Unsupported types
For file types without a preview, the detail panel shows file metadata and tags only.
Thumbnail generation
Thumbnails for the grid view are generated on demand and cached in .tessera/cache/thumbs/. Generation uses the image crate for raster images and ffmpeg for video.
To clear the thumbnail cache, use Settings → Database → Clear cache.
Resume and pop-out
The video and media viewers each have an opt-in resume control. Video positions and archive/PDF pages are stored in local browser storage; starts and completed items are discarded so reopening begins naturally. Resume never overrides an explicit browser Back/Forward restore, compact-player handoff, or pop-out handoff.
Open in new window transfers the current viewer state to a real browser window on the same Tessera origin, including video time, pause state, volume, playback speed, and media page. If the browser blocks the popup, the original viewer stays open.
Settings
Open the settings panel by clicking the gear icon in the top-right corner. Settings are organised into tabs.
Features
| Setting | Description |
|---|---|
| AI endpoint | URL of the OpenAI-compatible VLM endpoint for AI tag analysis |
| AI model | Model name to use for analysis |
| Tag prefix | Prefix applied to AI-generated tags (e.g. ai/) |
| Collection description | Optional context passed to the model |
| Image/video/archive prompt | Override the default analysis prompts |
| Face detection endpoint | URL for the face detection model |
| Videolab | Show the optional video-processing workspace |
| Archive optimisation | Enable archive rebuilding actions |
| Replace original | Allow an optimised archive to replace its source after confirmation |
| Archive exclusions | Optional patterns omitted while optimising an archive |
Video
| Setting | Description |
|---|---|
| Max trickplay frames | Maximum number of frames in a trickplay sprite sheet |
| Frame selection | interval (evenly spaced) or scene (scene-change detection) |
Tiles
Controls the appearance of the file browser grid:
| Setting | Description |
|---|---|
| Tile size | Small / Medium / Large |
| Show filenames | Whether to show filenames below thumbnails |
Embeddings
| Setting | Description |
|---|---|
| Text endpoint | URL of the text embedding endpoint |
| Text model | Model name for text embedding |
| Vision endpoint | URL of the vision embedding endpoint |
| Vision model | Model name for vision embedding |
| Index all files | Start bulk indexing of all files in the database |
| Cancel indexing | Stop the running index job |
See Semantic search for a full setup guide.
Database
| Action | Description |
|---|---|
| Clear cache | Delete all thumbnails, trickplay sprites, and transcoded files |
| Database info | Show statistics (file count, tag count, database size) |
Compression and archive optimisation
Compression settings control new archives created from file selections. The automatic ZIP mode stores already-compressed media and deflates text and metadata. Archive optimisation is a separate opt-in feature: it can rebuild an existing archive, optionally exclude configured metadata patterns, and—only when explicitly enabled and confirmed—replace the original.
The replace option is destructive in intent but still participates in Tessera's filesystem-history and recovery mechanisms. Leave it disabled when you want the optimised archive beside the source for comparison.
Authentication
Default behaviour
| Binding | Default |
|---|---|
localhost (default) | No authentication |
Non-loopback (e.g. 0.0.0.0) | Random password auto-generated and printed to stderr |
Setting a password
# Command-line flag
tessera-web --password mysecretpassword ~/Photos
# Environment variable (useful in scripts and containers)
TESSERA_PASSWORD=mysecretpassword tessera-web ~/Photos
# Read password from a file (takes precedence over --password and env)
tessera-web --password-file /run/secrets/tessera ~/Photos
# Generate a random password and print it to stderr
tessera-web -P ~/Photos
Disabling authentication
If tessera-web is running behind a reverse proxy that handles authentication (e.g. Caddy, nginx, Authelia), you can disable the built-in auth:
tessera-web --no-auth --bind 0.0.0.0 ~/Photos
Session management
Successful login sets an HttpOnly, SameSite=Strict cookie (ft_session) that lasts 24 hours. To log out, click the Log out button in the top-right corner or navigate to /logout.
Security notes
- The
Securecookie flag is not set — tessera-web does not manage TLS. If you expose the server over the internet, place it behind a TLS-terminating reverse proxy. - Passwords are stored as SHA-256 hashes in memory only; they are never written to disk.
- There is no rate limiting on the login endpoint; use a reverse proxy or firewall rules to limit access from untrusted networks.
Query syntax
Tessera queries combine tags, file properties, paths, subjects, and optional
semantic ranking in one boolean expression. The language is used by tessera find, tessera view, and the web query composer. Features that depend on the
live filesystem are identified below.
Literals, quoting, and operators
Tag names are plain tokens. Quote values that contain spaces:
genre/rock
"Extra models"
artist="Kraftwerk live"
| Operator | Meaning | Example |
|---|---|---|
and | Both conditions match | lossless and genre/rock |
or | Either condition matches | genre/jazz or genre/blues |
not | The condition does not match | lossless and not live |
Precedence is not, then and, then or. Parentheses override it:
(genre/rock or genre/metal) and not live
Tags and values
A bare tag matches files carrying that tag. tag: makes the same intent
explicit and is useful when a single bare word in the web UI would otherwise
start broad discovery.
favourite
tag:favourite
genre/rock
Key/value tags support =, !=, <, <=, >, and >=:
year=2024
rating>=4
artist!=Unknown
Numeric-looking values compare numerically. Other values compare as strings. Property contracts may additionally validate types, units, ranges, cardinality, and controlled concepts; see Property contracts and concepts.
Use * to match tag namespaces:
genre/*
Names, paths, and directories
name: (aliases filename: and file:) matches the complete root-relative
file path. A plain pattern is a case-insensitive substring; a pattern containing
* or ? is a glob over the whole path.
name:invoice
name:"summer holiday"
name:photos/*.jpg
dir: restricts results to one concrete root-relative directory. Without a
trailing slash it selects direct children; with a trailing slash it includes
all descendants:
dir:Photos
dir:Photos/
dir:Photos/2026/
In the web UI, directory scope chips are kept separately from the text query.
Multiple chips are ORed together; their combined scope is ANDed with all query
criteria, including any dir: criterion. Scope chips carry a root ID, so they
remain unambiguous across multiple or nested databases.
dirname: searches for directory objects by basename in the web interface.
Like name:, it uses a case-insensitive substring unless * or ? is present:
dirname:holiday
dirname:"Old photos"
dirname:project-20??
The matching directories themselves appear in the results. An active directory
scope limits where Tessera searches. dirname: is currently web-only because
the CLI query universe consists of file records rather than live directory
entries.
Regular-expression literals match root-relative file paths:
/IMG_\d{4}/
File types
Use type: to select a logical file category:
| Expression | Matches |
|---|---|
type:image | JPEG, PNG, WebP, GIF, TIFF, HEIC, RAW, and other images |
type:video | MP4, MKV, AVI, MOV, WebM, and other video formats |
type:audio | MP3, FLAC, OGG, WAV, AAC, Opus, and other audio formats |
type:document | PDF, office documents, text, Markdown, EPUB, and more |
type:archive | ZIP, CBZ, CBR, RAR, 7z, TAR, and more |
Aliases include img, photo, and pic for images; vid and movie for
video; aud and music for audio; doc for documents; and arc for archives.
Subjects
subject: matches a subject and its descendants. Subject groups bind several
conditions to the same subject assignment:
subject:person/alice
{species=bird and colour=blue}
Subject and concept synonyms are resolved within their own domains; a subject alias does not broaden an unrelated plain-tag query.
Intrinsic properties
Intrinsic file predicates compose with tags and other criteria. Available forms cover dimensions, aspect ratio, orientation, dominant colour, and GPS location. Common examples:
width>=1920 and height>=1080
ratio>=16:9
megapixels>=12
orientation=landscape
color:blue
gps
near:52.37:4.90:10
near: is latitude, longitude, then radius in kilometres.
Semantic ranking
~ ranks the candidates selected by the rest of the expression using an
embedding:
~"sunset over the ocean"
~"jazz piano" and type:audio
~"invoice from 2023" and dir:Documents/
A semantic term may occur only on the positive and spine. It cannot be used
under or, not, or inside a subject group because those placements do not
define one coherent candidate ranking. See Semantic search
for setup and indexing.
Combined examples
(genre/rock or genre/metal) and year>=2000 and not live
type:image and width>=3000 and color:blue
dirname:archive and dir:Projects/
~"technical drawing" and type:document and not status=obsolete
Tag values
Tags in tessera are either plain words or key=value pairs.
Plain tags
A plain tag is any string without =:
tessera tag photo.jpg -t favourite
tessera tag photo.jpg -t genre/rock
tessera tag photo.jpg -t status/todo
Key=value tags
A key=value tag stores a value alongside the tag name:
tessera tag photo.jpg -t year=2024
tessera tag photo.jpg -t rating=5
tessera tag photo.jpg -t artist=Kraftwerk
tessera tag photo.jpg -t "title=Autobahn"
The key and value are stored separately but treated as a single unit. Querying year without a value matches any file that has a year tag regardless of its value. Querying year=2024 matches only files where the value is exactly 2024.
Numeric comparisons
Values that look like numbers support comparison operators in queries:
year>=2020
rating>3 and rating<=5
Values are stored as strings; numeric comparison is attempted first and falls back to lexicographic comparison.
Tag hierarchy with /
The / character is just a separator in the tag name string — there is no tree structure in the database. genre/rock and genre/jazz are two independent tags that happen to share a prefix. Queries like genre/* use glob matching on the stored strings.
Special characters
- Commas (
,) separate multiple tags on the command line:-t lossless,year=2024 - Spaces: wrap in double quotes in the query language:
"Extra models" /: used as namespace separator by convention, but has no special database meaning=: separates key from value*: glob wildcard in queries only
Listing all tags in a database
tessera tags # all tags in the current database
tessera info # database statistics including tag count
Property contracts and concepts
Every key=value tag is schemaless by default. Existing tags therefore keep
working without configuration. A root administrator can optionally give a key
a property contract to add validation, cardinality, units, or a controlled
concept vocabulary.
Supported value kinds are text, concept, number, quantity, range, date,
datetime, boolean, and identifier. Cardinality is many (the default) or
one; applying a new value to a single-valued property replaces the previous
value for that file/subject.
Declarative CLI management
Property resources are versioned JSON documents, designed for source control
and repeatable application in the same spirit as kubectl apply -f:
{
"apiVersion": "tessera.dev/v1alpha1",
"kind": "PropertySet",
"properties": [
{
"name": "size-class",
"value_kind": "concept",
"cardinality": "one",
"concepts": [
{
"value": "small",
"labels": [
{ "language": "nl", "label": "klein", "preferred": true },
{ "language": "en", "label": "small", "preferred": true },
{ "language": "en", "label": "S", "preferred": false }
]
},
{
"value": "microscopic",
"parent": "small",
"labels": [
{ "language": "nl", "label": "microscopisch", "preferred": true }
]
}
]
},
{
"name": "fibre-diameter",
"value_kind": "quantity",
"cardinality": "many",
"canonical_unit": "µm",
"allowed_units": ["µm", "nm", "mm"]
}
]
}
tessera property apply -f properties.json
tessera property apply -f - < properties.json
tessera property get
tessera property get size-class
tessera property delete size-class
get always emits a complete PropertySet, so its output can be edited and
applied again. Applying a manifest replaces the concept metadata for the named
properties but never rewrites unrelated properties or tag assignments.
Deleting a contract also leaves assignments intact; the key simply becomes
schemaless again.
Controlled concepts
A concept has a stable canonical value, an optional parent, and any number of
language-aware labels. Labels work as key-scoped aliases in equality queries:
size-class=klein
matches assignments stored as size-class=small. A query for a parent concept
also includes its descendants, so size-class=small includes
size-class=microscopic.
Concepts can carry typed properties whose names refer to other property contracts. Files and subjects still store only the stable concept value; shared facts about that concept live in one place:
{
"properties": [
{
"name": "typical-age",
"value_kind": "range",
"cardinality": "one",
"canonical_unit": "years",
"allowed_units": ["years"]
},
{
"name": "life-stage",
"value_kind": "concept",
"cardinality": "one",
"concepts": [
{
"value": "menopause",
"labels": [
{ "language": "nl", "label": "overgang", "preferred": true }
],
"properties": [
{ "name": "typical-age", "value": "40-60 years" }
]
}
]
}
]
}
An assignment such as life-stage=menopause is therefore the reference. Its
typical-age metadata is validated using the range contract. Referenced
property contracts may appear before or after the concept in one manifest.
Quantities
A quantity validates a numeric magnitude followed by an optional unit. When
allowed_units is non-empty, input must use one of those exact units. The
initial v1alpha1 contract does not convert magnitudes between units: the
canonical unit records intent and supports consistent administration, but
queries still compare the stored textual values using Tessera's existing
numeric behaviour. Unit conversion can be added without turning each measured
value into a concept.
Ranges
A range is one value with two bounds. Bounds may be numeric with a shared unit,
such as type1/size=2-10 µm or age=40-60 years, textual (letter=a-z), or
ISO dates (period=2020-01-01..2022-12-31). The separators -, –, —, and
.. are accepted. Use .. when a bound itself contains a hyphen, especially
for dates and compound words.
Numeric lower bounds must not exceed their upper bounds. ISO date bounds are
also checked chronologically. As with quantities, allowed_units can restrict
the shared unit; configured units make numeric bounds mandatory.
range is a value kind, not a cardinality. Cardinality independently says
whether one or several ranges may be assigned to the same file or subject.
Web administration
Select a tag in Tag Manager and open Property contract. The editor can set the value kind, cardinality, quantity/range units, description, and controlled concept structure. Removing a contract from this screen never removes tags from files, directories, or subjects.
File types
tessera recognises file types by extension for two purposes: the type: query filter and the embedding modality selector.
Type filter categories
| Category | Extensions |
|---|---|
image | jpg, jpeg, png, gif, webp, bmp, tiff, tif, heic, heif, avif, cr2, cr3, nef, arw, raf, dng, orf, rw2, pef, srw, x3f, raw |
video | mp4, mkv, avi, mov, webm, m4v, ts, flv, wmv, ogv, 3gp |
audio | mp3, flac, ogg, opus, wav, aac, m4a, wma, alac, aiff, ape, mka |
document | pdf, docx, doc, odt, rtf, txt, md, markdown, rst, tex, epub, html, htm |
archive | zip, cbz, cbr, rar, 7z, tar, gz, bz2, xz, zst |
Aliases for type: queries:
| Alias | Resolves to |
|---|---|
img, photo, pic | image |
vid, movie | video |
aud, music | audio |
doc | document |
arc, archive | archive |
Embedding modalities
For semantic search, files are embedded according to their extension:
| Modality | Extensions | Method |
|---|---|---|
| Vision | jpg, jpeg, png, webp, bmp, gif, tiff, tif | Thumbnail → vision model |
| Vision | mp4, mkv, avi, mov, webm, m4v, ts, flv | Sprite sheet (video frames) → vision model |
| Vision | cbz, cbr | Cover image → vision model |
| Text | txt, md, markdown, rst | File content → text model |
| Text | epub | Extracted text → text model |
| Text | zip | Entry listing → text model |
Files with other extensions are skipped during bulk indexing.
Configuration keys
tessera-web stores configuration in the settings table of the database (key TEXT PRIMARY KEY, value TEXT). Keys can be read and written via Settings in the web interface, or directly with tessera-web's settings API.
AI analysis
| Key | Default | Description |
|---|---|---|
ai.endpoint | (empty) | URL of the OpenAI-compatible VLM endpoint |
ai.model | (empty) | Model name for image/video/archive analysis |
ai.api_key | (empty) | API key (if required by the endpoint) |
ai.tag_prefix | ai/ | Prefix added to all AI-generated tags |
ai.max_tokens | 512 | Maximum tokens in the model's response |
ai.format | json | Response format (json or text) |
ai.subject | (empty) | Description of the collection, sent as context |
ai.prompt_image | (built-in) | Override the image analysis prompt |
ai.prompt_video | (built-in) | Override the video analysis prompt |
ai.prompt_archive | (built-in) | Override the archive analysis prompt |
ai.video_sheet_max_frames | 64 | Maximum frames per sprite sheet for video analysis |
ai.video_frame_selection | interval | Frame selection method: interval or scene |
Embedding / semantic search
| Key | Default | Description |
|---|---|---|
embedding.text_endpoint | (empty) | URL of the text embedding endpoint |
embedding.text_model | nomic-ai/nomic-embed-text-v1.5 | Text embedding model name |
embedding.vision_endpoint | (falls back to text_endpoint) | URL of the vision embedding endpoint |
embedding.vision_model | nomic-ai/nomic-embed-vision-v1.5 | Vision embedding model name |
Face detection
| Key | Default | Description |
|---|---|---|
face.endpoint | (empty) | URL of the face detection endpoint |
face.model | (empty) | Face detection model name |