nasindex · Documentation
Documentation · as of 8 October 2026

nasindex Documentation

nasindex makes a document archive on a NAS searchable for Claude without changing anything there. This page describes how it works, how to set it up and run it, and where its limits are.

1 container, indexer and server in one 0 write access to the archive 5 tools for Claude
LanguageThe setup wizard, the commands' output and the log messages are in German. This page quotes them exactly as they appear and explains them in English.
1 · Overview

A finding aid, not a knowledge store

The index takes Claude to the right five documents. Claude then reads the complete original with its tables intact. That way no figures torn out of context end up in an answer, figures that sound plausible and are wrong.

Read-only

The archive is never written to. Access runs through a folder path on the NAS itself or through a NAS user that may only read.

The folders carry the knowledge

nasindex reads project, phase and year from the path before it opens a file. In construction project archives also BKP code, trade, award stage and document class.

Claude quotes the original

Answers with figures always rest on the document that was read, never on a search snippet.

2 · How it works

From archive to answer

One container does both: it indexes the archive in a continuous loop and answers Claude's requests. The index lives inside the container on its own volume, never on the archive.

ArchiveNAS shareread only
IndexerCycle every houronly new and changed files
IndexFull text and metadatalocal in the container
MCP serverClaude asksvia Tailscale, with a key

One indexing cycle

The wizard shows these steps by their German names.

StepWhat happens
Vorlage-Katalog einspielenload seed catalogueOnly on the first start and only if a catalogue from another computer is provided (seed).
Dateien suchenfind filesThe archive is walked. The metadata comes from each path.
Texterkennung prüfencheck text recognitionOne-off calibration of the text extractor on sample files.
Dateien abgleichenreconcile filesNew, changed or gone? Detected by size and modification date.
Prüfsummen bildencompute checksumsSHA-256 per file, for changes and duplicates.
Texte auslesenread textsFull text from PDF, Office and scans. Scans go through text recognition (OCR, German).
Volltextindex aufbauenbuild full-text indexSearch index over file name, path, trade and content.
Katalog exportierenexport catalogueA copy of the index from which another computer can build its own index.

The first cycle over a large archive takes hours. After that, a cycle over an unchanged archive takes only seconds to minutes.

What the path reveals

In any archive the first folder level counts as the project and the second as the phase. The year comes from the path or file name. If a document in a construction project archive sits under a folder «Aufträge» (orders), nasindex also recognises BKP code, trade, award stage and document class. From 24031-MST / 1 Aufträge / 22810_Sonnenschutz Südfassade / 2 Vergabe / 1 Angebote / Offerte_250312.pdf it reads:

Project

24031-MST

Phase

1 Aufträge

BKP and trade

22810 · Sonnenschutz Südfassade

Award stage

2 Vergabe

Document class

Angebot (offer)

Year, approximate

2025

BKP is the Swiss construction cost classification. Duplicates are merged by content. The copy that wins has a document class, then a trade, then lies outside backup, copy or dispatch folders, then has the shorter path. The year comes from the path or file name, otherwise from the file date. It is good enough for date-range filters, not for statements like «awarded in year X».

3 · Installation

Three ways, one wizard

nasindex comes as a package nasindex-<version>.zip with the program for Intel/AMD and ARM. The install script loads the matching program, starts the setup wizard, checks access to the archive and starts nasindex. The step-by-step guide is included in the package as ANLEITUNG.md (German).

WayWhenArchive access
Directly on the NASPermanent operation, if the NAS runs Docker (Synology Container Manager)bind: the folder path of the share, e.g. /volume1/Projekte
On another computerA Linux machine that runs around the clock, or a Mac with Docker Desktopcifs: the container connects to the share itself, with a read-only user
Mac first, then NASLarge archive, weak NAS: the Mac reads everything once, the NAS takes overfirst cifs on the Mac, then bind on the NAS with the Mac's catalogue

Requirements

  • Docker with Docker Compose v2. Check with docker compose version.
  • At least 5 GB free in the installation folder, plus space for the index, about a tenth of the archive size.
  • Tailscale on the computer running nasindex and on every workstation using Claude.
  • On the workstations Claude Desktop with Node.js, or Claude Code.
  • In cifs mode: a NAS user with read-only access to the archive share. nasindex never needs an administrator account.

Starting

Unpack the package, do not rename or move the nasindex folder, then inside it:

sh installieren.sh

On the Mac a double-click on Installieren.command works too. If Docker needs administrator rights, as is usual on a Synology NAS, the script asks for the password itself.

Seed: catalogue from another computer

A more powerful computer builds the index once in full. Its catalogue consists of four entries: catalog.jsonl.gz, catalog.json, text/ and meta/. Inside the container they are under /daten:

