Skip to content

mlimarenko/IronRAG.BookStack

Repository files navigation

IronRAG ↔ BookStack connector

Sync a BookStack wiki into IronRAG: periodic polling + webhook intake, multimodal (pages + attachments + images).

Release License Docker pulls Python


Built on the IronRAG Connector Template — this repo only owns BookStack-specific code (REST client, markdown image collector, shelf reverse index, webhook verifier).

What it pushes into IronRAG

kind external key source
page bookstack:page:<id> /api/pages/{id}/export/markdown
attachment bookstack:attachment:<id> /api/attachments/{id} (base64 decoded)
image bookstack:image:<sha256-prefix-16> inline <img> URLs in page markdown (content-addressed dedup)

Pages, attachments, and inline images all carry their original BookStack URL via document_hint, which IronRAG surfaces in MCP citations.

Page attachments and inline images now ingest as attached context of their source page instead of as peer documents. Each dependent is auto-linked to its page via the framework's parent_external_key; no config change is required.

Sync loop runs every SYNC_INTERVAL_SECONDS (default 1800s) and ships only the diff: pages whose updated_at advanced since the last successful push. Webhook endpoint /webhook/bookstack (HMAC-SHA256 + admin bearer) routes through the same orchestrator.

Full sweeps collect routing facts from the /api/pages summary first. The connector fetches full page context and refreshes the shelf index only when routing.yaml rules require facts that are missing from the summary, such as shelf or tag matches.

Quick start (Docker)

docker run -d \
    --name ironrag-bookstack \
    --env-file .env.local \
    -v $(pwd)/routing.yaml:/app/routing.yaml:ro \
    -v ironrag_bookstack_state:/var/lib/ironrag-connector \
    -p 8088:8088 \
    pipingspace/ironrag.bookstack:latest

Minimal .env.local:

BOOKSTACK_BASE_URL=https://wiki.example.com
BOOKSTACK_TOKEN_ID=...
BOOKSTACK_TOKEN_SECRET=...
BOOKSTACK_WEBHOOK_SECRET=...
IRONRAG_BASE_URL=https://ironrag.example.com
IRONRAG_API_TOKEN=...
ADMIN_BEARER_TOKEN=...
RUN_MODE=both

routing.yaml (single library):

default:
  workspace: 00000000-0000-0000-0000-000000000000
  library:   00000000-0000-0000-0000-000000000000

policies:
  page:       { on_missing: delete }
  attachment: { on_missing: delete }
  image:      { on_missing: ignore }   # content-addressed; may be shared across pages

Quick start (dev)

git clone git@github.com:mlimarenko/IronRAG.BookStack.git
cd IronRAG.BookStack
cp .env.example .env.local            # fill in BookStack + IronRAG creds
cp routing.yaml.example routing.yaml

uv sync --all-extras
uv run pytest
uv run bookstack-connector

Operational notes

  • See docs/ARCHITECTURE.md for the page → external_key mapping and failure modes.
  • Manual sweep trigger: curl -X POST http://localhost:8088/sync/run -H "Authorization: Bearer $ADMIN_BEARER_TOKEN".
  • First sweep against a library that already contains BookStack docs: optionally run uv run python -m bookstack_connector.seed_cursor once to populate the SQLite cursor from existing IronRAG documents (avoids re-uploading on first pass).
  • CURSOR_LIBRARY_LOOKUP_TIMEOUT_SECONDS controls best-effort legacy cursor backfill separately from large upload/replace timeouts. If ownership lookup times out, cleanup for that historical library waits for a later sweep while duplicate uploads remain blocked.
  • CURSOR_LIBRARY_LOOKUP_MAX_ROWS_PER_SWEEP caps how many legacy cursor rows are backfilled before source enumeration; the rest are handled lazily per item or deferred.
  • IRONRAG_MUTATION_TIMEOUT_SECONDS bounds upload/replace/delete admission calls separately from generic HTTP requests. Admission timeouts leave the cursor on the previous source version so the next sweep retries instead of marking the version as handled.
  • REAPER_LIST_TIMEOUT_SECONDS bounds post-sweep IronRAG prefix-list requests so cleanup cannot hold the manual sync request after source enumeration has finished.

Deploy with Docker Compose

cp .env.example .env.local             # BOOKSTACK_* + IRONRAG_* + ADMIN_BEARER_TOKEN
cp routing.yaml.example routing.yaml   # map shelves/books → (workspace, library)
docker compose up -d
docker compose logs -f

docker-compose.yml pulls the released image, mounts routing.yaml read-only, and persists the SQLite cursor in a named volume. With the default RUN_MODE=both it also serves /webhook/bookstack on port 8088 (published on localhost) — front it with a TLS reverse proxy for public delivery.

Related

License

MIT — see LICENSE.

About

A connector between BookStack and IronRAG for updating and automatically populating the agent database

Resources

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors