# Wiki.js Maintainer > ๐Ÿ‡ฌ๐Ÿ‡ง English ยท [๐Ÿ‡ฉ๐Ÿ‡ช Deutsch](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/README.de.md) Obsidian plugin that publishes and syncs notes from one folder of your vault to a [Wiki.js](https://js.wiki/) 2.x instance over its GraphQL API. [![License: AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/LICENSE) [![Docs: CC BY-SA 4.0](https://img.shields.io/badge/docs-CC%20BY--SA%204.0-lightgrey.svg)](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/LICENSE-DOCS) [![Release](https://img.shields.io/github/v/release/johannes-kaindl/wikijs-maintainer?label=release)](https://github.com/johannes-kaindl/wikijs-maintainer/releases) ![Platform](https://img.shields.io/badge/platform-Obsidian-lightgrey)

The sync status view listing pages as New, Changed locally, Conflict and Occupied, with a slug-collision warning and an ambiguous-note-name hint above the list

## Features - **Publish by moving.** Notes in one folder of your vault (default `_published`) become wiki pages; the folder structure maps 1:1 onto wiki paths. Moving a note in there *is* the decision to publish it. - **Never overwrites silently.** Before every write the plugin re-checks the page's remote timestamp. If it moved, you get a line diff and decide โ€” the push does not proceed on its own. - **Two-way, as far as the MVP goes.** Pages changed or newly created in the wiki can be pulled into the vault. - **Obsidian markdown is translated on the way out.** Wikilinks become wiki links, callouts become Wiki.js blockquote classes; links to unpublished notes are defused to plain text instead of pointing nowhere, and counted in the sync report. - **Removing a note asks first**, and defaults to unpublishing (reversible) rather than deleting. - **Collisions are caught, not resolved behind your back.** Two notes that map onto the same wiki path block each other until you rename one. ## Requirements - Obsidian 1.11.4 or newer. - A **Wiki.js 2.x** instance you can reach over HTTPS. - An API key from a Wiki.js group with **write access to pages** (Administration โ†’ Groups โ†’ API Access). ## Install From Obsidian: **Settings โ†’ Community plugins โ†’ Browse**, search for "Wiki.js Maintainer". (Listed since 2026-08-12.) ### Manual 1. Copy `main.js`, `manifest.json` and `styles.css` into `/.obsidian/plugins/wikijs-maintainer/`. 2. Enable **Wiki.js Maintainer** under Settings โ†’ Community plugins. ### From source ```bash npm install npm run build # main.js manifest.json styles.css โ†’ /.obsidian/plugins/wikijs-maintainer/ ``` Or, with the target folder in the environment: `OBSIDIAN_PLUGIN_DIR=โ€ฆ npm run deploy`. Note that Obsidian keeps the previously loaded `main.js` in memory โ€” after deploying, toggle the plugin off and on (or restart Obsidian) to pick up the new build. ## Configuration Open the plugin's settings tab and fill in: - **Wiki URL** โ€” base URL of your Wiki.js instance, without `/graphql` (e.g. `https://wiki.example.org`). - **API key** โ€” a token from a Wiki.js **group with write access to pages** (Administration โ†’ Groups โ†’ API Access). A key without page-write rights fails every push with an authentication error, not a silent no-op. - **Sync folder** โ€” the vault folder whose notes get published (default `_published`). Its structure maps 1:1 onto wiki paths: `_published/Network/DNS-Setup.md` becomes `network/dns-setup`. Moving a note into this folder is a publication decision. - **Test connection** โ€” lists the pages of your locale to check URL and API key in one click - **Snapshot folder** โ€” where the sync snapshots live. Empty (default): the plugin folder, which does not travel with your vault; a vault folder such as `.wikijs-snapshots` lets a second device open the same vault with a clean plan - **Required frontmatter fields** โ€” comma-separated keys every page must carry before it is pushed (empty by default). A page missing one shows as blocked with the field name in the status view, and *Push all changes* skips it - **Wiki locale** โ€” the Wiki.js locale of the pages this plugin manages: new pages are created in it, pages of other locales are ignored (default `de`). - **Request timeout** โ€” per-request timeout in seconds (5โ€“120, default 30). The plugin's settings tab: Wiki URL, API key, sync folder, wiki locale and request timeout ## Usage Five commands, all from the command palette: - **Show sync status** โ€” opens a view listing every page under the sync root as new, changed, conflicting, removed, or unchanged. Not every row has a button: **Push** appears for "New", "Changed locally" and "Conflict"; **Pull** appears for "Changed on wiki" and "New on wiki". The other states ("Occupied", "Removed locally", "Removed on wiki", "Stale snapshot", "Unchanged") show status only โ€” there is nothing to push or pull, or (for "Removed locally") the action needs the confirmation dialog that only a collect push shows. Slug collisions and ambiguous note names are called out above the list (see below). - **Push current note** โ€” publishes or updates the active note. - **Push all changes** โ€” runs the full sync plan. Continues past a failing page instead of stopping the batch; failures are collected and reported in a summary Notice at the end. - **Import existing wiki pages** โ€” for a wiki that already has pages: creates a note for every wiki page you do not have yet and writes the snapshot, so everything starts as unchanged. A report comes first (how many notes are new, how many existing notes lie on a wiki page); for those you choose *Import new only* or *Wiki wins* (the wiki text replaces the note's text, its frontmatter stays). It never writes to the wiki. The new notes carry the page's title, description and tags as frontmatter, and `wiki_published: false` for an unpublished page, so the next push changes nothing by accident. A page whose wiki path a file name cannot reproduce (a `รŸ`, an umlaut or capital letters) also gets a `wiki_path` line, which pins the path; remove it to let the file name decide again. - **Pull current note from wiki** โ€” pulls the active note's content from the wiki when it changed remotely. A pull replaces only the text below the frontmatter: the note's frontmatter block (`title`, `tags`, `seitentyp`, `aliases`, โ€ฆ) stays exactly as it is, because the wiki does not hold it. ## How it works - **One vault folder maps onto one wiki**, 1:1 by path. There is no multi-instance profile concept. - **Drift guard.** Immediately before every write, the plugin re-checks the page's remote `updatedAt`. If it moved since the last plan โ€” including if the wiki page was reset back to an older state โ€” a **conflict dialog** opens showing a line diff between the wiki version and your local version. You choose "Keep local" (overwrite), "Keep remote" (abort the push and leave the wiki page untouched โ€” this does **not** pull; see the note under "Limits of this MVP") or "Cancel" (leave the wiki untouched, same effect as "Keep remote"). The conflict dialog: a line diff between the wiki version and the local version, with Keep local, Keep remote and Cancel buttons - **Slug collisions.** Two local notes that map to the same wiki path block each other's push entirely; the status view shows the collision and both vault paths until you rename one file. - **Ambiguous note names.** Two files with the same basename in different folders make `[[Name]]`-style wikilinks ambiguous. This does **not** block publishing โ€” the affected pages still push, but the ambiguous link is left as plain text instead of a wiki link. The status view shows a hint per ambiguous name; fix it by renaming a file or linking by full path. - **Removing a note from the sync folder** does not delete the wiki page by itself. A collect push asks what to do, defaulting to **unpublish** (reversible; the page's history stays intact). Pressing Esc or closing the dialog always means **keep** โ€” never unpublish or delete by accident. The removal dialog after a collect push: Unpublish, Delete and Keep, with Unpublish highlighted as the recommended action - **Markdown conversion is one-way.** Wikilinks become wiki links, callouts become Wiki.js blockquote classes (`{.is-info}` / `.is-success` / `.is-warning` / `.is-danger`). The transformed text is not translated back; the plugin keeps your raw note content and the pushed version as separate snapshots so a later merge can use both. ## Limits of this MVP - **Text only.** Images and other attachments are not synced โ€” they are planned for a later version (asset upload + path rewriting). - **No merge.** Conflicting changes are never combined automatically; you always resolve them by hand through the conflict dialog. A real three-way merge on top of the existing snapshots is planned for a later version. - **No automatic pull out of a conflict.** As long as a page is both changed locally and changed on the wiki, the plugin gives you no button that fetches the wiki version into your vault โ€” a conflicting entry is not pull-able (only "new on wiki" and "changed on wiki" states are). "Keep remote" in the conflict dialog only stops the push; it does not write anything. If you want the wiki's version, copy it out of the diff shown in the conflict dialog, or edit your local note by hand until it matches โ€” once it does, the entry stops being a conflict. - **Removed on wiki.** If a page is deleted directly in the wiki (e.g. via the Wiki.js admin) while its note still exists locally, the status view shows "Removed on wiki" and no push is offered for it. Recreating a deleted page from the local note isn't supported yet โ€” that's planned for a later version. The snapshot for that page also isn't cleaned up on its own (see "Stale snapshots" below); nothing in the vault is affected. - **Occupied is only resolved when the page is provably yours.** A page shows as "Occupied" when a local note and a wiki page map onto the same wiki path without a snapshot tying them together. The status view offers **Adopt page**, which fetches the wiki page and โ€” *only* if its content matches your note character for character โ€” writes the missing snapshot, after which the page syncs normally. That covers the way this state actually arises: the page was created successfully and writing the snapshot failed right after. If the content differs, the page is a different page and nothing is adopted; you are told so, and the fix stays manual (rename the local note, or remove the page in the wiki). Adopting never writes to the wiki. - **Stale snapshots are discarded on request, not automatically.** If both the local note and the wiki page a snapshot pointed at are gone, the status view offers **Discard snapshot** for that row. Nothing is discarded on its own โ€” a snapshot is the basis of every later drift check, so removing one is always your call. ## Documentation Start at the [documentation index](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/docs/README.md): - **[Getting started](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/docs/getting-started.md)** โ€” from the install to your first published page. - **[Troubleshooting](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/docs/troubleshooting.md)** โ€” the message you see, what it means, what to do. For maintainers and contributors: - [`docs/SMOKE.md`](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/docs/SMOKE.md) โ€” the checklist this plugin is verified against, eleven points of it automated (`npm run smoke:gui`) - [`docs/LAB.md`](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/docs/LAB.md) โ€” the GraphQL schema **as measured against a running instance**, including the two places where it deviates from what the schema declares - [`AGENTS.md`](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/AGENTS.md) โ€” architecture and the decisions behind it - [`CONTRIBUTING.md`](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/CONTRIBUTING.md) ยท [`SECURITY.md`](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/SECURITY.md) ยท [`CHANGELOG.md`](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/CHANGELOG.md) ## License Code: **AGPL-3.0-or-later** ([`LICENSE`](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/LICENSE)) โ€” dual-licensing available, see [`LICENSING.md`](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/LICENSING.md). Documentation and text: **CC BY-SA 4.0** ([`LICENSE-DOCS`](https://github.com/johannes-kaindl/wikijs-maintainer/blob/main/LICENSE-DOCS)).