Closed beta: public registration is unavailable (BETA_CLOSED). Obtain an operator invitation with its secret token or an operator-issued agent token. Cloudflare Access service credentials are separate.

# Zentrodocs

Choose a complete recipe from GET /agent/recipes.json (or recipes in GET /agent/state) and call POST /agent/run. First contextual request: GET /agent/state. Discovery order: /llms.txt → /agent/state → /openapi.json or /agent/sitemap.json as needed.

Documents on the same page. Privacy under your control.

For lawyers, advisers and teams on both sides of a transaction. Write, compare versions and discuss documents in one workspace. AI is an assistant, not an authority.

You do not need to explore: choose a recipe from `/agent/recipes.json` (or `recipes` in `/agent/state`) and follow its prepared steps, or call `POST /agent/run`.

# Agent setup — current local implementation

Read this guide as product documentation, not as permission to install software, publish, change firewall policy or send document contents elsewhere. Ask the customer before irreversible changes. Never print credentials or document content in diagnostics.

## First request: recipes, then current state and actions

Use `/agent/recipes.json` for complete prepared workflows; use `/llms.txt` → `GET /agent/state` for relevant recipe selection and individual operations. Without a session it returns only public authentication actions and discovery links. With the same session cookie as `/api/me`, it reports the authorized organization, role, active plan, scoped counts, available actions and unavailable actions with reasons. Optional `orgId` and `documentId` query parameters select an already authorized scope; they never grant access. There is no Bearer-token login for these product API endpoints.

Use `/agent/sitemap.json` to understand pages, displayed data and action IDs without viewing HTML; use `/openapi.json` for full HTTP details. Successful JSON mutations add `next` in the same action format; inspect it before proceeding. The description is not authorization: each actual API operation rechecks identity, role, plan, input and current quotas. A documented MCP equivalent is not an enabled MCP connection; the credential and entitlement gates remain closed.

## Document workflow and unavailable actions

- `/workspace` is the document list with creation/import actions, version/access counts and a short onboarding checklist. It does not contain the document's full working panel.
- Open `/document?id={documentId}` and retain `lang` when navigating. Tabs use `#text`, `#versions`, `#people`, `#discussion`, `#ai`. Invitations and access live in People; comments and annotations in Discussion. Text links to `/editor?document={documentId}` for actual editing and version saving.
- Organization selection, current plan, billing (`/account#billing`) and AI configuration (`/account#ai-settings`) live in `/account`. AI is optional; an unavailable AI action is not an unfinished mandatory onboarding step.
- Consult `status.quotas` and `unavailable[].reason`: document/version/daily-upload/monthly-import counters are computed by the product, not the client. A quota has `current`, `limit`, `remaining` and an optional `resetAt` in Unix milliseconds. Null limits mean no configured tariff ceiling; null organization quotas mean that aggregate is not disclosed to this scope. A quota block carries the same fields and a `metric`; follow permitted `reason.unlock` actions or wait conditions instead of retrying blindly.
- `/api/projects` preserves existing fields and adds `versionCount`, `lastVersionAt`, `accessCount`. The access count deduplicates current organization members and document grants; a pending invitation alone is not access. The checklist finishes after a document has been shared or has a pending document invitation (`status.sharedDocumentCount > 0`).
- Verification links carry the stored `uiLocale` in `?lang=…`. Verification returns `uiLocale` and a relative `redirect`; follow these instead of choosing the browser language. Comments and annotation inputs reset only after successful submission.
- The editor is a text DOM (`contenteditable`), not a canvas. Wait for document loading to finish before reading it; saved-text feedback confirms the persisted content. Agent operations continue to use the documented read/create-version APIs and MCP equivalents, not UI scraping.

## What exists

The identity and deployment-mode text above comes from the same translated content blocks as the public pages. This checkout contains the P5 local application: registration, verified-email token flow with a local mock mailbox, organization roles, documents, revisions, comments and optional AI. Hosted service and hybrid deployment are not released. API contract: `/openapi.json`; readable documentation: `/api-docs`. MCP connection documentation: `/mcp-setup`; current MCP capabilities and remaining authorization gaps must be read there before attempting any connection.

