> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mesh.texturehq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Read documents

> Read supported attachments and understand document extraction limits.

Mesh's standard release reads Slack DOCX, embedded-text PDF and UTF-8 TXT
attachments out of the box. Every Slack agent gets `list_documents` and
`read_document`; no parser image, environment variable, execution backend,
third-party account or per-agent opt-in is required. Files shared earlier in the
conversation are eligible too, subject to current authorization.

## Default processing path

The release embeds a fixed Go document parser compiled to WebAssembly/WASI. The
control plane passes authorized attachment bytes to a fresh wazero instance and
receives bounded JSON. The worker has:

* **No filesystem mounts**, host paths, environment, credentials or socket API.
* No shell, child processes, arbitrary commands or model-supplied executable code.
* 256 MiB maximum linear memory, a 45-second execution deadline, and
  at most two active document workers per Mesh process.
* Immediate busy/retry error when both slots are occupied; no unbounded queue
  retaining uploads or consuming another document’s execution budget.
* 8 MiB input, 4 MiB JSON output, 500 PDF pages, 2,000 archive entries, 32 MiB
  declared/actual DOCX expansion, and 128 XML nesting levels.
* Rejection of encrypted/duplicate archive entries, DTD/entities and unsupported
  XML encodings. Relationships, macros and external resources are not resolved.

The deadline interrupts even a worker stuck in a CPU loop. Output is bounded at
its host writer, not after collecting an unlimited buffer. Instances are closed
on success, failure or cancellation. Aggregate linear memory is bounded to
512 MiB; **this is not a total process RSS limit** (Go, compiled code, buffers and
other runtime work also consume memory). Deployments must budget for that overhead.
WASM validation, wazero and its WASI implementation are part of the trusted
computing base; this is not a separate OS-process boundary.

This is a deliberately narrow built-in capability, **not an agent execution
backend granting ambient access to the control-plane host**. The standard image
remains non-root and distroless, with no writable filesystem or additional
services required. Parsing libraries are imported only by the worker, never used
on untrusted content in the host process.

The checked-in compressed worker makes ordinary source builds work without a
separate generation step. Maintainers regenerate it with
`sh tools/document/build.sh` using the pinned Go toolchain and module versions.
CI rejects source/asset drift; the image build independently rebuilds it. The
release carries the parsers: operators do not build or publish them.

## Tool contract and coverage

`list_documents()` returns opaque attachment IDs, names, media types and sizes
from the current authorized conversation, bounded to the latest 100. It reports
whether older entries were omitted.

`read_document(attachment_id, cursor?)` returns the content SHA-256 revision,
parser version, located text, extraction limitations, delivered coverage and an
opaque continuation. With no cursor it starts at the beginning. Results are
bounded to 16,000 Unicode code points and 200 blocks; blocks can span responses.
Continuation is bound to attachment, revision and parser version, and every call
reauthorizes and refetches. A changed/deleted source fails rather than silently
splicing revisions. This release changes the parser version; old cursors must
restart from the beginning.

* **PDF:** physical page anchors. The bundled BSD-licensed Go PDF library reads
  embedded page text, not images, charts, form XObjects or annotations. Font
  decoding, layout and reading order can be imperfect; complex tables/columns
  are not faithfully reconstructed. PDF results always carry these limitations.
  Blank/scanned/unsupported pages are individually reported; entirely unreadable
  documents fail rather than claim a successful empty read.
* **DOCX:** paragraph and table-row anchors, not invented page numbers. Main-body
  paragraphs/tables only; headers, footers, notes, images, nested tables, text
  boxes, numbering and tracked-change semantics are not fully represented.
* **TXT:** UTF-8 (optional BOM), line anchors; binary/control-character content
  is rejected.

Extraction coverage is distinct from delivered coverage. Reaching the last
response means the extracted text was delivered, not that images or omitted
structures were read. Scans need rendering/OCR, **not yet available**. Unsupported,
malformed, encrypted, empty and resource-exhausting inputs return an explicit
failure without parser diagnostics/source bytes in logs.

## Permissions and retention

The existing conversation/evidence rules remain unchanged:

* Queries scope attachment IDs to agent perspective and conversation and require
  a source audience compatible with the current audience. IDs are not authority.
* The owning Slack connector fetches using its credentials, after authorization,
  with size bounds and redirect/host restrictions. Credentials never enter WASI.
* Source message evidence is accumulated before reading; derived replies and
  memories inherit it and the existing destination-audience recheck.
* Document content is untrusted source material, never higher-priority instructions.
* Mesh does not persist originals, extracted documents or page images. Authorized
  excerpts can occur in existing tool/run records under their retention policy.

## Optional remote processing

Existing deployments explicitly setting `MESH_DOCUMENT_IMAGE` retain their remote
processor override. This is advanced compatibility, **not activation or normal
setup**. An override requires an immutable parser image and an execution backend
that enforces no egress (currently Modal). Failure is explicit; an opted-in remote
policy never silently falls back to local processing. With the setting absent,
no execution-backend resolver or credential is consulted.

`tools/document/Dockerfile` and its Python/Poppler tests are retained for this
optional path. Its parser has different layout behavior from the bundled Go
parser; both expose extraction limitations. The default path needs neither
Python nor Poppler on the host.

## Verification

* `go test ./internal/document/...` runs real TXT/DOCX/PDF extraction through the
  embedded worker, pagination, partial/empty PDFs, malformed archives, output
  bounds, cancellation, WASI imports and hostile CPU/memory limit probes.
* Turn tests exercise the native tools with the default processor and preserve
  authorization-before-fetch and source evidence checks. PostgreSQL tests cover
  cross-perspective, guessed-ID, wrong-conversation and wider-audience denial.
* `mesh document-check` runs fixed DOCX/PDF/TXT fixtures through the bundled
  reader without database, provider credentials or environment configuration.
  Image CI invokes it in the actual release image with network disabled,
  read-only filesystem, all capabilities dropped and no-new-privileges. This
  check gates publishing.
* A live Slack upload still requires a normally provisioned Slack agent with
  `files:read`; verify real DOCX/PDF uploads on the deployment before declaring
  the live incident resolved.

Telegram attachment ingestion, page rendering/OCR, private Drive/Docs adapters
and extraction caching remain outside this release.

## Conversation access failures

An `unknown` audience means Mesh could not establish a safe disclosure scope,
not that the upload is absent or its format unsupported. Document tools return
an explicit access-verification error before querying attachment metadata or
fetching bytes. Reuploading or converting to PDF does not repair this condition.

For Slack, inspect the `Slack audience verification failed` warning in the
instance logs. Its sanitized reason identifies the failing API method and a
recognized Slack error code, HTTP status, timeout, or incomplete-evidence error.
Tokens, response bodies, channel identifiers, and membership lists are omitted.
A `missing_scope` error calls for checking the installed app's conversation-read
permissions; `not_in_channel` calls for checking app membership. Neither is
assumed from a generic failure. Public externally shared channels remain
unverifiable by the current audience model and produce an unavailable warning.

These diagnostics do not relax authorization or repair previously stored
unknown source evidence. Verify a new conversation turn and upload after
correcting the underlying cause; historical evidence must not be relabeled from
current membership alone.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.