Skip to content

Source access

Choose local sources, inspect access, preview files, and revoke access.

Brainlyy reads only configured notes folders and repositories. The shared backend keeps source identity, preview limits and download validation consistent between a local reader and a reader reached through the relay.

Stable source handles

files.public_sources() returns id, name, kind, available and note_prefix. It omits absolute machine paths. An ID hashes the source kind and canonical filesystem location. Reordering the configuration, inserting another source or temporarily taking a folder offline does not assign its handle to another folder. Removing a configured source makes its old handle unavailable. Aliases of the same canonical location and kind are deduplicated.

These are location identities. Moving a folder changes its ID; replacing a folder at the same canonical path does not. They are neither credentials nor an immutable filesystem identity. Every read resolves the handle against the current configuration. Keep the exact root and relative path returned by the backend instead of constructing IDs such as vault-0.

Legacy files.listing(vault, rel), files.read(vault, rel) and note links remain supported. Human-readable note prefixes can change when the configured set changes between one and several folders. Duplicate folder names receive stable digest suffixes, and offline folders retain their place in that naming scheme.

Shared read boundary

resolve_source, listing_source, preview_source and download_chunk reject unconfigured handles, absolute paths, traversal, Windows alternate streams and child symlinks or reparse points. Hidden paths, common generated directories, instance credentials, private key suffixes and notes.exclude patterns are excluded from file reads and note search. Explicitly configured root aliases are resolved before accessing their children. Reads open regular files only.

POSIX reads hold directory descriptors and refuse symlinks at each step. Windows checks reparse components before opening; this is not a handle-based sandbox against an attacker concurrently replacing local directories. Hosts must protect configured roots from untrusted filesystem writers.

File reader contract

Helper Result and limit
listing_source(root_id, relative="", offset=0, limit=100) One directory, at most 200 entries per page. Follow has_more using the number of returned entries. Each entry includes an openable root and path.
preview_source(root_id, relative) Metadata plus a preview type: text, table, document, image, media or metadata. Check ok, truncated and preview_note.
download_chunk(root_id, relative, offset=0, version="") Original bytes encoded as base64 in chunks of at most 384 KiB; files at most 64 MiB.
read_source(root_id, relative) Compatibility text reader using the legacy {text, meta, reason} shape; at most 512 KiB. Use preview_source for the relay-facing reader.

Text previews read at most 256 KiB and may be shortened further to fit JSON encoding. HTML and SVG remain text. Raster image previews include original bytes only up to 600 KiB. Audio/video responses describe MIME type and an explicit-load preview limit of 32 MiB; opening a file does not load its media bytes. The browser must enforce that assembly limit and render text as text. File payloads are capped at 850,000 bytes including a second JSON escaping layer, leaving space for the relay's enclosing response.

DOCX and ODT previews extract text. XLSX previews preserve source row numbers, sparse cells and saved values; cached formula results can be stale, uncached formulas remain text, and date values may be serial numbers. CSV and TSV retain quoted fields and source order and accept UTF-8 or BOM-marked UTF-16. These formats do not reproduce page layout, charts, merged cells or embedded objects. PDF, older Office formats, ODS and RTF currently return an explicit metadata fallback. Download them for a native reader.

Document limits are 8 MiB input, 2 MiB per XML part, 8 MiB aggregate inflated content, 512 archive members, 400 paragraphs, 8 sheets, 200 rows per sheet, 40 columns, 8,000 cells and 80,000 text characters. Escaped output has an additional byte budget, so Unicode can truncate sooner. Two document previews may run concurrently. Archives are never extracted to disk; DTDs, entities, external worksheet relationships and encrypted or unsupported archives are refused with a reading fallback.

For downloads, retain version from the first response and send it with every continuation at next_offset. Stop only when done is true. A replacement, resize, descriptor change or loss of source access refuses continuation; restart instead of appending the failed response. The version hashes file metadata, not contents, and is a change detector rather than an immutable snapshot. Cancellation means stopping further chunk requests and discarding the client buffer: the backend retains no open file or transfer session. HTTP adapters return JSON and must preserve these checks and byte ceilings.

