# Wiki.js as Org Knowledge

If your organization keeps its knowledge in [Wiki.js](https://js.wiki/), GoClaw can read it
from there instead of from its own [org wiki](../architecture/memory.md#org-wiki). A sync job
copies the pages through the Wiki.js API. The agent finds them with `memory_search` and reads
them with `wiki_read`, and SOP and cron runs find them too. Wiki.js stays where the pages are
written: in GoClaw they are read-only copies, refreshed on a schedule.

GoClaw's own org wiki is still there for a site without Wiki.js. Both can be used at once.

Supported: Wiki.js 2.x (tested with 2.5). Wiki.js 3.0 is still in beta and is not supported yet.

## How it works

- One sync of a site makes one `pages.list` GraphQL call. That call returns every page the API
  key may see, with its last update time.
- The sync compares that list with the copies it already holds. It downloads only the pages
  that are new or changed, each from `GET /d/<locale>/<path>` (the page's source).
- Pages deleted on Wiki.js, or no longer visible to the key, are removed from the copies.
- Drafts (unpublished pages) and private pages are not copied.
- Pages written in the visual editor (HTML) are converted to Markdown. Pages over
  `max_page_bytes` (default 1 MiB) are cut, and `wiki_read` says so.
- The copies are kept in the `wikijs_pages` table. A searchable copy of each page goes into
  memory (scope `wikijs`), indexed separately for each site.
- A sync makes no LLM calls. Only the embedding of new or changed text costs anything, and
  only when embeddings are configured.

Wiki.js 2.x sends no change notifications, so GoClaw polls: every 30 minutes by default. The
**Sync now** button on the Integrations page syncs a site at once.

GoClaw does not use the Wiki.js search. Its default engine matches page titles and
descriptions only, not the text, while GoClaw indexes the text itself, Mongolian included.

## Set up Wiki.js

1. **Administration → API Access**: turn on **Enable API**. Until it is on, every key fails.
2. **Administration → Groups**: create a group for GoClaw, for example `goclaw-reader`.
   - **Permissions**: `read:pages` and `read:source`. Nothing else.
   - **Page Rules**: allow the paths GoClaw should read, and deny the rest, for example HR pages.
3. **Administration → API Access → New API Key**: choose the group from step 2, not
   *Full Access*, and set an expiry.

GoClaw sees exactly what that group sees. It **refuses a key that can do more than read**: a
full-access key, or one whose group can edit (`write:pages`, `manage:pages`) or delete
(`delete:pages`) pages. The Test button and every sync check this with read-only queries.

A page the group may list but whose source it may not read (`read:pages` without `read:source`
on that path) is left out, with a warning on the Integrations page.

## Configure GoClaw

On the **Integrations** page, choose **Add Integration → Wiki.js**. Enter the URL and the API
key; **Store in vault** keeps the key in the secrets vault. Press **Test**: it lists the pages
the key sees and says how many of them would be copied. Then save.

The same definition in `config.yaml`:

```yaml
tools:
  wikijs:
    docs:
      url: "https://wiki.example.com"
      token: "${secret:WIKIJS_DOCS_TOKEN}"
      description: "Handbook and runbooks"
      locales: ["mn", "en"]         # empty: every locale
      include: ["ops", "policy"]    # path prefixes; empty: every path
      exclude: ["ops/archive"]      # wins over include
      sync_interval: "30m"          # a duration, or "off" for Sync now only
      # agents: ["agent-ops"]       # only these agents see the pages; empty: all
      # admin_only: true            # only admins and SOP/cron runs
      # max_page_bytes: 1048576
      # insecure: true              # self-signed certificate
```

Prefixes match on path segments: `ops` covers `ops` and `ops/runbook`, but not `operations`.

The Wiki.js host does not need to be on `tools.http.allow`. As with the other integrations,
GoClaw may reach the configured `url`, while `tools.http.deny` still applies, and a redirect
to another host stays under the HTTP policy. The key is sent only in the `Authorization`
header.

## Who sees the pages

Wiki.js API keys belong to a group, not to a person, so GoClaw cannot apply each person's
Wiki.js rights. Each site definition is one group's view, and GoClaw decides who that view is
shown to:

- By default, every user of every agent, like GoClaw's own org wiki.
- With `agents`, only the agents listed.
- With `admin_only: true`, only admins and SOP and cron runs. Other users and delegated runs
  do not see the pages, in `memory_search` or in `wiki_read`.

For pages only some people should read, make a second group and key with the stricter page
rules, and add it as a second site with `admin_only` or its own `agents`.

## Reading the pages

A copied page's slug is `wikijs/<site>/<locale>/<path>`. Each search result includes it:

```
wiki_read slug="wikijs/docs/mn/ops/runbook"
wiki_read prefix="wikijs/docs/mn/ops"        # list a folder
wiki_read prefix="wikijs"                    # list the sites
wiki_read slug="wikijs/docs/mn/ops/runbook" refresh=true   # fetch it from Wiki.js now
```

`wiki_read` with no slug lists the org wiki pages and then the Wiki.js sites. The page text
ends with a link to the page on Wiki.js. Saved queries and tool presets can link a copied page
in their wiki field, like an org wiki page.

`wiki_write` refuses these slugs: edit the page on Wiki.js, and the next sync copies it.

## Status and troubleshooting

The Wiki.js card on the Integrations page shows how many pages are copied, the last sync and
its warnings. The gateway log has one line per sync:

```
[wikijs] docs: synced — 120 listed, 96 mirrored, 3 fetched, 3 updated, 1 deleted, 0 warning(s) in 1.2s
```

| Message | What to do |
|---|---|
| `API is disabled` | Turn on **Enable API** under Administration → API Access. |
| `API Key is invalid or was revoked` | Create a new key and save it. |
| `this API key administers the site` / `can edit pages` / `can delete or manage pages` | Use a key for a group with `read:pages` and `read:source` only. |
| `listed no pages for this API key; the … mirrored pages are kept` | The key sees nothing any more. The copies are kept on purpose, so that a revoked key does not empty the org knowledge. Fix the key or its group's page rules. |
| `… is listed but its source is not readable` | Give the group `read:source` on that path, or deny the path entirely. |

If you remove a site from `config.yaml` or from the Integrations page, its copies are dropped
as well.