mkdir nasindex-katalog
docker cp nasindex:/daten/catalog.jsonl.gz nasindex-katalog/
docker cp nasindex:/daten/catalog.json nasindex-katalog/
docker cp nasindex:/daten/text nasindex-katalog/
docker cp nasindex:/daten/meta nasindex-katalog/

Copy the folder to the target system and give it to the wizard when it asks for the seed path. On the first start nasindex loads it, visible in the log as a line seed: …. After that the reconciliation usually reports «0 neu, 0 geändert» (0 new, 0 changed).

ImportantThe target system must read exactly the same share as the computer that built the catalogue. Otherwise the paths do not match and nasindex reads everything again. Only load a seed from a trusted source: the catalogue is taken over unchecked.
4 · Setup wizard

Questions, files, checks

The wizard is divided into numbered sections: Voraussetzungen, Archivzugang, Startdaten, Netzwerk, Feldextraktion, Dateien schreiben, Start und Erstindexierung, Abschluss (requirements, archive access, start data, network, field extraction, writing files, start and first indexing, completion). Before it writes anything, it shows the requirements with their check results.

QuestionMeaning
Läuft der Container auf dem NAS selbst?Is the container running on the NAS itself?Yes: mode bind, then the archive path. No: mode cifs, then SMB server, share, read-only user, domain (optional) and password.
Katalog von einem anderen Rechner (Seed-Pfad)Catalogue from another computer (seed path)Empty: first indexing here. Otherwise the folder with the catalogue.
Tailscale-IP-AdresseTailscale IP addressThe suggestion comes from tailscale ip -4. Only an IP address, no host name. The wizard rejects 0.0.0.0. lokal binds to 127.0.0.1 only.
PortDefault 8765.
FeldextraktionField extractionProvider, model, optionally client name, key and approval. Default: anthropic, no key, no approval. See Field extraction.

What it creates

FileContent
.envMode, Compose files, address and port, field extraction settings. No credentials.
docker/mcp.tokenThe access key for Claude, randomly generated.
docker/smb.credOnly in cifs mode: read-only user and password.
docker/llm.keyOptional: API key for field extraction.

All files have permissions 600, only the owner can read them. The wizard overwrites existing files only after asking. In cifs mode it checks the credentials against the NAS before starting, and asks again up to three times on failure.

Progress and running it again

After the start the wizard shows the first cycle live, for example Schritt 5 von 7: Texte auslesen - 1234 von 20000 (6 %), noch ~3 h 10 min. Ctrl-C only ends the display, indexing continues. To see the status again later:

sh installieren.sh

The script then asks «Die Fragen neu beantworten?» (answer the questions again?). Enter means no: it only checks and shows the status.

5 · Connecting Claude

Connect the workstations

At the end the wizard prints both recipes with address and port. Read the key from docker/mcp.token. It gives full read access to the indexed archive: treat it like a password.

Claude Desktop

Add this to claude_desktop_config.json, then quit Claude Desktop completely and restart it. --allow-http is correct, Tailscale provides the encryption.

{
  "mcpServers": {
    "archiv": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://<tailscale-ip>:8765/mcp", "--allow-http",
               "--header", "Authorization: Bearer <key>"]
    }
  }
}

Claude Code

claude mcp add --transport http archiv http://<tailscale-ip>:8765/mcp --header "Authorization: Bearer <key>"

Skill

In Claude under Settings → Capabilities, upload the file claude/archiv-skill.zip from the package. The skill tells Claude which tool to use when, that figures come from the original and how to cite sources. Test: «What is the status of the archive?»

6 · Tools for Claude

Search finds, reading proves

ToolPurpose
archiv_sucheFull-text search with filters on project, document class, trade, BKP and year.
archiv_lesenThe whole document from the index, with tables. This is where figures and dates come from.
archiv_dateiThe original file straight into the chat: PDF and images (png, jpg, gif, webp) up to 10 MB. Otherwise path, size and the reason.
archiv_sqlA single read-only SQL query. Count, group, find gaps.
archiv_statusWhat is in the index, how current it is, whether the archive is reachable.

Questions this answers

QuestionRoute
What was offered for the sun shading on the south façade?Search with document class Angebot, then read
How many offers do we have per trade?SQL with GROUP BY gattung
Which trades have invoices but no contract?SQL with HAVING
Where else does company X appear?Search without filters across all projects
All contracts over CHF 50,000SQL on the table felder, once field extraction has run
7 · Data model

What archiv_sql sees

For counting and comparison questions. The query is read-only, a single SELECT. Column names are German.

Table docs

One row per file. The most important columns:

  • rel, filename, ext, size: path in the archive, name, extension, size
  • projekt, phase, bkp, gattung, stufe, dokklasse, jahr: from the path (project, phase, BKP, trade, stage, document class, year)
  • dup_of: set for a duplicate, points to the original
  • chars, page_count, table_count, ocr_used: about the extracted text
  • extract_status: ok, meta_only, duplicate, pending, error, missing