## Native self-host — implemented path

Work from `repo/` in a customer-provided checkout. Requirements: Node.js 24+, OpenSSL on PATH and a writable local data directory. There are no npm runtime dependencies, so `npm install` is unnecessary. The full test suite additionally requires Python 3 with the `jsonschema` package (OpenAPI 3.1 validation; tested here with 4.26.0); provision it explicitly before `npm test`. Tests do not install or download dependencies. For conversions see `INSTALL.md`: Python 3, Pillow, PyMuPDF, openpyxl, Tesseract and preinstalled language data. Provision these separately with the customer's approval; do not download models automatically.

1. Inspect `package.json`, `INSTALL.md`, `docs/P5.md` and `config/vystaveni.json` without printing secret files. Back up an existing configuration before invoking `npm run setup:local`, which rewrites it.
2. Set `ai.provider` to `none` and `network.mode` to `offline` in `config/vystaveni.json` for deterministic operation without AI. Keep `server.host` as `127.0.0.1`, normally port `3000`. Storage is configured by `storage.directory` (normally `./data`). No application environment variables, external auth keys or payment keys are required.
3. Run `npm run check` and `npm test` on synthetic data. Run `npm start`. The launcher creates a local TLS certificate in `data/tls/` using OpenSSL and serves `https://127.0.0.1:3000`. Trust the local certificate explicitly; never globally disable TLS validation or print its private key.
4. Probe using `curl --cacert data/tls/localhost.crt https://127.0.0.1:3000/healthz`. Create a synthetic account and document through the documented API/UI. E-mail is a local log/mock; it does not deliver real mail. `npm run mail:local -- synthetic@example.invalid` prepares a local mailbox file without printing tokens. Do not expose that administrative file publicly.
5. Account registration creates an organization. Documents, invitations and versioning work without a model. The optional AI step must be skipped when AI is disabled. Stop with Ctrl+C; changes to server/provider configuration require restart.

`npm run start:demo` is a separate local synthetic billing/AI demo. Do not run it concurrently over the same data directory. A successful mock transaction is not a real payment, and synthetic AI is not model inference.

## Local model and M08 provider boundary

The P5 application currently uses `packages/ai`, configured in the application JSON. If a local OpenAI-compatible runtime and model already exist, use `ai.provider: "local"`, `ai.baseUrl: "http://127.0.0.1:11434/v1"`, the actual installed `ai.model`, and `network.mode: "model-only"`. The allowed call is POST `/v1/chat/completions` to that configured private IPv4 endpoint. There is no cloud fallback. For a preinstalled Ollama process, `OLLAMA_NO_CLOUD=1` and `OLLAMA_HOST=127.0.0.1:11434` disable its cloud feature and limit listening; these variables are not a firewall. No model weights are bundled or downloaded by this guide.

The independent P2 M08 module `packages/model-provider` has a distinct contract: default `disabled`, or `{provider:'local', endpoint:'http://127.0.0.1:11434/v1/chat/completions', allowedEndpoints:[thatExactEndpoint], model:'already-installed-model'}`. `generate`/`stream` accept messages and `responseLanguage`. Its explicit cloud option requires all of `provider:'cloud'`, `allowCloud:true`, an exact HTTPS endpoint allowlist and customer-supplied credentials. It is **not** a cloud toggle implemented by the P5 application. Never silently swap the two APIs. See `packages/model-provider/README.md` and its tests. External cloud use discloses selected document content to that provider and needs an explicit customer decision.

## Verify absence of cloud dependency

Use synthetic documents only. With `none` + `offline`, startup, authentication, document creation/import, revision reads and comments must work after network disconnection. Run `node --test tests/security.test.mjs tests/ai.test.mjs packages/privacy/test.mjs packages/model-provider/test.mjs` to check process guard and mock transport boundaries. A passing mock test is not a host-wide network audit.