Claims and saved graphs

Claim receipts bind the parsed claim, recheck interval, named check definitions and canonical note location. A changed or older unbound definition cannot replay as VERIFIED. Body prose and unrelated check definitions do not invalidate the claim. A fresh successful verification records a new bound receipt. Search, note reads and the notes index evaluate all configured vaults using the same source namespaces and recorded evidence.

Saved graphs are bounded to 40 MiB aggregate input, 100,000 nodes, 250,000 relationships and 64 sources per request, with two concurrent reads. Oversized or malformed sources report a reason. Totals describe accepted nodes and relationships; per-source details report skipped entries and scope_limited reports omitted sources. Typed node identifiers, parallel edges, self-loops and source boundaries are preserved. Node handles hash their source handle and original typed identifier, so configuration ordering does not redirect them.

Node file references are interpreted lexically. An absolute reference outside the approved source, including a UNC path, is discarded without resolving or contacting that location. Current file exclusions hide excluded references and participate in the cache identity. Labels and descriptions remain metadata from the approved saved index; the graph reader does not open their source files or independently verify those descriptions.

Cache hits and result delivery validate the current exclusion policy and open each included index with the guarded regular-file reader to establish current read permission. Losing index access refuses cached metadata, even when the file's size and timestamps have not changed. Cached indexes also have their actual bytes read and hashed under the same aggregate 40 MiB input budget. This detects edits that preserve file size and timestamps, including Windows creation time. Unchanged content reuses the parsed graph. Changed bytes are parsed from that same bounded read. The returned revision includes the content digests. Each graph request can therefore read up to 40 MiB even on a cache hit. These checks do not lock the filesystem after delivery.

The drawing starts with at most 450 nodes and 2,500 relationships and can shrink further. sampled and displayed_edges disclose the result. graph_data_json bounds the doubly escaped JSON string to 748,976 bytes before the JSON-RPC envelope; measuring only an unescaped HTTP payload is insufficient. Relations retain available provenance labels such as extracted or inferred. They describe the saved index, not runtime reachability or independently proven relationships.

graph_queries.ask delegates only the configured Graphify question modes. It uses an argument list, rejects batch-script executables, limits runtime to 20 seconds and shares one 64 KiB capture budget across stdout and stderr. Two children may run concurrently. Overflow stops the process and returns an error; successful text is limited to 6,000 characters. POSIX termination includes the dedicated process group; Windows termination covers the direct indexer process, not arbitrary descendants created by a custom indexer.

Company read requests

Hermes exposes only fintex_search and agent_status. The remote Python command remains fixed, queries travel as JSON on stdin, and SSH runs with an argument list and no shell. Batch-script executables are refused. Search queries remain limited to 500 characters and 25 results.

Two company requests may run concurrently per read-server process; further requests are refused without queuing. Each request shares one 64 KiB byte budget across stdout and stderr and has a maximum 90-second deadline, including blocked stdin and inherited output pipes. Callers may choose a shorter timeout. Only a successful SSH exit with a UTF-8 JSON object, nested at most 64 levels, can return a result. Overflow, timeout and unreadable responses return bounded errors, never a partial successful answer.

Pipe capture uses no reader threads or disk spools. Windows uses nonblocking pipe APIs compatible with Python 3.10/3.11; validation here used Python 3.13. An unavailable pipe API refuses the request. Termination covers the direct SSH child on Windows and its dedicated local process group on POSIX. Stopping SSH does not establish that a remote dispatcher or its descendants have stopped. Tests replace SSH with local child processes; no live company host was tested.

Saved-index paths

Endpoint search and shortest paths use the full accepted saved index for one configured source. They do not call Graphify, run a model or read source file contents. Search includes labels, symbols, kinds, communities and paths. Select both endpoints from its returned IDs; the service never substitutes a match.

Search returns at most 50 candidates and reports the total and truncation. Paths are unweighted. Directed traversal is the default. Reverse traversal retains the original edge direction and marks each reversed step. Source boundaries cannot be crossed. A path describes recorded relationships, not proof of runtime reachability or correctness.