Table felder

One row per processed contract, joinable on rel:

  • auftragnehmer, gegenstand, vertragsdatum, vertragsnummer (contractor, subject, contract date, contract number)
  • netto, brutto as numbers, waehrung (net, gross, currency)
  • unterzeichner_ag, unterzeichner_an, sachbearbeiter, telefon, email (signatories, contact person, phone, email)
  • status per document: ok, leer, zu_gross, fehler (empty, too large, error)
  • <feld>_status per field: wert, nicht_im_dokument, nicht_extrahiert (value, not in document, not extracted)

meta_only means no full text, for example for CAD drawings and images. They can still be found by path and file name. missing means the file has disappeared from the archive.

8 · Operation

What runs by itself, what you do

nasindex starts by itself after every restart of the computer and checks the archive every hour for new and changed files. All commands run in the nasindex folder. On a Synology NAS, prefix them with sudo.

You want toCommand
See whether it is runningdocker compose ps
See the latest messagesdocker compose logs --tail 200 nasindex
See progress and statussh installieren.sh, then Enter
Know how much is in the indexdocker exec nasindex python3 /app/nasindex.py stats
Index now instead of waiting for the hourdocker exec nasindex python3 /app/docker/entrypoint.py build
Check the server without Claudedocker exec nasindex python3 /app/docker/entrypoint.py selbsttest
Check the NAS credentials (cifs)docker compose run --rm --no-deps nasindex smb-pruefen
Restartdocker compose restart
Stop and start againdocker compose stop and docker compose up -d
Change the key for ClaudeWrite a new value to docker/mcp.token (at least 32 characters), then docker compose restart. Update the workstations.

Updating

An update is a new package. Unpack it in the same place and let it overwrite existing files. .env and the credentials are not in the package and stay. Then run sh installieren.sh and answer «Die Fragen neu beantworten?» with Enter (no). The index is kept.

9 · Field extraction

Amounts from contracts

Full-text search finds documents but cannot calculate. For thresholds and totals, felder reads twelve fields from each contract (Werkvertrag) and writes them into the table felder. To do this the contract text goes to a language model. This is the only outbound network call in nasindex.

Never automatic

The container never starts felder by itself. It only runs on your command.

Only with approval

Without approval felder refuses every run, even the dry run. The wizard asks for it, the default is no.

The original is quoted

Amounts are never calculated or converted, only taken over when they appear like that in the document.

Providers

ProviderAddressModelKey
anthropicfixed, api.anthropic.comoptional, default claude-sonnet-5required, starts with sk-ant-
openaifixed, api.openai.comrequiredrequired, starts with sk-
eigener (own)required, an OpenAI-compatible endpoint such as vLLM, llama.cpp or OpenRouter. http:// only inside the tailnet.requiredoptional

Digital Apes has only tested anthropic with the default model. With openai and eigener, the model must follow a JSON schema and fit documents of up to 500,000 characters, about 125,000 tokens.

Procedure

docker compose exec nasindex python3 /app/nasindex.py felder --trockenlauf
docker compose exec nasindex python3 /app/nasindex.py felder --limit 15
docker compose exec nasindex python3 /app/nasindex.py felder

The dry run (--trockenlauf) shows count, characters and, with anthropic, the estimated cost, without sending a document. Reference value: 541 contracts with 14.7 million characters cost around 13 to 15 US dollars. --limit 15 gives a small set for proofreading. An interrupted run continues on the next call. Do not start it in parallel with a running indexing cycle.

Pre-check with your own model

Before every run nasindex asks the server for the context size and sends a probe with a made-up mini contract. That shows in advance whether the model keeps to the format. Documents that are too large are left for a model with more context. If the server does not report a context size (Ollama, LM Studio, llama.cpp), set it in the wizard or as EXTRAKTION_KONTEXT. Ollama silently truncates inputs that are too long.

Field rules

  • Contract number only if the document calls it a contract, order, purchase, addendum or additional-cost number. Never project, object, cost-centre or BKP numbers.
  • The contractor is never the architect, specialist planner, site management or engineering firm, except in that planner's own contract. Nor a delivery or invoice address.
  • Contractor's signatory only from the contractor's company.
  • Currency only with a stated amount, never from an IBAN.
  • Phone and email only if they clearly belong to the contractor.
  • Optionally a client name: that name is then never taken as the contractor.

A rule change only affects contracts read afterwards. felder --force reads all of them again and costs as much as a full run.

Revoke approvalEmpty EXTRAKTION_FREIGABE= in .env, then docker compose up -d. The status is shown by docker compose logs nasindex | grep felder:.
10 · Configuration

Settings

