# AI installation runbook (English) [Language selection / 中文](/agent-install.md) This is the English edition of Mendry's single AI installation runbook, for Claude, Codex, and other assistants installing or restarting a **source evaluation environment**. It describes the same workflow as the Chinese edition, not an alternative installation method. Other documents are API and feature references. Commands must match the current checkout; stop and report discrepancies rather than guessing interfaces or modifying application code to bypass installation problems. ## Language policy - Follow the user's explicit language preference first; otherwise use the language of their current request/conversation. Use the Chinese edition for Chinese and this edition for English. Do not infer language from the OS, shell, timezone, username, or location. - If the preference is unclear or another language is used, briefly ask “中文还是 English? / Chinese or English?” before proceeding. Do not silently choose an unsupported translation. - Use the selected language for plans, consent requests, progress, error explanations, next steps, and the final handoff. A language switch does not authorize rerunning completed steps. - Keep commands, paths, configuration keys, API fields, model IDs, and status labels (`PASS`, `FAIL`, `SKIPPED`, `BLOCKED`) unchanged. Explain redacted native errors in the selected language without altering their technical meaning. - Both editions have identical authorization boundaries and verification gates. Language is independent of platform and installation target. If editions disagree on an executable step or safety rule, stop and report the discrepancy rather than selecting the more permissive one. **Status: Preview.** Source-built Docker Compose evaluation assets are provided; there is no supported production image, production installation bundle, upgrade path, or rollback procedure. Do not report this workflow as a successful production deployment. This runbook also does not certify full Windows functionality or cross-platform SSH probe and Docker validation support. ## 0. Execution agreement and authorization Confirm the following together before starting; do not ask again for information already known: - Target: **incident console** (recommended default, API and frontend), or **offline Local Harness evaluation**? - Console deployment method: **Docker Compose container evaluation** (no host Go/Node; section 10), or **native source execution** (sections 1–5)? Choose independently of the experience target; do not silently select for the user. Docker also requires choosing bundled PostgreSQL/Redis or existing dependencies. Local Harness retains the source workflow in section 8. - OS and shell: Linux/macOS Bash, Windows PowerShell, or WSL? Do not mix Windows and WSL tools, paths, or database addresses. - Absolute repository path and target revision; new or existing environment? Never overwrite existing configuration, accounts, or databases. - For native source or Docker with existing dependencies: are a dedicated PostgreSQL database, Redis, connection permissions, and the database's original encryption key available? - Are Go/npm dependency or container image downloads, image builds, container/network/persistent-volume creation, database migrations, and starting the selected services authorized? - Is the goal service startup only, or also model configuration and verification that may incur charges and send data to a provider? Present the plan and resources to be written before executing within the granted scope. If required information is missing, report `BLOCKED` and ask. Do not independently install system services, create cloud resources, or change firewall rules. ### Mandatory safety boundaries 1. Never overwrite an existing `.env` or regenerate `MENDRY_ENCRYPTION_KEY` for an existing database. Stop if the old key is missing; a new key cannot recover existing credentials. 2. Do not delete databases, Redis data, volumes, migration history, or run snapshots. Do not use `seed`, destructive integration tests, or account resets to “fix installation.” 3. Never put passwords, connection strings, model API keys, cookies, or full webhook URLs into chat, command history, shared logs, or commits. Users supply credentials through a private editor or hidden local input. Check presence and validity without displaying values. 4. Do not print `.env`, the full process environment, or credential-bearing requests. Keep `MENDRY_HTTP_REQUEST_DEBUG=false` and `MENDRY_POSTGRES_QUERY_DEBUG=false`. 5. Bind to loopback by default and retain `analysis_only`. Public access, external model calls, SSH installation, repository writes, automatic hotfix, and Enhanced validation each require explicit authorization. 6. Do not automatically `git pull`, switch branches, reset user changes, or terminate unknown processes. Dependency installation executes third-party build/install logic; only do it in a source evaluation environment the user trusts. 7. Check the exit code after every short command and stop on failure. Use `$?` in Bash and `$LASTEXITCODE` for native commands in PowerShell. Do not execute the entire document as one unchecked batch. ## 1. Read-only preflight Working directory: the user-confirmed repository root. If there is no checkout, confirm the destination and revision before cloning from `https://github.com/Funovate/Mendry`. Do not overwrite an existing directory. Run individually in either shell: ```text git status --short git rev-parse --short HEAD go version ``` For the console, also run: ```text node --version npm --version ``` Pass criteria: - `backend/go.mod`, `backend/.env.example`, and `frontend/package-lock.json` exist. - Go 1.25+; for the console, Node.js `^20.19.0 || >=22.12.0` with npm (from Vite's `engines` in `frontend/package-lock.json`; `frontend/` declares no Node pin of its own). - Existing working-tree changes are recorded and will not be overwritten. - This runbook calls Go directly and does not require Make. Initial key generation in Bash requires OpenSSL; PowerShell uses .NET cryptographic randomness. - PostgreSQL and Redis have been prepared by the user/operator and are authorized evaluation resources. **This runbook does not supply an unverified database auto-installer.** If dependencies are missing, stop and list the required database, account, connection addresses, and TLS requirements. Local Harness does not need these dependencies. For missing commands or incompatible versions, explain the gap, obtain approval to install or switch toolchains, then repeat preflight. Do not silently upgrade global tools. Local Harness users proceed to section 8; console users continue below. ## 2. Prepare private configuration (console) Working directory: `backend/` under the repository root. Enter it once; subsequent backend commands run there. ```text cd backend ``` ### 2.1 Create `.env` only if absent **Bash:** ```bash if [ -e .env ]; then printf '%s\n' 'Reuse existing .env; do not overwrite.' else (umask 077; set -C; cat .env.example > .env) fi ``` **PowerShell:** ```powershell if (Test-Path .env) { Write-Output 'Reuse existing .env; do not overwrite.' } else { [System.IO.File]::Copy((Join-Path $PWD '.env.example'), (Join-Path $PWD '.env'), $false) } ``` Keep the file in a directory readable only by the current user and authorized administrators. Check Unix permissions or Windows file and parent-directory ACLs; do not assume a copied file is automatically private. ### 2.2 Generate the initial encryption key (new database only) The following commands replace only the example placeholder key, save directly to `.env`, and do not display the key. Run one variant only after confirming a new database and safe file permissions. Skip this step for existing environments and preserve the original value. **Bash:** ```bash # Run in the user's private local terminal; disable shell tracing. ( set +x set -e key=$(openssl rand -base64 32) MENDRY_NEW_KEY="$key" node -e 'const fs=require("fs"); const p=".env"; const s=fs.readFileSync(p,"utf8"); const old="MENDRY_ENCRYPTION_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; if(s.split(old).length!==2) throw Error("Expected exactly one placeholder; stop without changes"); fs.writeFileSync(p,s.replace(old,"MENDRY_ENCRYPTION_KEY="+process.env.MENDRY_NEW_KEY));' ) ``` Check the entire subshell's exit code; stop on nonzero. Temporary variables do not remain in the parent shell. **PowerShell:** ```powershell $path = Join-Path $PWD '.env' $content = [System.IO.File]::ReadAllText($path) $placeholder = 'MENDRY_ENCRYPTION_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' if (($content.Split([string[]]@($placeholder), [System.StringSplitOptions]::None)).Count -ne 2) { throw 'Expected exactly one placeholder; stop without changes' } $bytes = New-Object byte[] 32 $rng = [System.Security.Cryptography.RandomNumberGenerator]::Create() try { $rng.GetBytes($bytes) $content = $content.Replace($placeholder, 'MENDRY_ENCRYPTION_KEY=' + [Convert]::ToBase64String($bytes)) [System.IO.File]::WriteAllText($path, $content, (New-Object System.Text.UTF8Encoding($false))) } finally { $rng.Dispose() [Array]::Clear($bytes, 0, $bytes.Length) Remove-Variable content, bytes -ErrorAction SilentlyContinue } ``` Pass criteria: the key is standard base64 encoding of 32 random bytes, is persistently saved, and is not the example value. The user knows its backup location. Do not include the key in the handoff report. ### 2.3 Have the user enter connection settings privately Edit `backend/.env` in a private editor: | Setting | Requirement | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | `MENDRY_POSTGRES_URL` | Authorized database; percent-encode reserved URL characters in usernames/passwords; follow the provider's remote TLS requirements | | `MENDRY_REDIS_URL` | Match password/ACL/TLS requirements; omit database paths such as `/0` | | `MENDRY_REDIS_DB` | Set the Redis logical database separately; do not clear Redis to resolve conflicts | | `MENDRY_ENCRYPTION_KEY` | Newly generated value for a new environment; original key for an existing database | | `MENDRY_HTTP_ADDR` | Retain `127.0.0.1:8080` for local evaluation | | `MENDRY_PUBLIC_URL` | Retain `http://127.0.0.1:8080` for local evaluation | Keep other example defaults. See the [configuration template](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/backend/.env.example) for formats. The backend loads `.env` from `backend/`. Existing process environment variables override the file; `MENDRY_*` overrides the compatible `FIXTHE_*`, including explicit empty values. When settings do not match expectations, inspect names and presence only, never dump values. Obtain consent before clearing or changing existing environment variables. Do not execute the file with `source .env` or `Invoke-Expression`. Verify that Git does not track `.env`: ```text git check-ignore .env git ls-files --error-unmatch .env ``` The first command should show that the file is ignored. The second should exit nonzero because it is untracked; this is an explicitly allowed expected nonzero result. If tracked or not ignored, stop and resolve the issue without committing credentials. ## 3. Database migration and login account Working directory: `backend/`. ### 3.1 Migration Migrations change the database schema. For an existing database, first obtain confirmation of a usable backup and authorization to migrate. This is not a supported production upgrade procedure. ```text go run ./cmd/migrate ``` Pass criterion: exit code 0. The API does not migrate automatically. Repeated execution uses migration history to identify pending work; do not manually delete history. Stop and report lock timeouts, checksum conflicts, or incompatible older schemas rather than deleting and recreating the database. ### 3.2 Initial account Skip if an account exists and the user can log in. Rerunning bootstrap does not resolve a forgotten password. For the first run, have the user temporarily add `MENDRY_BOOTSTRAP_ADMIN_PASSWORD` to `.env` using a private editor (12–72 bytes; random ASCII from a password manager is recommended). Only the bootstrap command reads it. Do not transmit it through chat. Confirm that no exported variable with the same name overrides it. ```text go run ./cmd/bootstrap-admin --username admin ``` Confirm with the user before replacing `admin` with another username. Pass criterion: exit code 0. Then remove the temporary password setting from `.env`, retaining the password-manager record. Clean up the temporary setting on failure as well. Repeated execution for the same enabled account does not replace its password hash. The database permits only one account; do not try another username to create a second identity. ## 4. Start and verify the API Working directory: `backend/`. Run in the foreground in a dedicated terminal: ```text go run ./cmd/api ``` This is a long-running process; do not wait for normal exit before proceeding. Record its terminal and working directory. Do not leave an unmanaged background process. If the port is occupied, establish ownership first; never kill an unknown process. Check from another terminal, allowing up to 120 seconds. Compilation/download waits may be extended with user approval. Inspect only redacted startup errors on failure; do not retry indefinitely. **Bash:** ```bash curl --fail --silent --show-error --max-time 10 http://127.0.0.1:8080/livez curl --fail --silent --show-error --max-time 10 http://127.0.0.1:8080/readyz ``` **PowerShell:** ```powershell (Invoke-WebRequest -UseBasicParsing -TimeoutSec 10 http://127.0.0.1:8080/livez).StatusCode (Invoke-WebRequest -UseBasicParsing -TimeoutSec 10 http://127.0.0.1:8080/readyz).StatusCode ``` Pass criteria: both endpoints return HTTP 200, and responses are confirmed to come from the intended instance. `livez` proves only process liveness; `readyz` includes PostgreSQL and Redis readiness. Neither proves model or Git configuration is usable. ## 5. Start and verify the console Open another dedicated terminal and enter `frontend/` from the repository root: ```text cd frontend npm ci npm run dev -- --host 127.0.0.1 --port 5173 --strictPort ``` Confirm `npm ci` exits with code 0 before starting the development server. It rebuilds this package's `node_modules`. Do not substitute `npm install` or automatically fix audit findings and rewrite the lockfile. The explicit host overrides the package script's `0.0.0.0` and binds only to loopback. `--strictPort` prevents silently switching ports on conflict. Pass criteria: 1. The browser opens `http://127.0.0.1:5173`. 2. The user successfully signs in with the initialized account; do not capture or display session cookies. 3. Vite proxies `/api` requests to `127.0.0.1:8080`, without persistent authentication or server errors. Without browser capability, report only “API ready, frontend started; login pending user verification.” Do not claim full console verification. Vite is an evaluation development server, not a production deployment. If the API port must change, also inspect the proxy in `frontend/vite.config.ts`; do not change only `.env` and ignore console failures. ## 6. Optional: from service startup to working AI This section requires authorization for project configuration, external requests, and potential model charges. Without it, skip and report “service available; AI not verified.” Without browser or corresponding API capability, explicitly ask the user to perform the following actions rather than guessing request formats. 1. Create a dedicated evaluation project in the console. Confirm ownership before reusing an existing name; do not overwrite existing configuration. 2. Save the environment, authorized read-only Git baseline, and selected evidence source. Configuration steps save independently. See [first project](/docs/get-started/first-project/) and [configuration reference](/docs/reference/configuration/). The first-project webhook example supplies ingestion requirements, not proof that Git or the model is ready. 3. Create the model credential and configure provider, HTTPS base URL, API mode, and model. OpenAI-compatible uses `chat_completions` or `responses`; Anthropic uses `messages`. Enter credentials privately through the console. Discover models, select a returned ID, test the connection, and save. See [LLM providers](/docs/guides/llm-providers/); do not hard-code guessed model IDs. 4. Retain `analysis_only`. Do not enable `auto_hotfix`, install remote SSH probes, or grant Git write access just to pass verification. 5. After approval, send only a synthetic signal with no real business data. See [first incident](/docs/get-started/first-incident/). Use the actual inbound URL in a private terminal without copying its token into chat or logs. 6. Verify the event-stream record, corresponding incident, and final analysis. Webhook `202 accepted` confirms only synchronous acceptance. Model failure can fall back to deterministic behavior, so an incident appearing does not prove model analysis succeeded. Report end-to-end AI verification only when the connection test passes and the incident contains a reviewable model analysis result. Record insufficient evidence, model failure, or deterministic-only normalization separately; never bypass restrictions. Remote signals require a reachable `MENDRY_PUBLIC_URL`; remote machines cannot use the local loopback address. Vite proxies `/api`, not `/hooks`, and SSH probe delivery requires HTTPS. If these capabilities are needed, stop the local installation workflow and separately confirm network/TLS and host permissions. ## 7. Stop, restart, and handle failures - Stop: press Ctrl+C in each API/frontend terminal created for this installation and wait for normal exit. Do not delete data or configuration. - Same-version restart: reuse `backend/.env`, run `go run ./cmd/api` from `backend/`, start the development server using section 5 from `frontend/`, and repeat health/login checks. Without dependency changes, `npm ci` need not be repeated. - Version change: never update automatically. Reinstall relevant dependencies and apply pending migrations only after user approval, backup confirmation, and version checks. There is no verified automatic rollback promise. | Symptom | Next step and prohibited actions | | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Go/npm download failure | Check versions, network, and proxy; do not disable TLS or switch to untrusted mirrors automatically | | PostgreSQL connection/migration failure | Check target database, permissions, URL encoding, TLS, locks, and history; do not delete the database or migration records | | Redis failure or non-200 readyz | Check URL, ACL, TLS, logical database, and redacted errors; never `FLUSHALL` | | Missing encryption key/decryption failure | Recover the original key for that database; do not generate a replacement | | Login failure | Check account, Redis, browser requests, and proxy; do not recreate accounts or edit database password hashes | | Port 8080/5173 occupied | Establish ownership and ask the user; do not kill unknown processes | | Model connection failure | Check provider, API mode, model permissions, and network; do not display keys or count fallback as success | | Signal accepted but no analysis | Distinguish asynchronous ingestion, deterministic fallback, missing Git/evidence, and model failure; do not weaken repair policy | See [troubleshooting](/docs/guides/troubleshooting/) for more diagnostics. At the agreed timeout, report `BLOCKED` with the failed step, redacted error, completed steps, retained resources, and decisions needed from the user. ## 8. Independent branch: offline Local Harness evaluation Execute only sections 0, 1, and this section. Do not install the database, account, API, or frontend. Go is required; initial compilation may download modules, but the fixture itself needs neither external model network access nor an API key. Working directory: `backend/`. Run individually in either shell: ```text go run ./cmd/agentcore-local run --config examples/agentcore-local/fixture-report.config.json --event examples/agentcore-local/report.event.json --state-dir .agentcore/runs ``` Pass criteria: exit code 0 and a run ID in the output. Replace `RUN_ID` below with the actual ID before executing; do not run the placeholder literally: ```text go run ./cmd/agentcore-local inspect --state-dir .agentcore/runs --run-id RUN_ID ``` Inspect the persisted snapshot's state, completion reason, and artifacts. Do not delete an existing run. To continue, use the same config/event with `run --resume`; see [Local Harness](/docs/get-started/local-harness/). Never automatically replay or independently `resolve` an unknown write outcome. Report “Local Harness fixture verified,” not “incident console installed” or “real model verified.” ## 9. Required handoff report Include no credentials. Use `PASS`, `FAIL`, `SKIPPED`, and `BLOCKED` to distinguish verified outcomes from unperformed steps. ```text Target: incident console / Local Harness Deployment method: native source / Docker Compose Compose files, project name, image IDs, persistent volumes (if applicable): Source revision, OS, and shell: Absolute repository path: Preflight: Configuration: saved path and permission confirmation (no contents) Database migration / account bootstrap: API livez / readyz: Frontend access / user login: Model connection / end-to-end analysis: Local fixture / snapshot: Created or reused resources and running terminals: Stop and restart instructions: Incomplete steps, failure reasons, and next actions: Conclusion: source evaluation services ready / end-to-end AI verified / Local fixture verified / BLOCKED ``` ## 10. Docker Compose branch (container evaluation) After choosing Docker, follow section 0, this section, optional section 6, and section 9. Skip the host Go/npm workflow in sections 1–5. No host Go/Node is required. Images are built from this checkout; container end-to-end verification is still pending. This is not a production installation bundle. ### 10.1 Preflight and resource authorization From the repository root, individually check `git status --short`, `git rev-parse --short HEAD`, `docker version`, and `docker compose version`. Require a working Linux container engine and Compose **2.30.0+** (raw env_file support). On Windows use Docker Desktop Linux containers or an explicitly selected WSL engine; do not mix paths. Confirm the Docker context targets the authorized local engine, not an unknown remote engine. Confirm port 8080, Compose project name, image downloads/builds, network/container/volume creation, and migration permissions. Choose: - **Bundled dependencies**: use `compose.yaml + compose.local.yaml` only for a new dedicated evaluation database. Creates PostgreSQL 17, Redis 7, and persistent volumes; no database ports are published. Redis has no password: use only on a trusted local Docker host, not as a multi-tenant security boundary. - **Existing dependencies**: use only `compose.yaml`; no bundled databases are started. Confirm container-reachable addresses, ACL/TLS, backups, and the original encryption key. Container `127.0.0.1` is not the host. Do not guess gateways or disable TLS. If Docker is missing, report `BLOCKED` and obtain installation approval. No public exposure or Docker socket mount by default; this image does not provide Enhanced Docker validation or an automatic hotfix runtime. ### 10.2 Private configuration Enter the repository's `deploy/` directory; run all subsequent Compose commands there. Only if `.env` is absent, use section 2.1's safe copy commands with this directory's `.env.example`. Check permissions, Git ignore, and untracked status. Do not start with the empty example key. The user supplies values through a password manager/private editor, never chat, command arguments, or logs: - New database: generate and preserve **32 random bytes encoded as standard base64** for `MENDRY_ENCRYPTION_KEY`. Existing database: retain the original key. Docker does not require installing Node for key generation; use a password manager or local .NET/OpenSSL, with output saved only to a private file. - Bundled dependencies: generate a random hexadecimal database password, set `POSTGRES_PASSWORD`, and privately put the same value in `MENDRY_POSTGRES_URL=postgres://mendry:PASSWORD@postgres:5432/mendry?sslmode=disable`. Replace PASSWORD; never retain the placeholder. Keep `MENDRY_REDIS_URL=redis://redis:6379`. - Existing dependencies: supply actual PostgreSQL/Redis URLs following section 2.3's encoding/TLS rules; `POSTGRES_PASSWORD` is unnecessary. Agree on read-only certificate mounts if needed. - Keep `COMPOSE_PROJECT_NAME` stable and user-confirmed. Confirm ownership of existing same-name resources. Renaming selects different volumes; do not use this to bypass existing data. - The API listens on `0.0.0.0:8080` inside its container, but only Nginx's `127.0.0.1:8080` is published on the host. Keep `MENDRY_PUBLIC_URL=http://127.0.0.1:8080` by default. Raw `env_file` values must not be quoted or have trailing comments; dollar signs remain literal. Compose still parses the project name and POSTGRES_PASSWORD, so use hexadecimal for the bundled password. Check for overriding shell variable names without printing values. Never run secret-expanding `docker compose config` without `--quiet`, or `docker inspect`. Docker administrators can access container environments and must be trusted. ### 10.3 Build, migrate, and initialize the account Commands below are for **bundled dependencies**. For **existing dependencies**, remove `-f compose.local.yaml` from every command and skip starting postgres/redis. Use the same file combination for stop/restart. Check each short command's exit code and stop on failure. ```text docker compose --env-file .env -f compose.yaml -f compose.local.yaml config --quiet docker compose --env-file .env -f compose.yaml -f compose.local.yaml build api web docker compose --env-file .env -f compose.yaml -f compose.local.yaml up -d --wait --wait-timeout 120 postgres redis docker compose --env-file .env -f compose.yaml -f compose.local.yaml run --rm --no-deps migrate ``` Confirm the database target and backups before migration; require exit code 0. The API does not migrate automatically. Image tags are mutable evaluation tags; record the source revision and actual image IDs, without claiming reproducible production distribution. Skip bootstrap if an account already works. For the first account, temporarily add `MENDRY_BOOTSTRAP_ADMIN_PASSWORD` (12–72 bytes) to `deploy/.env` privately, then run: ```text docker compose --env-file .env -f compose.yaml -f compose.local.yaml run --rm --no-deps bootstrap-admin ``` The default username is `admin`; for an approved alternative append `bootstrap-admin --username NAME`. Remove the temporary password setting on success or failure before starting the API, so it is not retained in long-running containers. Never reset existing accounts or create a second account. ### 10.4 Start and verify ```text docker compose --env-file .env -f compose.yaml -f compose.local.yaml up -d --wait --wait-timeout 120 api web docker compose --env-file .env -f compose.yaml -f compose.local.yaml ps ``` After exit code 0 and healthy services, run section 4's two HTTP health checks (same addresses, now through Nginx). Open `http://127.0.0.1:8080` and verify login and page refresh. Nginx serves built static assets with SPA fallback and same-origin `/api` and `/hooks` proxies, not a Vite development server. Without a browser, mark login pending. Healthy containers do not prove model functionality. Continue with section 6 only with separate authorization; remote webhook/SSH use still requires a separate HTTPS/network plan. ### 10.5 Stop, restart, and preserve data ```text docker compose --env-file .env -f compose.yaml -f compose.local.yaml stop docker compose --env-file .env -f compose.yaml -f compose.local.yaml up -d --wait --wait-timeout 120 api web ``` The second command is a same-version restart; depends_on starts bundled dependencies. Preserve `deploy/.env`, the original encryption key, project name, and PostgreSQL/Redis/runtime volumes. runtime stores artifacts/workspaces; back up the database, runtime data, and encryption key together. Never use `down -v`, volume prune, database deletion, or key regeneration. For version changes obtain backup confirmation and authorization before build/migrate/start; no automatic rollback is promised. On health timeout, check `ps` and privately reviewed redacted logs: database readiness, addresses, original key, volume write permissions, and port conflicts. Do not output container environments or credential-bearing logs. Preserve failed resources and report `BLOCKED`; never retry indefinitely or clean up automatically. Record the Compose file combination, project name, image IDs, volumes, and verification outcomes in section 9's handoff. ## Maintenance references When changing installation steps, check the [root README](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/README.md), [backend guide](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/backend/README.md), [configuration template](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/backend/.env.example), [Makefile](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/backend/Makefile), [frontend scripts](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/frontend/package.json), the Docker Compose assets ([compose.yaml](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/deploy/compose.yaml), [compose.local.yaml](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/deploy/compose.local.yaml), [deploy/.env.example](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/deploy/.env.example), [backend Dockerfile](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/backend/Dockerfile), [frontend Dockerfile](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/frontend/Dockerfile), [nginx.conf](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/frontend/nginx.conf)), and references above. After changing section 10 or those assets, run `npm run test:deploy` from `frontend/` ([deploy/check.test.cjs](https://github.com/Funovate/Mendry/blob/a60d47164f602d692cd56edecdb19315c37b830a/deploy/check.test.cjs)). Keep this edition and [the Chinese edition](/agent-install.md) synchronized: section numbers, commands, safety boundaries, and pass criteria must agree. Translate explanations and handoff templates, not executable identifiers. Entry files link to this runbook instead of duplicating the procedure. Documentation changes cannot establish unverified production distribution capabilities.