Responses include the observed index revision. The next request rechecks source access and reloads changed indexes. Results are limited to 200 relationships and 748,976 bytes including the escaped MCP content representation. If a longer path exists, the response says that it was limited; it does not report disconnection. Path queries and the display sample share two read slots and the same index limits. The Graph view provides a repository picker, a searchable list of 25 endpoint candidates, explicit start and end selection, and saved relationship details. Changing the index requires selecting the endpoints again. Search requests are cancelled when replaced; selections stay in page memory across navigation and are checked against the index on return. Missing indexes and denied reads do not display prior path details. The existing graph overview remains available in a disclosure. Opening either view never runs the external indexer; its question buttons require an explicit action. HTTP and MCP interfaces remain available.

Graph endpoint search trims whole candidate records to fit that content budget. It preserves the total match count and marks truncation. A candidate that cannot fit returns an empty candidate list with a response-limit explanation. The graph_data, graph_nodes and graph_path MCP tools retain their JSON string in content[0].text and suppress the SDK's duplicate structuredContent payload. The content budget reserves 1,024 bytes for the JSON-RPC/SSE envelope. Actual MCP fixtures with numeric IDs and a 128-character ASCII ID remain below 750,000 bytes. Unbounded caller-controlled request IDs are outside that qualified envelope limit.

Local model and retrieval requests

Local model and retrieval calls use explicit numeric loopback HTTP addresses, without environment proxies or redirects. They share an admission limit of two requests per process, a 128 KiB request limit, a 512 KiB response limit and a 64-level JSON nesting limit. Absolute deadlines of 180 seconds for model calls and 60 seconds for retrieval stop slow responses. Invalid or incomplete JSON is refused. Model prompts are limited to 16,000 characters and retrieval queries to 4,000 characters. The selected service and model are retained without a fallback. Loopback addressing alone does not establish the selected runner's own data policy.

Model discovery uses the same bounded transport with a two-second deadline. It reads the Ollama model catalog and accepts at most 256 well-formed entries. An unrelated HTTP response does not count as a runner. Empty catalogs mean a runner was detected but no model was listed. Discovery sends no prompt, downloads no model and does not establish inference readiness. Tests use a local HTTP fixture; live Ollama qualification is separate.

Discovered names must be printable Unicode without line separators, lone surrogates or configuration comment/quote delimiters. The same validation runs before model inspection. CLI setup writes discovered names as quoted string values, preserving names such as true, 123 and ordinary Unicode exactly. Catalog text cannot add configuration lines or redirect an approved source.

Ollama can route its local API to cloud models. Automatic model selection excludes cloud names and catalog entries that declare remote_host or remote_model. Before each local model call, Brainlyy sends only the selected model name to /api/show, with a five-second deadline. It requires GGUF details and local model metadata. Remote or incomplete metadata refuses the call before the prompt is sent. Cloud model consent is not implemented in this local tool, so it cannot enable a cloud model.

These checks use the documented Ollama response fields. They do not sandbox the runner or prevent its operator from changing an alias between inspection and execution. Use a trusted local runner. Fixture tests do not qualify a live provider, its data policy or a supported version matrix.

Regression checks

python -m unittest discover -s tests -p test_backend_consolidation.py -v
python -m unittest discover -s tests -p test_document_previews.py -v
python -m unittest discover -s tests -p test_file_transfers.py -v
python -m unittest discover -s tests -p test_hermes_limits.py -v
python -m unittest discover -s tests -p test_local_http.py -v
python -m unittest discover -s tests -p test_model_discovery.py -v
python -m unittest discover -s tests -p test_local_model_policy.py -v
python -m unittest discover -s tests -p test_index_paths.py -v
python -m unittest discover -s tests -p test_graph_api.py -v
python -m unittest discover -s tests -p test_graph_review_boundaries.py -v
python -m unittest discover -s tests -p test_index_review_limits.py -v
python -m unittest discover -s tests -p test_model_review_boundaries.py -v

These exercise real evidence records and filesystem changes, real flooding and timed-out child processes, complete escaped graph envelopes, document parser refusals, original-byte download assembly and cancellation without held files.