Contextator
ENTR

Sources

Local Directory Source

Indexing a folder already on the server in place, the allowed-roots security boundary, and what gets scanned and skipped.

Updated:

A folder that is already on the server — mounted into the container — scanned in place. Nothing is copied, nothing is modified, and edits show up on the next index run.

This is the simplest source and the right one when the documentation is already on the machine: a repository checkout, a synced folder, a network share, an Obsidian vault on the same host.

Making the folder visible to the container

The container can only see what you mount. With Compose, DOCS_HOST_PATH is mounted read-only at /docs:

# .env
DOCS_HOST_PATH=/srv/documentation

To mount several folders, add them to docker-compose.yml and list them in ALLOWED_DOC_ROOTS:

volumes:
  - /srv/documentation:/docs:ro
  - /srv/handbook:/handbook:ro
ALLOWED_DOC_ROOTS=/docs,/handbook

Adding the source

Add source → Local directory

Field Example Notes
Name handbook The mount prefix of every path from this folder. Immutable
Directory /docs/ + handbook The dashboard shows the allowed root as a prefix; you type the rest
Content type Plain Markdown / text Pick Obsidian vault for a vault — see Obsidian Vaults — or OpenAPI / Swagger for a folder of specifications
File types .md, .mdx Add .txt, .html/.htm, .csv, .docx or .pdf if you want them too — everything but the first three is converted to Markdown as it is indexed

When several allowed roots are configured, the prefix becomes a dropdown.

The allowed-roots rule

A local source’s directory must resolve inside one of ALLOWED_DOC_ROOTS. The path is resolved through symlinks first, so:

  • .. escapes are rejected;
  • a symlink pointing outside an allowed root is rejected;
  • a path that is not a directory is rejected.

If you see “Directory is outside the allowed document roots”, either use a path under an allowed root (inside Docker that means /docs/...), or add the root to ALLOWED_DOC_ROOTS and restart.

This is a genuine security boundary, not a convenience check — see Security.

What is scanned

Starting at the directory, recursively:

  • only the file types the source selected;
  • dotfiles, dot-directories, node_modules, dist, build, vendor, __pycache__ are skipped;
  • symlinks that escape the directory are skipped;
  • IGNORE_GLOBS patterns are skipped.

Paths keep their structure and gain the source name: guides/install.md inside a source named handbook is indexed, searched and read as handbook/guides/install.md.

Keeping it up to date

There is nothing to fetch — the folder is read at index time. Trigger a run when the files change:

  • Sync on the source row, or Re-index in the project header;
  • or POST /api/projects/:id/reindex from a cron job or a CI step — see Admin API.

Because indexing is incremental, running it often is cheap: unchanged files are skipped by hash.

Read-only mounts

Mount your documentation read-only (:ro), as the shipped compose file does. Contextator never writes to a local source, and the mount makes that guarantee structural.

Arrow keys to move, Enter to open.