The wizard writes the .env. The other values are in the environment section of the Compose file, compose.bind.yml or compose.cifs.yml. After a change run docker compose up -d. Credentials never go into these files, only into the files under docker/.

SettingDefaultMeaning
INDEX_INTERVALL3600Seconds between two cycles, at least 60
ARCHIV_PROJEKTEempty = allProjects, comma-separated
OCRanaus turns off text recognition for scans
OCR_SPRACHEdeuLanguage of text recognition
EXTRAKT_JOBS2Files read at the same time
EXTRAKT_TIMEOUT600Seconds per file before it is aborted
XBERG_THREADS2Threads per extraction
MCP_HOST_ADRESSE127.0.0.1Address the port is bound to. The Tailscale IP, never 0.0.0.0
MCP_PORT8765Port for Claude
EXTRAKTION_FREIGABEemptyja approves field extraction
EXTRAKTION_ANBIETERanthropicanthropic, openai or eigener
EXTRAKTION_MODELLwith anthropic: claude-sonnet-5Model, required for openai and eigener
EXTRAKTION_BASIS_URLemptyAddress of your own endpoint, only with eigener
EXTRAKTION_KONTEXTemptyContext size of your own model in tokens
EXTRAKTION_AUFTRAGGEBERemptyClient name for the prompt

Resources: The Compose files set 4 CPUs and 4 GB of memory. Rule of thumb: 1 GB per file read at the same time plus 1 GB base load. An invalid value stops the start with a message that names only the setting.

11 · Security

What is protected, and how

Archive read-only

In bind mode the folder is mounted read-only. In cifs mode the container connects with hard-wired read-only options, without execution. The read-only user has no write rights anyway.

Access only inside the tailnet

The server speaks HTTP without its own TLS. Tailscale provides the encryption. The port is bound only to the Tailscale address, never to 0.0.0.0. Public exposure is not intended.

The key is full access

Anyone who knows docker/mcp.token can search, read and retrieve original files. Do not pass it on by email or chat. A change takes effect after a restart.

Credentials as files

Password, token and API key live in files with permissions 600, never in environment variables and never in the program. Messages never show a key or a password.

A single way out

Only field extraction sends text to a provider, only with approval and only on command. A key only goes to the configured address, never to a redirect target.

Two routes

POST /mcp requires the key. GET /gesundheit (health) answers without a key, only with ok or nicht ok. Every other path returns 404.

12 · Limitations

What nasindex cannot do

  • CAD drawings and images have no full text. They can be found by path and file name.
  • The year is an approximation from path, file name or file date.
  • Amounts are not numbers in the full text. Thresholds and totals only work via the table felder, currently for contracts.
  • New files appear with the next cycle, that is within an hour at the latest.
  • Own models for field extraction are untested. Quality applies per model.
  • The container runs as root, in cifs mode with the right to mount shares.
  • Windows with Docker Desktop is untested in cifs mode. A Mac with a network drive as bind is not supported, use cifs there.
13 · Troubleshooting

When something goes wrong

If the archive goes away, search keeps working. The index lives in the container. Only new files and reading originals are missing.

What you seeWhat helps
«nasindex erreicht das Archiv nicht»nasindex cannot reach the archivePath, server, share, user or password are wrong. Start the wizard again and answer the questions anew.
Claude cannot find the serverIs Tailscale active on both devices? Address, port and key correct? Claude Desktop fully restarted?
Claude cannot find a new fileThe next cycle picks it up. Right away: entrypoint.py build, see Operation.
scan: missing-Abgleich uebersprungenscan: reconciliation of missing files skippedA safety guard triggered because many files are suddenly missing. Nothing is lost. If a folder really was removed, see below.
felder: Konfiguration unvollstaendig (…)felder: configuration incompleteThe named field extraction setting is missing or invalid. Start the wizard again.
Kein Dokument passt in den KontextNo document fits into the contextNot an error of the run. Choose a model with more context or set the context size correctly.

Folder removed on purpose

The safety guard prevents a briefly missing network drive from emptying the index. If the drop is intended, confirm it once:

docker exec nasindex python3 /app/nasindex.py scan --verschwunden-ok --root /archiv --index /daten --cache /daten --rolle indexer

Messages from archiv_status

CodeMeaning
erreichbarThe archive is there and has content.
leerThe folder is empty, usually a wrong path.
fehltThe folder does not exist, or read permission is missing.
haengtNo answer in time, probably a hanging connection to the NAS.

Exit codes of build

CodeMeaning
0Success, including «nothing to do»
1Configuration error or archive unreachable, the message names the reason
75Archive source unreachable, or a safety guard triggered
130Run aborted by a stop
137Terminated from outside, often too little memory

If you have questions, send Digital Apes the output of docker compose logs --tail 200 nasindex. It contains no credentials.