leafwiki

perberMIT活跃维护

单文件 Go 二进制即可跑起来的自托管 Wiki,文档以 Markdown 存盘、用 SQLite 索引,不需要外部数据库,适合团队沉淀知识库和运维手册。

LeafWiki - Self-hosted wiki. Single Go binary, SQLite, Markdown on disk. No external database required.

v0.14.2发布于 2026-10-22 13:31 UTC1,220star +2 / 7 天102fork同步于 2026-11-10 11:49 UTC

怎么上手

从 README 代码块原样提取,可能是其中一种安装方式
docker run -p 8080:8080 -v ~/leafwiki-data:/app/data \
docker run -p 8080:8080 \
git clone https://github.com/perber/leafwiki.git
硬件要求按 README 关键词推断
有 Docker 镜像
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

这个项目的说明还在生成中,先参考下面「最近发版」和 README。

最近发版

5 个版本v0.14.2
  1. v0.14.2LeafWiki v0.14.2最新正式版2026-10-22 · 2026-10-22 13:31 UTC

    📝 Changelog for v0.14.2

    v0.14.2 makes Git backup sync safer: a pull now applies all or nothing, and a force push is refused when it would drop commits on the remote. Damaged SQLite files no longer cost you real data, since only rebuildable indexes are deleted. Pages are easier to read: text, code and tables share one centered column. API keys (experimental) can now write, the install and update scripts verify the release checksum, and there are Linux 32-bit x86 binaries.

    ⚠️ Behavior changes

    • Unreadable SQLite files are handled per store. The search, link, tag and property indexes are rebuilt as before. Sessions, favorites, user settings and email tokens are moved aside to <name>.corrupt-<timestamp> and recreated, so affected users may have to log in again. If users.db is unreadable, LeafWiki now refuses to start and logs what to do, instead of recreating it. (#1704)
    • API keys can make write requests. This applies only with the experimental --enable-api-key-management flag. A key can do what both its own role and its user's role allow. Routes that manage passwords, TOTP, sessions, users or API keys still require a browser session. (#1662)
    • Git backup force push is refused when the local backup is behind the remote, for example after a file conflict. It answers 409, because overwriting would only drop the remote commits. Diverged history can still be force pushed. (#1703)
    • Absolute links to your own wiki open in the current tab. External links still open in a new tab. (#1678)
    • A text file dropped into the editor is uploaded as an attachment and linked, instead of having its content pasted into the page.
    • install.sh and update.sh verify the SHA-256 checksum of the downloaded binary and stop on a mismatch. They now need sha256sum. (#1658)

    🙌 Thanks to contributors

    @zorak1103, for API keys that can write and for the fixes and tests around markdown links and revisions in this release. Contributions like these are what make LeafWiki better than one person could build alone.


    ✨ Features

    • feat(install): verify the release checksum in install.sh and update.sh (#1658) (@perber)
    • feat(auth): allow API keys to write, keep credential routes session-only (#1662) (@zorak1103)
    • feat(release): publish Linux 32-bit x86 binaries (#1652) (@perber)

    🐛 Bug Fixes

    • fix(wiki): close all databases on shutdown and after a failed boot (@perber)
    • fix(build): build with go 1.27.3 (@perber)
    • fix(backup): name only the remote revert as the way out of a file conflict (#1706) (@perber)
    • fix(backup): refuse force push when the local backup is behind the remote (#1703) (@perber)
    • fix(storage): per-store SQLite corruption policy, never delete real state (#1704) (@perber)
    • fix(backup): validate tree entry names from the backup remote (@perber)
    • fix(backup): apply pull changes all-or-nothing and accept already-applied files (@perber)
    • fix(editor): upload dropped text files instead of inlining their content (@perber)
    • fix(preview): improve readability with a consistent reading column (#1696) (@perber)
    • fix(preview): re-resolve markdown links when the page tree changes (zorak1103)
    • fix(preview): open same-origin web links in current tab (#1678) (@perber)
    • fix: tooltip typo in fr (@perber)
    • fix(assets): make page attachments visible in viewer and editor (#1656) (@perber)
    • fix(backup): disable go-git pack delta compression for git sync (#1660) (@perber)
    • fix(release): write .sha256 files without the releases/ path prefix (#1657) (@perber)
    • fix(deps): upgrade all selector parsers past CVE-2026-104844 (#1653) (@perber)
    • fix(deps): deduplicate KaTeX on the patched direct dependency (#1654) (@perber)

    ⚡ Performance

    • perf(tree): precompute filename sort keys when needed (#1648) (@perber)
    • perf(links): skip markdown parsing when no wikilinks match (#1646) (@perber)
    • perf(tree): remove redundant sorting after assigning positions (#16
  2. v0.14.1LeafWiki v0.14.12026-10-09 · 2026-10-09 16:26 UTC
    查看更新说明

    📝 Changelog for v0.14.1

    🐛 Bug Fixes

    • fix(revisions): serve revision assets with the same download headers as /assets (#1640) (@perber)
  3. v0.14.0LeafWiki v0.14.02026-10-09 · 2026-10-09 15:22 UTC
    查看更新说明

    📝 Changelog for v0.14.0

    PDFs now embed right in the page, LeafWiki speaks French and Simplified Chinese, and admins can make the table of contents always visible. Most of the rest of this release is hardening: how uploaded files are served, how sessions end after a password change, and how search handles odd input.

    🌟 Highlights

    • PDF embeds: link a PDF and it renders inline in the page. Only same-origin PDFs are embedded; external ones and mobile get a file card with an open button. (#1582, #1633, #1637, #1592)
    • French and Simplified Chinese UI translations. (#1584, #1587)
    • Always show TOC: a new instance-wide admin setting. (#1576)
    • Search: opening a result scrolls to the first match (#712, #1389), and search no longer crashes on punctuation or unusual FTS5 syntax. (#1578, #1632)
    • Git backup: LEAFWIKI_GIT_BACKUP_PATH to choose where the backup repository lives (#1563), and runs without changes are much faster because existing objects are no longer rewritten (#1635).
    • Links: ambiguous [[Title]] links are no longer reported as broken. (#1615)
    • Full backups now include the public-access and TOC settings. (#1634)

    ⚠️ Behavior changes

    • Uploaded files outside images, PDF, audio and video are now downloaded instead of opened in the browser. Links to .svg, .txt, .md, .csv, .json or .html assets trigger a download. SVGs embedded as images still render. This closes a stored-XSS vector via uploaded files. (#1622)
    • Changing a password ends that user's other sessions. Other devices are signed out once their current access token expires (15 minutes by default), not instantly. (#1630)
    • Updating users via PUT /users/:id is admin-only. Admins change their own password in Account Settings, not in User Management. (#1630)

    💛 Thanks to our sponsors

    A heartfelt thank you to everyone supporting LeafWiki via GitHub Sponsors & Ko-fi. If you'd like to support LeafWiki too: GitHub Sponsors or a one-time coffee on Ko-fi

    🙌 Thanks to contributors

    @richardmilles, @Hugo-Galley, @lboehm, @jochumdev, and @yuyami-ya — for the PDF embed, the French translation, the git backup path, scroll-to-match, and fixes in this release. Contributions like these are what make LeafWiki better than one person could build alone.


    ✨ Features

    • feat(viewer): scroll to first search match when opening a result (#712) (#1389) (@richardmilles)
    • feat(chatops): add /command for build test pr in PR (#1355) (@Hugo-Galley)
    • feat(local): add french langage (#1584) (@Hugo-Galley)
    • feat(i18n): add Simplified Chinese (zh) locale (#1587) (@perber)
    • feat: pdf embed (#1582) (@lboehm)
    • feat(viewer): add instance-wide "always show TOC" admin setting (#1576) (@perber)
    • feat(git): implement LEAFWIKI_GIT_BACKUP_PATH (#1563) (@jochumdev)

    🐛 Bug Fixes

    • fix(preview): show a file card instead of a bare button for non-embedded PDFs (#1637) (@perber)
    • fix(preview): only embed same-origin PDFs as iframe (#1633) (@perber)
    • fix(backup): include public access and TOC settings in full backups (#1634) (@perber)
    • fix(search): only trust well-formed operator fields in FTS5 queries (#1632) (@perber)
    • fix(links): use tagged switch in classifyTitleMatches (staticcheck QF1002) (Patrick Erber)
    • fix(links): store link state (resolved/broken/ambiguous), fix ambiguous wikilinks listed as broken (#1615) (Patrick Erber)
    • fix(ui): prevent image preview label clipping (#1628) (@yuyami-ya)
    • fix(assets): serve uploaded assets with nosniff and download disposition (#1622) (@perber)
    • fix(scripts): resolve GitHub handles in changelog output (#1602) (@perber)
    • fix(ui): close dialog before navigating to avoid render race flicker (#1601) (@perber)
    • fix(viewer): remove bullet marker from GFM task list items (#1600) (@perber)
    • fix(viewer): hide Attached Media label when TOC sidebar is collapsed (#1597) (@perber)
  4. v0.13.0LeafWiki v0.13.02026-09-08 · 2026-09-08 15:14 UTC
    查看更新说明

    📝 Changelog for v0.13.0

    This is a Settings release. Git backup and public mode can now be configured from the app instead of only at startup — and each instance still picks one source of truth: set the flag (--git-backup, --public-access) and that config wins with the Settings page deferring to it, or leave it unset and the UI drives it. That keeps a GitOps-style setup clean while giving everyone else a UI. Alongside it, the Settings area was rebuilt behind one navigation and the first per-user settings landed: language, date format, and avatar.

    🌟 Highlights

    Settings — per instance

    • Git backup from the UI: a configuration form in Settings (remote URL, SSH key or HTTP(S) username+password, schedule) with a "Test remote" check, stored in <data-dir>/git-backup.json. Keep it env-managed with --git-backup and the form defers to your flags. (#1439)
    • Public mode at runtime: toggle it from Settings instead of only via a startup flag. Pin it with --public-access and the page shows a status-only view.
    • One Settings area: every settings page now lives behind a single navigation and a section registry. (#1448)
    • Broken Links maintenance page to find unresolved links across the wiki. (#1434)
    • Explorer refresh button replaces the old maintenance settings page. (#1460)

    Settings — per user

    • German and Spanish UI translations, with a per-user language picker in Account Settings and live switching. (#1457, #1459)
    • Configurable date & time format, and date/time rendering that follows the active language.
    • Self-service avatar upload. (#1461)
    • Autosave is now a per-user preference, with a confirmation toast on save. (#1454, #1458)

    Everyone

    • Email (SMTP) password reset and user invitations: invite a teammate by address; they set their own password from the link. SMTP itself is configured via LEAFWIKI_SMTP_* flags/env. (#1331)
    • Editor: Tab inserts a tab at the cursor instead of reindenting the whole line; inline code stays visible in headings; print page breaks no longer clip article text. (#1537)
    • Images: percentage widths, inline-block rendering, and linked images that follow the link target. (#1483, #1520, #1446)
    • Auth hardening: minimum length on the initial admin password (#1449), readable error when password reset rejects a weak password (#1539), invitation-pending cleared when an admin sets a password (#1540), external login return URLs preserved.
    • Observability: Go runtime/process collectors and scrape-time content and user gauges on the Prometheus endpoint.
    • Internals: CLI rebuilt on urfave/cli v3

    💛 Thanks to our sponsors

    A heartfelt thank you to everyone supporting LeafWiki via GitHub Sponsors & Ko-fi. If you'd like to support LeafWiki too: leafwiki.com/support

    🙌 Thanks to contributors

    @ribbal, @Slydite4, @jochumdev, @richardmilles, @rzaman8677, @DavidMoffatt-web, @mrchypark, @bferanmi806-sketch, and @ashudhanda — for translations, features, fixes, and docs in this release. Contributions like these are what make LeafWiki better than one person could build alone.


    ✨ Features

    • feat: allow displaying images in percentage (#1483) (@ribbal)
    • feat: add broken links maintenance page (#1434) (@ribbal)
    • feat: use session storage for sidebar (#1518) (@ribbal)
    • feat(i18n): add Spanish translation (@Slydite4)
    • feat(i18n): add German translation and dynamic language infrastructure (#1457) (@perber)
    • feat(settings): add language picker to Account Settings (#1459) (@perber)
    • feat(settings): configurable date & time format in Account Settings (@perber)
    • feat(settings): add per-user UserSettings backend (language, autosave) (#1454) (@perber)
    • feat(settings): integrate per-user UserSettings into the Settings page (#1458) (@perber)
    • feat(settings): unify settings pag
  5. v0.12.1LeafWiki v0.12.12026-08-16 · 2026-08-16 15:18 UTC
    查看更新说明

    📝 Changelog for v0.12.1

    🌟 Highlights

    • Markdown & Preview: ==highlight== syntax renders as <mark>, collapsible blocks, opt-in line numbers for fenced code blocks, and an inline-code copy button
    • Auth: auto-create users from a trusted reverse-proxy header (with email support), plus forced-logout and TOTP-availability fixes
    • Reliability: SQLite concurrency fixes with a new load-test suite, size caps on zip extraction during import/restore, and api_keys.db now included in backup/restore
    • UX: hotkeys now fire in the capture phase, section sorting options, clickable sidebar accordion headers, TOC subheader transition fix
    • i18n: closed the remaining hardcoded-string gaps across the frontend

    🙌 Thanks to our contributors

    Big thanks to everyone who contributed code to this release: @richardmilles, @happykawayigt, @ribbal, and @Guflly

    💛 Thanks to our sponsors

    A heartfelt thank you to everyone supporting LeafWiki via GitHub Sponsors & Ko-Fi If you'd like to support LeafWiki too: leafwiki.com/support


    ✨ Features

    • feat(markdown): support ==highlight== syntax rendering as (#1421) (@perber)
    • feat(preview): opt-in line numbers for fenced code blocks (#1386) (@richardmilles)
    • feat(auth): auto-create users from trusted reverse-proxy header (#1330) (#1363) (@perber)
    • feat(preview): add inline code copy action (#1359) (@happykawayigt)
    • feat: render Collapsible Blocks from Markdown (#1382) (@ribbal)
    • feat(page): add section sorting options (@Guflly)
    • feat(i18n): close remaining hardcoded-string gaps (#1349) (@perber)
    • feat(i18n): close hardcoded-string gaps in frontend features (#1347) (@perber)

    🐛 Bug Fixes

    • fix(preview): hide inline-code copy button on mobile (#1429) (@perber)
    • fix(ui): rename Git backup Push now to Sync now (#1425) (@richardmilles)
    • fix(ui): keep subheader padding-right animated when leaving a toc page (#1420) (@perber)
    • fix(ui): style collapse block to match shoutout card design (#1416) (@perber)
    • fix(deps): bump dompurify 3.4.12 -> 3.4.13 (transitive via mermaid) (#1415) (@perber)
    • fix: stop swallowing store-unavailable errors in API-key and TOTP-login paths (#1414) (@perber)
    • fix(ui): prefer color emoji fonts in the sans stack (#1402) (@richardmilles)
    • fix(hotkeys): handle shortcuts in capture phase (#1314) (#1390) (@richardmilles)
    • fix: snapshot polling and block directive bugs (#1413) (@perber)
    • fix: prettier warning (@Copilot)
    • fix: stop forced logout when auth mode is unconfirmed after a config load (#1409) (@perber)
    • fix(i18n): replace leftover hardcoded English UI strings (#1397) (@richardmilles)
    • fix: stop UserService from swallowing the suspended-store error (@perber)
    • fix: stop user-management/UserResolver/remote-user from going stale after live restore (@perber)
    • fix: prefer exact title match over route-path for slash-containing wikilinks (#1381) (@perber)
    • fix(wikilink): create pages from broken [[Title]] links in the current folder (#1377) (@perber)
    • fix(sidebar): make accordion section header clickable, not just chevron (#1376) (@perber)
    • fix: include api_keys.db in backup/restore (#1374) (@perber)
    • fix(preview): remove dead inline-code pseudo-element reset (@perber)
    • fix(auth): accept email in reverse-proxy remote-user header (#1351) (@perber)
    • fix(build): inject main.Version via ldflags in release Docker builds (@perber)
    • fix(auth): hide TOTP enable option when no encryption key is configured (#1343) (@perber)

    🧾 Documentation

    • docs: Update version numbers for HTTP remote user settings (#1417) (@perber)
    • docs: align README keyboard shortcuts with shortcut catalog (#1395) (@richardmilles)
    • docs: fix nginx and Raspberry Pi install guides (#1394) (@richardmilles)
    • docs: do

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    🌿 LeafWiki

    GitHub Stars Latest Release Backend CI Frontend CI

    Self-hosted wiki. Single Go binary. SQLite + Markdown stored on disk.

    For engineers and self-hosters who want structured, long-lived documentation. No Node.js, no Redis, no Postgres — just a binary and a data directory.

    LeafWiki

    If you've looked at Wiki.js or Outline and thought "this is too much to operate for what I need" — this could fit for you.

    → Try it without installing: demo.leafwiki.com · Ctrl+E edit · Ctrl+S save · resets hourly
    → If it fits, a star helps others find it.
    → Useful to you? Sponsor on GitHub · Buy a coffee on Ko-fi

    docker run -p 8080:8080 -v ~/leafwiki-data:/app/data \
      ghcr.io/perber/leafwiki:latest \
      --jwt-secret=yoursecret --admin-password=yourpassword --allow-insecure=true
    

    → All install options (Docker Compose, Linux installer, binary)


    Table of Contents


    Features

    Operations:

    • Single Go binary — no external database, no runtime dependencies
    • Markdown on disk — page content is readable outside the app, backup is cp -r (stop the app first)
    • Runs on Linux, macOS, Windows, Raspberry Pi (x86_64 and ARM64; 32-bit x86 Linux via standalone binary)
    • Reverse-proxy friendly with --base-path
    • Reverse-proxy authentication via trusted HTTP header (v0.10+)
    • API keys for programmatic and agent access, admin-managed, scoped by role — in development, not yet ready for use
    • Three access modes: fully internal, public read with login-only editing, or open editing without login (see Operating Modes)
    • Roles: admin, editor, viewer

    Core functionality:

    • Tree navigation — explicit hierarchy, not flat note feeds
    • Manual page ordering — sort order is explicit, not driven by filename (see Sorting Pages)
    • Full-text search across titles and content, with tag-based filtering
    • Tags on pages — searchable and filterable across the wiki
    • Backlinks and link status per page (incoming, outgoing, broken links), with a dedicated admin view for auditing broken links
    • Built-in Markdown editor with live preview, keyboard shortcuts, and autocomplete for internal page links
    • Optimistic locking for concurrent edits
    • Markdown: tables, task lists, footnotes, callouts (:::info / :::warning), collapsible blocks (:::collapsible / :::collapsed), Mermaid diagrams, KaTeX math blocks ($$...$$, inline $...$ not supported), sanitized inline HTML, embedded PDFs (![name](https://github.com/perber/leafwiki/blob/HEAD/file.pdf), optionally opened on a specific page with ![name](https://github.com/perber/leafwiki/blob/HEAD/file.pdf#page=3))

    Customization:

    • Custom stylesheet (--custom-stylesheet, v0.8.5+)
    • Inject HTML/JS into <head> for analytics or custom CSS
    • Branding: logo, favicon, site name
    • Dark mode and mobile-friendly UI
    • Translated UI — ships in English, German, and Spanish; --default-language picks the default. Contributions of other languages are very welcome — see docs/i18n.md

    Opt-in via feature flags:

    • Revision history (--enable-revision)
    • Automatic link rewriting when pages are renamed or moved (--enable-link-refactor)
    • Email via SMTP for password reset and user invitations (--smtp-host, v0.13.0)
    • Git backup — push wiki content to a remote Git repository via SSH or HTTP(S) (--git-backup, v0.11.3, experimental)

    Markdown import:

    • ZIP-based importer for editors and admins
    • Supports Obsidian-style wiki link rewriting on import
    • Best results with a reasonably clean folder structure; not a fully automatic converter for all source formats

    Mobile:


    Good fit / not a fit

    Good fit:

    • Personal wikis, engineering notebooks, and runbooks
    • Internal team or homelab documentation
    • Existing Markdown or Obsidian vaults that need a structured wiki UI
    • Small teams that want tree navigation over flat note feeds
    • Self-hosted environments with low operational overhead

    Probably not a fit:

    • Organizations needing complex enterprise permissions or approval workflows
    • Real-time collaborative editing
    • Teams looking for a Confluence or Notion replacement

    LeafWiki is intentionally narrower than those systems. That focus is part of the value.


    Install

    Docker

    docker run -p 8080:8080 \
        -v ~/leafwiki-data:/app/data \
        ghcr.io/perber/leafwiki:latest \
        --jwt-secret=yoursecret \
        --admin-password=yourpassword \
        --allow-insecure=true
    

    --allow-insecure=true is required for plain HTTP. Omit it when serving over HTTPS (make sure your reverse proxy forwards X-Forwarded-Proto: https).

    Non-root:

    docker run -p 8080:8080 \
        -u 1000:1000 \
        -v ~/leafwiki-data:/app/data \
        ghcr.io/perber/leafwiki:latest \
        --jwt-secret=yoursecret \
        --admin-password=yourpassword \
        --allow-insecure=true
    

    The data directory must be writable by the specified user.

    Docker Compose

    services:
      leafwiki:
        image: ghcr.io/perber/leafwiki:latest
        container_name: leafwiki
        user: 1000:1000
        ports:
          - "8080:8080"
        environment:
          - LEAFWIKI_JWT_SECRET=yourSecret
          - LEAFWIKI_ADMIN_PASSWORD=yourPassword
          - LEAFWIKI_ALLOW_INSECURE=true  # Required for plain HTTP. Omit for HTTPS (ensure `X-Forwarded-Proto: https` is forwarded).
        volumes:
          - ${HOME}/leafwiki-data:/app/data
        restart: unless-stopped
    

    Kubernetes

    See docs/install/kubernetes.md for a Deployment + PersistentVolumeClaim + Service + Ingress example (Gateway API alternative included).

    Linux installer

    sudo /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/perber/leafwiki/main/install.sh)"
    

    Installs LeafWiki as a system service. Tested on Ubuntu, Debian, and Raspbian.

    Update:

    sudo /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/perber/leafwiki/main/update.sh)"
    

    Only works if you installed with the script above. Not compatible with Docker or binary installs.

    Non-interactive mode:

    cp .env.example .env
    # Edit .env with your configuration
    sudo ./install.sh --non-interactive --env-file ./.env
    

    Security: in interactive mode, environment variables are written in plain text to /etc/leafwiki/.env. Restrict access to that file.

    Deployment examples:

    Binary

    Download leafwiki-<version>-<os>-<arch> and its .sha256 file from GitHub Releases (linux-amd64, linux-arm64, linux-386, darwin-amd64, darwin-arm64, windows-amd64), check that the hash matches the one in the .sha256 file, and rename it to leafwiki:

    sha256sum leafwiki-<version>-<os>-<arch>
    cat leafwiki-<version>-<os>-<arch>.sha256
    mv leafwiki-<version>-<os>-<arch> leafwiki
    chmod +x leafwiki
    ./leafwiki --jwt-secret=yoursecret --admin-password=yourpassword --allow-insecure=true
    

    The server binds to 127.0.0.1:8080 by default. To expose it on the network:

    ./leafwiki --jwt-secret=yoursecret --admin-password=yourpassword --host=0.0.0.0 --allow-insecure=true
    

    Default data directory is ./data. Change with --data-dir.

    32-bit x86 Linux (i686, e.g. Alpine on an Intel Atom) is supported on a best-effort basis: use the linux-386 binary. It is built without CGO, so it doesn't need glibc and runs on musl-based distros. The systemd installer and Docker images only cover amd64/arm64, so the binary is the only install path on 32-bit x86 (that includes update.sh, which expects the systemd setup).

    Build from source

    Requires Go and Node.js (versions: the toolchain line in go.mod, and .nvmrc). make build compiles the UI, embeds it, and produces a self-contained leafwiki binary (same as release/Docker builds). Use HTTP (http://localhost:8080/), not HTTPS, unless you terminate TLS in front of LeafWiki.

    git clone https://github.com/perber/leafwiki.git
    cd leafwiki
    git switch --detach v0.13.0   # or any tag / main
    make build
    ./leafwiki --disable-auth --host=127.0.0.1 --data-dir ./data --allow-insecure=true
    

    For API-only local development with Vite, use make build-api (or make run) instead — see Dev Setup.

    Reset admin password

    ./leafwiki reset-admin-password
    

    Restore a snapshot (offline)

    For disaster recovery or migrating to a fresh instance, restore a snapshot ZIP into the data directory while the server is stopped:

    ./leafwiki --data-dir ./data restore-snapshot ./data/snapshots/leafwiki-snapshot-2026-01-01.zip
    

    When --snapshot is enabled, a live restore is also available from the admin UI (Settings → Full Backup). Use this offline command when the server can't start or you're seeding a new data directory. The --snapshot* flags are in CLI Flags.


    Operating Modes

    LeafWiki supports three access modes. Pick the one that matches your environment:

    1. Internal wiki — login required (default)

    All access requires authentication. Nobody can read or edit without a valid account. This is the default behavior when no access flags are set.

    ./leafwiki --jwt-secret=yoursecret --admin-password=yourpassword
    

    Use this for team-internal wikis or homelab setups where content should stay private.

    2. Public read, login required for editing

    Anyone can browse the wiki without logging in. Only authenticated users with an editor or admin role can make changes.

    ./leafwiki --jwt-secret=yoursecret --admin-password=yourpassword --public-access=true
    

    Use this for open documentation or project wikis where readers don't need accounts, but you still want to control who can edit.

    3. No login — everyone can read and edit (--disable-auth)

    Authentication is completely disabled. Anyone who can reach the server can read and edit all pages.

    ./leafwiki --disable-auth --host=127.0.0.1
    

    ⚠️ Only use this on trusted internal networks or local setups. Never expose a --disable-auth instance to the public internet.


    Dev Setup

    Stack: Go · React (Vite) · SQLite

    git clone https://github.com/perber/leafwiki.git
    cd leafwiki
    
    mise install   # optional: Node from .nvmrc and Go; nvm/fnm and any Go >= 1.21 work too
    make setup     # npm dependencies
    make dev       # backend + Vite; Ctrl-C stops both
    

    Vite starts on http://localhost:5173 (login admin / devpassword) and proxies the API to the backend on 127.0.0.1:8080. make doctor checks your Go and Node versions. No local toolchain? The Dev Container sets everything up in VS Code or GitHub Codespaces; then run make dev.

    See CONTRIBUTING.md for running the two separately and for contribution guidelines.


    Configuration

    Required

    FlagDescription
    --jwt-secretSecret for signing JWTs. Keep it secure.
    --admin-passwordInitial admin password (only applied if no admin exists yet).

    Optional admin identity

    FlagDescriptionDefault
    --admin-usernameInitial admin username (only applied if no admin exists yet).admin
    --admin-emailInitial admin email (only applied if no admin exists yet).admin@localhost

    For plain HTTP: add --allow-insecure=true so login and CSRF cookies work.

    CLI Flags

    FlagDescriptionDefaultSince
    --hostHost/IP the server binds to127.0.0.1–
    --portPort the server listens on8080–
    --unix-socketUnix domain socket path; overrides --host and --port""v0.11.3
    --data-dirDirectory where data is stored./data–
    --admin-usernameInitial admin username (only applied if no admin exists yet)adminv0.12.0
    --admin-emailInitial admin email (only applied if no admin exists yet)admin@localhostv0.12.0
    --public-accessAllow public read-only accessfalse–
    --base-pathURL prefix for reverse proxy setups (e.g. /wiki)""v0.8.2
    --public-urlPublic base URL of this wiki (absolute http(s)://); used for links in outgoing email, required when --smtp-host is set""v0.13.0
    --allow-insecure⚠️ Enables HTTP for auth cookies (required for plain HTTP)falsev0.7.0
    --disable-auth⚠️ Disable all authentication (internal networks only)falsev0.7.0
    --access-token-timeoutAccess token duration (e.g. 24h, 15m)15mv0.7.0
    --refresh-token-timeoutRefresh token duration (e.g. 168h)168hv0.7.0
    --max-asset-upload-sizeMax upload size (e.g. 50MiB, 52428800)50MiBv0.8.5
    --custom-stylesheetPath to a .css file inside the data dir""v0.8.5
    --default-languageDefault UI language code (en, de, es)""v0.13.0
    --inject-code-in-headerRaw HTML/JS injected into <head>""v0.6.0
    --hide-link-metadata-sectionHide backlinks and link status panelfalse–
    --enable-revisionEnable revision historyfalsev0.9.0
    --enable-link-refactorEnable link rewriting on rename/movefalsev0.9.0
    --max-revision-historyMax revisions per page; 0 = unlimited100v0.9.0
    --revision-coalesce-windowWindow for coalescing rapid successive auto-save revisions by the same author; 0 = disabled5mv0.11.0
    --enable-http-remote-userEnable reverse-proxy auth via HTTP headerfalsev0.10.0
    --http-remote-user-header-nameHeader name carrying the username or email from the proxyRemote-Userv0.10.0
    --enable-http-remote-user-auto-createAuto-provision users the proxy asserts but LeafWiki doesn't knowfalsev0.12.1
    --http-remote-user-email-header-nameHeader name carrying the email for auto-created users""v0.12.1
    --http-remote-user-default-roleRole assigned to auto-created users; must not be adminviewerv0.12.1
    --trusted-proxy-ipsTrusted proxy IPs/CIDRs for remote-user header""v0.10.0
    --login-urlRedirect to an external URL instead of the built-in login form""v0.12.0
    --logout-urlRedirect to an external URL after logout""v0.12.0
    --http-remote-user-logout-url⚠️ Deprecated, use --logout-url instead""v0.10.0
    --user-management-urlReplace the built-in User Management UI with a link to an external page""v0.11.4
    --disable-request-logSuppress per-request HTTP access log linesfalsev0.10.1
    --log-formatLog output format: text or jsontextv0.12.0
    --totp-encryption-keyKey to encrypt per-user TOTP secrets at rest (min 32 bytes); required only once a user enables TOTP""v0.12.0
    --enable-metricsEnable the Prometheus /metrics endpoint on a separate listenerfalsev0.12.0
    --metrics-hostHost/IP for the metrics listener127.0.0.1v0.12.0
    --metrics-portPort for the metrics listener9091v0.12.0
    --snapshotEnable full backup snapshots (ZIP incl. the SQLite database)truev0.12.0
    --snapshot-intervalSnapshot interval (e.g. 24h, 6h); 0 = manual-only24hv0.12.0
    --snapshot-retentionNumber of most recent snapshots to keep; <= 0 = keep all10v0.12.0
    --snapshot-dirDirectory to store snapshot ZIPs in<data-dir>/snapshotsv0.12.0
    --restore-upload-max-sizeMax size for an uploaded backup ZIP to restore from500MiBv0.12.0
    --git-backup⚗️ Enable git backup to a remote repositoryfalsev0.11.3
    --git-backup-remote⚗️ SSH remote URL for git backup (e.g. [email protected]:user/repo.git)""v0.11.3
    --git-backup-branch⚗️ Branch to push tomainv0.11.3
    --git-backup-path⚗️ Directory holding root/ and assets/ (e.g. docs → docs/root); empty = top level""v0.13.0
    --git-backup-ssh-key⚗️ Raw SSH private key (prefer env var)""v0.11.3
    --git-backup-ssh-key-path⚗️ Path to SSH private key file""v0.11.3
    --git-backup-ssh-known-hosts⚗️ Path to known_hosts for MITM protection""v0.11.3
    --git-backup-http-username⚗️ Username for HTTP(S) basic auth on http(s):// remotes""v0.13.0
    --git-backup-http-password⚗️ Password or access token for HTTP(S) basic auth (prefer env var)""v0.13.0
    --git-backup-author-name⚗️ Git commit author nameLeafWiki Backupv0.11.3
    --git-backup-author-email⚗️ Git commit author email[email protected]v0.11.3
    --git-backup-interval⚗️ Backup interval (e.g. 60m, 2h); 0 = manual-only60mv0.11.3
    --smtp-hostSMTP host for password-reset and invite email; unset disables email""v0.13.0
    --smtp-portSMTP server port587v0.13.0
    --smtp-usernameSMTP auth username""v0.13.0
    --smtp-passwordSMTP auth password (prefer env var)""v0.13.0
    --smtp-fromFrom address for outgoing email (required when --smtp-host is set)""v0.13.0
    --smtp-from-nameFrom display name for outgoing emailLeafWikiv0.13.0
    --smtp-securityTransport security: none, starttls, or tlsstarttlsv0.13.0
    --smtp-insecure-skip-verifySkip TLS certificate verification for SMTP (testing only)falsev0.13.0
    --smtp-timeoutTimeout for a single SMTP send (e.g. 10s)10sv0.13.0

    Docker image default: LEAFWIKI_HOST is set to 0.0.0.0 automatically by the container entrypoint if neither --host nor LEAFWIKI_HOST is provided.

    Environment Variables

    VariableDescriptionDefaultSince
    LEAFWIKI_HOSTHost/IP address127.0.0.1–
    LEAFWIKI_PORTPort8080–
    LEAFWIKI_UNIX_SOCKETUnix domain socket path; overrides host/port""v0.11.3
    LEAFWIKI_DATA_DIRData directory path./data–
    LEAFWIKI_ADMIN_PASSWORDInitial admin password (required)––
    LEAFWIKI_ADMIN_USERNAMEInitial admin username (only applied if no admin exists yet)adminv0.12.0
    LEAFWIKI_ADMIN_EMAILInitial admin email (only applied if no admin exists yet)admin@localhostv0.12.0
    LEAFWIKI_JWT_SECRETJWT signing secret (required)––
    LEAFWIKI_PUBLIC_ACCESSAllow public read-only accessfalse–
    LEAFWIKI_BASE_PATHURL prefix for reverse proxy""v0.8.2
    LEAFWIKI_PUBLIC_URLPublic base URL of this wiki (absolute http(s)://); used for links in outgoing email, required with LEAFWIKI_SMTP_HOST""v0.13.0
    LEAFWIKI_ALLOW_INSECURE⚠️ HTTP auth cookiesfalsev0.7.0
    LEAFWIKI_DISABLE_AUTH⚠️ Disable authenticationfalsev0.7.0
    LEAFWIKI_ACCESS_TOKEN_TIMEOUTAccess token duration15mv0.7.0
    LEAFWIKI_REFRESH_TOKEN_TIMEOUTRefresh token duration168hv0.7.0
    LEAFWIKI_MAX_ASSET_UPLOAD_SIZEMax upload size50MiBv0.8.5
    LEAFWIKI_CUSTOM_STYLESHEETPath to .css file inside data dir""v0.8.5
    LEAFWIKI_DEFAULT_LANGUAGEDefault UI language code (en, de, es)""v0.13.0
    LEAFWIKI_INJECT_CODE_IN_HEADERHTML/JS injected into <head>""v0.6.0
    LEAFWIKI_HIDE_LINK_METADATA_SECTIONHide backlinks and link status panelfalse–
    LEAFWIKI_ENABLE_REVISIONRevision historyfalsev0.9.0
    LEAFWIKI_ENABLE_LINK_REFACTORLink rewriting on rename/movefalsev0.9.0
    LEAFWIKI_MAX_REVISION_HISTORYMax revisions per page; 0 = unlimited100v0.9.0
    LEAFWIKI_REVISION_COALESCE_WINDOWWindow for coalescing rapid successive auto-save revisions; 0 = disabled5mv0.11.0
    LEAFWIKI_ENABLE_HTTP_REMOTE_USERReverse-proxy auth via headerfalsev0.10.0
    LEAFWIKI_HTTP_REMOTE_USER_HEADER_NAMEUsername or email header from proxyRemote-Userv0.10.0
    LEAFWIKI_ENABLE_HTTP_REMOTE_USER_AUTO_CREATEAuto-provision users the proxy asserts but LeafWiki doesn't knowfalsev0.12.1
    LEAFWIKI_HTTP_REMOTE_USER_EMAIL_HEADER_NAMEEmail header for auto-created users""v0.12.1
    LEAFWIKI_HTTP_REMOTE_USER_DEFAULT_ROLERole assigned to auto-created users; must not be adminviewerv0.12.1
    LEAFWIKI_TRUSTED_PROXY_IPSTrusted proxy IPs/CIDRs""v0.10.0
    LEAFWIKI_LOGIN_URLRedirect to an external URL instead of the login form""v0.12.0
    LEAFWIKI_LOGOUT_URLRedirect to an external URL after logout""v0.12.0
    LEAFWIKI_HTTP_REMOTE_USER_LOGOUT_URL⚠️ Deprecated, use LEAFWIKI_LOGOUT_URL instead""v0.10.0
    LEAFWIKI_USER_MANAGEMENT_URLReplace the built-in User Management UI with an external link""v0.11.4
    LEAFWIKI_DISABLE_REQUEST_LOGSuppress per-request HTTP access log linesfalsev0.10.1
    LEAFWIKI_LOG_FORMATLog output format: text or jsontextv0.12.0
    LEAFWIKI_LOG_LEVELLog level: debug, info, warn, error (env-var only, no CLI flag)infov0.8.0
    LEAFWIKI_TOTP_ENCRYPTION_KEYKey to encrypt per-user TOTP secrets at rest (min 32 bytes)""v0.12.0
    LEAFWIKI_ENABLE_METRICSEnable the Prometheus /metrics endpointfalsev0.12.0
    LEAFWIKI_METRICS_HOSTHost/IP for the metrics listener127.0.0.1v0.12.0
    LEAFWIKI_METRICS_PORTPort for the metrics listener9091v0.12.0
    LEAFWIKI_SNAPSHOTEnable full backup snapshotstruev0.12.0
    LEAFWIKI_SNAPSHOT_INTERVALSnapshot interval; 0 = manual-only24hv0.12.0
    LEAFWIKI_SNAPSHOT_RETENTIONNumber of most recent snapshots to keep; <= 0 = keep all10v0.12.0
    LEAFWIKI_SNAPSHOT_DIRDirectory to store snapshot ZIPs in<data-dir>/snapshotsv0.12.0
    LEAFWIKI_RESTORE_UPLOAD_MAX_SIZEMax size for an uploaded backup ZIP to restore from500MiBv0.12.0
    LEAFWIKI_GIT_BACKUP⚗️ Enable git backupfalsev0.11.3
    LEAFWIKI_GIT_BACKUP_REMOTE⚗️ SSH remote URL""v0.11.3
    LEAFWIKI_GIT_BACKUP_BRANCH⚗️ Branch to push tomainv0.11.3
    LEAFWIKI_GIT_BACKUP_PATH⚗️ Directory holding root/ and assets/ (e.g. docs → docs/root); empty = top level""v0.13.0
    LEAFWIKI_GIT_BACKUP_SSH_KEY⚗️ Raw SSH private key (preferred over path)""v0.11.3
    LEAFWIKI_GIT_BACKUP_SSH_KEY_PATH⚗️ Path to SSH private key file""v0.11.3
    LEAFWIKI_GIT_BACKUP_SSH_KNOWN_HOSTS⚗️ Path to known_hosts file""v0.11.3
    LEAFWIKI_GIT_BACKUP_HTTP_USERNAME⚗️ Username for HTTP(S) basic auth""v0.13.0
    LEAFWIKI_GIT_BACKUP_HTTP_PASSWORD⚗️ Password or access token for HTTP(S) basic auth""v0.13.0
    LEAFWIKI_GIT_BACKUP_AUTHOR_NAME⚗️ Git commit author nameLeafWiki Backupv0.11.3
    LEAFWIKI_GIT_BACKUP_AUTHOR_EMAIL⚗️ Git commit author email[email protected]v0.11.3
    LEAFWIKI_GIT_BACKUP_INTERVAL⚗️ Backup interval (e.g. 60m); 0 = manual-only60mv0.11.3
    LEAFWIKI_SMTP_HOSTSMTP host for password-reset/invite email; unset disables email""v0.13.0
    LEAFWIKI_SMTP_PORTSMTP server port587v0.13.0
    LEAFWIKI_SMTP_USERNAMESMTP auth username""v0.13.0
    LEAFWIKI_SMTP_PASSWORDSMTP auth password""v0.13.0
    LEAFWIKI_SMTP_FROMFrom address (required with LEAFWIKI_SMTP_HOST)""v0.13.0
    LEAFWIKI_SMTP_FROM_NAMEFrom display nameLeafWikiv0.13.0
    LEAFWIKI_SMTP_SECURITYTransport security: none, starttls, or tlsstarttlsv0.13.0
    LEAFWIKI_SMTP_INSECURE_SKIP_VERIFYSkip TLS certificate verification for SMTPfalsev0.13.0
    LEAFWIKI_SMTP_TIMEOUTTimeout for a single SMTP send10sv0.13.0

    Custom Stylesheet

    Place a .css file inside your data directory and pass its path:

    ./leafwiki \
      --data-dir=./data \
      --custom-stylesheet=custom.css \
      --jwt-secret=yoursecret \
      --admin-password=yourpassword
    
    • File must exist at ./data/custom.css
    • Served as /custom.css (or ${base-path}/custom.css with --base-path)
    • The endpoint is publicly accessible

    Reverse-Proxy Authentication

    Available since v0.10.0. Use when an upstream proxy authenticates users and forwards the username or email via HTTP header.

    ./leafwiki \
      --jwt-secret=yoursecret \
      --admin-password=yourpassword \
      --enable-http-remote-user=true \
      --http-remote-user-header-name=X-Forwarded-User \
      --trusted-proxy-ips=127.0.0.1,172.18.0.0/16 \
      --login-url=https://auth.example.com/login \
      --logout-url=https://auth.example.com/logout
    
    • Only trusts the header from IPs listed in --trusted-proxy-ips
    • If the forwarded username or email doesn't match a LeafWiki user, the request is rejected — unless --enable-http-remote-user-auto-create is set (see below)
    • Do not enable without configuring --trusted-proxy-ips
    • --login-url and --logout-url are independent, optional redirect targets — set either or both to send users to an external IdP instead of the built-in login form / to redirect after logout
    • --login-url, --logout-url, and --user-management-url must all start with http:// or https://; the server refuses to start otherwise (relative paths are not accepted for any of them)
    • ⚠️ --login-url takes effect regardless of --enable-http-remote-user and has no in-app bypass: once set, every unauthenticated visit (including /login itself) redirects to it immediately. Double-check the URL before setting it — a wrong or unreachable value locks all users, including admins, out of the built-in login form
    • --http-remote-user-logout-url (v0.10.0) is deprecated; use --logout-url instead. It still works as a fallback when --logout-url/LEAFWIKI_LOGOUT_URL isn't set, but a deprecation warning is logged
    Auto-creating users (v0.12.1)

    By default, a proxy-asserted identity with no matching LeafWiki account is rejected (401). Set --enable-http-remote-user-auto-create=true to provision one automatically instead:

    ./leafwiki \
      --jwt-secret=yoursecret \
      --admin-password=yourpassword \
      --enable-http-remote-user=true \
      --http-remote-user-header-name=X-Forwarded-User \
      --trusted-proxy-ips=127.0.0.1,172.18.0.0/16 \
      --enable-http-remote-user-auto-create=true \
      --http-remote-user-email-header-name=X-Forwarded-Email \
      --http-remote-user-default-role=viewer
    
    • Requires --enable-http-remote-user to also be set; the server refuses to start otherwise
    • The value in --http-remote-user-header-name becomes the new account's username verbatim, even if it looks like an email address — if your proxy sends an email in that header and you want a distinct, readable username, point --http-remote-user-email-header-name at a separate proxy header
    • If --http-remote-user-email-header-name isn't set or the header is empty, a non-deliverable placeholder email (<username>@remote-user.invalid) is used instead
    • Auto-created accounts get a random password nobody is told — they can only ever authenticate via the trusted proxy, not the built-in login form
    • --http-remote-user-default-role must not be admin — the server refuses to start otherwise. A forged or misrouted header must not be able to mint an admin account by itself; promote an auto-created user to admin manually if needed

    Unix Socket (v0.11.3)

    Use --unix-socket when LeafWiki should listen on a local unix domain socket instead of TCP.

    ./leafwiki \
      --unix-socket=/run/leafwiki/leafwiki.sock \
      --data-dir=./data \
      --jwt-secret=yoursecret \
      --admin-password=yourpassword
    
    • --unix-socket overrides --host and --port
    • LeafWiki still serves normal HTTP; a reverse proxy such as Nginx or Caddy connects to the socket
    • If a stale socket file exists from a previous run, LeafWiki removes it before listening
    • New socket files are created with permissions 0660
    • On Windows, unix sockets are not supported and LeafWiki returns a startup error if this option is used

    Git Backup (v0.11.3, experimental)

    Experimental — This feature is new and may change in future releases. Test it thoroughly before relying on it for critical data.

    Git Backup pushes wiki content to a remote Git repository on a configurable interval, via SSH or HTTP(S). Only the root/ (pages) and assets/ directories are committed, so database files (.db, .db-wal, etc.) and other runtime files are never included; .gitignore rules are still honoured. By default the content is committed at the repository top level — set --git-backup-path to nest it under a directory (useful for a monorepo).

    Backups run automatically on a configurable interval and can also be triggered manually from the Git Content Backup page.

    CLI flags (v0.11.3+):

    FlagDescriptionDefault
    --git-backupEnable git backupfalse
    --git-backup-remoteSSH or HTTP(S) remote URL (e.g. [email protected]:user/repo.git, https://github.com/user/repo.git)""
    --git-backup-branchBranch to push tomain
    --git-backup-pathDirectory holding root/ and assets/, e.g. docs → docs/root + docs/assets; empty = repository top level (v0.13.0+)""
    --git-backup-ssh-keyRaw SSH private key (prefer env var)""
    --git-backup-ssh-key-pathPath to SSH private key file""
    --git-backup-ssh-known-hostsPath to known_hosts for MITM protection""
    --git-backup-http-usernameUsername for HTTP(S) basic auth (v0.13.0+)""
    --git-backup-http-passwordPassword or access token for HTTP(S) basic auth (prefer env var, v0.13.0+)""
    --git-backup-author-nameGit commit author nameLeafWiki Backup
    --git-backup-author-emailGit commit author email[email protected]
    --git-backup-intervalBackup interval (e.g. 60m, 2h); 0 = manual-only60m

    Environment variables:

    VariableDescription
    LEAFWIKI_GIT_BACKUPEnable git backup
    LEAFWIKI_GIT_BACKUP_REMOTESSH or HTTP(S) remote URL
    LEAFWIKI_GIT_BACKUP_BRANCHBranch to push to
    LEAFWIKI_GIT_BACKUP_PATHDirectory holding root/ and assets/, e.g. docs → docs/root + docs/assets
    LEAFWIKI_GIT_BACKUP_SSH_KEYRaw SSH private key
    LEAFWIKI_GIT_BACKUP_SSH_KEY_PATHPath to SSH private key file
    LEAFWIKI_GIT_BACKUP_SSH_KNOWN_HOSTSPath to known_hosts file
    LEAFWIKI_GIT_BACKUP_HTTP_USERNAMEUsername for HTTP(S) basic auth
    LEAFWIKI_GIT_BACKUP_HTTP_PASSWORDPassword or access token for HTTP(S) basic auth
    LEAFWIKI_GIT_BACKUP_AUTHOR_NAMEGit commit author name
    LEAFWIKI_GIT_BACKUP_AUTHOR_EMAILGit commit author email
    LEAFWIKI_GIT_BACKUP_INTERVALBackup interval

    Example — SSH (Docker Compose):

    environment:
      - LEAFWIKI_GIT_BACKUP=true
      - [email protected]:youruser/yourwiki-backup.git
      - LEAFWIKI_GIT_BACKUP_BRANCH=main
      - LEAFWIKI_GIT_BACKUP_SSH_KEY=${LEAFWIKI_GIT_BACKUP_SSH_KEY}  # from .env file
      - LEAFWIKI_GIT_BACKUP_INTERVAL=60m
    

    Example — HTTPS with an access token (Docker Compose, v0.13.0+):

    environment:
      - LEAFWIKI_GIT_BACKUP=true
      - LEAFWIKI_GIT_BACKUP_REMOTE=https://github.com/youruser/yourwiki-backup.git
      - LEAFWIKI_GIT_BACKUP_BRANCH=main
      - LEAFWIKI_GIT_BACKUP_HTTP_USERNAME=youruser
      - LEAFWIKI_GIT_BACKUP_HTTP_PASSWORD=${LEAFWIKI_GIT_BACKUP_HTTP_PASSWORD}  # from .env file
      - LEAFWIKI_GIT_BACKUP_INTERVAL=60m
    

    On GitHub, create a fine-grained personal access token limited to the backup repository with Contents: Read and write permission, and use it as the password. The username can be your GitHub username.

    Notes:

    • --git-backup-remote is required when pushing to a remote. It must be an SSH URL (git@... or ssh://...) or an HTTP(S) URL (https://..., http://...). Leave it unset for local-only backups.
    • For SSH remotes, either --git-backup-ssh-key or --git-backup-ssh-key-path is required. Prefer the environment variable to avoid the key appearing in process listings.
    • For HTTP(S) remotes, both --git-backup-http-username and --git-backup-http-password are required. Prefer LEAFWIKI_GIT_BACKUP_HTTP_PASSWORD over the flag — LeafWiki warns at startup when the password is passed as a flag, since flags are visible in process listings. Credentials embedded directly in the remote URL (https://user:token@host/repo.git) also work and are masked in logs and in the UI.
    • Prefer https:// over http://: with plain http:// the credentials and your wiki content travel unencrypted, and LeafWiki logs a warning at startup.
    • --git-backup-ssh-known-hosts is optional but recommended for SSH remotes. If not set, LeafWiki falls back to ~/.ssh/known_hosts. If that file does not exist either (common in containers), SSH host key verification is disabled — leaving connections open to MITM attacks. Set this flag explicitly in production. It has no effect on HTTP(S) remotes, which are verified via TLS.
    • If the remote diverges (e.g. someone pushed directly to the backup branch), LeafWiki will stop auto-pushing and show a Conflict — remote diverged warning in the UI. Click Force Push in the UI to overwrite the remote with the current local backup history. Your wiki content is never lost — the local backup repo is always authoritative.
    • This backs up content only — the SQLite database is not included. For a full backup, use your data directory (cp -r with the app stopped).
    • When LeafWiki first connects to a remote that already contains a backup, it syncs that content into the local data directory before the wiki loads its pages (local files always win — nothing is overwritten). A new instance therefore picks up the backed-up pages instead of overwriting the remote with an empty wiki, and it does not seed its default welcome page when the backup already has content. After that first sync, backups behave normally, including committing deletions.
    • --git-backup-path (e.g. docs) commits root/ and assets/ under that directory instead of the top level — docs produces docs/root/ and docs/assets/ — and pulls external edits back from there. This is handy when the backup remote is a monorepo: files outside the configured directory are left untouched. LeafWiki's live content still lives in <data-dir>/root and <data-dir>/assets; nothing is copied into the repository working tree. Leave it empty to keep the historical top-level layout.

    Email (SMTP)

    Available since v0.13.0. Configure an SMTP server to enable the forgot-password flow and email-based user invitations. Without --smtp-host, both features stay off and the login form shows no password-reset link.

    ./leafwiki \
      --jwt-secret=yoursecret \
      --admin-password=yourpassword \
      --smtp-host=smtp.example.com \
      --smtp-port=587 \
      --smtp-security=starttls \
      [email protected] \
      --smtp-password=yourpassword \
      [email protected] \
      --public-url=https://wiki.example.com
    
    • --smtp-from and --public-url are required once --smtp-host is set — the server refuses to start otherwise
    • --public-url must be an absolute http(s):// URL; it is the base for links in outgoing email
    • --smtp-security is one of none, starttls (default), or tls
    • Prefer LEAFWIKI_SMTP_PASSWORD over the flag to keep the password out of process listings
    • --smtp-insecure-skip-verify disables TLS certificate verification — for testing only

    Security

    Enabled by default since v0.7.0:

    • Secure, HttpOnly cookies for session handling
    • CSRF protection on all state-changing requests
    • Rate limiting on auth endpoints
    • Role-based access: admin, editor, viewer

    --disable-auth removes all authentication. Only use for local development, trusted internal networks, or isolated environments.

    # Safe local-only example:
    ./leafwiki --disable-auth --host=127.0.0.1
    

    For most setups, prefer --public-access for read-only public access and the viewer role for restricted accounts.

    Operations notes

    • Default bind: 127.0.0.1 (binary) / 0.0.0.0 (Docker image)
    • Default data dir: ./data (binary) / /app/data (container)
    • Defaults are intentionally conservative — a fresh install does not become network-exposed by accident

    Keyboard Shortcuts

    ActionShortcut
    Shortcuts helpCtrl + / / Cmd + /
    Edit modeCtrl + E / Cmd + E
    SaveCtrl + S / Cmd + S
    SearchCtrl + Shift + F / Cmd + Shift + F
    Navigation paneCtrl + Shift + E / Cmd + Shift + E
    Go to pageCtrl + Alt + P / Cmd + Option + P
    Toggle TOCCtrl + Shift + O / Cmd + Shift + O
    Copy page linkCtrl + Shift + S / Cmd + Shift + S
    Share / permalinkCtrl + Shift + L / Cmd + Shift + L
    Page historyCtrl + H / Cmd + H
    Print pageCtrl + P / Cmd + P
    Delete pageCtrl + Delete / Cmd + Delete
    BoldCtrl + B / Cmd + B
    ItalicCtrl + I / Cmd + I
    Insert linkCtrl + K / Cmd + K
    Headline 1–3Ctrl + Alt + 1–3 / Cmd + Alt + 1–3

    Ctrl+V / Cmd+V for pasting images and files works in the editor.
    Esc closes modals, dialogs, and edit mode.

    Press Ctrl+/ / Cmd+/ in the app for the full in-product shortcuts list.


    Relative Markdown Links

    LeafWiki resolves relative page links with page-as-folder semantics: the current page path is treated as a folder, so [Setup](https://github.com/perber/leafwiki/blob/HEAD/setup) on /docs/guide resolves to /docs/guide/setup, not a sibling /docs/setup.

    A trailing .md suffix in a link target is ignored for page lookup (for example setup.md → setup), which matches common filesystem / Obsidian-style Markdown.

    External Edits & Resync

    LeafWiki is intended to be the primary writer for a workspace. However, Markdown files may still be changed outside LeafWiki — for example through a text editor, Git, a script, or a bulk import.

    LeafWiki does not continuously watch the filesystem for these changes. To make externally modified files visible to LeafWiki, trigger a resync in one of two ways:

    • Admin UI: click the Refresh button in the page-tree toolbar (admins only). It runs a resync with live progress across four phases (tree, links, tags, search); if HTTP Git Backup is configured, it first pulls the latest commits from the remote. Non-admin users see the same button, but for them it only re-fetches the current page tree.
    • OS signal: send SIGUSR1 or SIGHUP to the running process — no restart required. This can be useful when an external workflow needs to explicitly tell LeafWiki that files have changed.

    Both paths run the same resync job and produce the same result. A resync should be considered an explicit reconciliation of the workspace rather than continuous bidirectional filesystem synchronization.

    Changes to .leafwikiignore are separate and are only read at startup.

    New files without a leafwiki_id: LeafWiki stores the identity of a page in the leafwiki_id field in its frontmatter rather than deriving it from the filename or path. This allows LeafWiki to retain the identity of a document when it is renamed or moved.

    If a Markdown file created outside LeafWiki does not yet contain a leafwiki_id, the next resync generates one and writes it back to the file. No manual action is required, but the file will therefore change on disk during the resync.

    If root/ is managed by a separate Git workflow outside LeafWiki's built-in Git Backup, this generated ID will appear as an additional diff.


    Sorting Pages

    Page order in LeafWiki is explicit and manual — it does not follow filename or alphabetical order automatically. By default, pages appear in the order they were created.

    LeafWiki is not a file browser. The tree reflects the structure you define, and the order you set is the order your readers see.

    To reorder the pages inside a section or under a parent page:

    1. Hover over the section or page in the sidebar tree to reveal the action buttons
    2. Click the ⋮ (more actions) button
    3. Select Sort Section Children or Sort Page Children

    Sort context menu

    The sort dialog lets you drag items into position, use the ↑ ↓ arrow buttons, or jump to alphabetical order with A → Z / Z → A. Click Save to apply.

    Sort dialog

    Sorting is per level — the order of a section's direct children is independent of deeper nested items.


    Support this project

    If it's useful to you:

    Need help deploying LeafWiki for your team? Business support & setup →


    Contributing

    Contributions, discussions, and feedback are welcome.
    Open an issue or start a discussion on GitHub. Follow the repository to get notified about new releases.

    Translations: the UI currently ships in English, German, Spanish, French, and Simplified Chinese. Adding another language is a self-contained contribution — docs/i18n.md walks through it.