Archive Relationship and Enrichment Provenance Orchestrator
Local-first knowledge mapping for user-owned documents.
AREPO is experimental, pre-release software and is not yet production-ready.
AREPO maps ordinary documents, tracks relationships and provenance, and enables
optional enrichment without taking ownership of the source files. Markdown is
the first-class V1 format: plain .md files are the source of truth, and the
app index, graph inputs, and cache must be rebuildable from those files.
Every enrichment producer is disabled by default and requires separate,
explicit per-vault consent. A complete Markdown-only experience never requires
enrichment.
AREPO automatically builds a machine index from the Markdown files in each
configured vault. A user-authored index.md file is optional; if it exists, it
is treated as a normal Markdown note, not as the app's machine index.
The first deliberately narrow non-Markdown experiment supports UTF-8 .txt
files as read-only source files. They appear in Tree, can be opened as plain
text, participate in browser-local filename/body search, and refresh after
external disk changes. They are not notes: .txt files never enter the machine
index, graph, backlinks, Markdown preview, frontmatter, tags, headings, or
validation. AREPO does not create, save, rename, or delete them.
AREPO also supports one deliberately exact structured import:
*.arepo-chat.json files whose root declares "format": "arepo-chat-export"
and numeric "version": 1. These canonical JSON files are read only and remain
unchanged on disk. AREPO validates and displays the conversation and its ordered
messages, preserving conversation/message IDs and source timestamp strings.
Unsupported or malformed chat exports remain inspectable as canonical raw text
when safely bounded. Other .json files are also discovered, opened, searched,
and refreshed as read-only raw UTF-8 text without schema assumptions. JSON is
never converted to Markdown or added to Markdown graph/index semantics.
Sync, history, and backups are intentionally external:
- Syncthing for device sync
- Git for version history
- Borg, Restic, or Kopia for backups
AREPO does not replace those tools.
AREPO is designed to run as a standalone local tool. It does not require a hosted project platform, cloud account, hosted database, or remote storage provider for basic local use. Runtime error handling logs locally to the browser or backend console. Disabled auth remains the default compatibility mode, and protected mode is available for deliberate local bearer-token testing, but neither mode makes LAN, reverse-proxy, or internet exposure safe.
A normal user runs a local Node backend on one machine and opens the web UI in a browser. The browser talks to the backend on localhost. The backend only reads supported sources in configured vault folders and only mutates Markdown notes.
The backend already exposes local node/vault concepts so future versions can display multiple registered AREPO nodes from a hub UI. V1 only implements the local node. It does not implement federation, live sync, hosted auth, or remote registration.
See docs/roadmap.md for the current local-node roadmap and docs/local-node-self-host.md for single-node self-host notes. Phase 4 security design work is tracked in docs/node-security-checkpoint.md. The local protected-mode operator flow is documented in docs/protected-mode-operator-workflow.md. The evidence and decision boundary for any future zero-read source validation is documented in docs/source-observation-feasibility.md.
Install dependencies:
npm installStart the guided local dev workflow:
npm run backend:devThis starts the backend in the current terminal and tries to open a second terminal with frontend/build targets:
devstarts the Vite UI onhttp://localhost:8733.dev:portstarts the Vite UI on a custom port.build,build:dev, andpreviewrun the matching npm targets.
If your desktop environment does not allow AREPO to open a second terminal, run the menu yourself in another terminal:
npm run frontend:menuFor a backend-only terminal, use:
npm run backend:dev:serverThe backend host and port remain configurable through environment variables:
AREPO_HOST=127.0.0.1 AREPO_PORT=9002 npm run backend:dev:serverYou can also start the frontend directly. It defaults to the local UI port
http://localhost:8733:
npm run devNormal Vite CLI overrides still work for a different frontend port:
npm run dev -- --port 9001Open the URL printed by Vite. The dev server proxies /api to the backend at
http://127.0.0.1:8734 by default. The Vite dev proxy works with overridden
frontend ports such as 9001 because browser requests stay same-origin to Vite.
If you serve the frontend without the Vite dev proxy, or call the backend directly from another browser origin, add that origin to the backend CORS allowlist:
AREPO_ALLOWED_ORIGINS=http://localhost:9001 npm run backend:devThe repository includes test-vault/ as a small example vault for demos and
manual acceptance testing. It contains deliberately synthetic source content,
not generated app data or a real user vault.
To try it, start AREPO locally, open Vault Settings, and add the absolute path
to test-vault/ as a vault. From the repository root, this prints the path to
use:
realpath test-vaultAREPO automatically builds the machine index when the vault is added. A
user-authored index.md is not required; if one exists in any vault, AREPO
treats it as a normal note. The example vault intentionally includes broken
wikilinks so validation, inspect mode, and graph missing-link nodes have
something to report. It also demonstrates folder-qualified links, heading
anchors, backlinks, and ignored wikilinks inside code.
Generated machine index/cache files are stored outside the vault under the
configured app data directory. Do not commit .arepo/, app-data caches,
dist/, dist-backend/, or node_modules/.
Vault configuration is stored locally in:
.arepo/config.json
Example:
{
"node": {
"nodeId": "local",
"displayName": "Local Node",
"mode": "local",
"apiVersion": 1
},
"appDataDir": "/absolute/path/to/arepo-data",
"vaults": [
{
"id": "notes",
"displayName": "Notes",
"rootPath": "/absolute/path/to/notes",
"permissions": {
"readIndex": true,
"readContent": true,
"writeContent": true,
"deleteFiles": false
}
}
]
}The UI can also add an existing local vault folder through the top bar. The backend never scans the whole filesystem; only configured vault roots are accessible.
A vault registration has a stable ID and policy independent of whether its configured filesystem root is currently available. If a root is moved, deleted, inaccessible, or temporarily unmounted, AREPO keeps the structurally valid registration and reports that vault as unavailable without blocking other vaults. Vault Settings can Locate / Rebind the same registration to an existing directory. Rebinding changes only AREPO's configured root path: it does not move source files, and it preserves the vault ID, permissions, and index scope. Forget vault remains a separate option.
Vault Settings also shows read-only local node diagnostics from
GET /api/node/status, including backend host/port, startup warnings, vault
runtime health, auth posture, protected-mode readiness, and disabled V1
capability flags.
appDataDir is optional. It controls where AREPO writes local app data such as
machine-index caches and durable enrichment preferences. You can also set AREPO_APP_DATA_DIR; the
environment variable takes precedence over config.
Durable per-vault enrichment preferences and user-authored Related Notes
curation also live under appDataDir, separate from vault sources and
disposable caches:
<appDataDir>/preferences/<vault-id>.enrichment.json
<appDataDir>/curation/<vault-id>.related-notes.json
Missing, unknown-version, or unreadable preference state fails closed to all enrichment disabled. A malformed semantic section fails that producer closed while retaining a separately valid deterministic choice. Removing generated index/cache data retains these explicit user preferences and kept/dismissed curation decisions. Preference schema versioning is independent from Related Notes producer, derivation, and disposable cache versions; changing a user's preference does not redefine the scoring algorithm.
Recommended app data locations:
- Local Linux users:
~/.local/share/arepo - Development fallback:
.arepo/datain the project directory - Self-hosted or enterprise deployments: a dedicated writable data volume outside user vault roots
Do not place AREPO app data inside a user vault unless you explicitly want AREPO cache and preference files to appear beside user notes.
GET /api/healthGET /api/node/statusGET /api/vaultsPOST /api/vaultsPOST /api/vaults/:vaultId/rebindDELETE /api/vaults/:vaultIdGET /api/vaults/:vaultId/filesGET /api/vaults/:vaultId/file?path=...PUT /api/vaults/:vaultId/file?path=...POST /api/vaults/:vaultId/filePOST /api/vaults/:vaultId/folderPOST /api/vaults/:vaultId/renamePOST /api/vaults/:vaultId/relationships/promoteDELETE /api/vaults/:vaultId/file?path=...POST /api/vaults/:vaultId/reindexGET /api/vaults/:vaultId/indexGET /api/vaults/:vaultId/enrichment/settingsPUT /api/vaults/:vaultId/enrichment/settingsGET /api/vaults/:vaultId/enrichment/semantic/statusPOST /api/vaults/:vaultId/enrichment/semantic/testGET /api/vaults/:vaultId/enrichment/related?path=...GET /api/vaults/:vaultId/enrichment/related/curation?path=...PUT /api/vaults/:vaultId/enrichment/related/curationDELETE /api/vaults/:vaultId/enrichment/related/curationGET /api/vaults/:vaultId/storagePOST /api/node/credentials/bootstrapGET /api/node/credentialsPOST /api/node/credentialsPOST /api/node/credentials/:credentialId/revokePOST /api/node/credentials/:credentialId/rotate
POST /api/vaults/:vaultId/reindex means "rebuild the generated machine index
from Markdown files." It does not create or require a user-authored index.md.
GET /api/node/status is a local-node-only diagnostic surface. It reports node
identity, local runtime host/port, startup warnings such as non-local binding,
auth posture, vault count, per-vault watcher/index health,
storage-summary availability, protected-mode readiness, safe credential
lifecycle posture when authorized, and explicit capability flags showing that
browser login, browser sessions, sync, AI, database support, migration support,
and remote node registration are not active in V1.
- The backend binds to
127.0.0.1by default. - Disabled auth remains the default local compatibility mode.
auth.mode = "protected"enables local bearer-token request authorization. Protected mode verifies bearer tokens, enforces route permissions, fails closed when readiness is incomplete, returns reduced anonymous status, and writes sanitized audit records.- In protected mode, vault-scoped credentials discover only vaults for which
they have an applicable vault grant. Ordinary source grants do not reveal
unrelated registrations or configured absolute root paths. Root paths and
full registration policy are management metadata; a credential with
manageVaultsreceives the complete vault-management view. - Disabled/local auth mode retains complete local registration visibility, including configured roots needed by the local Vault Settings UI.
- Protected mode still does not make LAN, reverse-proxy, or internet exposure safe.
- Bearer tokens are secrets. Raw bearer tokens are returned only once during bootstrap, create, or rotate responses and must not be pasted into bug reports, screenshots, docs, logs, or commits.
- AREPO does not currently provide browser login, browser sessions, CSRF-protected browser authentication, frontend token storage, or a full credential-management UI.
x-arepo-confirmation: confirmis a backend/operator confirmation signal for protected routes, not final browser UX.GET /api/node/statusreturns reduced anonymous diagnostics in protected mode. A valid authorized bearer token may receive full diagnostics and safe credential lifecycle posture.- Non-local binding requires explicit
AREPO_HOSTconfiguration and prints a startup warning. - CORS is restricted to the local frontend dev origins by default:
http://localhost:8733andhttp://127.0.0.1:8733. - Extra CORS origins can be added with comma-separated
AREPO_ALLOWED_ORIGINS; do not use wildcard origins. For example:AREPO_ALLOWED_ORIGINS=http://localhost:9001 npm run backend:dev. - The Vite dev proxy strips browser
Originbefore forwarding local/apirequests, so normalnpm run dev -- --port ...overrides do not require a CORS change. - If the frontend is served without the Vite dev proxy, the backend CORS allowlist must include that frontend origin.
- Only configured vault roots in
.arepo/config.jsonare accessible. - Config structure is validated at startup. Duplicate vault IDs, malformed JSON, malformed root-path values, and unsafe permission/index-scope shapes are rejected. A structurally valid registration with a missing or inaccessible root remains loadable and is reported as unavailable at runtime.
- The index/cache is rebuildable from Markdown files and is not canonical state.
- Syncthing, Git, and Borg/Restic/Kopia remain external responsibilities.
See docs/protected-mode-operator-workflow.md for protected-mode configuration, bootstrap, credential creation, rotation, revocation, failure checks, and audit sanitization steps.
The backend accepts only POSIX-style relative vault paths. It rejects absolute
paths, .., empty segments, duplicate slashes, symlink path traversal, and paths
that resolve outside the configured vault root. Content reads accept .md,
.txt, and .json; the exact case-insensitive *.arepo-chat.json suffix takes
precedence over generic JSON classification. All file
mutation operations require .md.
File saves write and sync a uniquely named temporary file in the same directory, rename it into place, and clean up the temporary file if the operation fails. Writes to one path are serialized so concurrent saves cannot both consume the same optimistic precondition. The UI does not mark a file saved until the backend confirms the write. Saves include the file hash/mtime seen by the UI; if the file changed on disk, the backend rejects the write and the UI keeps the editor dirty.
The backend reads Markdown files from disk and rebuilds the generated machine
index from source files. The index includes paths, titles, frontmatter ids,
tags, headings, anchors, wikilinks, outgoing links, backlinks, broken links,
orphan notes, duplicate ids, and duplicate anchors. It deliberately excludes
full Markdown bodies. A credential with readIndex can retrieve structural
index data but cannot recover source content through index endpoints.
When a vault is added, AREPO builds the machine index automatically. Ordinary index, search, filter, and inspection reads reuse the persisted index only after validating its derivation version, index scope, Markdown inventory, and current in-scope content hashes. Manual reindexing always forces a rebuild from the current Markdown files.
Generated machine indexes are stored outside user vaults by default, under the configured app data directory:
<appDataDir>/indexes/<vault-id>-<vault-root-hash>.json
These files are disposable caches. They may be deleted and rebuilt from the
Markdown vault. Markdown files remain the source of truth. AREPO must not
depend on a user-created index.md file to make graph, backlinks, validation,
or search work. The version 5 cache stores disposable per-source Markdown
structural derivations once, without full source bodies, plus a compact global
overlay for link, backlink, duplicate, orphan, and validation state. A validated
unchanged cache materializes the public notes map from those derivatives without
rerunning global assembly or republishing the cache. Matching content hashes let
unchanged sources skip parsing while global state is reassembled deterministically
when inputs change.
Whole-index validation still reads and hashes every in-scope Markdown body
because the current watcher cannot prove exact-on-access content identity; AREPO
falls back to canonical source reads rather than trusting size, mtime, or a
possibly stale watcher snapshot.
The frontend still owns preview rendering and graph layout, but note content and the index now come from the local backend.
AREPO keeps source-authored and app-owned relationships distinct:
- A body wikilink is canonical Markdown, explicit, and visible in rendered prose.
- A frontmatter
relatedentry is canonical Markdown metadata, explicit, hidden from rendered prose, and included in outgoing relationships, incoming relationships, validation, orphan detection, Inspect, and the structural graph. - A kept Related Notes relationship is durable AREPO app metadata. It is not a Markdown link and does not enter structural graph semantics.
- An inferred Related Notes candidate is disposable enrichment.
AREPO V1 interprets this deliberately narrow shape:
---
related:
- "[[folder/note]]"
- "[[another-note]]"
---related must be a YAML sequence of quoted strings, each containing exactly one
note-level Markdown wikilink. Aliases, anchors, external URLs, non-Markdown
targets, objects, and scalars are preserved but reported as unsupported. When an
authorized user explicitly promotes a kept relationship, the backend can append
to a supported sequence or insert a minimal field without reserializing unrelated
YAML. Unsafe or malformed frontmatter is left untouched. The user chooses the
current note only, the related note only, or Both. Both is one backend operation
that preflights both sources and writes reciprocal metadata in deterministic
current-then-related order. Reciprocal declarations remain authored in both
notes while mapping to one undirected graph relationship.
Frontmatter and wikilinks are common knowledge-base conventions, but neither
AREPO's related meaning nor wikilinks are standardized by CommonMark. Other
applications may understand, display, preserve, or ignore these entries. The
source remains readable Markdown/YAML owned by the user. AREPO-managed and
external renames follow the existing authored-link behavior: incoming body or
metadata targets are not automatically rewritten.
For Markdown notes, the Inspect panel can lazily request a small ranked list of unlinked related notes. This first enrichment producer is entirely local and deterministic: it combines normalized tag, title-term, heading-term, and common resolved-neighbour overlap with sparse TF-IDF cosine similarity over visible Markdown body text. Direct links in either direction are omitted.
Related Notes is explicitly off for every vault until a vault manager enables
it in Enrichment settings. Off means no derivation, enrichment body rereads,
cache generation, or cached candidate presentation. The Basic settings offer
Conservative, Balanced, and Exploratory styles plus friendly evidence controls
and a 1–10 result limit. Balanced is deterministic V1: minimum score 0.10,
text-only minimum 0.16, ten suggestions, and relative
tag/title/heading/common-link/text weights of 20/10/10/20/40. Advanced
settings expose bounded thresholds and relative importance controls; enabled
weights normalize proportionally and any edit is explicitly Custom.
Results are read-only suggestions. They do not create links, mutate Markdown,
or add inferred edges to the structural graph. The endpoint requires both
readIndex and readContent, returns no source bodies, and exposes bounded
structured evidence explaining each candidate.
Users with writeContent and readIndex authority can make two durable,
symmetric decisions about a suggestion. Keep remembers that the two notes
are meaningfully related and presents the pair separately from inferred
suggestions. Dismiss suppresses the inferred pair across cache rebuilds and
scoring changes. Either decision can be cleared. These decisions are stored by
canonical vault-relative path with the source hashes observed at decision time,
so later changes can be described without erasing user intent. Missing notes
remain dormant records and may become current again if restored.
Curation is applied after deterministic derivation and cache loading. It never changes TF-IDF, evidence weights, cache validity, the locked evaluation baseline, or producer scoring. It survives generated-data cleanup, disabling and re-enabling Related Notes, app restart, and a vault-root rebind that keeps the same vault ID. AREPO-managed file/folder renames update path references; external renames are deliberately not inferred from matching content hashes. A kept relationship is AREPO app metadata—not a wikilink, backlink, graph edge, or relationship visible to external Markdown editors. Decisions are not training data and are never sent anywhere.
For a kept pair, an authorized user may explicitly choose Add to note metadata and store the declaration in the current note only, related note only, or Both. Every requested owner has an optimistic source-hash precondition and uses the same verified, atomic per-file Markdown mutation boundary. Both is not a cross-file transaction: if the current write succeeds and the related write then fails, AREPO reports partial success, keeps the first write, retains the kept decision, and permits an idempotent retry. The kept decision clears only when all requested declarations are satisfied. Existing body wikilinks do not block an explicit metadata-ownership choice. Promotion never inserts visible prose or runs automatically. Manually authored metadata has the same structural meaning and suppresses redundant inferred suggestions in either relationship orientation.
Related-note results live separately from machine-index v5 at:
<appDataDir>/enrichments/<vault-id>-<vault-root-hash>.related-notes.json
The cache contains paths, hashes, scores, and bounded evidence but no source bodies. Its validity is tied to the producer/scoring versions and a SHA-256 corpus key over sorted eligible Markdown path/hash pairs plus the canonical resolved effective settings. It is rebuilt lazily when missing, malformed, stale, or derived under different preferences. Disabling Related Notes removes its verified disposable cache; disabled state never serves an old cache. Enrichment failure never changes structural index validity, and both generated files can be discarded independently of canonical Markdown.
This producer uses no AI model, embedding, vector database, or external service. Exact token matching has expected limits for synonyms, morphology, multilingual prose, and code-heavy notes; fenced and inline code, URLs, frontmatter, and heading lines are excluded from lexical body scoring. Future semantic producers may supplement this evidence, but they must remain optional derived state and must not replace explicit Markdown link semantics.
AREPO now has an evaluation-stage semantic producer foundation that uses a
separately installed Ollama service over HTTP loopback. AREPO includes no model,
model runtime, Ollama SDK, vector database, download command, or installation
workflow. It calls only Ollama's model-list and embedding endpoints, from the
backend, after validating a configured localhost, 127.0.0.1, or ::1
origin. localhost is immediately canonicalized to literal 127.0.0.1, so
accepted requests never depend on DNS or host-file resolution. Redirects,
credentials, arbitrary paths, remote hosts, oversized
responses, invalid vectors, and raw provider errors are rejected.
Semantic Similarity has its own per-vault toggle and defaults off. It is a sibling of deterministic Related Notes: neither toggle enables the other, and provider failure never causes deterministic fallback. While semantic is off, AREPO performs no provider discovery or inference. Existing version-1 preference files migrate with their deterministic setting intact and semantic consent off; version-2 semantic settings preserve their provider choice but migrate to an empty Selected scope, authorizing no vault content. Malformed semantic settings also fail closed without erasing a valid deterministic choice.
Semantic consent has an independent per-vault target scope. All Markdown notes dynamically permits every readable, in-index-scope Markdown note, including eligible notes created later. Selected Markdown notes permits only canonical paths the user explicitly checks; selected notes are compared only with other selected notes. Missing or unreadable selections remain durable but inactive and revive if restored. AREPO-managed file/folder moves rewrite stored Selected paths, including dormant choices while All mode is active; external renames are deliberately not guessed. Plain text, JSON, symlinks, missing sources, and out-of-scope sources are never eligible. An empty Selected scope is valid and sends no vault content.
Before any vault-derived text can enter provider-request preparation, the
backend resolves this scope against the current structural source manifest and
rejects paths outside the effective authorized set. The browser's filtering is
only UI, not the consent boundary. Any permitted version-1 semantic preparation
uses bounded Markdown-derived title, heading, and prose text. In a self-hosted
deployment the configured service is on the AREPO backend machine—not
necessarily the browser user's desktop. The browser never contacts Ollama
directly. Test connection is scope-independent and uses only the fixed
AREPO-owned probe AREPO semantic capability test, never vault text. Public
status keeps provider health and bounded scope counts separate and exposes no
vectors or raw response bodies.
Production semantic Related Notes candidates and vector persistence remain deliberately deferred until the external-provider evaluation justifies them. No semantic vector or result cache is written by this tranche. Durable Keep / Dismiss decisions, canonical Markdown relationships, deterministic caches, and generated-data cleanup remain provider-independent.
Developers can run the synthetic, exhaustively human-reviewed V1 baseline with
npm run eval:related (or add -- --json for machine-readable output). The
ratings are evaluation data, not training data, and never affect production
scoring or preferences. See
Related-note evaluation for the rubric,
metrics, privacy rules, and future-producer comparison contract.
An explicitly configured real-provider experiment is separate:
AREPO_OLLAMA_MODEL=<installed-embedding-model> npm run eval:semantic
AREPO_OLLAMA_URL=http://127.0.0.1:11434 \
AREPO_OLLAMA_MODEL=<installed-embedding-model> npm run eval:semanticThe command never chooses or downloads a model. Without a model it prints a bounded skipped message; when Ollama is unavailable it reports the experiment as unavailable without affecting ordinary tests. It evaluates semantic-only cosine rankings against the unchanged locked corpus and reports provider, model, digest when available, dimensions, producer/text versions, headline metrics, semantic-gap recall, and hard-negative surfacing without printing vectors.
The structural-index benchmark generates deterministic synthetic vaults in a temporary directory, measures indexing work and broad timing/memory phases, and removes the fixtures when it finishes or fails:
npm run bench:index -- --profile small
npm run bench:index -- --profile medium
npm run bench:index -- --profile datacentre
npm run bench:index:gc -- --profile mediumThe 50,000-file datacentre profile is explicitly opt-in and checks available
temporary-filesystem space before generation. File count, average source size,
links per source, seed, warm iterations, and an optional JSON result path can be
overridden with --files, --average-bytes, --links-per-file, --seed,
--warm-iterations, and --json. The file-heavy and byte-heavy profiles
separate per-file filesystem overhead from canonical read/hash bandwidth.
The optional bench:index:gc form exposes Node's garbage collector and runs it
between scenarios for cleaner comparative memory samples. The JSON result also
includes cache-section sizes, isolated parse/retention measurements, and measured
derivative-only versus compact-global-overlay reconstruction candidates. AREPO
application code never invokes or depends on explicit garbage collection.
Benchmark timings are machine- and environment-specific. Repeated runs are normally affected by the operating system page cache; the harness does not pretend to flush it. These measurements are not performance thresholds and do not run in the normal test, lint, or build commands. Correctness and work-count contracts remain covered by deterministic automated tests instead.
The UI includes read-only structural index filters for broken links, orphan notes, tags, folders, duplicate frontmatter IDs, and duplicate heading anchors. These filters query the generated machine index and do not persist new canonical search or graph state.
The UI also includes backend-owned, non-semantic index search over structural fields such as file paths, titles, frontmatter IDs, tags, headings, anchors, outgoing link targets, and backlinks. It is not AI search, vector search, or full document body search.
The Tree's browser-local text search can match Markdown, plain-text, and generic
JSON bodies
only after the frontend has fetched those files through the content-read
endpoint. That path requires readContent; it does not obtain bodies from the
structural index. Plain-text results are identified separately from Markdown
notes. For a valid loaded chat export, file-level search text is derived only
from the conversation title/ID and message IDs, authors, and text. It does not
search arbitrary JSON keys or punctuation. Unsupported or malformed chat files
use their bounded raw source for body search. Unreadable or oversized sources
remain filename/path-searchable but contribute no body search text.
The supported structured dialect has this shape:
{
"format": "arepo-chat-export",
"version": 1,
"conversation": {
"id": "conv-001",
"title": "Example conversation"
},
"messages": [
{
"id": "msg-001",
"author": "Alice",
"timestamp": "2026-08-24T10:00:00Z",
"text": "Hello."
}
]
}Conversation IDs must be non-empty. Message IDs must be non-empty and unique
within the conversation. Every message requires string author, timestamp, and
text fields. Timestamps must be parseable ISO-8601-style values with Z or an
explicit numeric offset. Array order is canonical. Message timestamps are
source event time; filesystem mtime remains separate operational observation
time. Unknown fields are left untouched in the canonical file but are not part
of the V1 view. Structured interpretation is limited to 4 MiB, 10,000 messages,
100,000 JavaScript string code units per message text, and 4,000,000 aggregate
searchable-text code units. Sources outside those structured limits fall back
to canonical raw text when they remain within the separate 8 MiB JSON open
limit. Unsupported schemas show an unsupported-format notice; malformed JSON
shows a malformed notice. Neither is treated as an unreadable source failure.
Individual source-content reads are isolated from the vault metadata and structural-index load. If one supported file cannot be read, AREPO keeps the Tree, file metadata, Markdown index, issues, and successfully loaded files available. The unreadable file is reported individually; filename/path search still includes it, while body search covers only successfully loaded content.
The Inspect panel can show read-only file-level machine-index details for a selected note or structural filter result, including headings, anchors, outgoing links, backlinks, broken outgoing links, duplicate IDs, duplicate anchors, and orphan status. Backlink and outgoing-link rows can navigate to related notes without modifying Markdown files.
Graph selection can show combined metadata for multiple selected notes, including selected file count, combined file size, tags, headings, outgoing links, backlinks, issue count, and selected paths.
The local UI includes a document close workflow that clears the central document workspace without modifying files. If the editor has unsaved changes, AREPO asks for confirmation before discarding the in-memory buffer.
The desktop workspace has resizable and tuckable left and right panes. Tucked panes keep narrow resize strips and tapered restore tabs available, and the center workspace clips content when panes are resized to extreme widths.
Tree and Graph views can be assigned to the center workspace. Opening a document takes over the center workspace; closing it restores the previously assigned Tree or Graph center view when one was set.
GET /api/vaults/:vaultId/storage reports local storage use for a configured
vault. Per-file size is shown in the Metadata panel, graph multi-selection shows
combined selected-file size, and full vault storage details are shown in Vault
Settings. The backend scans only inside the configured vault root, counts regular
files, and skips symlinks. It reports:
- total vault file count and bytes
- Markdown/text file count and bytes
- attachment/other file count and bytes
- AREPO app-data cache bytes for the vault's generated machine index files
Generated machine indexes live under the app data directory and are disposable cache. They are not user-authored Markdown content and can be rebuilt from the vault files.
These storage categories are physical reporting buckets, not the supported source-kind taxonomy. Chat and generic JSON currently remain in the broad attachment/other bucket; this does not make them attachments or place them in the Markdown index.
The current indexer strips fenced code blocks and inline code before extracting headings and wikilinks. It is still a lightweight Markdown parser, not a full CommonMark AST pipeline.
The local backend watches configured vault roots for supported .md, .txt,
and .json file changes, additions, deletions, and renames. It does
not watch outside configured vault roots and continues to ignore symlink
escapes. Watch events are debounced. Markdown changes mark the vault index stale
and rebuild it after the event burst settles; plain-text-only and JSON-only
changes remain visible in watcher status without staling or rebuilding the
Markdown machine index.
The frontend polls vault status from the local backend. If the open Markdown file changes on disk and the editor has no unsaved changes, AREPO refreshes from disk. Open plain-text and JSON files always reload their read-only view from disk. If the Markdown editor is dirty, AREPO keeps the browser buffer intact and shows a conflict warning with actions to keep edits, reload the disk version, or save the buffer as a new file. If an open Markdown file is deleted on disk, AREPO shows a warning and lets the user close the buffer or save it as a new file; a deleted read-only source offers only the close path. A chat edit that becomes malformed or unsupported reloads into bounded raw fallback; a later valid V1 edit restores the structured view. A JSON file that grows beyond 8 MiB remains visible but shows a too-large state instead of a truncated preview.
This third source is implemented directly and narrowly. AREPO does not yet have generic adapters, record locators, provenance, or a universal structured-source index.
The .txt experiment uses Node's normal UTF-8 decoding and does not detect
legacy encodings; invalid UTF-8 may therefore be displayed with replacement
characters. A plain-text rename is treated as deletion of the old path plus
discovery of the new path. The old read-only buffer is closed or refreshed
before the new path is opened; AREPO does not assign stable identity across the
rename.
Manual Rebuild index remains available. It forces the generated machine index to be rebuilt from current Markdown files and is useful after very large external operations or if the operating system drops filesystem events.
Watcher limitations:
- The backend watches only configured vault roots.
- Directory watchers are refreshed as folders appear and disappear.
- Filesystem events can be coalesced or dropped by the operating system during large bursts, so status polling also rescans supported file metadata.
- This is change detection only. It is not sync, version history, or backup.
localStorage is only used for UI preferences such as theme and the last
selected vault id. It is not used as canonical note storage.
npm run backend:test
npm run frontend:test
npm run lint
npm run buildUse this before treating a local build as daily-driver ready:
- Install dependencies with
npm install. - Start the backend with
npm run backend:dev. - Start the frontend with
npm run devand openhttp://localhost:8733. - Add the absolute path to
test-vault/through Vault Settings. - Run the manual acceptance checklist in
docs/manual-acceptance.md. - Run
npm run backend:test,npm run frontend:test,npm run lint, andnpm run build. - Confirm
.arepo/, generated app data,dist/,dist-backend/, andnode_modules/are not being committed.
The canonical manual local-mode gate is
docs/manual-acceptance.md. It uses test-vault/
and includes Local Node Diagnostics checks.
- Localhost-first backend; disabled auth is the default compatibility mode.
- Protected mode supports local bearer-token authorization for API/operator workflows, but it is not a LAN, reverse-proxy, or internet safety claim.
- No live sync, Git replacement, or backup system.
- No remote node registration yet.
- Markdown preview is sanitized in the browser, but the backend API should still not be exposed to untrusted networks.
AREPO is licensed under the Apache License 2.0.
Copyright 2026 Synavera Discorporated Ltd.