mirror of
https://github.com/zadam/trilium.git
synced 2025-12-16 05:09:54 +01:00
chore(docs): revert changes to HTML for diffing purposes
This commit is contained in:
@@ -1,145 +0,0 @@
|
||||
<h2>Advanced Search Expressions</h2>
|
||||
<p>This guide covers complex search expressions that combine multiple criteria,
|
||||
use advanced operators, and leverage Trilium's relationship system for
|
||||
sophisticated queries.</p>
|
||||
<h2>Complex Query Construction</h2>
|
||||
<h3>Boolean Logic with Parentheses</h3>
|
||||
<p>Use parentheses to group expressions and control evaluation order:</p><pre><code class="language-text-x-trilium-auto">(#book OR #article) AND #author=Tolkien</code></pre>
|
||||
<p>Finds notes that are either books or articles, written by Tolkien.</p><pre><code class="language-text-x-trilium-auto">#project AND (#status=active OR #status=pending)</code></pre>
|
||||
<p>Finds active or pending projects.</p><pre><code class="language-text-x-trilium-auto">meeting AND (#priority=high OR #urgent) AND note.dateCreated >= TODAY-7</code></pre>
|
||||
<p>Finds recent high-priority or urgent meetings.</p>
|
||||
<h3>Negation Patterns</h3>
|
||||
<p>Use <code>NOT</code> or the <code>not()</code> function to exclude certain
|
||||
criteria:</p><pre><code class="language-text-x-trilium-auto">#book AND not(#genre=fiction)</code></pre>
|
||||
<p>Finds non-fiction books.</p><pre><code class="language-text-x-trilium-auto">project AND not(note.isArchived=true)</code></pre>
|
||||
<p>Finds non-archived notes containing "project".</p><pre><code class="language-text-x-trilium-auto">#!completed</code></pre>
|
||||
<p>Short syntax for notes without the "completed" label.</p>
|
||||
<h3>Mixed Search Types</h3>
|
||||
<p>Combine full-text, attribute, and property searches:</p><pre><code class="language-text-x-trilium-auto">development #category=work note.type=text note.dateModified >= TODAY-30</code></pre>
|
||||
<p>Finds text notes about development, categorized as work, modified in the
|
||||
last 30 days.</p>
|
||||
<h2>Advanced Attribute Searches</h2>
|
||||
<h3>Fuzzy Attribute Matching</h3>
|
||||
<p>When fuzzy attribute search is enabled, you can use partial matches:</p><pre><code class="language-text-x-trilium-auto">#lang</code></pre>
|
||||
<p>Matches labels like "language", "languages", "programming-lang", etc.</p><pre><code class="language-text-x-trilium-auto">#category=prog</code></pre>
|
||||
<p>Matches categories like "programming", "progress", "program", etc.</p>
|
||||
<h3>Multiple Attribute Conditions</h3><pre><code class="language-text-x-trilium-auto">#book #author=Tolkien #publicationYear>=1950 #publicationYear<1960</code></pre>
|
||||
<p>Finds Tolkien's books published in the 1950s.</p><pre><code class="language-text-x-trilium-auto">#task #priority=high #status!=completed</code></pre>
|
||||
<p>Finds high-priority incomplete tasks.</p>
|
||||
<h3>Complex Label Value Patterns</h3>
|
||||
<p>Use various operators for sophisticated label matching:</p><pre><code class="language-text-x-trilium-auto">#isbn %= '978-[0-9-]+' </code></pre>
|
||||
<p>Finds notes with ISBN labels matching the pattern (regex).</p><pre><code class="language-text-x-trilium-auto">#email *=* @company.com</code></pre>
|
||||
<p>Finds notes with email labels containing "@company.com".</p><pre><code class="language-text-x-trilium-auto">#version >= 2.0</code></pre>
|
||||
<p>Finds notes with version labels of 2.0 or higher (numeric comparison).</p>
|
||||
<h2>Relationship Traversal</h2>
|
||||
<h3>Basic Relation Queries</h3><pre><code class="language-text-x-trilium-auto">~author.title *=* Tolkien</code></pre>
|
||||
<p>Finds notes with an "author" relation to notes containing "Tolkien" in
|
||||
the title.</p><pre><code class="language-text-x-trilium-auto">~project.labels.status = active</code></pre>
|
||||
<p>Finds notes related to projects with active status.</p>
|
||||
<h3>Multi-Level Relationships</h3><pre><code class="language-text-x-trilium-auto">~author.relations.publisher.title = "Penguin Books"</code></pre>
|
||||
<p>Finds notes authored by someone published by Penguin Books.</p><pre><code class="language-text-x-trilium-auto">~project.children.title *=* documentation</code></pre>
|
||||
<p>Finds notes related to projects that have child notes about documentation.</p>
|
||||
<h3>Relationship Direction</h3><pre><code class="language-text-x-trilium-auto">note.children.title = "Chapter 1"</code></pre>
|
||||
<p>Finds parent notes that have a child titled "Chapter 1".</p><pre><code class="language-text-x-trilium-auto">note.parents.labels.category = book</code></pre>
|
||||
<p>Finds notes whose parents are categorized as books.</p><pre><code class="language-text-x-trilium-auto">note.ancestors.title = "Literature"</code></pre>
|
||||
<p>Finds notes with "Literature" anywhere in their ancestor chain.</p>
|
||||
<h2>Property-Based Searches</h2>
|
||||
<h3>Note Metadata Queries</h3><pre><code class="language-text-x-trilium-auto">note.type=code note.mime=text/javascript note.dateCreated >= MONTH</code></pre>
|
||||
<p>Finds JavaScript code notes created this month.</p><pre><code class="language-text-x-trilium-auto">note.isProtected=true note.contentSize > 1000</code></pre>
|
||||
<p>Finds large protected notes.</p><pre><code class="language-text-x-trilium-auto">note.childrenCount >= 10 note.type=text</code></pre>
|
||||
<p>Finds text notes with many children.</p>
|
||||
<h3>Advanced Property Combinations</h3><pre><code class="language-text-x-trilium-auto">note.parentCount > 1 #template</code></pre>
|
||||
<p>Finds template notes that are cloned in multiple places.</p><pre><code class="language-text-x-trilium-auto">note.attributeCount > 5 note.type=text note.contentSize < 500</code></pre>
|
||||
<p>Finds small text notes with many attributes (heavily tagged short notes).</p><pre><code class="language-text-x-trilium-auto">note.revisionCount > 10 note.dateModified >= TODAY-7</code></pre>
|
||||
<p>Finds frequently edited notes modified recently.</p>
|
||||
<h2>Date and Time Expressions</h2>
|
||||
<h3>Relative Date Calculations</h3><pre><code class="language-text-x-trilium-auto">#dueDate <= TODAY+7 #dueDate >= TODAY</code></pre>
|
||||
<p>Finds tasks due in the next week.</p><pre><code class="language-text-x-trilium-auto">note.dateCreated >= MONTH-2 note.dateCreated < MONTH</code></pre>
|
||||
<p>Finds notes created in the past two months.</p><pre><code class="language-text-x-trilium-auto">#eventDate = YEAR note.dateCreated >= YEAR-1</code></pre>
|
||||
<p>Finds events scheduled for this year that were planned last year.</p>
|
||||
<h3>Complex Date Logic</h3><pre><code class="language-text-x-trilium-auto">(#startDate <= TODAY AND #endDate >= TODAY) OR #status=ongoing</code></pre>
|
||||
<p>Finds current events or ongoing items.</p><pre><code class="language-text-x-trilium-auto">#reminderDate <= NOW+3600 #reminderDate > NOW</code></pre>
|
||||
<p>Finds reminders due in the next hour (using seconds offset).</p>
|
||||
<h2>Fuzzy Search Techniques</h2>
|
||||
<h3>Fuzzy Exact Matching</h3><pre><code class="language-text-x-trilium-auto">#title ~= managment</code></pre>
|
||||
<p>Finds notes with titles like "management" even with typos.</p><pre><code class="language-text-x-trilium-auto">~category.title ~= progaming</code></pre>
|
||||
<p>Finds notes related to categories like "programming" with misspellings.</p>
|
||||
<h3>Fuzzy Contains Matching</h3><pre><code class="language-text-x-trilium-auto">note.content ~* algoritm</code></pre>
|
||||
<p>Finds notes containing words like "algorithm" with spelling variations.</p><pre><code class="language-text-x-trilium-auto">#description ~* recieve</code></pre>
|
||||
<p>Finds notes with descriptions containing "receive" despite the common
|
||||
misspelling.</p>
|
||||
<h3>Progressive Fuzzy Strategy</h3>
|
||||
<p>By default, Trilium uses exact matching first, then fuzzy as fallback:</p><pre><code class="language-text-x-trilium-auto">development project</code></pre>
|
||||
<p>First finds exact matches for "development" and "project", then adds fuzzy
|
||||
matches if needed.</p>
|
||||
<p>To force fuzzy behavior:</p><pre><code class="language-text-x-trilium-auto">#title ~= development #category ~= projet</code></pre>
|
||||
<h2>Ordering and Limiting</h2>
|
||||
<h3>Multiple Sort Criteria</h3><pre><code class="language-text-x-trilium-auto">#book orderBy #publicationYear desc, note.title asc limit 20</code></pre>
|
||||
<p>Orders books by publication year (newest first), then by title alphabetically,
|
||||
limited to 20 results.</p><pre><code class="language-text-x-trilium-auto">#task orderBy #priority desc, #dueDate asc</code></pre>
|
||||
<p>Orders tasks by priority (high first), then by due date (earliest first).</p>
|
||||
<h3>Dynamic Ordering</h3><pre><code class="language-text-x-trilium-auto">#meeting note.dateCreated >= TODAY-30 orderBy note.dateModified desc</code></pre>
|
||||
<p>Finds recent meetings ordered by last modification.</p><pre><code class="language-text-x-trilium-auto">#project #status=active orderBy note.childrenCount desc limit 10</code></pre>
|
||||
<p>Finds the 10 most complex active projects (by number of sub-notes).</p>
|
||||
<h2>Performance Optimization Patterns</h2>
|
||||
<h3>Efficient Query Structure</h3>
|
||||
<p>Start with the most selective criteria:</p><pre><code class="language-text-x-trilium-auto">#book #author=Tolkien note.dateCreated >= 1950-01-01</code></pre>
|
||||
<p>Better than:</p><pre><code class="language-text-x-trilium-auto">note.dateCreated >= 1950-01-01 #book #author=Tolkien</code></pre>
|
||||
<h3>Fast Search for Large Datasets</h3><pre><code class="language-text-x-trilium-auto">#category=project #status=active</code></pre>
|
||||
<p>With fast search enabled, this searches only attributes, not content.</p>
|
||||
<h3>Limiting Expensive Operations</h3><pre><code class="language-text-x-trilium-auto">note.content *=* "complex search term" limit 50</code></pre>
|
||||
<p>Limits content search to prevent performance issues.</p>
|
||||
<h2>Error Handling and Debugging</h2>
|
||||
<h3>Syntax Validation</h3>
|
||||
<p>Invalid syntax produces helpful error messages:</p><pre><code class="language-text-x-trilium-auto">#book AND OR #author=Tolkien</code></pre>
|
||||
<p>Error: "Mixed usage of AND/OR - always use parentheses to group AND/OR
|
||||
expressions."</p>
|
||||
<h3>Debug Mode</h3>
|
||||
<p>Enable debug mode to see how queries are parsed:</p><pre><code class="language-text-x-trilium-auto">#book #author=Tolkien</code></pre>
|
||||
<p>With debug enabled, shows the internal expression tree structure.</p>
|
||||
<h3>Common Pitfalls</h3>
|
||||
<ul>
|
||||
<li>Unescaped special characters: Use quotes or backslashes</li>
|
||||
<li>Missing parentheses in complex boolean expressions</li>
|
||||
<li>Incorrect property names: Use <code>note.title</code> not <code>title</code>
|
||||
</li>
|
||||
<li>Case sensitivity assumptions: All searches are case-insensitive</li>
|
||||
</ul>
|
||||
<h2>Expression Shortcuts</h2>
|
||||
<h3>Label Shortcuts</h3>
|
||||
<p>Full syntax:</p><pre><code class="language-text-x-trilium-auto">note.labels.category = book</code></pre>
|
||||
<p>Shortcut:</p><pre><code class="language-text-x-trilium-auto">#category = book</code></pre>
|
||||
<h3>Relation Shortcuts</h3>
|
||||
<p>Full syntax:</p><pre><code class="language-text-x-trilium-auto">note.relations.author.title *=* Tolkien</code></pre>
|
||||
<p>Shortcut:</p><pre><code class="language-text-x-trilium-auto">~author.title *=* Tolkien</code></pre>
|
||||
<h3>Property Shortcuts</h3>
|
||||
<p>Some properties have convenient shortcuts:</p><pre><code class="language-text-x-trilium-auto">note.text *=* content</code></pre>
|
||||
<p>Searches both title and content for "content".</p>
|
||||
<h2>Real-World Complex Examples</h2>
|
||||
<h3>Project Management</h3><pre><code class="language-text-x-trilium-auto">(#project OR #task) AND #status!=completed AND
|
||||
(#priority=high OR #dueDate <= TODAY+7) AND
|
||||
not(note.isArchived=true)
|
||||
orderBy #priority desc, #dueDate asc</code></pre>
|
||||
<h3>Research Organization</h3><pre><code class="language-text-x-trilium-auto">(#paper OR #article OR #book) AND
|
||||
~author.title *=* smith AND
|
||||
#topic *=* "machine learning" AND
|
||||
note.dateCreated >= YEAR-2
|
||||
orderBy #citationCount desc limit 25</code></pre>
|
||||
<h3>Content Management</h3><pre><code class="language-text-x-trilium-auto">note.type=text AND note.contentSize > 5000 AND
|
||||
#category=documentation AND note.childrenCount >= 3 AND
|
||||
note.dateModified >= MONTH-1
|
||||
orderBy note.dateModified desc</code></pre>
|
||||
<h3>Knowledge Base Maintenance</h3><pre><code class="language-text-x-trilium-auto">note.attributeCount = 0 AND note.childrenCount = 0 AND
|
||||
note.parentCount = 1 AND note.contentSize < 100 AND
|
||||
note.dateModified < TODAY-90</code></pre>
|
||||
<p>Finds potential cleanup candidates: small, untagged, isolated notes not
|
||||
modified in 90 days.</p>
|
||||
<h2>Next Steps</h2>
|
||||
<ul>
|
||||
<li><a href="#root/_help_yAFfA1SAYlr7">Search Examples and Use Cases</a> -
|
||||
Practical applications</li>
|
||||
<li><a href="#root/_help_UUBStSxWzjgA">Saved Searches</a> - Creating reusable
|
||||
search configurations</li>
|
||||
<li><a href="#root/_help_ttFz520bcNLf">Technical Search Details</a> - Implementation
|
||||
details and performance tuning</li>
|
||||
</ul>
|
||||
158
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Advanced Usage/Search/README.html
generated
vendored
158
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Advanced Usage/Search/README.html
generated
vendored
@@ -1,158 +0,0 @@
|
||||
<h2>Trilium Search Documentation</h2>
|
||||
<p>Welcome to the comprehensive guide for Trilium's powerful search capabilities.
|
||||
This documentation covers everything from basic text searches to advanced
|
||||
query expressions and performance optimization.</p>
|
||||
<h2>Quick Start</h2>
|
||||
<p>New to Trilium search? Start here:</p>
|
||||
<ul>
|
||||
<li><strong><a href="#root/_help_WcwMZ2tDZmXK">Search Fundamentals</a></strong> -
|
||||
Basic concepts, syntax, and operators</li>
|
||||
</ul>
|
||||
<h2>Documentation Sections</h2>
|
||||
<h3>Core Search Features</h3>
|
||||
<ul>
|
||||
<li><strong><a href="#root/_help_WcwMZ2tDZmXK">Search Fundamentals</a></strong> -
|
||||
Basic search syntax, operators, and concepts</li>
|
||||
<li><strong><a href="#root/_help_ey9TMFyD8SHR">Advanced Search Expressions</a></strong> -
|
||||
Complex queries, boolean logic, and relationship traversal</li>
|
||||
</ul>
|
||||
<h3>Practical Applications</h3>
|
||||
<ul>
|
||||
<li><strong><a href="#root/_help_yAFfA1SAYlr7">Search Examples and Use Cases</a></strong> -
|
||||
Real-world examples for common workflows</li>
|
||||
<li><strong><a href="#root/_help_UUBStSxWzjgA">Saved Searches</a></strong> -
|
||||
Creating dynamic collections and dashboards</li>
|
||||
</ul>
|
||||
<h3>Technical Reference</h3>
|
||||
<ul>
|
||||
<li><strong><a href="#root/_help_ttFz520bcNLf">Technical Search Details</a></strong> -
|
||||
Performance, implementation, and optimization</li>
|
||||
</ul>
|
||||
<h2>Key Search Capabilities</h2>
|
||||
<h3>Full-Text Search</h3>
|
||||
<ul>
|
||||
<li>Search note titles and content</li>
|
||||
<li>Exact phrase matching with quotes</li>
|
||||
<li>Case-insensitive with diacritic normalization</li>
|
||||
<li>Support for multiple note types (text, code, mermaid, canvas)</li>
|
||||
</ul>
|
||||
<h3>Attribute-Based Search</h3>
|
||||
<ul>
|
||||
<li>Label searches: <code>#tag</code>, <code>#category=book</code>
|
||||
</li>
|
||||
<li>Relation searches: <code>~author</code>, <code>~author.title=Tolkien</code>
|
||||
</li>
|
||||
<li>Complex attribute combinations</li>
|
||||
<li>Fuzzy attribute matching</li>
|
||||
</ul>
|
||||
<h3>Property Search</h3>
|
||||
<ul>
|
||||
<li>Note metadata: <code>note.type=text</code>, <code>note.dateCreated >= TODAY-7</code>
|
||||
</li>
|
||||
<li>Hierarchical queries: <code>note.parents.title=Books</code>
|
||||
</li>
|
||||
<li>Relationship traversal: <code>note.children.labels.status=active</code>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Advanced Features</h3>
|
||||
<ul>
|
||||
<li><strong>Progressive Search</strong>: Exact matching first, fuzzy fallback
|
||||
when needed</li>
|
||||
<li><strong>Fuzzy Search</strong>: Typo tolerance and spelling variations</li>
|
||||
<li><strong>Boolean Logic</strong>: Complex AND/OR/NOT combinations</li>
|
||||
<li><strong>Date Arithmetic</strong>: Dynamic date calculations (TODAY-30,
|
||||
YEAR+1)</li>
|
||||
<li><strong>Regular Expressions</strong>: Pattern matching with <code>%=</code> operator</li>
|
||||
<li><strong>Ordering and Limiting</strong>: Custom sort orders and result
|
||||
limits</li>
|
||||
</ul>
|
||||
<h2>Search Operators Quick Reference</h2>
|
||||
<h3>Text Operators</h3>
|
||||
<ul>
|
||||
<li><code>=</code> - Exact match</li>
|
||||
<li><code>!=</code> - Not equal</li>
|
||||
<li><code>*=*</code> - Contains</li>
|
||||
<li><code>=*</code> - Starts with</li>
|
||||
<li><code>*=</code> - Ends with</li>
|
||||
<li><code>%=</code> - Regular expression</li>
|
||||
<li><code>~=</code> - Fuzzy exact match</li>
|
||||
<li><code>~*</code> - Fuzzy contains match</li>
|
||||
</ul>
|
||||
<h3>Numeric Operators</h3>
|
||||
<ul>
|
||||
<li><code>=</code>, <code>!=</code>, <code>></code>, <code>>=</code>, <code><</code>, <code><=</code>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Boolean Operators</h3>
|
||||
<ul>
|
||||
<li><code>AND</code>, <code>OR</code>, <code>NOT</code>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Special Syntax</h3>
|
||||
<ul>
|
||||
<li><code>#labelName</code> - Label exists</li>
|
||||
<li><code>#labelName=value</code> - Label equals value</li>
|
||||
<li><code>~relationName</code> - Relation exists</li>
|
||||
<li><code>~relationName.property</code> - Relation target property</li>
|
||||
<li><code>note.property</code> - Note property access</li>
|
||||
<li><code>"exact phrase"</code> - Quoted phrase search</li>
|
||||
</ul>
|
||||
<h2>Common Search Patterns</h2>
|
||||
<h3>Simple Searches</h3><pre><code class="language-text-x-trilium-auto">hello world # Find notes containing both words
|
||||
"project management" # Find exact phrase
|
||||
#task # Find notes with "task" label
|
||||
~author # Find notes with "author" relation</code></pre>
|
||||
<h3>Attribute Searches</h3><pre><code class="language-text-x-trilium-auto">#book #author=Tolkien # Books by Tolkien
|
||||
#task #priority=high #status!=completed # High-priority incomplete tasks
|
||||
~project.title *=* alpha # Notes related to projects with "alpha" in title</code></pre>
|
||||
<h3>Date-Based Searches</h3><pre><code class="language-text-x-trilium-auto">note.dateCreated >= TODAY-7 # Notes created in last week
|
||||
#dueDate <= TODAY+30 # Items due in next 30 days
|
||||
#eventDate = YEAR # Events scheduled for this year</code></pre>
|
||||
<h3>Complex Queries</h3><pre><code class="language-text-x-trilium-auto">(#book OR #article) AND #topic=programming AND note.dateModified >= MONTH
|
||||
#project AND (#status=active OR #status=pending) AND not(note.isArchived=true)</code></pre>
|
||||
<h2>Getting Started Checklist</h2>
|
||||
<ol>
|
||||
<li><strong>Learn Basic Syntax</strong> - Start with simple text and tag searches</li>
|
||||
<li><strong>Understand Operators</strong> - Master the core operators (<code>=</code>, <code>*=*</code>,
|
||||
etc.)</li>
|
||||
<li><strong>Practice Attributes</strong> - Use <code>#</code> for labels and <code>~</code> for
|
||||
relations</li>
|
||||
<li><strong>Try Boolean Logic</strong> - Combine searches with AND/OR/NOT</li>
|
||||
<li><strong>Explore Properties</strong> - Use <code>note.</code> prefix for metadata
|
||||
searches</li>
|
||||
<li><strong>Create Saved Searches</strong> - Turn useful queries into dynamic
|
||||
collections</li>
|
||||
<li><strong>Optimize Performance</strong> - Learn about fast search and limits</li>
|
||||
</ol>
|
||||
<h2>Performance Tips</h2>
|
||||
<ul>
|
||||
<li><strong>Use Fast Search</strong> for attribute-only queries</li>
|
||||
<li><strong>Set Reasonable Limits</strong> to prevent large result sets</li>
|
||||
<li><strong>Start Specific</strong> with the most selective criteria first</li>
|
||||
<li><strong>Leverage Attributes</strong> instead of content search when possible</li>
|
||||
<li><strong>Cache Common Queries</strong> as saved searches</li>
|
||||
</ul>
|
||||
<h2>Need Help?</h2>
|
||||
<ul>
|
||||
<li><strong>Examples</strong>: Check <a href="#root/_help_yAFfA1SAYlr7">Search Examples and Use Cases</a> for
|
||||
practical patterns</li>
|
||||
<li><strong>Complex Queries</strong>: See <a href="#root/_help_ey9TMFyD8SHR">Advanced Search Expressions</a> for
|
||||
sophisticated techniques</li>
|
||||
<li><strong>Performance Issues</strong>: Review <a href="#root/_help_ttFz520bcNLf">Technical Search Details</a> for
|
||||
optimization</li>
|
||||
<li><strong>Dynamic Collections</strong>: Learn about <a href="#root/_help_UUBStSxWzjgA">Saved Searches</a> for
|
||||
automated organization</li>
|
||||
</ul>
|
||||
<h2>Search Workflow Integration</h2>
|
||||
<p>Trilium's search integrates seamlessly with your note-taking workflow:</p>
|
||||
<ul>
|
||||
<li><strong>Quick Search</strong> (Ctrl+S) for instant access</li>
|
||||
<li><strong>Saved Searches</strong> for dynamic organization</li>
|
||||
<li><strong>Search from Subtree</strong> for focused queries</li>
|
||||
<li><strong>Auto-complete</strong> suggestions in search dialogs</li>
|
||||
<li><strong>URL-triggered searches</strong> for bookmarkable queries</li>
|
||||
</ul>
|
||||
<p>Start with the fundamentals and gradually explore advanced features as
|
||||
your needs grow. Trilium's search system is designed to scale from simple
|
||||
text queries to sophisticated knowledge management systems.</p>
|
||||
<p>Happy searching! 🔍</p>
|
||||
@@ -1,293 +0,0 @@
|
||||
<h2>Saved Searches</h2>
|
||||
<p>Saved searches in Trilium allow you to create dynamic collections of notes
|
||||
that automatically update based on search criteria. They appear as special
|
||||
notes in your tree and provide a powerful way to organize and access related
|
||||
content.</p>
|
||||
<h2>Understanding Saved Searches</h2>
|
||||
<p>A saved search is a special note type that:</p>
|
||||
<ul>
|
||||
<li>Stores search criteria and configuration</li>
|
||||
<li>Dynamically displays matching notes as children</li>
|
||||
<li>Updates automatically when notes change</li>
|
||||
<li>Can be bookmarked and accessed like any other note</li>
|
||||
<li>Supports all search features including ordering and limits</li>
|
||||
</ul>
|
||||
<h2>Creating Saved Searches</h2>
|
||||
<h3>From Search Dialog</h3>
|
||||
<ol>
|
||||
<li>Open the search dialog (Ctrl+S or search icon)</li>
|
||||
<li>Configure your search criteria and options</li>
|
||||
<li>Click "Save to note" button</li>
|
||||
<li>Choose a name and location for the saved search</li>
|
||||
</ol>
|
||||
<h3>Manual Creation</h3>
|
||||
<ol>
|
||||
<li>Create a new note and set its type to "Saved Search"</li>
|
||||
<li>Configure the search using labels:
|
||||
<ul>
|
||||
<li><code>#searchString</code> - The search query</li>
|
||||
<li><code>#fastSearch</code> - Enable fast search mode</li>
|
||||
<li><code>#includeArchivedNotes</code> - Include archived notes</li>
|
||||
<li><code>#orderBy</code> - Sort field</li>
|
||||
<li><code>#orderDirection</code> - "asc" or "desc"</li>
|
||||
<li><code>#limit</code> - Maximum number of results</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<h3>Using Search Scripts</h3>
|
||||
<p>For complex logic, create a JavaScript note and link it:</p>
|
||||
<ul>
|
||||
<li><code>~searchScript</code> - Relation pointing to a backend script note</li>
|
||||
</ul>
|
||||
<h2>Basic Saved Search Examples</h2>
|
||||
<h3>Simple Text Search</h3><pre><code class="language-text-x-trilium-auto">#searchString=project management</code></pre>
|
||||
<p>Finds all notes containing "project management".</p>
|
||||
<h3>Tag-Based Collection</h3><pre><code class="language-text-x-trilium-auto">#searchString=#book #author=Tolkien
|
||||
#orderBy=publicationYear
|
||||
#orderDirection=desc</code></pre>
|
||||
<p>Creates a collection of Tolkien's books ordered by publication year.</p>
|
||||
<h3>Task Dashboard</h3><pre><code class="language-text-x-trilium-auto">#searchString=#task #status!=completed #assignee=me
|
||||
#orderBy=priority
|
||||
#orderDirection=desc
|
||||
#limit=20</code></pre>
|
||||
<p>Shows your top 20 incomplete tasks by priority.</p>
|
||||
<h3>Recent Activity</h3><pre><code class="language-text-x-trilium-auto">#searchString=note.dateModified >= TODAY-7
|
||||
#orderBy=dateModified
|
||||
#orderDirection=desc
|
||||
#limit=50</code></pre>
|
||||
<p>Shows the 50 most recently modified notes from the last week.</p>
|
||||
<h2>Advanced Saved Search Patterns</h2>
|
||||
<h3>Dynamic Date-Based Collections</h3>
|
||||
<h4>This Week's Content</h4><pre><code class="language-text-x-trilium-auto">#searchString=note.dateCreated >= TODAY-7 note.dateCreated < TODAY
|
||||
#orderBy=dateCreated
|
||||
#orderDirection=desc</code></pre>
|
||||
<h4>Monthly Review Collection</h4><pre><code class="language-text-x-trilium-auto">#searchString=#reviewed=false note.dateCreated >= MONTH note.dateCreated < MONTH+1
|
||||
#orderBy=dateCreated</code></pre>
|
||||
<h4>Upcoming Deadlines</h4><pre><code class="language-text-x-trilium-auto">#searchString=#dueDate >= TODAY #dueDate <= TODAY+14 #status!=completed
|
||||
#orderBy=dueDate
|
||||
#orderDirection=asc</code></pre>
|
||||
<h3>Project-Specific Collections</h3>
|
||||
<h4>Project Dashboard</h4><pre><code class="language-text-x-trilium-auto">#searchString=#project=alpha (#task OR #milestone OR #document)
|
||||
#orderBy=priority
|
||||
#orderDirection=desc</code></pre>
|
||||
<h4>Project Health Monitor</h4><pre><code class="language-text-x-trilium-auto">#searchString=#project=alpha #status=blocked OR (#dueDate < TODAY #status!=completed)
|
||||
#orderBy=dueDate
|
||||
#orderDirection=asc</code></pre>
|
||||
<h3>Content Type Collections</h3>
|
||||
<h4>Documentation Hub</h4><pre><code class="language-text-x-trilium-auto">#searchString=(#documentation OR #guide OR #manual) #product=api
|
||||
#orderBy=dateModified
|
||||
#orderDirection=desc</code></pre>
|
||||
<h4>Learning Path</h4><pre><code class="language-text-x-trilium-auto">#searchString=#course #level=beginner #topic=programming
|
||||
#orderBy=difficulty
|
||||
#orderDirection=asc</code></pre>
|
||||
<h2>Search Script Examples</h2>
|
||||
<p>For complex logic that can't be expressed in search strings, use JavaScript:</p>
|
||||
<h3>Custom Business Logic</h3><pre><code class="language-application-javascript-env-backend">// Find notes that need attention based on complex criteria
|
||||
const api = require('api');
|
||||
|
||||
const cutoffDate = new Date();
|
||||
cutoffDate.setDate(cutoffDate.getDate() - 30);
|
||||
|
||||
const results = [];
|
||||
|
||||
// Find high-priority tasks overdue by more than a week
|
||||
const overdueTasks = api.searchForNotes(`
|
||||
#task #priority=high #dueDate < TODAY-7 #status!=completed
|
||||
`);
|
||||
|
||||
// Find projects with no recent activity
|
||||
const staleProjets = api.searchForNotes(`
|
||||
#project #status=active note.dateModified < TODAY-30
|
||||
`);
|
||||
|
||||
// Find notes with many attributes but no content
|
||||
const overlabeledNotes = api.searchForNotes(`
|
||||
note.attributeCount > 5 note.contentSize < 100
|
||||
`);
|
||||
|
||||
return [...overdueTasks, ...staleProjects, ...overlabeledNotes]
|
||||
.map(note => note.noteId);</code></pre>
|
||||
<h3>Dynamic Tag-Based Grouping</h3><pre><code class="language-application-javascript-env-backend">// Group notes by quarter based on creation date
|
||||
const api = require('api');
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
const results = [];
|
||||
|
||||
for (let quarter = 1; quarter <= 4; quarter++) {
|
||||
const startMonth = (quarter - 1) * 3 + 1;
|
||||
const endMonth = quarter * 3;
|
||||
|
||||
const quarterNotes = api.searchForNotes(`
|
||||
note.dateCreated >= "${currentYear}-${String(startMonth).padStart(2, '0')}-01"
|
||||
note.dateCreated < "${currentYear}-${String(endMonth + 1).padStart(2, '0')}-01"
|
||||
#project
|
||||
`);
|
||||
|
||||
results.push(...quarterNotes.map(note => note.noteId));
|
||||
}
|
||||
|
||||
return results;</code></pre>
|
||||
<h3>Conditional Search Logic</h3><pre><code class="language-application-javascript-env-backend">// Smart dashboard that changes based on day of week
|
||||
const api = require('api');
|
||||
|
||||
const today = new Date();
|
||||
const dayOfWeek = today.getDay(); // 0 = Sunday, 1 = Monday, etc.
|
||||
|
||||
let searchQuery;
|
||||
|
||||
if (dayOfWeek === 1) { // Monday - weekly planning
|
||||
searchQuery = '#task #status=planned #week=' + getWeekNumber(today);
|
||||
} else if (dayOfWeek === 5) { // Friday - weekly review
|
||||
searchQuery = '#task #completed=true #week=' + getWeekNumber(today);
|
||||
} else { // Regular days - focus on today's work
|
||||
searchQuery = '#task #dueDate=TODAY #status!=completed';
|
||||
}
|
||||
|
||||
const notes = api.searchForNotes(searchQuery);
|
||||
return notes.map(note => note.noteId);
|
||||
|
||||
function getWeekNumber(date) {
|
||||
const firstDay = new Date(date.getFullYear(), 0, 1);
|
||||
const pastDays = Math.floor((date - firstDay) / 86400000);
|
||||
return Math.ceil((pastDays + firstDay.getDay() + 1) / 7);
|
||||
}</code></pre>
|
||||
<h2>Performance Optimization</h2>
|
||||
<h3>Fast Search for Large Collections</h3>
|
||||
<p>For collections that don't need content search:</p><pre><code class="language-text-x-trilium-auto">#searchString=#category=reference #type=article
|
||||
#fastSearch=true
|
||||
#limit=100</code></pre>
|
||||
<h3>Efficient Ordering</h3>
|
||||
<p>Use indexed properties for better performance:</p><pre><code class="language-text-x-trilium-auto">#orderBy=dateCreated
|
||||
#orderBy=title
|
||||
#orderBy=noteId</code></pre>
|
||||
<p>Avoid complex calculated orderings in large collections.</p>
|
||||
<h3>Result Limiting</h3>
|
||||
<p>Always set reasonable limits for large collections:</p><pre><code class="language-text-x-trilium-auto">#limit=50</code></pre>
|
||||
<p>For very large result sets, consider breaking into multiple saved searches.</p>
|
||||
<h2>Saved Search Organization</h2>
|
||||
<h3>Hierarchical Organization</h3>
|
||||
<p>Create a folder structure for saved searches:</p><pre><code class="language-text-x-trilium-auto">📁 Searches
|
||||
├── 📁 Projects
|
||||
│ ├── 🔍 Active Projects
|
||||
│ ├── 🔍 Overdue Tasks
|
||||
│ └── 🔍 Project Archive
|
||||
├── 📁 Content
|
||||
│ ├── 🔍 Recent Drafts
|
||||
│ ├── 🔍 Published Articles
|
||||
│ └── 🔍 Review Queue
|
||||
└── 📁 Maintenance
|
||||
├── 🔍 Untagged Notes
|
||||
├── 🔍 Cleanup Candidates
|
||||
└── 🔍 Orphaned Notes</code></pre>
|
||||
<h3>Search Naming Conventions</h3>
|
||||
<p>Use clear, descriptive names:</p>
|
||||
<ul>
|
||||
<li>"Active High-Priority Tasks"</li>
|
||||
<li>"This Month's Meeting Notes"</li>
|
||||
<li>"Unprocessed Inbox Items"</li>
|
||||
<li>"Literature Review Papers"</li>
|
||||
</ul>
|
||||
<h3>Search Labels</h3>
|
||||
<p>Tag saved searches for organization:</p><pre><code class="language-text-x-trilium-auto">#searchType=dashboard
|
||||
#searchType=maintenance
|
||||
#searchType=archive
|
||||
#frequency=daily
|
||||
#frequency=weekly</code></pre>
|
||||
<h2>Dashboard Creation</h2>
|
||||
<h3>Personal Dashboard</h3>
|
||||
<p>Combine multiple saved searches in a parent note:</p><pre><code class="language-text-x-trilium-auto">📋 My Dashboard
|
||||
├── 🔍 Today's Tasks
|
||||
├── 🔍 Urgent Items
|
||||
├── 🔍 Recent Notes
|
||||
├── 🔍 Upcoming Deadlines
|
||||
└── 🔍 Weekly Review Items</code></pre>
|
||||
<h3>Project Dashboard</h3><pre><code class="language-text-x-trilium-auto">📋 Project Alpha Dashboard
|
||||
├── 🔍 Active Tasks
|
||||
├── 🔍 Blocked Items
|
||||
├── 🔍 Recent Updates
|
||||
├── 🔍 Milestones
|
||||
└── 🔍 Team Notes</code></pre>
|
||||
<h3>Content Dashboard</h3><pre><code class="language-text-x-trilium-auto">📋 Content Management
|
||||
├── 🔍 Draft Articles
|
||||
├── 🔍 Review Queue
|
||||
├── 🔍 Published This Month
|
||||
├── 🔍 High-Engagement Posts
|
||||
└── 🔍 Content Ideas</code></pre>
|
||||
<h2>Maintenance and Updates</h2>
|
||||
<h3>Regular Review</h3>
|
||||
<p>Periodically review saved searches for:</p>
|
||||
<ul>
|
||||
<li>Outdated search criteria</li>
|
||||
<li>Performance issues</li>
|
||||
<li>Unused collections</li>
|
||||
<li>Scope creep</li>
|
||||
</ul>
|
||||
<h3>Search Evolution</h3>
|
||||
<p>As your note-taking evolves, update searches:</p>
|
||||
<ul>
|
||||
<li>Add new tags to existing searches</li>
|
||||
<li>Refine criteria based on usage patterns</li>
|
||||
<li>Split large collections into smaller ones</li>
|
||||
<li>Merge rarely-used collections</li>
|
||||
</ul>
|
||||
<h3>Performance Monitoring</h3>
|
||||
<p>Watch for performance issues:</p>
|
||||
<ul>
|
||||
<li>Slow-loading saved searches</li>
|
||||
<li>Memory usage with large result sets</li>
|
||||
<li>Search timeout errors</li>
|
||||
</ul>
|
||||
<h2>Troubleshooting</h2>
|
||||
<h3>Common Issues</h3>
|
||||
<h4>Empty Results</h4>
|
||||
<ul>
|
||||
<li>Check search syntax</li>
|
||||
<li>Verify tag spellings</li>
|
||||
<li>Ensure notes have required attributes</li>
|
||||
<li>Test search components individually</li>
|
||||
</ul>
|
||||
<h4>Performance Problems</h4>
|
||||
<ul>
|
||||
<li>Add <code>#fastSearch=true</code> for attribute-only searches</li>
|
||||
<li>Reduce result limits</li>
|
||||
<li>Simplify complex criteria</li>
|
||||
<li>Use indexed properties for ordering</li>
|
||||
</ul>
|
||||
<h4>Unexpected Results</h4>
|
||||
<ul>
|
||||
<li>Enable debug mode to see query parsing</li>
|
||||
<li>Test search in search dialog first</li>
|
||||
<li>Check for case sensitivity issues</li>
|
||||
<li>Verify date formats and ranges</li>
|
||||
</ul>
|
||||
<h3>Best Practices</h3>
|
||||
<h4>Search Design</h4>
|
||||
<ul>
|
||||
<li>Start simple and add complexity gradually</li>
|
||||
<li>Test searches thoroughly before saving</li>
|
||||
<li>Document complex search logic</li>
|
||||
<li>Use meaningful names and descriptions</li>
|
||||
</ul>
|
||||
<h4>Performance</h4>
|
||||
<ul>
|
||||
<li>Set appropriate limits</li>
|
||||
<li>Use fast search when possible</li>
|
||||
<li>Avoid overly complex expressions</li>
|
||||
<li>Monitor search execution time</li>
|
||||
</ul>
|
||||
<h4>Organization</h4>
|
||||
<ul>
|
||||
<li>Group related searches</li>
|
||||
<li>Use consistent naming conventions</li>
|
||||
<li>Archive unused searches</li>
|
||||
<li>Regular cleanup and maintenance</li>
|
||||
</ul>
|
||||
<h2>Next Steps</h2>
|
||||
<ul>
|
||||
<li><a href="#root/_help_ttFz520bcNLf">Technical Search Details</a> - Understanding
|
||||
search performance and implementation</li>
|
||||
<li><a href="#root/_help_yAFfA1SAYlr7">Search Examples and Use Cases</a> -
|
||||
More practical examples</li>
|
||||
<li><a href="#root/_help_ey9TMFyD8SHR">Advanced Search Expressions</a> - Complex
|
||||
query construction</li>
|
||||
</ul>
|
||||
@@ -1,152 +0,0 @@
|
||||
<h2>Search Examples and Use Cases</h2>
|
||||
<p>This guide provides practical examples of how to use Trilium's search
|
||||
capabilities for common organizational patterns and workflows.</p>
|
||||
<h2>Personal Knowledge Management</h2>
|
||||
<h3>Research and Learning</h3>
|
||||
<p>Track your learning progress and find related materials:</p><pre><code class="language-text-x-trilium-auto">#topic=javascript #status=learning</code></pre>
|
||||
<p>Find all JavaScript materials you're currently learning.</p><pre><code class="language-text-x-trilium-auto">#course #completed=false note.dateCreated >= MONTH-1</code></pre>
|
||||
<p>Find courses started in the last month that aren't completed.</p><pre><code class="language-text-x-trilium-auto">#book #topic *=* programming #rating >= 4</code></pre>
|
||||
<p>Find highly-rated programming books.</p><pre><code class="language-text-x-trilium-auto">#paper ~author.title *=* "Andrew Ng" #field=machine-learning</code></pre>
|
||||
<p>Find machine learning papers by Andrew Ng.</p>
|
||||
<h3>Meeting and Event Management</h3>
|
||||
<p>Organize meetings, notes, and follow-ups:</p><pre><code class="language-text-x-trilium-auto">#meeting note.dateCreated >= TODAY-7 #attendee *=* smith</code></pre>
|
||||
<p>Find this week's meetings with Smith.</p><pre><code class="language-text-x-trilium-auto">#meeting #actionItems #status!=completed</code></pre>
|
||||
<p>Find meetings with outstanding action items.</p><pre><code class="language-text-x-trilium-auto">#event #date >= TODAY #date <= TODAY+30</code></pre>
|
||||
<p>Find upcoming events in the next 30 days.</p><pre><code class="language-text-x-trilium-auto">#meeting #project=alpha note.dateCreated >= MONTH</code></pre>
|
||||
<p>Find this month's meetings about project alpha.</p>
|
||||
<h3>Note Organization and Cleanup</h3>
|
||||
<p>Maintain and organize your note structure:</p><pre><code class="language-text-x-trilium-auto">note.childrenCount = 0 note.parentCount = 1 note.contentSize < 50 note.dateModified < TODAY-180</code></pre>
|
||||
<p>Find small, isolated notes not modified in 6 months (cleanup candidates).</p><pre><code class="language-text-x-trilium-auto">note.attributeCount = 0 note.type=text note.contentSize > 1000</code></pre>
|
||||
<p>Find large text notes without any labels (might need categorization).</p><pre><code class="language-text-x-trilium-auto">#draft note.dateCreated < TODAY-30</code></pre>
|
||||
<p>Find old draft notes that might need attention.</p><pre><code class="language-text-x-trilium-auto">note.parentCount > 3 note.type=text</code></pre>
|
||||
<p>Find notes that are heavily cloned (might indicate important content).</p>
|
||||
<h2>Project Management</h2>
|
||||
<h3>Task Tracking</h3>
|
||||
<p>Manage tasks and project progress:</p><pre><code class="language-text-x-trilium-auto">#task #priority=high #status!=completed #assignee=me</code></pre>
|
||||
<p>Find your high-priority incomplete tasks.</p><pre><code class="language-text-x-trilium-auto">#task #dueDate <= TODAY+3 #dueDate >= TODAY #status!=completed</code></pre>
|
||||
<p>Find tasks due in the next 3 days.</p><pre><code class="language-text-x-trilium-auto">#project=website #task #status=blocked</code></pre>
|
||||
<p>Find blocked tasks in the website project.</p><pre><code class="language-text-x-trilium-auto">#task #estimatedHours > 0 #actualHours > 0 orderBy note.dateModified desc</code></pre>
|
||||
<p>Find tasks with time tracking data, sorted by recent updates.</p>
|
||||
<h3>Project Oversight</h3>
|
||||
<p>Monitor project health and progress:</p><pre><code class="language-text-x-trilium-auto">#project #status=active note.children.labels.status = blocked</code></pre>
|
||||
<p>Find active projects with blocked tasks.</p><pre><code class="language-text-x-trilium-auto">#project #startDate <= TODAY-90 #status!=completed</code></pre>
|
||||
<p>Find projects that started over 90 days ago but aren't completed.</p><pre><code class="language-text-x-trilium-auto">#milestone #targetDate <= TODAY #status!=achieved</code></pre>
|
||||
<p>Find overdue milestones.</p><pre><code class="language-text-x-trilium-auto">#project orderBy note.childrenCount desc limit 10</code></pre>
|
||||
<p>Find the 10 largest projects by number of sub-notes.</p>
|
||||
<h3>Resource Planning</h3>
|
||||
<p>Track resources and dependencies:</p><pre><code class="language-text-x-trilium-auto">#resource #type=person #availability < 50</code></pre>
|
||||
<p>Find people with low availability.</p><pre><code class="language-text-x-trilium-auto">#dependency #status=pending #project=mobile-app</code></pre>
|
||||
<p>Find pending dependencies for the mobile app project.</p><pre><code class="language-text-x-trilium-auto">#budget #project #spent > #allocated</code></pre>
|
||||
<p>Find projects over budget.</p>
|
||||
<h2>Content Creation and Writing</h2>
|
||||
<h3>Writing Projects</h3>
|
||||
<p>Manage articles, books, and documentation:</p><pre><code class="language-text-x-trilium-auto">#article #status=draft #wordCount >= 1000</code></pre>
|
||||
<p>Find substantial draft articles.</p><pre><code class="language-text-x-trilium-auto">#chapter #book=novel #status=outline</code></pre>
|
||||
<p>Find novel chapters still in outline stage.</p><pre><code class="language-text-x-trilium-auto">#blog-post #published=false #topic=technology</code></pre>
|
||||
<p>Find unpublished technology blog posts.</p><pre><code class="language-text-x-trilium-auto">#documentation #lastReviewed < TODAY-90 #product=api</code></pre>
|
||||
<p>Find API documentation not reviewed in 90 days.</p>
|
||||
<h3>Editorial Workflow</h3>
|
||||
<p>Track editing and publication status:</p><pre><code class="language-text-x-trilium-auto">#article #editor=jane #status=review</code></pre>
|
||||
<p>Find articles assigned to Jane for review.</p><pre><code class="language-text-x-trilium-auto">#manuscript #submissionDate >= TODAY-30 #status=pending</code></pre>
|
||||
<p>Find manuscripts submitted in the last 30 days still pending.</p><pre><code class="language-text-x-trilium-auto">#publication #acceptanceDate >= YEAR #status=accepted</code></pre>
|
||||
<p>Find accepted publications this year.</p>
|
||||
<h3>Content Research</h3>
|
||||
<p>Organize research materials and sources:</p><pre><code class="language-text-x-trilium-auto">#source #reliability >= 8 #topic *=* climate</code></pre>
|
||||
<p>Find reliable sources about climate topics.</p><pre><code class="language-text-x-trilium-auto">#quote #author *=* Einstein #verified=true</code></pre>
|
||||
<p>Find verified Einstein quotes.</p><pre><code class="language-text-x-trilium-auto">#citation #used=false #relevance=high</code></pre>
|
||||
<p>Find high-relevance citations not yet used.</p>
|
||||
<h2>Business and Professional Use</h2>
|
||||
<h3>Client Management</h3>
|
||||
<p>Track client relationships and projects:</p><pre><code class="language-text-x-trilium-auto">#client=acme #project #status=active</code></pre>
|
||||
<p>Find active projects for ACME client.</p><pre><code class="language-text-x-trilium-auto">#meeting #client #date >= MONTH #followUp=required</code></pre>
|
||||
<p>Find client meetings this month requiring follow-up.</p><pre><code class="language-text-x-trilium-auto">#contract #renewalDate <= TODAY+60 #renewalDate >= TODAY</code></pre>
|
||||
<p>Find contracts expiring in the next 60 days.</p><pre><code class="language-text-x-trilium-auto">#invoice #status=unpaid #dueDate < TODAY</code></pre>
|
||||
<p>Find overdue unpaid invoices.</p>
|
||||
<h3>Process Documentation</h3>
|
||||
<p>Maintain procedures and workflows:</p><pre><code class="language-text-x-trilium-auto">#procedure #department=engineering #lastUpdated < TODAY-365</code></pre>
|
||||
<p>Find engineering procedures not updated in a year.</p><pre><code class="language-text-x-trilium-auto">#workflow #status=active #automation=possible</code></pre>
|
||||
<p>Find active workflows that could be automated.</p><pre><code class="language-text-x-trilium-auto">#checklist #process=onboarding #role=developer</code></pre>
|
||||
<p>Find onboarding checklists for developers.</p>
|
||||
<h3>Compliance and Auditing</h3>
|
||||
<p>Track compliance requirements and audits:</p><pre><code class="language-text-x-trilium-auto">#compliance #standard=sox #nextReview <= TODAY+30</code></pre>
|
||||
<p>Find SOX compliance items due for review soon.</p><pre><code class="language-text-x-trilium-auto">#audit #finding #severity=high #status!=resolved</code></pre>
|
||||
<p>Find unresolved high-severity audit findings.</p><pre><code class="language-text-x-trilium-auto">#policy #department=hr #effectiveDate >= YEAR</code></pre>
|
||||
<p>Find HR policies that became effective this year.</p>
|
||||
<h2>Academic and Educational Use</h2>
|
||||
<h3>Course Management</h3>
|
||||
<p>Organize courses and educational content:</p><pre><code class="language-text-x-trilium-auto">#course #semester=fall-2024 #assignment #dueDate >= TODAY</code></pre>
|
||||
<p>Find upcoming assignments for fall 2024 courses.</p><pre><code class="language-text-x-trilium-auto">#lecture #course=physics #topic *=* quantum</code></pre>
|
||||
<p>Find physics lectures about quantum topics.</p><pre><code class="language-text-x-trilium-auto">#student #grade < 70 #course=mathematics</code></pre>
|
||||
<p>Find students struggling in mathematics.</p><pre><code class="language-text-x-trilium-auto">#syllabus #course #lastUpdated < TODAY-180</code></pre>
|
||||
<p>Find syllabi not updated in 6 months.</p>
|
||||
<h3>Research Management</h3>
|
||||
<p>Track research projects and publications:</p><pre><code class="language-text-x-trilium-auto">#experiment #status=running #endDate <= TODAY+7</code></pre>
|
||||
<p>Find experiments ending in the next week.</p><pre><code class="language-text-x-trilium-auto">#dataset #size > 1000000 #cleaned=true #public=false</code></pre>
|
||||
<p>Find large, cleaned, private datasets.</p><pre><code class="language-text-x-trilium-auto">#hypothesis #tested=false #priority=high</code></pre>
|
||||
<p>Find high-priority untested hypotheses.</p><pre><code class="language-text-x-trilium-auto">#collaboration #institution *=* stanford #status=active</code></pre>
|
||||
<p>Find active collaborations with Stanford.</p>
|
||||
<h3>Grant and Funding</h3>
|
||||
<p>Manage funding applications and requirements:</p><pre><code class="language-text-x-trilium-auto">#grant #deadline <= TODAY+30 #deadline >= TODAY #status=in-progress</code></pre>
|
||||
<p>Find grant applications due in the next 30 days.</p><pre><code class="language-text-x-trilium-auto">#funding #amount >= 100000 #status=awarded #startDate >= YEAR</code></pre>
|
||||
<p>Find large grants awarded this year.</p><pre><code class="language-text-x-trilium-auto">#report #funding #dueDate <= TODAY+14 #status!=submitted</code></pre>
|
||||
<p>Find funding reports due in 2 weeks.</p>
|
||||
<h2>Technical Documentation</h2>
|
||||
<h3>Code and Development</h3>
|
||||
<p>Track code-related notes and documentation:</p><pre><code class="language-text-x-trilium-auto">#bug #severity=critical #status!=fixed #product=webapp</code></pre>
|
||||
<p>Find critical unfixed bugs in the web app.</p><pre><code class="language-text-x-trilium-auto">#feature #version=2.0 #status=implemented #tested=false</code></pre>
|
||||
<p>Find version 2.0 features that are implemented but not tested.</p><pre><code class="language-text-x-trilium-auto">#api #endpoint #deprecated=true #removalDate <= TODAY+90</code></pre>
|
||||
<p>Find deprecated API endpoints scheduled for removal soon.</p><pre><code class="language-text-x-trilium-auto">#architecture #component=database #lastReviewed < TODAY-180</code></pre>
|
||||
<p>Find database architecture documentation not reviewed in 6 months.</p>
|
||||
<h3>System Administration</h3>
|
||||
<p>Manage infrastructure and operations:</p><pre><code class="language-text-x-trilium-auto">#server #status=maintenance #scheduledDate >= TODAY #scheduledDate <= TODAY+7</code></pre>
|
||||
<p>Find servers scheduled for maintenance this week.</p><pre><code class="language-text-x-trilium-auto">#backup #status=failed #date >= TODAY-7</code></pre>
|
||||
<p>Find backup failures in the last week.</p><pre><code class="language-text-x-trilium-auto">#security #vulnerability #severity=high #patched=false</code></pre>
|
||||
<p>Find unpatched high-severity vulnerabilities.</p><pre><code class="language-text-x-trilium-auto">#monitoring #alert #frequency > 10 #period=week</code></pre>
|
||||
<p>Find alerts triggering more than 10 times per week.</p>
|
||||
<h2>Data Analysis and Reporting</h2>
|
||||
<h3>Performance Tracking</h3>
|
||||
<p>Monitor metrics and KPIs:</p><pre><code class="language-text-x-trilium-auto">#metric #kpi=true #trend=declining #period=month</code></pre>
|
||||
<p>Find declining monthly KPIs.</p><pre><code class="language-text-x-trilium-auto">#report #frequency=weekly #lastGenerated < TODAY-10</code></pre>
|
||||
<p>Find weekly reports that haven't been generated in 10 days.</p><pre><code class="language-text-x-trilium-auto">#dashboard #stakeholder=executive #lastUpdated < TODAY-7</code></pre>
|
||||
<p>Find executive dashboards not updated this week.</p>
|
||||
<h3>Trend Analysis</h3>
|
||||
<p>Track patterns and changes over time:</p><pre><code class="language-text-x-trilium-auto">#data #source=sales #period=quarter #analyzed=false</code></pre>
|
||||
<p>Find unanalyzed quarterly sales data.</p><pre><code class="language-text-x-trilium-auto">#trend #direction=up #significance=high #period=month</code></pre>
|
||||
<p>Find significant positive monthly trends.</p><pre><code class="language-text-x-trilium-auto">#forecast #accuracy < 80 #model=linear #period=quarter</code></pre>
|
||||
<p>Find inaccurate quarterly linear forecasts.</p>
|
||||
<h2>Search Strategy Tips</h2>
|
||||
<h3>Building Effective Queries</h3>
|
||||
<ol>
|
||||
<li><strong>Start Specific</strong>: Begin with the most selective criteria</li>
|
||||
<li><strong>Add Gradually</strong>: Build complexity incrementally</li>
|
||||
<li><strong>Test Components</strong>: Verify each part of complex queries</li>
|
||||
<li><strong>Use Shortcuts</strong>: Leverage <code>#</code> and <code>~</code> shortcuts
|
||||
for efficiency</li>
|
||||
</ol>
|
||||
<h3>Performance Optimization</h3>
|
||||
<ol>
|
||||
<li><strong>Use Fast Search</strong>: For large databases, enable fast search
|
||||
when content isn't needed</li>
|
||||
<li><strong>Limit Results</strong>: Add limits to prevent overwhelming result
|
||||
sets</li>
|
||||
<li><strong>Order Strategically</strong>: Put the most useful results first</li>
|
||||
<li><strong>Cache Common Queries</strong>: Save frequently used searches</li>
|
||||
</ol>
|
||||
<h3>Maintenance Patterns</h3>
|
||||
<p>Regular queries for note maintenance:</p><pre><code class="language-text-x-trilium-auto"># Weekly cleanup check
|
||||
note.attributeCount = 0 note.type=text note.contentSize < 100 note.dateModified < TODAY-30
|
||||
|
||||
# Monthly project review
|
||||
#project #status=active note.dateModified < TODAY-30
|
||||
|
||||
# Quarterly archive review
|
||||
note.isArchived=false note.dateModified < TODAY-90 note.childrenCount = 0</code></pre>
|
||||
<h2>Next Steps</h2>
|
||||
<ul>
|
||||
<li><a href="#root/_help_UUBStSxWzjgA">Saved Searches</a> - Convert these examples
|
||||
into reusable saved searches</li>
|
||||
<li><a href="#root/_help_ttFz520bcNLf">Technical Search Details</a> - Understanding
|
||||
performance and implementation</li>
|
||||
<li><a href="#root/_help_WcwMZ2tDZmXK">Search Fundamentals</a> - Review basic
|
||||
concepts and syntax</li>
|
||||
</ul>
|
||||
@@ -1,153 +0,0 @@
|
||||
<h2>Search Fundamentals</h2>
|
||||
<p>Trilium's search system is a powerful tool for finding and organizing
|
||||
notes. It supports multiple search modes, from simple text queries to complex
|
||||
expressions using attributes, relationships, and note properties.</p>
|
||||
<h2>Search Types Overview</h2>
|
||||
<p>Trilium provides three main search approaches:</p>
|
||||
<ol>
|
||||
<li><strong>Full-text Search</strong> - Searches within note titles and content</li>
|
||||
<li><strong>Attribute Search</strong> - Searches based on labels and relations
|
||||
attached to notes</li>
|
||||
<li><strong>Property Search</strong> - Searches based on note metadata (type,
|
||||
creation date, etc.)</li>
|
||||
</ol>
|
||||
<p>These can be combined in powerful ways to create precise queries.</p>
|
||||
<h2>Basic Search Syntax</h2>
|
||||
<h3>Simple Text Search</h3><pre><code class="language-text-x-trilium-auto">hello world</code></pre>
|
||||
<p>Finds notes containing both "hello" and "world" anywhere in the title
|
||||
or content.</p>
|
||||
<h3>Quoted Text Search</h3><pre><code class="language-text-x-trilium-auto">"hello world"</code></pre>
|
||||
<p>Finds notes containing the exact phrase "hello world".</p>
|
||||
<h3>Attribute Search</h3><pre><code class="language-text-x-trilium-auto">#tag</code></pre>
|
||||
<p>Finds notes with the label "tag".</p><pre><code class="language-text-x-trilium-auto">#category=book</code></pre>
|
||||
<p>Finds notes with label "category" set to "book".</p>
|
||||
<h3>Relation Search</h3><pre><code class="language-text-x-trilium-auto">~author</code></pre>
|
||||
<p>Finds notes with a relation named "author".</p><pre><code class="language-text-x-trilium-auto">~author.title=Tolkien</code></pre>
|
||||
<p>Finds notes with an "author" relation pointing to a note titled "Tolkien".</p>
|
||||
<h2>Search Operators</h2>
|
||||
<h3>Text Operators</h3>
|
||||
<ul>
|
||||
<li><code>=</code> - Exact match</li>
|
||||
<li><code>!=</code> - Not equal</li>
|
||||
<li><code>*=*</code> - Contains (substring)</li>
|
||||
<li><code>=*</code> - Starts with</li>
|
||||
<li><code>*=</code> - Ends with</li>
|
||||
<li><code>%=</code> - Regular expression match</li>
|
||||
<li><code>~=</code> - Fuzzy exact match</li>
|
||||
<li><code>~*</code> - Fuzzy contains match</li>
|
||||
</ul>
|
||||
<h3>Numeric Operators</h3>
|
||||
<ul>
|
||||
<li><code>=</code> - Equal</li>
|
||||
<li><code>!=</code> - Not equal</li>
|
||||
<li><code>></code> - Greater than</li>
|
||||
<li><code>>=</code> - Greater than or equal</li>
|
||||
<li><code><</code> - Less than</li>
|
||||
<li><code><=</code> - Less than or equal</li>
|
||||
</ul>
|
||||
<h3>Boolean Operators</h3>
|
||||
<ul>
|
||||
<li><code>AND</code> - Both conditions must be true</li>
|
||||
<li><code>OR</code> - Either condition must be true</li>
|
||||
<li><code>NOT</code> or <code>not()</code> - Condition must be false</li>
|
||||
</ul>
|
||||
<h2>Search Context and Scope</h2>
|
||||
<h3>Search Scope</h3>
|
||||
<p>By default, search covers:</p>
|
||||
<ul>
|
||||
<li>Note titles</li>
|
||||
<li>Note content (for text-based note types)</li>
|
||||
<li>Label names and values</li>
|
||||
<li>Relation names</li>
|
||||
<li>Note properties</li>
|
||||
</ul>
|
||||
<h3>Fast Search Mode</h3>
|
||||
<p>When enabled, fast search:</p>
|
||||
<ul>
|
||||
<li>Searches only titles and attributes</li>
|
||||
<li>Skips note content</li>
|
||||
<li>Provides faster results for large databases</li>
|
||||
</ul>
|
||||
<h3>Archived Notes</h3>
|
||||
<ul>
|
||||
<li>Excluded by default</li>
|
||||
<li>Can be included with "Include archived" option</li>
|
||||
</ul>
|
||||
<h2>Case Sensitivity and Normalization</h2>
|
||||
<ul>
|
||||
<li>All searches are case-insensitive</li>
|
||||
<li>Diacritics are normalized ("café" matches "cafe")</li>
|
||||
<li>Unicode characters are properly handled</li>
|
||||
</ul>
|
||||
<h2>Performance Considerations</h2>
|
||||
<h3>Content Size Limits</h3>
|
||||
<ul>
|
||||
<li>Note content is limited to 10MB for search processing</li>
|
||||
<li>Larger notes are still searchable by title and attributes</li>
|
||||
</ul>
|
||||
<h3>Progressive Search Strategy</h3>
|
||||
<ol>
|
||||
<li><strong>Exact Search Phase</strong>: Fast exact matching (handles 90%+
|
||||
of searches)</li>
|
||||
<li><strong>Fuzzy Search Phase</strong>: Activated when exact search returns
|
||||
fewer than 5 high-quality results</li>
|
||||
<li><strong>Result Ordering</strong>: Exact matches always appear before fuzzy
|
||||
matches</li>
|
||||
</ol>
|
||||
<h3>Search Optimization Tips</h3>
|
||||
<ul>
|
||||
<li>Use specific terms rather than very common words</li>
|
||||
<li>Combine full-text with attribute searches for precision</li>
|
||||
<li>Use fast search for large databases when content search isn't needed</li>
|
||||
<li>Limit results when dealing with very large result sets</li>
|
||||
</ul>
|
||||
<h2>Special Characters and Escaping</h2>
|
||||
<h3>Reserved Characters</h3>
|
||||
<p>These characters have special meaning in search queries:</p>
|
||||
<ul>
|
||||
<li><code>#</code> - Label indicator</li>
|
||||
<li><code>~</code> - Relation indicator</li>
|
||||
<li><code>()</code> - Grouping</li>
|
||||
<li><code>"</code> <code>'</code> <code>`</code> - Quotes for exact phrases</li>
|
||||
</ul>
|
||||
<h3>Escaping Special Characters</h3>
|
||||
<p>Use backslash to search for literal special characters:</p><pre><code class="language-text-x-trilium-auto">\#hashtag</code></pre>
|
||||
<p>Searches for the literal text "#hashtag" instead of a label.</p>
|
||||
<p>Use quotes to include special characters in phrases:</p><pre><code class="language-text-x-trilium-auto">"note.txt file"</code></pre>
|
||||
<p>Searches for the exact phrase including the dot.</p>
|
||||
<h2>Date and Time Values</h2>
|
||||
<h3>Special Date Keywords</h3>
|
||||
<ul>
|
||||
<li><code>TODAY</code> - Current date</li>
|
||||
<li><code>NOW</code> - Current date and time</li>
|
||||
<li><code>MONTH</code> - Current month</li>
|
||||
<li><code>YEAR</code> - Current year</li>
|
||||
</ul>
|
||||
<h3>Date Arithmetic</h3><pre><code class="language-text-x-trilium-auto">#dateCreated >= TODAY-30</code></pre>
|
||||
<p>Finds notes created in the last 30 days.</p><pre><code class="language-text-x-trilium-auto">#eventDate = YEAR+1</code></pre>
|
||||
<p>Finds notes with eventDate set to next year.</p>
|
||||
<h2>Search Results and Scoring</h2>
|
||||
<h3>Result Ranking</h3>
|
||||
<p>Results are ordered by:</p>
|
||||
<ol>
|
||||
<li>Relevance score (based on term frequency and position)</li>
|
||||
<li>Note depth (closer to root ranks higher)</li>
|
||||
<li>Alphabetical order for ties</li>
|
||||
</ol>
|
||||
<h3>Progressive Search Behavior</h3>
|
||||
<ul>
|
||||
<li>Exact matches always rank before fuzzy matches</li>
|
||||
<li>High-quality exact matches prevent fuzzy search activation</li>
|
||||
<li>Fuzzy matches help find content with typos or variations</li>
|
||||
</ul>
|
||||
<h2>Next Steps</h2>
|
||||
<ul>
|
||||
<li><a href="#root/_help_ey9TMFyD8SHR">Advanced Search Expressions</a> - Complex
|
||||
queries and combinations</li>
|
||||
<li><a href="#root/_help_yAFfA1SAYlr7">Search Examples and Use Cases</a> -
|
||||
Practical applications</li>
|
||||
<li><a href="#root/_help_UUBStSxWzjgA">Saved Searches</a> - Creating dynamic
|
||||
collections</li>
|
||||
<li><a href="#root/_help_ttFz520bcNLf">Technical Search Details</a> - Under-the-hood
|
||||
implementation</li>
|
||||
</ul>
|
||||
@@ -1,486 +0,0 @@
|
||||
<h2>Technical Search Details</h2>
|
||||
<p>This guide provides technical information about Trilium's search implementation,
|
||||
performance characteristics, and optimization strategies for power users
|
||||
and administrators.</p>
|
||||
<h2>Search Architecture Overview</h2>
|
||||
<h3>Three-Layer Search System</h3>
|
||||
<p>Trilium's search operates across three cache layers:</p>
|
||||
<ol>
|
||||
<li><strong>Becca (Backend Cache)</strong>: Server-side entity cache containing
|
||||
notes, attributes, and relationships</li>
|
||||
<li><strong>Froca (Frontend Cache)</strong>: Client-side mirror providing
|
||||
fast UI updates</li>
|
||||
<li><strong>Database Layer</strong>: SQLite database with FTS (Full-Text Search)
|
||||
support</li>
|
||||
</ol>
|
||||
<h3>Search Processing Pipeline</h3>
|
||||
<ol>
|
||||
<li><strong>Lexical Analysis</strong>: Query parsing and tokenization</li>
|
||||
<li><strong>Expression Building</strong>: Converting tokens to executable
|
||||
expressions</li>
|
||||
<li><strong>Progressive Execution</strong>: Exact search followed by optional
|
||||
fuzzy search</li>
|
||||
<li><strong>Result Scoring</strong>: Relevance calculation and ranking</li>
|
||||
<li><strong>Result Presentation</strong>: Formatting and highlighting</li>
|
||||
</ol>
|
||||
<h2>Query Processing Details</h2>
|
||||
<h3>Lexical Analysis (Lex)</h3>
|
||||
<p>The lexer breaks down search queries into components:</p><pre><code class="language-application-javascript-env-backend">// Input: 'project #status=active note.dateCreated >= TODAY-7'
|
||||
// Output:
|
||||
{
|
||||
fulltextTokens: ['project'],
|
||||
expressionTokens: ['#status', '=', 'active', 'note', '.', 'dateCreated', '>=', 'TODAY-7']
|
||||
}</code></pre>
|
||||
<h4>Token Types</h4>
|
||||
<ul>
|
||||
<li><strong>Fulltext Tokens</strong>: Regular search terms</li>
|
||||
<li><strong>Expression Tokens</strong>: Attributes, operators, and property
|
||||
references</li>
|
||||
<li><strong>Quoted Strings</strong>: Exact phrase matches</li>
|
||||
<li><strong>Escaped Characters</strong>: Literal special characters</li>
|
||||
</ul>
|
||||
<h3>Expression Building (Parse)</h3>
|
||||
<p>Tokens are converted into executable expression trees:</p><pre><code class="language-application-javascript-env-backend">// Expression tree for: #book AND #author=Tolkien
|
||||
AndExp([
|
||||
AttributeExistsExp('label', 'book'),
|
||||
LabelComparisonExp('label', 'author', equals('tolkien'))
|
||||
])</code></pre>
|
||||
<h4>Expression Types</h4>
|
||||
<ul>
|
||||
<li><code>AndExp</code>, <code>OrExp</code>, <code>NotExp</code>: Boolean logic</li>
|
||||
<li><code>AttributeExistsExp</code>: Label/relation existence</li>
|
||||
<li><code>LabelComparisonExp</code>: Label value comparison</li>
|
||||
<li><code>RelationWhereExp</code>: Relation target queries</li>
|
||||
<li><code>PropertyComparisonExp</code>: Note property filtering</li>
|
||||
<li><code>NoteContentFulltextExp</code>: Content search</li>
|
||||
<li><code>OrderByAndLimitExp</code>: Result ordering and limiting</li>
|
||||
</ul>
|
||||
<h3>Progressive Search Strategy</h3>
|
||||
<h4>Phase 1: Exact Search</h4><pre><code class="language-application-javascript-env-backend">// Fast exact matching
|
||||
const exactResults = performSearch(expression, searchContext, false);</code></pre>
|
||||
<p>Characteristics:</p>
|
||||
<ul>
|
||||
<li>Substring matching for text</li>
|
||||
<li>Exact attribute matching</li>
|
||||
<li>Property-based filtering</li>
|
||||
<li>Handles 90%+ of searches</li>
|
||||
<li>Sub-second response time</li>
|
||||
</ul>
|
||||
<h4>Phase 2: Fuzzy Fallback</h4><pre><code class="language-application-javascript-env-backend">// Activated when exact results < 5 high-quality matches
|
||||
if (highQualityResults.length < 5) {
|
||||
const fuzzyResults = performSearch(expression, searchContext, true);
|
||||
return mergeExactAndFuzzyResults(exactResults, fuzzyResults);
|
||||
}</code></pre>
|
||||
<p>Characteristics:</p>
|
||||
<ul>
|
||||
<li>Edit distance calculations</li>
|
||||
<li>Phrase proximity matching</li>
|
||||
<li>Typo tolerance</li>
|
||||
<li>Performance safeguards</li>
|
||||
<li>Exact matches always rank first</li>
|
||||
</ul>
|
||||
<h2>Performance Characteristics</h2>
|
||||
<h3>Search Limits and Thresholds</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Value</th>
|
||||
<th>Purpose</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>MAX_SEARCH_CONTENT_SIZE</code>
|
||||
</td>
|
||||
<td>2MB</td>
|
||||
<td>Database-level content filtering</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MIN_FUZZY_TOKEN_LENGTH</code>
|
||||
</td>
|
||||
<td>3 chars</td>
|
||||
<td>Minimum length for fuzzy matching</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MAX_EDIT_DISTANCE</code>
|
||||
</td>
|
||||
<td>2 chars</td>
|
||||
<td>Maximum character changes for fuzzy</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>MAX_PHRASE_PROXIMITY</code>
|
||||
</td>
|
||||
<td>10 words</td>
|
||||
<td>Maximum distance for phrase matching</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>RESULT_SUFFICIENCY_THRESHOLD</code>
|
||||
</td>
|
||||
<td>5 results</td>
|
||||
<td>Threshold for fuzzy activation</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>ABSOLUTE_MAX_CONTENT_SIZE</code>
|
||||
</td>
|
||||
<td>100MB</td>
|
||||
<td>Hard limit to prevent system crash</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>ABSOLUTE_MAX_WORD_COUNT</code>
|
||||
</td>
|
||||
<td>2M words</td>
|
||||
<td>Hard limit for word processing</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Performance Optimization</h3>
|
||||
<h4>Database-Level Optimizations</h4><pre><code class="language-text-x-mariadb">-- Content size filtering at database level
|
||||
SELECT noteId, type, mime, content, isProtected
|
||||
FROM notes JOIN blobs USING (blobId)
|
||||
WHERE type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
|
||||
AND isDeleted = 0
|
||||
AND LENGTH(content) < 2097152 -- 2MB limit</code></pre>
|
||||
<h4>Memory Management</h4>
|
||||
<ul>
|
||||
<li>Single-array edit distance calculation</li>
|
||||
<li>Early termination for distant matches</li>
|
||||
<li>Progressive content processing</li>
|
||||
<li>Cached regular expressions</li>
|
||||
</ul>
|
||||
<h4>Search Context Optimization</h4><pre><code class="language-application-javascript-env-backend">// Efficient search context configuration
|
||||
const searchContext = new SearchContext({
|
||||
fastSearch: true, // Skip content search
|
||||
limit: 50, // Reasonable result limit
|
||||
orderBy: 'dateCreated', // Use indexed property
|
||||
includeArchivedNotes: false // Reduce search space
|
||||
});</code></pre>
|
||||
<h2>Fuzzy Search Implementation</h2>
|
||||
<h3>Edit Distance Algorithm</h3>
|
||||
<p>Trilium uses an optimized Levenshtein distance calculation:</p><pre><code class="language-application-javascript-env-backend">// Optimized single-array implementation
|
||||
function calculateOptimizedEditDistance(str1, str2, maxDistance) {
|
||||
// Early termination checks
|
||||
if (Math.abs(str1.length - str2.length) > maxDistance) {
|
||||
return maxDistance + 1;
|
||||
}
|
||||
|
||||
// Single array optimization
|
||||
let previousRow = Array.from({ length: str2.length + 1 }, (_, i) => i);
|
||||
let currentRow = new Array(str2.length + 1);
|
||||
|
||||
for (let i = 1; i <= str1.length; i++) {
|
||||
currentRow[0] = i;
|
||||
let minInRow = i;
|
||||
|
||||
for (let j = 1; j <= str2.length; j++) {
|
||||
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
||||
currentRow[j] = Math.min(
|
||||
previousRow[j] + 1, // deletion
|
||||
currentRow[j - 1] + 1, // insertion
|
||||
previousRow[j - 1] + cost // substitution
|
||||
);
|
||||
minInRow = Math.min(minInRow, currentRow[j]);
|
||||
}
|
||||
|
||||
// Early termination if row minimum exceeds threshold
|
||||
if (minInRow > maxDistance) return maxDistance + 1;
|
||||
|
||||
[previousRow, currentRow] = [currentRow, previousRow];
|
||||
}
|
||||
|
||||
return previousRow[str2.length];
|
||||
}</code></pre>
|
||||
<h3>Phrase Proximity Matching</h3>
|
||||
<p>For multi-token fuzzy searches:</p><pre><code class="language-application-javascript-env-backend">// Check if tokens appear within reasonable proximity
|
||||
function hasProximityMatch(tokenPositions, maxDistance = 10) {
|
||||
// For 2 tokens, simple distance check
|
||||
if (tokenPositions.length === 2) {
|
||||
const [pos1, pos2] = tokenPositions;
|
||||
return pos1.some(p1 => pos2.some(p2 => Math.abs(p1 - p2) <= maxDistance));
|
||||
}
|
||||
|
||||
// For multiple tokens, find sequence within range
|
||||
const findSequence = (remaining, currentPos) => {
|
||||
if (remaining.length === 0) return true;
|
||||
const [nextPositions, ...rest] = remaining;
|
||||
return nextPositions.some(pos =>
|
||||
Math.abs(pos - currentPos) <= maxDistance &&
|
||||
findSequence(rest, pos)
|
||||
);
|
||||
};
|
||||
|
||||
const [firstPositions, ...rest] = tokenPositions;
|
||||
return firstPositions.some(startPos => findSequence(rest, startPos));
|
||||
}</code></pre>
|
||||
<h2>Indexing and Storage</h2>
|
||||
<h3>Database Schema Optimization</h3><pre><code class="language-text-x-mariadb">-- Relevant indexes for search performance
|
||||
CREATE INDEX idx_notes_type ON notes(type);
|
||||
CREATE INDEX idx_notes_isDeleted ON notes(isDeleted);
|
||||
CREATE INDEX idx_notes_dateCreated ON notes(dateCreated);
|
||||
CREATE INDEX idx_notes_dateModified ON notes(dateModified);
|
||||
CREATE INDEX idx_attributes_name ON attributes(name);
|
||||
CREATE INDEX idx_attributes_type ON attributes(type);
|
||||
CREATE INDEX idx_attributes_value ON attributes(value);</code></pre>
|
||||
<h3>Content Processing</h3>
|
||||
<p>Notes are processed differently based on type:</p><pre><code class="language-application-javascript-env-backend">// Content preprocessing by note type
|
||||
function preprocessContent(content, type, mime) {
|
||||
content = normalize(content.toString());
|
||||
|
||||
if (type === "text" && mime === "text/html") {
|
||||
content = stripTags(content);
|
||||
content = content.replace(/&nbsp;/g, " ");
|
||||
} else if (type === "mindMap" && mime === "application/json") {
|
||||
content = processMindmapContent(content);
|
||||
} else if (type === "canvas" && mime === "application/json") {
|
||||
const canvasData = JSON.parse(content);
|
||||
const textElements = canvasData.elements
|
||||
.filter(el => el.type === "text" && el.text)
|
||||
.map(el => el.text);
|
||||
content = normalize(textElements.join(" "));
|
||||
}
|
||||
|
||||
return content.trim();
|
||||
}</code></pre>
|
||||
<h2>Search Result Processing</h2>
|
||||
<h3>Scoring Algorithm</h3>
|
||||
<p>Results are scored based on multiple factors:</p><pre><code class="language-application-javascript-env-backend">function computeScore(fulltextQuery, highlightedTokens, enableFuzzyMatching) {
|
||||
let score = 0;
|
||||
|
||||
// Title matches get higher score
|
||||
if (this.noteTitle.toLowerCase().includes(fulltextQuery.toLowerCase())) {
|
||||
score += 10;
|
||||
}
|
||||
|
||||
// Path matches (hierarchical context)
|
||||
const pathMatch = this.notePathArray.some(pathNote =>
|
||||
pathNote.title.toLowerCase().includes(fulltextQuery.toLowerCase())
|
||||
);
|
||||
if (pathMatch) score += 5;
|
||||
|
||||
// Attribute matches
|
||||
score += this.attributeMatches * 3;
|
||||
|
||||
// Content snippet quality
|
||||
if (this.contentSnippet && this.contentSnippet.length > 0) {
|
||||
score += 2;
|
||||
}
|
||||
|
||||
// Fuzzy match penalty
|
||||
if (enableFuzzyMatching && this.isFuzzyMatch) {
|
||||
score *= 0.8; // 20% penalty for fuzzy matches
|
||||
}
|
||||
|
||||
return score;
|
||||
}</code></pre>
|
||||
<h3>Result Merging</h3>
|
||||
<p>Exact and fuzzy results are carefully merged:</p><pre><code class="language-application-javascript-env-backend">function mergeExactAndFuzzyResults(exactResults, fuzzyResults) {
|
||||
// Deduplicate - exact results take precedence
|
||||
const exactNoteIds = new Set(exactResults.map(r => r.noteId));
|
||||
const additionalFuzzyResults = fuzzyResults.filter(r =>
|
||||
!exactNoteIds.has(r.noteId)
|
||||
);
|
||||
|
||||
// Sort within each category
|
||||
exactResults.sort(byScoreAndDepth);
|
||||
additionalFuzzyResults.sort(byScoreAndDepth);
|
||||
|
||||
// CRITICAL: Exact matches always come first
|
||||
return [...exactResults, ...additionalFuzzyResults];
|
||||
}</code></pre>
|
||||
<h2>Performance Monitoring</h2>
|
||||
<h3>Search Metrics</h3>
|
||||
<p>Monitor these performance indicators:</p><pre><code class="language-application-javascript-env-backend">// Performance tracking
|
||||
const searchMetrics = {
|
||||
totalQueries: 0,
|
||||
exactSearchTime: 0,
|
||||
fuzzySearchTime: 0,
|
||||
resultCount: 0,
|
||||
cacheHitRate: 0,
|
||||
slowQueries: [] // queries taking > 1 second
|
||||
};</code></pre>
|
||||
<h3>Memory Usage</h3>
|
||||
<p>Track memory consumption:</p><pre><code class="language-application-javascript-env-backend">// Memory monitoring
|
||||
const memoryMetrics = {
|
||||
searchCacheSize: 0,
|
||||
activeSearchContexts: 0,
|
||||
largeContentNotes: 0, // notes > 1MB
|
||||
indexSize: 0
|
||||
};</code></pre>
|
||||
<h3>Query Complexity Analysis</h3>
|
||||
<p>Identify expensive queries:</p><pre><code class="language-application-javascript-env-backend">// Query complexity factors
|
||||
const complexityFactors = {
|
||||
tokenCount: query.split(' ').length,
|
||||
hasRegex: query.includes('%='),
|
||||
hasFuzzy: query.includes('~=') || query.includes('~*'),
|
||||
hasRelationTraversal: query.includes('.relations.'),
|
||||
hasNestedProperties: (query.match(/\./g) || []).length > 2,
|
||||
hasOrderBy: query.includes('orderBy'),
|
||||
estimatedResultSize: 'unknown'
|
||||
};</code></pre>
|
||||
<h2>Troubleshooting Performance Issues</h2>
|
||||
<h3>Common Performance Problems</h3>
|
||||
<h4>Slow Full-Text Search</h4><pre><code class="language-application-javascript-env-backend">// Diagnosis
|
||||
- Check note content sizes
|
||||
- Verify content type filtering
|
||||
- Monitor regex usage
|
||||
- Review fuzzy search activation
|
||||
|
||||
// Solutions
|
||||
- Enable fast search for attribute-only queries
|
||||
- Add content size limits
|
||||
- Optimize regex patterns
|
||||
- Tune fuzzy search thresholds</code></pre>
|
||||
<h4>Memory Issues</h4><pre><code class="language-application-javascript-env-backend">// Diagnosis
|
||||
- Monitor result set sizes
|
||||
- Check for large content processing
|
||||
- Review search context caching
|
||||
- Identify memory leaks
|
||||
|
||||
// Solutions
|
||||
- Add result limits
|
||||
- Implement progressive loading
|
||||
- Clear unused search contexts
|
||||
- Optimize content preprocessing</code></pre>
|
||||
<h4>High CPU Usage</h4><pre><code class="language-application-javascript-env-backend">// Diagnosis
|
||||
- Profile fuzzy search operations
|
||||
- Check edit distance calculations
|
||||
- Monitor regex compilation
|
||||
- Review phrase proximity matching
|
||||
|
||||
// Solutions
|
||||
- Increase minimum fuzzy token length
|
||||
- Reduce maximum edit distance
|
||||
- Cache compiled regexes
|
||||
- Limit phrase proximity distance</code></pre>
|
||||
<h3>Debugging Tools</h3>
|
||||
<h4>Debug Mode</h4>
|
||||
<p>Enable search debugging:</p><pre><code class="language-application-javascript-env-backend">// Search context with debugging
|
||||
const searchContext = new SearchContext({
|
||||
debug: true // Logs expression parsing and execution
|
||||
});</code></pre>
|
||||
<p>Output includes:</p>
|
||||
<ul>
|
||||
<li>Token parsing results</li>
|
||||
<li>Expression tree structure</li>
|
||||
<li>Execution timing</li>
|
||||
<li>Result scoring details</li>
|
||||
</ul>
|
||||
<h4>Performance Profiling</h4><pre><code class="language-application-javascript-env-backend">// Manual performance measurement
|
||||
const startTime = Date.now();
|
||||
const results = searchService.findResultsWithQuery(query, searchContext);
|
||||
const endTime = Date.now();
|
||||
console.log(`Search took ${endTime - startTime}ms for ${results.length} results`);</code></pre>
|
||||
<h4>Query Analysis</h4><pre><code class="language-application-javascript-env-backend">// Analyze query complexity
|
||||
function analyzeQuery(query) {
|
||||
return {
|
||||
tokenCount: query.split(/\s+/).length,
|
||||
hasAttributes: /#|\~/.test(query),
|
||||
hasProperties: /note\./.test(query),
|
||||
hasRegex: /%=/.test(query),
|
||||
hasFuzzy: /~[=*]/.test(query),
|
||||
complexity: calculateComplexityScore(query)
|
||||
};
|
||||
}</code></pre>
|
||||
<h2>Configuration and Tuning</h2>
|
||||
<h3>Server Configuration</h3>
|
||||
<p>Relevant settings in <code>config.ini</code>:</p><pre><code class="language-text-x-toml"># Search-related settings
|
||||
[Search]
|
||||
maxContentSize=2097152 # 2MB content limit
|
||||
minFuzzyTokenLength=3 # Minimum chars for fuzzy
|
||||
maxEditDistance=2 # Edit distance limit
|
||||
resultSufficiencyThreshold=5 # Fuzzy activation threshold
|
||||
enableProgressiveSearch=true # Enable progressive strategy
|
||||
cacheSearchResults=true # Cache frequent searches
|
||||
|
||||
# Performance settings
|
||||
[Performance]
|
||||
searchTimeoutMs=30000 # 30 second search timeout
|
||||
maxSearchResults=1000 # Hard limit on results
|
||||
enableSearchProfiling=false # Performance logging</code></pre>
|
||||
<h3>Runtime Tuning</h3>
|
||||
<p>Adjust search behavior programmatically:</p><pre><code class="language-application-javascript-env-backend">// Dynamic configuration
|
||||
const searchConfig = {
|
||||
maxContentSize: 1024 * 1024, // 1MB for faster processing
|
||||
enableFuzzySearch: false, // Exact only for speed
|
||||
resultLimit: 50, // Smaller result sets
|
||||
useIndexedPropertiesOnly: true // Skip expensive calculations
|
||||
};</code></pre>
|
||||
<h2>Best Practices for Performance</h2>
|
||||
<h3>Query Design</h3>
|
||||
<ol>
|
||||
<li><strong>Start Specific</strong>: Use selective criteria first</li>
|
||||
<li><strong>Limit Results</strong>: Always set reasonable limits</li>
|
||||
<li><strong>Use Indexes</strong>: Prefer indexed properties for ordering</li>
|
||||
<li><strong>Avoid Regex</strong>: Use simple operators when possible</li>
|
||||
<li><strong>Cache Common Queries</strong>: Save frequently used searches</li>
|
||||
</ol>
|
||||
<h3>System Administration</h3>
|
||||
<ol>
|
||||
<li><strong>Monitor Performance</strong>: Track slow queries and memory usage</li>
|
||||
<li><strong>Regular Maintenance</strong>: Clean up unused notes and attributes</li>
|
||||
<li><strong>Index Optimization</strong>: Ensure database indexes are current</li>
|
||||
<li><strong>Content Management</strong>: Archive or compress large content</li>
|
||||
</ol>
|
||||
<h3>Development Guidelines</h3>
|
||||
<ol>
|
||||
<li><strong>Test Performance</strong>: Benchmark complex queries</li>
|
||||
<li><strong>Profile Regularly</strong>: Identify performance regressions</li>
|
||||
<li><strong>Optimize Incrementally</strong>: Make small, measured improvements</li>
|
||||
<li><strong>Document Complexity</strong>: Note expensive operations</li>
|
||||
</ol>
|
||||
<h2>Advanced Configuration</h2>
|
||||
<h3>Custom Search Extensions</h3>
|
||||
<p>Extend search functionality with custom expressions:</p><pre><code class="language-application-javascript-env-backend">// Custom expression example
|
||||
class CustomDateRangeExp extends Expression {
|
||||
constructor(dateField, startDate, endDate) {
|
||||
super();
|
||||
this.dateField = dateField;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
execute(inputNoteSet, executionContext, searchContext) {
|
||||
// Custom logic for date range filtering
|
||||
// with optimized performance characteristics
|
||||
}
|
||||
}</code></pre>
|
||||
<h3>Search Result Caching</h3>
|
||||
<p>Implement result caching for frequent queries:</p><pre><code class="language-application-javascript-env-backend">// Simple LRU cache for search results
|
||||
class SearchResultCache {
|
||||
constructor(maxSize = 100) {
|
||||
this.cache = new Map();
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
get(queryKey) {
|
||||
if (this.cache.has(queryKey)) {
|
||||
// Move to end (most recently used)
|
||||
const value = this.cache.get(queryKey);
|
||||
this.cache.delete(queryKey);
|
||||
this.cache.set(queryKey, value);
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
set(queryKey, results) {
|
||||
if (this.cache.size >= this.maxSize) {
|
||||
// Remove least recently used
|
||||
const firstKey = this.cache.keys().next().value;
|
||||
this.cache.delete(firstKey);
|
||||
}
|
||||
this.cache.set(queryKey, results);
|
||||
}
|
||||
}</code></pre>
|
||||
<h2>Next Steps</h2>
|
||||
<ul>
|
||||
<li><a href="#root/_help_WcwMZ2tDZmXK">Search Fundamentals</a> - Basic concepts
|
||||
and syntax</li>
|
||||
<li><a href="#root/_help_ey9TMFyD8SHR">Advanced Search Expressions</a> - Complex
|
||||
query construction</li>
|
||||
<li><a href="#root/_help_yAFfA1SAYlr7">Search Examples and Use Cases</a> -
|
||||
Practical applications</li>
|
||||
<li><a href="#root/_help_UUBStSxWzjgA">Saved Searches</a> - Creating dynamic
|
||||
collections</li>
|
||||
</ul>
|
||||
@@ -1,61 +0,0 @@
|
||||
<p>Running Trilium on a server lets you access your notes from any device
|
||||
through a web browser and enables synchronization between multiple Trilium
|
||||
instances. This guide covers the different ways to install and configure
|
||||
Trilium on your server.</p>
|
||||
<h2>Choose Your Installation Method</h2>
|
||||
<p>The easiest way to get started is with Docker, which works on most systems
|
||||
and architectures. If you prefer not to manage your own server, PikaPods
|
||||
offers managed hosting.</p>
|
||||
<p><strong>Recommended approaches:</strong>
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="#root/_help_FNVzcT2FwEjq">Docker Installation</a> - Works on AMD64
|
||||
and ARM architectures</li>
|
||||
<li><a href="https://www.pikapods.com/pods?run=trilium-next">PikaPods managed hosting</a> -
|
||||
No server management required</li>
|
||||
<li><a href="#root/_help_Q5jqLlwsnIBb">Packaged Server Installation</a> - Native
|
||||
Linux packages</li>
|
||||
</ul>
|
||||
<p><strong>Advanced options:</strong>
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="#root/_help_baqzSqQefEpE">Manual Installation</a> - Full control
|
||||
over the setup</li>
|
||||
<li><a href="#root/_help_9ErAobCLD1jl">Kubernetes</a> - For container orchestration</li>
|
||||
<li><a href="#root/_help_6YVZgQtfkxkS">NixOS Module</a> - Declarative configuration</li>
|
||||
</ul>
|
||||
<p>All server installations include both desktop and mobile web interfaces.</p>
|
||||
<h2>Configuration</h2>
|
||||
<p>Trilium stores its configuration in a <code>config.ini</code> file located
|
||||
in the <a href="#root/_help_dvbMBRXYMM2G">data directory</a>. To customize
|
||||
your installation, copy the sample configuration file and modify it:</p><pre><code class="language-text-x-sh">cp config-sample.ini config.ini</code></pre>
|
||||
<p>You can also use environment variables instead of the config file. This
|
||||
is particularly useful for Docker deployments. See the <a href="#root/_help_SneMubD5wTR6">configuration guide</a> for
|
||||
all available options.</p>
|
||||
<h3>Changing the Data Directory</h3>
|
||||
<p>To store Trilium's data (database, config, backups) in a custom location,
|
||||
set the <code>TRILIUM_DATA_DIR</code> environment variable:</p><pre><code class="language-text-x-sh">export TRILIUM_DATA_DIR=/path/to/your/trilium-data</code></pre>
|
||||
<h3>Upload Size Limits</h3>
|
||||
<p>By default, Trilium limits file uploads to 250MB. You can adjust this
|
||||
limit based on your needs:</p><pre><code class="language-text-x-sh"># Increase limit to 450MB
|
||||
export MAX_ALLOWED_FILE_SIZE_MB=450
|
||||
|
||||
# Remove limit entirely (use with caution)
|
||||
export TRILIUM_NO_UPLOAD_LIMIT=true</code></pre>
|
||||
<h3>Disabling Authentication</h3>
|
||||
<p>See <a class="reference-link" href="#root/_help_CpWc4lepON8y">Authentication</a>.</p>
|
||||
<h2>Reverse Proxy Setup</h2>
|
||||
<p>If you want to access Trilium through a domain name or alongside other
|
||||
web services, you'll need to configure a reverse proxy. Here's a basic
|
||||
nginx configuration:</p><pre><code class="language-text-x-nginx-conf">location /trilium/ {
|
||||
proxy_pass http://127.0.0.1:8080/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
|
||||
# Allow larger file uploads (in server block)
|
||||
client_max_body_size 0; # 0 = unlimited</code></pre>
|
||||
<p>For Apache configuration, see the <a href="#root/_help_Eu2zkbyE2QPK">Apache proxy setup</a> guide.</p>
|
||||
@@ -1,43 +0,0 @@
|
||||
<aside class="admonition warning">
|
||||
<p>This page describes manually installing Trilium on your server. <strong>Note that this is a not well supported way to install Trilium, problems may appear, information laid out here is quite out of date. It is recommended to use either</strong>
|
||||
<a
|
||||
class="reference-link" href="#root/_help_FNVzcT2FwEjq">Docker Server Installation</a> <strong>or</strong> <a class="reference-link"
|
||||
href="#root/_help_Q5jqLlwsnIBb">Packaged server installation</a><strong>.</strong>
|
||||
</p>
|
||||
</aside>
|
||||
<h2>Requirements</h2>
|
||||
<p>Trilium is a node.js application. Supported (tested) version of node.js
|
||||
is latest 14.X.X and 16.X.X. Trilium might work with older versions as
|
||||
well.</p>
|
||||
<p>You can check your node version with this command (node.js needs to be
|
||||
installed):</p><pre><code class="language-text-x-trilium-auto">node --version</code></pre>
|
||||
<p>If your Linux distribution has only an outdated version of node.js, you
|
||||
can take a look at the installation instruction on node.js website, which
|
||||
covers most popular distributions.</p>
|
||||
<h3>Dependencies</h3>
|
||||
<p>There are some dependencies required. You can see command for Debian and
|
||||
its derivatives (like Ubuntu) below:</p><pre><code class="language-text-x-trilium-auto">sudo apt install libpng16-16 libpng-dev pkg-config autoconf libtool build-essential nasm libx11-dev libxkbfile-dev</code></pre>
|
||||
<h2>Installation</h2>
|
||||
<h3>Download</h3>
|
||||
<p>You can either download source code zip/tar from <a href="https://github.com/TriliumNext/Trilium/releases/latest">https://github.com/TriliumNext/Trilium/releases/latest</a>.</p>
|
||||
<p>For the latest version including betas, clone Git repository <strong>from</strong> <code>main</code> <strong>branch</strong> with:</p><pre><code class="language-text-x-trilium-auto">git clone -b main https://github.com/triliumnext/trilium.git</code></pre>
|
||||
<h2>Installation</h2><pre><code class="language-text-x-trilium-auto">cd trilium
|
||||
|
||||
# download all node dependencies
|
||||
npm install
|
||||
|
||||
# make sure the better-sqlite3 binary is there
|
||||
npm rebuild
|
||||
|
||||
# bundles & minifies frontend JavaScript
|
||||
npm run webpack</code></pre>
|
||||
<h2>Run</h2><pre><code class="language-text-x-trilium-auto">cd trilium
|
||||
|
||||
# using nohup to make sure trilium keeps running after user logs out
|
||||
nohup TRILIUM_ENV=dev node src/www &</code></pre>
|
||||
<p>The application by default starts up on port 8080, so you can open your
|
||||
browser and navigate to <a href="http://localhost:8080">http://localhost:8080</a> to
|
||||
access Trilium (replace "localhost" with your hostname).</p>
|
||||
<h2>TLS</h2>
|
||||
<p>Don't forget to <a href="#root/_help_1G2n2zCStCir">configure TLS</a> which
|
||||
is required for secure usage!</p>
|
||||
@@ -1,25 +0,0 @@
|
||||
<p>Trilium does not support multiple users. In order to have two or more
|
||||
persons with their own set of notes, multiple server instances must be
|
||||
set up. It is also not possible to use multiple <a href="#root/_help_KFhm9yCthQOh">sync</a> servers.</p>
|
||||
<p>To allow multiple server instances on a single physical server:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>For <a class="reference-link" href="#root/_help_Q5jqLlwsnIBb">Packaged version for Linux</a> or
|
||||
<a
|
||||
class="reference-link" href="#root/_help_baqzSqQefEpE">Manually</a>, if starting the server manually just specify a different
|
||||
port and data directory per instance:</p><pre><code class="language-text-x-trilium-auto">TRILIUM_NETWORK_PORT=8080 TRILIUM_DATA_DIR=/path/to/your/data-dir-A /opt/trilium/trilium.sh</code></pre>
|
||||
<p>For a second instance:</p><pre><code class="language-text-x-trilium-auto">TRILIUM_NETWORK_PORT=8081 TRILIUM_DATA_DIR=/path/to/your/data-dir-B /opt/trilium/trilium.sh</code></pre>
|
||||
<p>If using <code>systemd</code>, then set the <a href="https://serverfault.com/questions/413397/how-to-set-environment-variable-in-systemd-service">environment variables in the service configuration</a>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>For <a class="reference-link" href="#root/_help_FNVzcT2FwEjq">Using Docker</a>,
|
||||
simply use two different containers, each with their own port binding and
|
||||
data directory.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>For <a class="reference-link" href="#root/_help_6YVZgQtfkxkS">On NixOS</a>,
|
||||
the only possible way is to use Docker OCI containers or at least one NixOS
|
||||
container with its own service definition.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>For support or additional context, see the related <a href="https://github.com/orgs/TriliumNext/discussions/1642#discussioncomment-12768808">GitHub Discussion</a>.</p>
|
||||
@@ -1,15 +0,0 @@
|
||||
<p>This page describes configuring the Trilium module included in NixOS.</p>
|
||||
<h2>Requirements</h2>
|
||||
<p><a href="https://nixos.org/">NixOS</a> installation.</p>
|
||||
<h2>Configuration</h2>
|
||||
<p>Add this to your <code>configuration.nix</code>:</p><pre><code class="language-text-x-trilium-auto">services.trilium-server.enable = true;
|
||||
|
||||
# default data directory: /var/lib/trilium
|
||||
#services.trilium-server.dataDir = "/var/lib/trilium-sync-server";
|
||||
|
||||
# default bind address: 127.0.0.1, port 8080
|
||||
#services.trilium-server.host = "0.0.0.0";
|
||||
#services.trilium-server.port = 12783;</code></pre>
|
||||
<p>Uncomment any option you would like to change.</p>
|
||||
<p>See the <a href="https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=trilium-server">NixOS options list</a> for
|
||||
more options (including nginx reverse proxy configuration).</p>
|
||||
@@ -1,167 +0,0 @@
|
||||
<p>This is essentially Trilium sources + node modules + node.js runtime packaged
|
||||
into one 7z file.</p>
|
||||
<h2>Steps</h2>
|
||||
<ul>
|
||||
<li>SSH into your server</li>
|
||||
<li>use <code>wget</code> (or <code>curl</code>) to download latest <code>TriliumNotes-Server-[VERSION]-linux-x64.tar.xz</code> (copy
|
||||
link from <a href="https://github.com/TriliumNext/Trilium/releases">release page</a>,
|
||||
notice <code>-Server</code> suffix) on your server.</li>
|
||||
<li>unpack the archive, e.g. using <code>tar -xf -d TriliumNotes-Server-[VERSION]-linux-x64.tar.xz</code>
|
||||
</li>
|
||||
<li><code>cd trilium-linux-x64-server</code>
|
||||
</li>
|
||||
<li><code>./trilium.sh</code>
|
||||
</li>
|
||||
<li>you can open the browser and open http://[your-server-hostname]:8080 and
|
||||
you should see Trilium initialization page</li>
|
||||
</ul>
|
||||
<p>The problem with above steps is that once you close the SSH connection,
|
||||
the Trilium process is terminated. To avoid that, you have two options:</p>
|
||||
<ul>
|
||||
<li>Kill it (with e.g. <kbd>Ctrl</kbd> + <kbd>C</kbd>) and run again like this: <code>nohup ./trilium.sh &</code>.
|
||||
(nohup keeps the process running in the background, <code>&</code> runs
|
||||
it in the background)</li>
|
||||
<li>Configure systemd to automatically run Trilium in the background on every
|
||||
boot</li>
|
||||
</ul>
|
||||
<h2>Configure Trilium to auto-run on boot with systemd</h2>
|
||||
<ul>
|
||||
<li>After downloading, extract and move Trilium:</li>
|
||||
</ul><pre><code class="language-text-x-trilium-auto">tar -xvf TriliumNotes-Server-[VERSION]-linux-x64.tar.xz
|
||||
sudo mv trilium-linux-x64-server /opt/trilium</code></pre>
|
||||
<ul>
|
||||
<li>Create the service:</li>
|
||||
</ul><pre><code class="language-text-x-trilium-auto">sudo nano /etc/systemd/system/trilium.service</code></pre>
|
||||
<ul>
|
||||
<li>Paste this into the file (replace the user and group as needed):</li>
|
||||
</ul><pre><code class="language-text-x-trilium-auto">[Unit]
|
||||
Description=Trilium Daemon
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
User=xxx
|
||||
Group=xxx
|
||||
Type=simple
|
||||
ExecStart=/opt/trilium/trilium.sh
|
||||
WorkingDirectory=/opt/trilium/
|
||||
|
||||
TimeoutStopSec=20
|
||||
# KillMode=process leads to error, according to https://www.freedesktop.org/software/systemd/man/systemd.kill.html
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target</code></pre>
|
||||
<ul>
|
||||
<li>Save the file (CTRL-S) and exit (CTRL-X)</li>
|
||||
<li>Enable and launch the service:</li>
|
||||
</ul><pre><code class="language-text-x-trilium-auto">sudo systemctl enable --now -q trilium</code></pre>
|
||||
<ul>
|
||||
<li>You can now open a browser to http://[your-server-hostname]:8080 and you
|
||||
should see the Trilium initialization page.</li>
|
||||
</ul>
|
||||
<h2>Simple Autoupdate for Server</h2>
|
||||
<p>Run as the same User Trilium runs</p>
|
||||
<p>if you run as root please remove 'sudo' from the commands</p>
|
||||
<p>requires "jq" <code>apt install jq</code>
|
||||
</p>
|
||||
<p>It will stop the service above, overwrite everything (i expect no config.ini),
|
||||
and start service It also creates a version file in the Trilium directory
|
||||
so it updates only with a newer Version</p><pre><code class="language-text-x-trilium-auto">#!/bin/bash
|
||||
|
||||
# Configuration
|
||||
REPO="TriliumNext/Trilium"
|
||||
PATTERN="TriliumNotes-Server-.*-linux-x64.tar.xz"
|
||||
DOWNLOAD_DIR="/var/tmp/trilium_download"
|
||||
OUTPUT_DIR="/opt/trilium"
|
||||
SERVICE_NAME="trilium"
|
||||
VERSION_FILE="$OUTPUT_DIR/version.txt"
|
||||
|
||||
# Ensure dependencies are installed
|
||||
command -v curl >/dev/null 2>&1 || { echo "Error: curl is required"; exit 1; }
|
||||
command -v jq >/dev/null 2>&1 || { echo "Error: jq is required"; exit 1; }
|
||||
command -v tar >/dev/null 2>&1 || { echo "Error: tar is required"; exit 1; }
|
||||
|
||||
# Create download directory
|
||||
mkdir -p "$DOWNLOAD_DIR" || { echo "Error: Cannot create $DOWNLOAD_DIR"; exit 1; }
|
||||
|
||||
# Get the latest release version
|
||||
LATEST_VERSION=$(curl -sL https://api.github.com/repos/$REPO/releases/latest | jq -r '.tag_name')
|
||||
if [ -z "$LATEST_VERSION" ]; then
|
||||
echo "Error: Could not fetch latest release version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check current installed version (from version.txt or existing tarball)
|
||||
CURRENT_VERSION=""
|
||||
if [ -f "$VERSION_FILE" ]; then
|
||||
CURRENT_VERSION=$(cat "$VERSION_FILE")
|
||||
elif [ -f "$DOWNLOAD_DIR/TriliumNotes-Server-$LATEST_VERSION-linux-x64.tar.xz" ]; then
|
||||
CURRENT_VERSION="$LATEST_VERSION"
|
||||
fi
|
||||
|
||||
# Compare versions
|
||||
if [ "$CURRENT_VERSION" = "$LATEST_VERSION" ]; then
|
||||
echo "Latest version ($LATEST_VERSION) is already installed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Download the latest release
|
||||
LATEST_URL=$(curl -sL https://api.github.com/repos/$REPO/releases/latest | jq -r ".assets[] | select(.name | test(\"$PATTERN\")) | .browser_download_url")
|
||||
if [ -z "$LATEST_URL" ]; then
|
||||
echo "Error: No asset found matching pattern '$PATTERN'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FILE_NAME=$(basename "$LATEST_URL")
|
||||
FILE_PATH="$DOWNLOAD_DIR/$FILE_NAME"
|
||||
|
||||
# Download if not already present
|
||||
if [ -f "$FILE_PATH" ]; then
|
||||
echo "Latest release $FILE_NAME already downloaded"
|
||||
else
|
||||
curl -LO --output-dir "$DOWNLOAD_DIR" "$LATEST_URL" || { echo "Error: Download failed"; exit 1; }
|
||||
echo "Downloaded $FILE_NAME to $DOWNLOAD_DIR"
|
||||
fi
|
||||
|
||||
# Extract the tarball
|
||||
EXTRACT_DIR="$DOWNLOAD_DIR/extracted"
|
||||
mkdir -p "$EXTRACT_DIR"
|
||||
tar -xJf "$FILE_PATH" -C "$EXTRACT_DIR" || { echo "Error: Extraction failed"; exit 1; }
|
||||
|
||||
# Find the extracted directory (e.g., TriliumNotes-Server-0.97.2-linux-x64)
|
||||
INNER_DIR=$(find "$EXTRACT_DIR" -maxdepth 1 -type d -name "TriliumNotes-Server-*-linux-x64" | head -n 1)
|
||||
if [ -z "$INNER_DIR" ]; then
|
||||
echo "Error: Could not find extracted directory matching TriliumNotes-Server-*-linux-x64"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Stop the trilium-server service
|
||||
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||
echo "Stopping $SERVICE_NAME service..."
|
||||
sudo systemctl stop "$SERVICE_NAME" || { echo "Error: Failed to stop $SERVICE_NAME"; exit 1; }
|
||||
fi
|
||||
|
||||
# Copy contents to /opt/trilium, overwriting existing files
|
||||
echo "Copying contents from $INNER_DIR to $OUTPUT_DIR..."
|
||||
sudo mkdir -p "$OUTPUT_DIR"
|
||||
sudo cp -r "$INNER_DIR"/* "$OUTPUT_DIR"/ || { echo "Error: Copy failed"; exit 1; }
|
||||
echo "$LATEST_VERSION" | sudo tee "$VERSION_FILE" >/dev/null
|
||||
echo "Files copied to $OUTPUT_DIR"
|
||||
|
||||
# Start the trilium-server service
|
||||
echo "Starting $SERVICE_NAME service..."
|
||||
sudo systemctl start "$SERVICE_NAME" || { echo "Error: Failed to start $SERVICE_NAME"; exit 1; }
|
||||
|
||||
# Clean up
|
||||
rm -rf "$EXTRACT_DIR"
|
||||
echo "Cleanup complete. Trilium updated to $LATEST_VERSION."</code></pre>
|
||||
<h2>Common issues</h2>
|
||||
<h3>Outdated glibc</h3><pre><code class="language-text-x-trilium-auto">Error: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by /var/www/virtual/.../node_modules/@mlink/scrypt/build/Release/scrypt.node)
|
||||
at Object.Module._extensions..node (module.js:681:18)
|
||||
at Module.load (module.js:565:32)
|
||||
at tryModuleLoad (module.js:505:12)</code></pre>
|
||||
<p>If you get an error like this, you need to either upgrade your glibc (typically
|
||||
by upgrading to up-to-date distribution version) or use some other <a href="#root/_help_U9HWXPbFIRW3">server installation</a> method.</p>
|
||||
<h2>TLS</h2>
|
||||
<p>Don't forget to <a href="#root/_help_1G2n2zCStCir">configure TLS</a>, which
|
||||
is required for secure usage!</p>
|
||||
@@ -1,192 +0,0 @@
|
||||
<p>Official docker images are published on docker hub for <strong>AMD64</strong>, <strong>ARMv7</strong> and <strong>ARM64/v8</strong>:
|
||||
<a
|
||||
href="https://hub.docker.com/r/triliumnext/trilium/">https://hub.docker.com/r/triliumnext/trilium/</a>
|
||||
</p>
|
||||
<h2>Prerequisites</h2>
|
||||
<p>Ensure Docker is installed on your system.</p>
|
||||
<p>If you need help installing Docker, reference the <a href="https://docs.docker.com/engine/install/">Docker Installation Docs</a>
|
||||
</p>
|
||||
<p><strong>Note:</strong> Trilium's Docker container requires root privileges
|
||||
to operate correctly.</p>
|
||||
<aside class="admonition warning">
|
||||
<p>If you're using a SMB/CIFS share or folder as your Trilium data directory,
|
||||
<a
|
||||
href="https://github.com/TriliumNext/Notes/issues/415#issuecomment-2344824400">you'll need</a>to add the mount options of <code>nobrl</code> and <code>noperm</code> when
|
||||
mounting your SMB share.</p>
|
||||
</aside>
|
||||
<h2>Running with Docker Compose</h2>
|
||||
<h3>Grab the latest docker-compose.yml:</h3><pre><code class="language-text-x-trilium-auto">wget https://raw.githubusercontent.com/TriliumNext/Trilium/master/docker-compose.yml</code></pre>
|
||||
<p>Optionally, edit the <code>docker-compose.yml</code> file to configure the
|
||||
container settings prior to starting it. Unless configured otherwise, the
|
||||
data directory will be <code>~/trilium-data</code> and the container will
|
||||
be accessible at port 8080.</p>
|
||||
<h3>Start the container:</h3>
|
||||
<p>Run the following command to start the container in the background:</p><pre><code class="language-text-x-trilium-auto">docker compose up -d</code></pre>
|
||||
<h2>Running without Docker Compose / Further Configuration</h2>
|
||||
<h3>Pulling the Docker Image</h3>
|
||||
<p>To pull the image, use the following command, replacing <code>[VERSION]</code> with
|
||||
the desired version or tag, such as <code>v0.91.6</code> or just <code>latest</code>.
|
||||
(See published tag names at <a href="https://hub.docker.com/r/triliumnext/trilium/tags">https://hub.docker.com/r/triliumnext/trilium/tags</a>.):</p><pre><code class="language-text-x-trilium-auto">docker pull triliumnext/trilium:v0.91.6</code></pre>
|
||||
<p><strong>Warning:</strong> Avoid using the "latest" tag, as it may automatically
|
||||
upgrade your instance to a new minor version, potentially disrupting sync
|
||||
setups or causing other issues.</p>
|
||||
<h3>Preparing the Data Directory</h3>
|
||||
<p>Trilium requires a directory on the host system to store its data. This
|
||||
directory must be mounted into the Docker container with write permissions.</p>
|
||||
<h3>Running the Docker Container</h3>
|
||||
<h4>Local Access Only</h4>
|
||||
<p>Run the container to make it accessible only from the localhost. This
|
||||
setup is suitable for testing or when using a proxy server like Nginx or
|
||||
Apache.</p><pre><code class="language-text-x-trilium-auto">sudo docker run -t -i -p 127.0.0.1:8080:8080 -v ~/trilium-data:/home/node/trilium-data triliumnext/trilium:[VERSION]</code></pre>
|
||||
<ol>
|
||||
<li>Verify the container is running using <code>docker ps</code>.</li>
|
||||
<li>Access Trilium via a web browser at <code>127.0.0.1:8080</code>.</li>
|
||||
</ol>
|
||||
<h4>Local Network Access</h4>
|
||||
<p>To make the container accessible only on your local network, first create
|
||||
a new Docker network:</p><pre><code class="language-text-x-trilium-auto">docker network create -d macvlan -o parent=eth0 --subnet 192.168.2.0/24 --gateway 192.168.2.254 --ip-range 192.168.2.252/27 mynet</code></pre>
|
||||
<p>Then, run the container with the network settings:</p><pre><code class="language-text-x-trilium-auto">docker run --net=mynet -d -p 127.0.0.1:8080:8080 -v ~/trilium-data:/home/node/trilium-data triliumnext/trilium:-latest</code></pre>
|
||||
<p>To set a different user ID (UID) and group ID (GID) for the saved data,
|
||||
use the <code>USER_UID</code> and <code>USER_GID</code> environment variables:</p><pre><code class="language-text-x-trilium-auto">docker run --net=mynet -d -p 127.0.0.1:8080:8080 -e "USER_UID=1001" -e "USER_GID=1001" -v ~/trilium-data:/home/node/trilium-data triliumnext/trilium:-latest</code></pre>
|
||||
<p>Find the local IP address using <code>docker inspect [container_name]</code> and
|
||||
access the service from devices on the local network.</p><pre><code class="language-text-x-trilium-auto">docker ps
|
||||
docker inspect [container_name]</code></pre>
|
||||
<h4>Global Access</h4>
|
||||
<p>To allow access from any IP address, run the container as follows:</p><pre><code class="language-text-x-trilium-auto">docker run -d -p 0.0.0.0:8080:8080 -v ~/trilium-data:/home/node/trilium-data triliumnext/trilium:[VERSION]</code></pre>
|
||||
<p>Stop the container with <code>docker stop <CONTAINER ID></code>,
|
||||
where the container ID is obtained from <code>docker ps</code>.</p>
|
||||
<h3>Custom Data Directory</h3>
|
||||
<p>For a custom data directory, use:</p><pre><code class="language-text-x-trilium-auto">-v ~/YourOwnDirectory:/home/node/trilium-data triliumnext/trilium:[VERSION]</code></pre>
|
||||
<p>If you want to run your instance in a non-default way, please use the
|
||||
volume switch as follows: <code>-v ~/YourOwnDirectory:/home/node/trilium-data triliumnext/trilium:<VERSION></code>.
|
||||
It is important to be aware of how Docker works for volumes, with the first
|
||||
path being your own and the second the one to virtually bind to. <a href="https://docs.docker.com/storage/volumes/">https://docs.docker.com/storage/volumes/</a> The
|
||||
path before the colon is the host directory, and the path after the colon
|
||||
is the container's path. More details can be found in the <a href="https://docs.docker.com/storage/volumes/">Docker Volumes Documentation</a>.</p>
|
||||
<h2>Reverse Proxy</h2>
|
||||
<ol>
|
||||
<li><a href="#root/_help_vFhVZ4ZRNxLY">Nginx</a>
|
||||
</li>
|
||||
<li><a href="#root/_help_Eu2zkbyE2QPK">Apache</a>
|
||||
</li>
|
||||
</ol>
|
||||
<h3>Note on --user Directive</h3>
|
||||
<p>The <code>--user</code> directive is unsupported. Instead, use the <code>USER_UID</code> and <code>USER_GID</code> environment
|
||||
variables to set the appropriate user and group IDs.</p>
|
||||
<h3>Note on timezones</h3>
|
||||
<p>If you are having timezone issues and you are not using docker-compose,
|
||||
you may need to add a <code>TZ</code> environment variable with the <a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">TZ identifier</a> of
|
||||
your local timezone.</p>
|
||||
<h2>Rootless Docker Image</h2>
|
||||
<aside class="admonition note">
|
||||
<p>Please keep in mind that the data directory is at <code>/home/trilium/trilium-data</code> instead
|
||||
of the typical <code>/home/node/trilium-data</code>. This is because a new
|
||||
user is created and used to run Trilium within the rootless containers.</p>
|
||||
</aside>
|
||||
<p>If you would prefer to run Trilium without having to run the Docker container
|
||||
as <code>root</code>, you can use either of the provided Debian (default)
|
||||
and Alpine-based images with the <code>rootless</code> tag. </p>
|
||||
<p><em><strong>If you're unsure, stick to the “rootful” Docker image referenced above.</strong></em>
|
||||
</p>
|
||||
<p>Below are some commands to pull the rootless images:</p><pre><code class="language-text-x-trilium-auto"># For Debian-based image
|
||||
docker pull triliumnext/trilium:rootless
|
||||
|
||||
# For Alpine-based image
|
||||
docker pull triliumnext/trilium:rootless-alpine</code></pre>
|
||||
<h3>Why Rootless?</h3>
|
||||
<p>Running containers as non-root is a security best practice that reduces
|
||||
the potential impact of container breakouts. If an attacker manages to
|
||||
escape the container, they'll only have the permissions of the non-root
|
||||
user instead of full root access to the host.</p>
|
||||
<h3>How It Works</h3>
|
||||
<p>The rootless Trilium image:</p>
|
||||
<ol>
|
||||
<li>Creates a non-root user (<code>trilium</code>) during build time</li>
|
||||
<li>Configures the application to run as this non-root user</li>
|
||||
<li>Allows runtime customization of the user's UID/GID via Docker's <code>--user</code> flag</li>
|
||||
<li>Does not require a separate Docker <code>entrypoint</code> script</li>
|
||||
</ol>
|
||||
<h3>Usage</h3>
|
||||
<h4><strong>Using docker-compose (Recommended)</strong></h4><pre><code class="language-text-x-trilium-auto"># Run with default UID/GID (1000:1000)
|
||||
docker-compose -f docker-compose.rootless.yml up -d
|
||||
|
||||
# Run with custom UID/GID (e.g., match your host user)
|
||||
TRILIUM_UID=$(id -u) TRILIUM_GID=$(id -g) docker-compose -f docker-compose.rootless.yml up -d
|
||||
|
||||
# Specify a custom data directory
|
||||
TRILIUM_DATA_DIR=/path/to/your/data TRILIUM_UID=$(id -u) TRILIUM_GID=$(id -g) docker-compose -f docker-compose.rootless.yml up -d
|
||||
</code></pre>
|
||||
<h4><strong>Using Docker CLI</strong></h4><pre><code class="language-text-x-trilium-auto"># Build the image
|
||||
docker build -t triliumnext/trilium:rootless -f apps/server/Dockerfile.rootless .
|
||||
|
||||
# Run with default UID/GID (1000:1000)
|
||||
docker run -d --name trilium -p 8080:8080 -v ~/trilium-data:/home/trilium/trilium-data triliumnext/trilium:rootless
|
||||
|
||||
# Run with custom UID/GID
|
||||
docker run -d --name trilium -p 8080:8080 --user $(id -u):$(id -g) -v ~/trilium-data:/home/trilium/trilium-data triliumnext/trilium:rootless
|
||||
</code></pre>
|
||||
<h3>Environment Variables</h3>
|
||||
<ul>
|
||||
<li><code>TRILIUM_UID</code>: UID to use for the container process (passed
|
||||
to Docker's <code>--user</code> flag)</li>
|
||||
<li><code>TRILIUM_GID</code>: GID to use for the container process (passed
|
||||
to Docker's <code>--user</code> flag)</li>
|
||||
<li><code>TRILIUM_DATA_DIR</code>: Path to the data directory inside the container
|
||||
(default: <code>/home/node/trilium-data</code>)</li>
|
||||
</ul>
|
||||
<p>For a complete list of configuration environment variables (network settings,
|
||||
authentication, sync, etc.), see <a class="reference-link" href="#root/_help_1CEQXvOOO4EK">Configuration (config.ini or environment variables)</a>.</p>
|
||||
<h3>Volume Permissions</h3>
|
||||
<p>If you encounter permission issues with the data volume, ensure that:</p>
|
||||
<ol>
|
||||
<li>The host directory has appropriate permissions for the UID/GID you're
|
||||
using</li>
|
||||
<li>You're setting both <code>TRILIUM_UID</code> and <code>TRILIUM_GID</code> to
|
||||
match the owner of the host directory</li>
|
||||
</ol><pre><code class="language-text-x-trilium-auto"># For example, if your data directory is owned by UID 1001 and GID 1001:
|
||||
TRILIUM_UID=1001 TRILIUM_GID=1001 docker-compose -f docker-compose.rootless.yml up -d
|
||||
</code></pre>
|
||||
<h3>Considerations</h3>
|
||||
<ul>
|
||||
<li>The container starts with a specific UID/GID which can be customized at
|
||||
runtime</li>
|
||||
<li>Unlike the traditional setup, this approach does not use a separate entrypoint
|
||||
script with <code>usermod</code>/<code>groupmod</code> commands</li>
|
||||
<li>The container cannot modify its own UID/GID at runtime, which is a security
|
||||
feature of rootless containers</li>
|
||||
</ul>
|
||||
<h3>Available Rootless Images</h3>
|
||||
<p>Two rootless variants are provided:</p>
|
||||
<ol>
|
||||
<li><strong>Debian-based</strong> (default): Uses the Debian Bullseye Slim
|
||||
base image
|
||||
<ul>
|
||||
<li>Dockerfile: <code>apps/server/Dockerfile.rootless</code>
|
||||
</li>
|
||||
<li>Recommended for most users</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>Alpine-based</strong>: Uses the Alpine base image for smaller
|
||||
size
|
||||
<ul>
|
||||
<li>Dockerfile: <code>apps/server/Dockerfile.alpine.rootless</code>
|
||||
</li>
|
||||
<li>Smaller image size, but may have compatibility issues with some systems</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<h3>Building Custom Rootless Images</h3>
|
||||
<p>If you would prefer, you can also customize the UID/GID at build time:</p><pre><code class="language-text-x-trilium-auto"># For Debian-based image with custom UID/GID
|
||||
docker build --build-arg USER=myuser --build-arg UID=1001 --build-arg GID=1001 \
|
||||
-t triliumnext/trilium:rootless-custom -f apps/server/Dockerfile.rootless .
|
||||
|
||||
# For Alpine-based image with custom UID/GID
|
||||
docker build --build-arg USER=myuser --build-arg UID=1001 --build-arg GID=1001 \
|
||||
-t triliumnext/trilium:alpine-rootless-custom -f apps/server/Dockerfile.alpine.rootless .
|
||||
</code></pre>
|
||||
<p>Available build arguments:</p>
|
||||
<ul>
|
||||
<li><code>USER</code>: Username for the non-root user (default: trilium)</li>
|
||||
<li><code>UID</code>: User ID for the non-root user (default: 1000)</li>
|
||||
<li><code>GID</code>: Group ID for the non-root user (default: 1000)</li>
|
||||
</ul>
|
||||
@@ -1,33 +0,0 @@
|
||||
<p>As Trilium can be run in Docker it also can be deployed in Kubernetes.
|
||||
You can either use our Helm chart, a community Helm chart, or roll your
|
||||
own Kubernetes deployment.</p>
|
||||
<p>The recommended way is to use a Helm chart.</p>
|
||||
<h2>Root privileges</h2>
|
||||
<aside class="admonition note">
|
||||
<p>The Trilium container at this time needs to be run with root privileges.
|
||||
It will swap to UID and GID <code>1000:1000</code> to run the <code>node</code> process
|
||||
after execution though, so the main process doesn't run with root privileges.</p>
|
||||
</aside>
|
||||
<p>The Trilium docker container needs to be run with root privileges. The
|
||||
node process inside the container will be started with reduced privileges
|
||||
(uid:gid 1000:1000) after some initialization logic. Please make sure that
|
||||
you don't use a security context (PodSecurityContext) which changes the
|
||||
user ID. To use a different uid:gid for file storage and the application,
|
||||
please use the <code>USER_UID</code> & <code>USER_GID</code> environment
|
||||
variables.</p>
|
||||
<p>The docker image will also fix the permissions of <code>/home/node</code> so
|
||||
you don't have to use an init container.</p>
|
||||
<h2>Helm Charts</h2>
|
||||
<p><a href="https://github.com/TriliumNext/helm-charts">Official Helm chart</a> from
|
||||
TriliumNext Unofficial helm chart by <a href="https://github.com/ohdearaugustin">ohdearaugustin</a>:
|
||||
<a
|
||||
href="https://github.com/ohdearaugustin/charts">https://github.com/ohdearaugustin/charts</a>
|
||||
</p>
|
||||
<h2>Adding a Helm repository</h2>
|
||||
<p>Below is an example of how</p><pre><code class="language-text-x-trilium-auto">helm repo add trilium https://triliumnext.github.io/helm-charts
|
||||
"trilium" has been added to your repositories</code></pre>
|
||||
<h2>How to install a chart</h2>
|
||||
<p>After reviewing the <a href="https://github.com/TriliumNext/helm-charts/blob/main/charts/trilium/values.yaml"><code>values.yaml</code></a> from
|
||||
the Helm chart, modifying as required and then creating your own:</p><pre><code class="language-text-x-trilium-auto">helm install --create-namespace --namespace trilium trilium trilium/trilium -f values.yaml</code></pre>
|
||||
<p>For more information on using Helm, please refer to the Helm documentation,
|
||||
or create a Discussion in the TriliumNext GitHub Organization.</p>
|
||||
@@ -1,79 +0,0 @@
|
||||
<p>I've assumed you have created a DNS A record for <code>trilium.yourdomain.com</code> that
|
||||
you want to use for your Trilium server.</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Download docker image and create container</p><pre><code class="language-text-x-trilium-auto"> docker pull triliumnext/trilium:[VERSION]
|
||||
docker create --name trilium -t -p 127.0.0.1:8080:8080 -v ~/trilium-data:/home/node/trilium-data triliumnext/trilium:[VERSION]</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>Configure Apache proxy and websocket proxy</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Enable apache proxy modules</p><pre><code class="language-text-x-trilium-auto"> a2enmod ssl
|
||||
a2enmod proxy
|
||||
a2enmod proxy_http
|
||||
a2enmod proxy_wstunnel</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>Create a new let's encrypt certificate</p><pre><code class="language-text-x-trilium-auto"> sudo certbot certonly -d trilium.mydomain.com</code></pre>
|
||||
<p>Choose standalone (2) and note the location of the created certificates
|
||||
(typically /etc/letsencrypt/live/...)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Create a new virtual host file for apache (you may want to use <code>apachectl -S</code> to
|
||||
determine the server root location, mine is /etc/apache2)</p><pre><code class="language-text-x-trilium-auto"> sudo nano /etc/apache2/sites-available/trilium.yourdomain.com.conf</code></pre>
|
||||
<p>Paste (and customize) the following text into the configuration file</p><pre><code class="language-text-x-trilium-auto">
|
||||
ServerName http://trilium.yourdomain.com
|
||||
RewriteEngine on
|
||||
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
|
||||
|
||||
|
||||
ServerName https://trilium.yourdomain.com
|
||||
RewriteEngine On
|
||||
RewriteCond %{HTTP:Connection} Upgrade [NC]
|
||||
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
||||
RewriteRule /(.*) ws://localhost:8080/$1 [P,L]
|
||||
AllowEncodedSlashes NoDecode
|
||||
ProxyPass / http://localhost:8080/ nocanon
|
||||
ProxyPassReverse / http://localhost:8080/
|
||||
SSLCertificateFile /etc/letsencrypt/live/trilium.yourdomain.com/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/trilium.yourdomain.com/privkey.pem
|
||||
Include /etc/letsencrypt/options-ssl-apache.conf
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>Enable the virtual host with <code>sudo a2ensite trilium.yourdomain.com.conf</code>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Reload apache2 with <code>sudo systemctl reload apache2</code>
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>
|
||||
<p>Create and enable a systemd service to start the docker container on boot</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Create a new empty file called <code>/lib/systemd/system/trilium.service</code> with
|
||||
the contents</p><pre><code class="language-text-x-trilium-auto"> [Unit]
|
||||
Description=Trilium Server
|
||||
Requires=docker.service
|
||||
After=docker.service
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/docker start -a trilium
|
||||
ExecStop=/usr/bin/docker stop -t 2 trilium
|
||||
|
||||
[Install]
|
||||
WantedBy=local.target</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>Install, enable and start service</p><pre><code class="language-text-x-trilium-auto"> sudo systemctl daemon-reload
|
||||
sudo systemctl enable trilium.service
|
||||
sudo systemctl start trilium.service</code></pre>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ol>
|
||||
@@ -1,74 +0,0 @@
|
||||
<p>Configure Nginx proxy and HTTPS. The operating system here is Ubuntu 18.04.</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Download Nginx and remove Apache2</p><pre><code class="language-text-x-trilium-auto">sudo apt-get install nginx
|
||||
sudo apt-get remove apache2</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>Create configure file</p><pre><code class="language-text-x-trilium-auto">cd /etc/nginx/conf.d
|
||||
vim default.conf</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>Fill the file with the context shown below, part of the setting show be
|
||||
changed. Then you can enjoy your web with HTTPS forced and proxy.</p><pre><code class="language-text-x-trilium-auto"># This part configures, where your Trilium server is running
|
||||
upstream trilium {
|
||||
zone trilium 64k;
|
||||
server 127.0.0.1:8080; # change it to a different hostname and port if non-default is used
|
||||
keepalive 2;
|
||||
}
|
||||
|
||||
# This part is for proxy and HTTPS configure
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name trilium.example.net; #change trilium.example.net to your domain without HTTPS or HTTP.
|
||||
ssl_certificate /etc/ssl/note/example.crt; #change /etc/ssl/note/example.crt to your path of crt file.
|
||||
ssl_certificate_key /etc/ssl/note/example.net.key; #change /etc/ssl/note/example.net.key to your path of key file.
|
||||
ssl_session_cache builtin:1000 shared:SSL:10m;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
|
||||
ssl_prefer_server_ciphers on;
|
||||
access_log /var/log/nginx/access.log; #check the path of access.log, if it doesn't fit your file, change it
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_pass http://trilium;
|
||||
proxy_read_timeout 90;
|
||||
}
|
||||
}
|
||||
|
||||
# This part is for HTTPS forced
|
||||
server {
|
||||
listen 80;
|
||||
server_name trilium.example.net; # change to your domain
|
||||
return 301 https://$server_name$request_uri;
|
||||
}</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>Alternatively if you want to serve the instance under a different path
|
||||
(useful e.g. if you want to serve multiple instances), update the location
|
||||
block like so:</p>
|
||||
<ul>
|
||||
<li>update the location with your desired path (make sure to not leave a trailing
|
||||
slash "/", if your <code>proxy_pass</code> does not end on a slash as well)</li>
|
||||
<li>add the <code>proxy_cookie_path</code> directive with the same path: this
|
||||
allows you to stay logged in at multiple instances at the same time.</li>
|
||||
</ul><pre><code class="language-text-x-trilium-auto"> location /trilium/instance-one {
|
||||
rewrite /trilium/instance-one/(.*) /$1 break;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_pass http://trilium;
|
||||
proxy_cookie_path / /trilium/instance-one
|
||||
proxy_read_timeout 90;
|
||||
}
|
||||
</code></pre>
|
||||
</li>
|
||||
</ol>
|
||||
@@ -1,35 +0,0 @@
|
||||
<h2>Disabling authentication</h2>
|
||||
<p>If you are running Trilium on <code>localhost</code> only or if authentication
|
||||
is handled by another component, you can disable Trilium’s authentication
|
||||
by adding the following to <code>config.ini</code>:</p><pre><code class="language-text-x-trilium-auto">[General]
|
||||
noAuthentication=true</code></pre>
|
||||
<p>Disabling authentication will bypass even the <a class="reference-link"
|
||||
href="#root/_help_iYzJELZlnPVk">Multi-Factor Authentication</a> since
|
||||
v0.94.1.</p>
|
||||
<h2>Understanding how the session works</h2>
|
||||
<p>Once logged into Trilium, the application will store this information
|
||||
about the login into a cookie on the browser, but also as a session on
|
||||
the server.</p>
|
||||
<p>If “Remember me” is checked, then the login will expire in 21 days. This
|
||||
period can be adjusted by modifying the <code>Session.cookieMaxAge</code> value
|
||||
in <code>config.ini</code>. For example, to have the session expire in one
|
||||
day:</p><pre><code class="language-text-x-trilium-auto">[Session]
|
||||
cookieMaxAge=86400</code></pre>
|
||||
<p>When “Remember me” is unchecked, the behavior is different. At client/browser
|
||||
level the authentication does not have any expiration date, but it will
|
||||
be automatically cleared as soon as the user closes the browser. Nevertheless,
|
||||
the server will also dismiss this authentication in around 24 hours from
|
||||
the <em>last interaction with the application</em>.</p>
|
||||
<h2>Viewing active sessions</h2>
|
||||
<p>The login sessions are now stored in the same <a class="reference-link"
|
||||
href="#root/_help_lvXOQ00dcRlk">Database</a> as the user data. In
|
||||
order to view which sessions are active, open the <a class="reference-link"
|
||||
href="#root/_help_hDJ4mPkZJQ4E">SQL Console</a> and run the following
|
||||
query:</p><pre><code class="language-text-x-trilium-auto">SELECT * FROM sessions</code></pre>
|
||||
<p>Expired sessions are periodically cleaned by the server, generally an
|
||||
hourly interval.</p>
|
||||
<h2>See also</h2>
|
||||
<ul>
|
||||
<li><a class="reference-link" href="#root/_help_iYzJELZlnPVk">Multi-Factor Authentication</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -1,103 +0,0 @@
|
||||
<p>Multi-factor authentication (MFA) is a security process that requires
|
||||
users to provide two or more verification factors to gain access to a system,
|
||||
application, or account. This adds an extra layer of protection beyond
|
||||
just using a password.</p>
|
||||
<p>By requiring more than one verification method, MFA helps reduce the risk
|
||||
of unauthorized access, even if someone has obtained your password. It’s
|
||||
highly recommended for securing sensitive information stored in your notes.</p>
|
||||
<aside
|
||||
class="admonition warning">
|
||||
<p>OpenID and TOTP cannot be both used at the same time!</p>
|
||||
</aside>
|
||||
<h2>Log in with your Google Account with OpenID!</h2>
|
||||
<p>OpenID is a standardized way to let you log into websites using an account
|
||||
from another service, like Google, to verify your identity.</p>
|
||||
<h2>Why Time-based One Time Passwords?</h2>
|
||||
<p>TOTP (Time-Based One-Time Password) is a security feature that generates
|
||||
a unique, temporary code on your device, like a smartphone, which changes
|
||||
every 30 seconds. You use this code, along with your password, to log into
|
||||
your account, making it much harder for anyone else to access them.</p>
|
||||
<h2>Setup</h2>
|
||||
<p>MFA can only be set up on a server instance.</p>
|
||||
<aside class="admonition note">
|
||||
<p>When Multi-Factor Authentication (MFA) is enabled on a server instance,
|
||||
a new desktop instance may fail to sync with it. As a temporary workaround,
|
||||
you can disable MFA to complete the initial sync, then re-enable MFA afterward.
|
||||
This issue will be addressed in a future release.</p>
|
||||
</aside>
|
||||
<h3>TOTP</h3>
|
||||
<ol>
|
||||
<li>Go to "Menu" -> "Options" -> "MFA"</li>
|
||||
<li>Click the “Enable Multi-Factor Authentication” checkbox if not checked</li>
|
||||
<li>Choose “Time-Based One-Time Password (TOTP)” under MFA Method</li>
|
||||
<li>Click the "Generate TOTP Secret" button</li>
|
||||
<li>Copy the generated secret to your authentication app/extension</li>
|
||||
<li>Click the "Generate Recovery Codes" button</li>
|
||||
<li>Save the recovery codes. Recovery codes can be used once in place of the
|
||||
TOTP if you loose access to your authenticator. After a rerecovery code
|
||||
is used, it will show the unix timestamp when it was used in the MFA options
|
||||
tab.</li>
|
||||
<li>Re-login will be required after TOTP setup is finished (After you refreshing
|
||||
the page).</li>
|
||||
</ol>
|
||||
<h3>OpenID</h3>
|
||||
<p>In order to setup OpenID, you will need to setup a authentication provider.
|
||||
This requires a bit of extra setup. Follow <a href="https://developers.google.com/identity/openid-connect/openid-connect">these instructions</a> to
|
||||
setup an OpenID service through google. The Redirect URL of Trilium is <code>https://<your-trilium-domain>/callback</code>.</p>
|
||||
<ol>
|
||||
<li>Set the <code>oauthBaseUrl</code>, <code>oauthClientId</code> and <code>oauthClientSecret</code> in
|
||||
the <code>config.ini</code> file (check <a class="reference-link" href="#root/_help_SneMubD5wTR6">Configuration (config.ini or environment variables)</a> for
|
||||
more information).
|
||||
<ol>
|
||||
<li>You can also setup through environment variables:
|
||||
<ul>
|
||||
<li>Standard: <code>TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHBASEURL</code>, <code>TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHCLIENTID</code>, <code>TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHCLIENTSECRET</code>
|
||||
</li>
|
||||
<li>Legacy (still supported): <code>TRILIUM_OAUTH_BASE_URL</code>, <code>TRILIUM_OAUTH_CLIENT_ID</code>, <code>TRILIUM_OAUTH_CLIENT_SECRET</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><code>oauthBaseUrl</code> should be the link of your Trilium instance server,
|
||||
for example, <code>https://<your-trilium-domain></code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>Restart the server</li>
|
||||
<li>Go to "Menu" -> "Options" -> "MFA"</li>
|
||||
<li>Click the “Enable Multi-Factor Authentication” checkbox if not checked</li>
|
||||
<li>Choose “OAuth/OpenID” under MFA Method</li>
|
||||
<li>Refresh the page and login through OpenID provider</li>
|
||||
</ol>
|
||||
<aside class="admonition note">
|
||||
<p>The default OAuth issuer is Google. To use other services such as Authentik
|
||||
or Auth0, you can configure the settings via <code>oauthIssuerBaseUrl</code>, <code>oauthIssuerName</code>,
|
||||
and <code>oauthIssuerIcon</code> in the <code>config.ini</code> file. Alternatively,
|
||||
these values can be set using environment variables:</p>
|
||||
<ul>
|
||||
<li>Standard: <code>TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHISSUERBASEURL</code>, <code>TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHISSUERNAME</code>, <code>TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHISSUERICON</code>
|
||||
</li>
|
||||
<li>Legacy (still supported): <code>TRILIUM_OAUTH_ISSUER_BASE_URL</code>, <code>TRILIUM_OAUTH_ISSUER_NAME</code>, <code>TRILIUM_OAUTH_ISSUER_ICON</code>
|
||||
</li>
|
||||
</ul>
|
||||
<p><code>oauthIssuerName</code> and <code>oauthIssuerIcon</code> are required
|
||||
for displaying correct issuer information at the Login page.</p>
|
||||
</aside>
|
||||
<h4>Authentik</h4>
|
||||
<p>If you don’t already have a running Authentik instance, please follow
|
||||
<a
|
||||
href="https://docs.goauthentik.io/docs/install-config/install/docker-compose">these instructions</a>to set one up.</p>
|
||||
<ol>
|
||||
<li>In the Authentik admin dashboard, create a new OAuth2 application by following
|
||||
<a
|
||||
href="https://docs.goauthentik.io/docs/add-secure-apps/providers/oauth2/create-oauth2-provider">these steps</a>. Make sure to set the Redirect URL to: <code>https://<your-trilium-domain>/callback</code>.</li>
|
||||
<li>In your config.ini file, set the relevant OAuth variables:
|
||||
<ol>
|
||||
<li><code>oauthIssuerBaseUrl</code> → Use the <code>OpenID Configuration Issuer</code> URL
|
||||
from your application's overview page.</li>
|
||||
<li><code>oauthIssuerName</code> and <code>oauthIssuerIcon</code> → Set these
|
||||
to customize the name and icon displayed on the login page. If omitted,
|
||||
Google’s name and icon will be shown by default.</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>Apply the changes by restarting your server.</li>
|
||||
<li>Proceed with the remaining steps starting from Step 3 in the OpenID section.</li>
|
||||
</ol>
|
||||
@@ -1,48 +0,0 @@
|
||||
<p>Configuring TLS is essential for <a href="#root/_help_U9HWXPbFIRW3">server installation</a> in
|
||||
Trilium. This guide details the steps to set up TLS within Trilium itself.</p>
|
||||
<p>For a more robust solution, consider using TLS termination with a reverse
|
||||
proxy (recommended, e.g., Nginx). You can follow a <a href="https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-20-04">guide like this</a> for
|
||||
such setups.</p>
|
||||
<h2>Obtaining a TLS Certificate</h2>
|
||||
<p>You have two options for obtaining a TLS certificate:</p>
|
||||
<ul>
|
||||
<li><strong>Recommended</strong>: Obtain a TLS certificate signed by a root
|
||||
certificate authority. For personal use, <a href="https://letsencrypt.org">Let's Encrypt</a> is
|
||||
an excellent choice. It is free, automated, and straightforward. Certbot
|
||||
can facilitate automatic TLS setup.</li>
|
||||
<li>Generate a self-signed certificate. This option is not recommended due
|
||||
to the additional complexity of importing the certificate into all machines
|
||||
connecting to the server.</li>
|
||||
</ul>
|
||||
<h2>Modifying <code>config.ini</code></h2>
|
||||
<p>Once you have your certificate, modify the <code>config.ini</code> file
|
||||
in the <a href="#root/_help_dvbMBRXYMM2G">data directory</a> to configure
|
||||
Trilium to use it:</p><pre><code class="language-text-x-trilium-auto">[Network]
|
||||
port=8080
|
||||
# Set to true for TLS/SSL/HTTPS (secure), false for HTTP (insecure).
|
||||
https=true
|
||||
# Path to the certificate (run "bash bin/generate-cert.sh" to generate a self-signed certificate).
|
||||
# Relevant only if https=true
|
||||
certPath=/[username]/.acme.sh/[hostname]/fullchain.cer
|
||||
keyPath=/[username]/.acme.sh/[hostname]/example.com.key</code></pre>
|
||||
<p>You can also review the <a href="#root/_help_SneMubD5wTR6">configuration</a> file
|
||||
to provide all <code>config.ini</code> values as environment variables instead.
|
||||
For example, you can configure TLS using environment variables:</p><pre><code class="language-text-x-sh">export TRILIUM_NETWORK_HTTPS=true
|
||||
export TRILIUM_NETWORK_CERTPATH=/path/to/cert.pem
|
||||
export TRILIUM_NETWORK_KEYPATH=/path/to/key.pem</code></pre>
|
||||
<p>The above example shows how this is set up in an environment where the
|
||||
certificate was generated using Let's Encrypt's ACME utility. Your paths
|
||||
may differ. For Docker installations, ensure these paths are within a volume
|
||||
or another directory accessible by the Docker container, such as <code>/home/node/trilium-data/[DIR IN DATA DIRECTORY]</code>.</p>
|
||||
<p>After configuring <code>config.ini</code>, restart Trilium and access the
|
||||
hostname using "https".</p>
|
||||
<h2>Self-Signed Certificate</h2>
|
||||
<p>If you opt to use a self-signed certificate for your server instance,
|
||||
note that the desktop instance will not trust it by default.</p>
|
||||
<p>To bypass this, disable certificate validation by setting the following
|
||||
environment variable (for Linux):</p><pre><code class="language-text-x-trilium-auto">export NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||
trilium</code></pre>
|
||||
<p>Trilium provides scripts to start in this mode, such as <code>trilium-no-cert-check.bat</code> for
|
||||
Windows.</p>
|
||||
<p><strong>Warning</strong>: Disabling TLS certificate validation is insecure.
|
||||
Proceed only if you fully understand the implications.</p>
|
||||
@@ -1,276 +0,0 @@
|
||||
<p>Trilium provides robust encryption capabilities through its Protected
|
||||
Notes system, ensuring your sensitive information remains secure even if
|
||||
your database is compromised.</p>
|
||||
<h2>Overview</h2>
|
||||
<p>Protected notes in Trilium use <strong>AES-128-CBC encryption</strong> with
|
||||
scrypt-based key derivation to protect sensitive content. The encryption
|
||||
is designed to be:</p>
|
||||
<ul>
|
||||
<li><strong>Secure</strong>: Uses industry-standard AES encryption with strong
|
||||
key derivation</li>
|
||||
<li><strong>Selective</strong>: Only notes marked as protected are encrypted</li>
|
||||
<li><strong>Session-based</strong>: Decrypted content remains accessible during
|
||||
a protected session</li>
|
||||
<li><strong>Zero-knowledge</strong>: The server never stores unencrypted protected
|
||||
content</li>
|
||||
</ul>
|
||||
<h2>How Encryption Works</h2>
|
||||
<h3>Encryption Algorithm</h3>
|
||||
<ul>
|
||||
<li><strong>Cipher</strong>: AES-128-CBC (Advanced Encryption Standard in
|
||||
Cipher Block Chaining mode)</li>
|
||||
<li><strong>Key Derivation</strong>: Scrypt with configurable parameters (N=16384,
|
||||
r=8, p=1)</li>
|
||||
<li><strong>Initialization Vector</strong>: 16-byte random IV generated for
|
||||
each encryption operation</li>
|
||||
<li><strong>Integrity Protection</strong>: SHA-1 digest (first 4 bytes) prepended
|
||||
to plaintext for tamper detection</li>
|
||||
</ul>
|
||||
<h3>Key Management</h3>
|
||||
<ol>
|
||||
<li><strong>Master Password</strong>: User-provided password used for key
|
||||
derivation</li>
|
||||
<li><strong>Data Key</strong>: 32-byte random key generated during setup,
|
||||
encrypted with password-derived key</li>
|
||||
<li><strong>Password-Derived Key</strong>: Generated using scrypt from master
|
||||
password and salt</li>
|
||||
<li><strong>Session Key</strong>: Data key loaded into memory during protected
|
||||
session</li>
|
||||
</ol>
|
||||
<h3>Encryption Process</h3><pre><code class="language-text-x-trilium-auto">1. Generate random 16-byte IV
|
||||
2. Compute SHA-1 digest of plaintext (use first 4 bytes)
|
||||
3. Prepend digest to plaintext
|
||||
4. Encrypt (digest + plaintext) using AES-128-CBC
|
||||
5. Prepend IV to encrypted data
|
||||
6. Encode result as Base64</code></pre>
|
||||
<h3>Decryption Process</h3><pre><code class="language-text-x-trilium-auto">1. Decode Base64 ciphertext
|
||||
2. Extract IV (first 16 bytes) and encrypted data
|
||||
3. Decrypt using AES-128-CBC with data key and IV
|
||||
4. Extract digest (first 4 bytes) and plaintext
|
||||
5. Verify integrity by comparing computed vs. stored digest
|
||||
6. Return plaintext if verification succeeds</code></pre>
|
||||
<h2>Setting Up Protected Notes</h2>
|
||||
<h3>Initial Setup</h3>
|
||||
<ol>
|
||||
<li><strong>Set Master Password</strong>: Configure a strong password during
|
||||
initial setup</li>
|
||||
<li><strong>Create Protected Note</strong>: Right-click a note and select
|
||||
"Toggle Protected Status"</li>
|
||||
<li><strong>Enter Protected Session</strong>: Click the shield icon or use
|
||||
Ctrl+Shift+P</li>
|
||||
</ol>
|
||||
<h3>Password Requirements</h3>
|
||||
<ul>
|
||||
<li><strong>Minimum Length</strong>: 8 characters (recommended: 12+ characters)</li>
|
||||
<li><strong>Complexity</strong>: Use a mix of uppercase, lowercase, numbers,
|
||||
and symbols</li>
|
||||
<li><strong>Uniqueness</strong>: Don't reuse passwords from other services</li>
|
||||
<li><strong>Storage</strong>: Consider using a password manager for complex
|
||||
passwords</li>
|
||||
</ul>
|
||||
<h3>Best Practices</h3>
|
||||
<ol>
|
||||
<li><strong>Strong Passwords</strong>: Use passphrases or generated passwords</li>
|
||||
<li><strong>Regular Changes</strong>: Update passwords periodically</li>
|
||||
<li><strong>Secure Storage</strong>: Store password recovery information securely</li>
|
||||
<li><strong>Backup Strategy</strong>: Ensure encrypted backups are properly
|
||||
secured</li>
|
||||
</ol>
|
||||
<h2>Protected Sessions</h2>
|
||||
<h3>Session Management</h3>
|
||||
<ul>
|
||||
<li><strong>Automatic Timeout</strong>: Sessions expire after configurable
|
||||
timeout (default: 10 minutes)</li>
|
||||
<li><strong>Manual Control</strong>: Explicitly enter/exit protected sessions</li>
|
||||
<li><strong>Activity Tracking</strong>: Session timeout resets with each protected
|
||||
note access</li>
|
||||
<li><strong>Multi-client</strong>: Each client maintains its own protected
|
||||
session</li>
|
||||
</ul>
|
||||
<h3>Session Lifecycle</h3>
|
||||
<ol>
|
||||
<li><strong>Enter Session</strong>: User enters master password</li>
|
||||
<li><strong>Key Derivation</strong>: System derives data key from password</li>
|
||||
<li><strong>Session Active</strong>: Protected content accessible in plaintext</li>
|
||||
<li><strong>Timeout/Logout</strong>: Data key removed from memory</li>
|
||||
<li><strong>Protection Restored</strong>: Content returns to encrypted state</li>
|
||||
</ol>
|
||||
<h3>Configuration Options</h3>
|
||||
<p>Access via Options → Protected Session:</p>
|
||||
<ul>
|
||||
<li><strong>Session Timeout</strong>: Duration before automatic logout (seconds)</li>
|
||||
<li><strong>Password Verification</strong>: Enable/disable password strength
|
||||
requirements</li>
|
||||
<li><strong>Recovery Options</strong>: Configure password recovery mechanisms</li>
|
||||
</ul>
|
||||
<h2>Performance Considerations</h2>
|
||||
<h3>Encryption Overhead</h3>
|
||||
<ul>
|
||||
<li><strong>CPU Impact</strong>: Scrypt key derivation is intentionally CPU-intensive</li>
|
||||
<li><strong>Memory Usage</strong>: Minimal additional memory for encrypted
|
||||
content</li>
|
||||
<li><strong>Storage Size</strong>: Encrypted content is slightly larger due
|
||||
to Base64 encoding</li>
|
||||
<li><strong>Network Transfer</strong>: Encrypted notes transfer as Base64
|
||||
strings</li>
|
||||
</ul>
|
||||
<h3>Optimization Tips</h3>
|
||||
<ol>
|
||||
<li><strong>Selective Protection</strong>: Only encrypt truly sensitive notes</li>
|
||||
<li><strong>Session Management</strong>: Keep sessions active during intensive
|
||||
work</li>
|
||||
<li><strong>Hardware Acceleration</strong>: Modern CPUs provide AES acceleration</li>
|
||||
<li><strong>Batch Operations</strong>: Group protected note operations when
|
||||
possible</li>
|
||||
</ol>
|
||||
<h2>Security Considerations</h2>
|
||||
<h3>Threat Model</h3>
|
||||
<p><strong>Protected Against</strong>:</p>
|
||||
<ul>
|
||||
<li>Database theft or unauthorized access</li>
|
||||
<li>Network interception (data at rest)</li>
|
||||
<li>Server-side data breaches</li>
|
||||
<li>Backup file compromise</li>
|
||||
</ul>
|
||||
<p><strong>Not Protected Against</strong>:</p>
|
||||
<ul>
|
||||
<li>Keyloggers or screen capture malware</li>
|
||||
<li>Physical access to unlocked device</li>
|
||||
<li>Memory dumps during active session</li>
|
||||
<li>Social engineering attacks</li>
|
||||
</ul>
|
||||
<h3>Limitations</h3>
|
||||
<ol>
|
||||
<li><strong>Note Titles</strong>: Currently encrypted, may leak structural
|
||||
information</li>
|
||||
<li><strong>Metadata</strong>: Creation dates, modification times remain unencrypted</li>
|
||||
<li><strong>Search Indexing</strong>: Protected notes excluded from full-text
|
||||
search</li>
|
||||
<li><strong>Sync Conflicts</strong>: May be harder to resolve for protected
|
||||
content</li>
|
||||
</ol>
|
||||
<h2>Troubleshooting</h2>
|
||||
<h3>Common Issues</h3>
|
||||
<h4>"Could not decrypt string" Error</h4>
|
||||
<p><strong>Causes</strong>:</p>
|
||||
<ul>
|
||||
<li>Incorrect password entered</li>
|
||||
<li>Corrupted encrypted data</li>
|
||||
<li>Database migration issues</li>
|
||||
</ul>
|
||||
<p><strong>Solutions</strong>:</p>
|
||||
<ol>
|
||||
<li>Verify password spelling and case sensitivity</li>
|
||||
<li>Check for active protected session</li>
|
||||
<li>Restart application and retry</li>
|
||||
<li>Restore from backup if corruption suspected</li>
|
||||
</ol>
|
||||
<h4>Protected Session Won't Start</h4>
|
||||
<p><strong>Causes</strong>:</p>
|
||||
<ul>
|
||||
<li>Password verification hash mismatch</li>
|
||||
<li>Missing encryption salt</li>
|
||||
<li>Database schema issues</li>
|
||||
</ul>
|
||||
<p><strong>Solutions</strong>:</p>
|
||||
<ol>
|
||||
<li>Check error logs for specific error messages</li>
|
||||
<li>Verify database integrity</li>
|
||||
<li>Restore from known good backup</li>
|
||||
<li>Contact support with error details</li>
|
||||
</ol>
|
||||
<h4>Performance Issues</h4>
|
||||
<p><strong>Symptoms</strong>:</p>
|
||||
<ul>
|
||||
<li>Slow password verification</li>
|
||||
<li>Long delays entering protected session</li>
|
||||
<li>High CPU usage during encryption</li>
|
||||
</ul>
|
||||
<p><strong>Solutions</strong>:</p>
|
||||
<ol>
|
||||
<li>Reduce scrypt parameters (advanced users only)</li>
|
||||
<li>Limit number of protected notes</li>
|
||||
<li>Upgrade hardware (more RAM/faster CPU)</li>
|
||||
<li>Close other resource-intensive applications</li>
|
||||
</ol>
|
||||
<h3>Recovery Procedures</h3>
|
||||
<h4>Password Recovery</h4>
|
||||
<p>If you forget your master password:</p>
|
||||
<ol>
|
||||
<li><strong>No Built-in Recovery</strong>: Trilium cannot recover forgotten
|
||||
passwords</li>
|
||||
<li><strong>Backup Restoration</strong>: Restore from backup with known password</li>
|
||||
<li><strong>Data Export</strong>: Export unprotected content before password
|
||||
change</li>
|
||||
<li><strong>Complete Reset</strong>: Last resort - lose all protected content</li>
|
||||
</ol>
|
||||
<h4>Data Recovery</h4>
|
||||
<p>For corrupted protected notes:</p>
|
||||
<ol>
|
||||
<li><strong>Verify Backup</strong>: Check if backups contain uncorrupted data</li>
|
||||
<li><strong>Export/Import</strong>: Try exporting and re-importing the note</li>
|
||||
<li><strong>Database Repair</strong>: Use database repair tools if available</li>
|
||||
<li><strong>Professional Help</strong>: Contact data recovery services for
|
||||
critical data</li>
|
||||
</ol>
|
||||
<h2>Advanced Configuration</h2>
|
||||
<h3>Custom Encryption Parameters</h3>
|
||||
<p><strong>Warning</strong>: Modifying encryption parameters requires advanced
|
||||
knowledge and may break compatibility.</p>
|
||||
<p>For expert users, encryption parameters can be modified in the source
|
||||
code:</p><pre><code class="language-application-typescript">// In my_scrypt.ts
|
||||
const scryptParams = {
|
||||
N: 16384, // CPU/memory cost parameter
|
||||
r: 8, // Block size parameter
|
||||
p: 1 // Parallelization parameter
|
||||
};</code></pre>
|
||||
<h3>Integration with External Tools</h3>
|
||||
<p>Protected notes can be accessed programmatically:</p><pre><code class="language-application-javascript-env-backend">// Backend script example
|
||||
const protectedNote = api.getNote('noteId');
|
||||
if (protectedNote.isProtected) {
|
||||
// Content will be encrypted unless in protected session
|
||||
const content = protectedNote.getContent();
|
||||
}</code></pre>
|
||||
<h2>Compliance and Auditing</h2>
|
||||
<h3>Encryption Standards</h3>
|
||||
<ul>
|
||||
<li><strong>Algorithm</strong>: AES-128-CBC (FIPS 140-2 approved)</li>
|
||||
<li><strong>Key Derivation</strong>: Scrypt (RFC 7914)</li>
|
||||
<li><strong>Random Generation</strong>: Node.js crypto.randomBytes() (OS entropy)</li>
|
||||
</ul>
|
||||
<h3>Audit Trail</h3>
|
||||
<ul>
|
||||
<li>Protected session entry/exit events logged</li>
|
||||
<li>Encryption/decryption operations tracked</li>
|
||||
<li>Password verification attempts recorded</li>
|
||||
<li>Key derivation operations monitored</li>
|
||||
</ul>
|
||||
<h3>Compliance Considerations</h3>
|
||||
<ul>
|
||||
<li><strong>GDPR</strong>: Encryption provides data protection safeguards</li>
|
||||
<li><strong>HIPAA</strong>: AES encryption meets security requirements</li>
|
||||
<li><strong>SOX</strong>: Audit trails support compliance requirements</li>
|
||||
<li><strong>PCI DSS</strong>: Strong encryption protects sensitive data</li>
|
||||
</ul>
|
||||
<h2>Migration and Backup</h2>
|
||||
<h3>Backup Strategies</h3>
|
||||
<ol>
|
||||
<li><strong>Encrypted Backups</strong>: Regular backups preserve encrypted
|
||||
state</li>
|
||||
<li><strong>Unencrypted Exports</strong>: Export protected content during
|
||||
session</li>
|
||||
<li><strong>Key Management</strong>: Securely store password recovery information</li>
|
||||
<li><strong>Testing</strong>: Regularly test backup restoration procedures</li>
|
||||
</ol>
|
||||
<h3>Migration Procedures</h3>
|
||||
<p>When moving to new installation:</p>
|
||||
<ol>
|
||||
<li><strong>Export Data</strong>: Export all notes including protected content</li>
|
||||
<li><strong>Backup Database</strong>: Create complete database backup</li>
|
||||
<li><strong>Transfer Files</strong>: Move exported files to new installation</li>
|
||||
<li><strong>Import Data</strong>: Import using same master password</li>
|
||||
<li><strong>Verify</strong>: Confirm all protected content accessible</li>
|
||||
</ol>
|
||||
<p>Remember: The security of protected notes ultimately depends on choosing
|
||||
a strong master password and following security best practices for your
|
||||
overall system.</p>
|
||||
Reference in New Issue
Block a user