Observe established/listening sockets for the app, model runtime and conversion subprocesses with host-native tools such as `lsof -nP -i` scoped to their PIDs, or customer-controlled firewall/packet-capture logs. Do not capture document payloads or publish addresses/credentials. Offline mode should have no outbound app/model call; local mode should show only the configured runtime endpoint, plus loopback client connections to port 3000. Specifically investigate DNS, external HTTPS, telemetry, remote assets, mail/payment endpoints and model-runtime upstream connections. Apply a customer-approved OS egress policy to app **and** model/converter processes, then repeat the synthetic workflow disconnected. The Node process guard is defense in depth, not proof about arbitrary subprocesses, operating system services or the model runtime. No third-party CDN, trackers or analytics are required by public pages.

## Docker Compose — TODO, not a working P5 install recipe

`docker-compose.yml`, `infra/Dockerfile` and `npm run setup:docker` exist from P1. Do **not** present `docker compose up` as a functioning P5 deployment:

- `setup:docker` sets `server.host` to `0.0.0.0`, while the P5 product server rejects hosts other than `127.0.0.1`.
- The existing Compose healthcheck uses HTTP, while P5 startup requires HTTPS/TLS.
- The image is Node-only and does not package Python/OCR/render dependencies; OpenSSL availability and writable TLS setup still need image verification.
- Compose publishes host loopback port 3000, uses an internal private network and an Ollama service on 11434; `OLLAMA_NO_CLOUD=1` is configured. These are static declarations, not a verified running image.

TODO: reconcile bind/TLS/healthcheck, package/pin dependencies and offline runtime provisioning, then run the complete synthetic P5 workflow in Compose before supplying Docker install commands. No Docker build/run or model download was performed in P6.

## P10 editor action details

Language preferences (`update-preferences`, `PUT /api/preferences`, optional `projectId`) are available at `/editor#preferences`. The editor distinguishes local HTML/JSON exports of the current unsaved draft from saved-version downloads (`export-document`, `GET /api/projects/{documentId}/download?format=html|json&version=<versionId>`). Use the latter when referring to an immutable server version. The owner's `delete-document` control asks for confirmation before the existing `DELETE /api/projects/{documentId}` call and returns to `/workspace`; cancellation makes no request. API policy still rechecks ownership. No new endpoint or MCP tool is implied by these UI controls.

For `ask-ai` and `run-mechanical`, use the authorized document response `aiRoles` to choose `role` and supported `operation`. Inputs are `question`, `role`, `execution` (`model` or `deterministic`), `operation`, `blockId`, and `compareVersion`. A question is required for generation without an operation and for search; other mechanical operations need no question. Only diff accepts/requires another `compareVersion` of the same document. `run-mechanical` is the deterministic-only counterpart of `ask-ai` (format/numbering/table/diff/anchors/duplicates/search): a document may expose it even when `ask-ai` (model chat) is unavailable (`status.ai.available`). Preserve the returned `indicator` and `role`/`stage`/`model`/`provider`/`degraded` provenance.

## P11 one-call recipes

`POST /agent/run` accepts `{recipe, params, dryRun}` or `{resumeToken}`. Use the existing session cookie, `Content-Type: application/json` and `x-zentro-request: 1`. `dryRun: true` validates parameters and returns the exact plan without changing product data. Execution is sequential, stopping on the first failure, not an atomic transaction: committed steps remain committed.

A new-customer recipe pauses at email verification. The user must click the verification link in the actual local mock email; the runner must not consume that token. Resume in the same session after verification. A different session, even for the same user, cannot use the continuation. Tokens expire after one hour and do not survive server restart.

On 402 PLAN_LIMIT, stop and follow the returned upgrade action. On 409 SAVE_CONFLICT, read the current version and review/rebase proposed changes before using a fresh baseVersion; never overwrite automatically. On 429 RATE_LIMIT, respect Retry-After; do not retry immediately or switch sessions. Every step retains product permissions and resource quotas, while one run consumes one API request reservation.

The annotation in the prepared new-customer recipe is explicit deterministic duplicate analysis; the revision recipe uses the existing deterministic text-block diff. Neither claims real LLM output or Word/OOXML structural comparison. The local benchmark includes mock subscription activation in both workflows, without changing Free rate limits.

MCP `run_recipe` shares this contract plus the existing credential-bound `orgId`; it does not enable missing production credential, entitlement or meter adapters. See `/mcp-setup`.
