Skip to main content

Setup Fixes & Configuration Changes

This document tracks configuration changes and fixes made to ensure the project runs correctly.

April 2026 — MCP Server Core (Phase 11.01)

New env vars: ENABLE_MCP, MCP_PORT, MCP_API_KEY

A new MCP (Model Context Protocol) server is available. It is disabled by default.
# .env
ENABLE_MCP=false        # Set to true to start the MCP server
MCP_PORT=3939           # Port for the MCP server (default: 3939)
MCP_API_KEY=            # Optional bearer token for authentication
When ENABLE_MCP=true, EchOS starts an HTTP server on MCP_PORT (default 3939) that exposes seven knowledge tools via the Model Context Protocol. The server only binds to 127.0.0.1. If MCP_API_KEY is unset, all requests are accepted without authentication (suitable for localhost-only use). If set, all requests must include Authorization: Bearer <MCP_API_KEY>.

March 2026 — SSHFS write access via Docker entrypoint ownership fix

Problem

SSHFS mounts of the knowledge directory were read-only. Docker was writing volume files as the node user (UID 1000), which differed from the server SSH user’s UID on some VPS providers (e.g. Oracle Cloud where the default ubuntu user is UID 1001).

Fix

A Docker entrypoint script (docker/entrypoint.sh) now runs as root on startup, detects any ownership mismatch in the data directories, corrects it, then drops to the node user via su-exec before starting the app. This self-heals on every container start with no manual steps. If upgrading from an older version, re-own once and restart:
sudo chown -R $(id -u):$(id -g) ~/echos/data
cd ~/echos/docker && docker compose up -d --force-recreate
See Remote Access → SSHFS write access for full details.

February 25, 2026 — Configurable Prompt Cache Retention

New env var: CACHE_RETENTION

pi-ai already supported a PI_CACHE_RETENTION env var for controlling Anthropic prompt cache TTL (checked inside resolveCacheRetention in the provider), defaulting to 'short' (5 min) unless PI_CACHE_RETENTION=long was set. EchOS now exposes this as a first-class Zod-validated config field, with a default of 'long' and automatic enforcement of 'none' for custom endpoints.
CACHE_RETENTION=long    # default — 1h TTL; best cost savings for Anthropic
CACHE_RETENTION=short   # 5min TTL — matches old pi-ai default
CACHE_RETENTION=none    # disable prompt caching entirely
What changed vs. the old PI_CACHE_RETENTION approach:
  • EchOS now defaults to 'long' instead of pi-ai’s 'short'
  • When LLM_BASE_URL is set (custom OpenAI-compatible endpoint), cacheRetention is forced to 'none' regardless of this env var — those endpoints do not support Anthropic-style prompt caching
  • The effective value is Zod-validated and logged at startup
{ "model": "...", "cacheRetention": "long", ... }
Files changed:
  • packages/shared/src/config/index.ts — added cacheRetention Zod enum field; CACHE_RETENTION env var mapping
  • packages/core/src/agent/index.tscacheRetention? in AgentDeps; effectiveCacheRetention computed (forced 'none' for non-Anthropic); passed to streamSimple; logged at startup
  • src/index.ts — passes config.cacheRetention into AgentDeps
  • .env.exampleCACHE_RETENTION=long added under LLM settings

February 19, 2026 — Standalone CLI & Model Deprecation Fix

pnpm echos — standalone three-mode CLI

packages/cli/src/index.ts is a standalone CLI binary — no daemon required. Three auto-detected modes:
InvocationModeBehaviour
pnpm echos "query"One-shotBoots agent, answers, exits
echo "msg" | pnpm echosPipeReads stdin, answers in plain text, exits
pnpm echos (TTY)Interactive REPLPersistent session with history
Interactive REPL features:
  • History persisted to ~/.echos_history (max 500 entries, restored on next run)
  • Ctrl+C cancels in-flight response, re-prompts (does not exit)
  • Tool calls shown in dim colour on TTY; plain text in pipe mode
  • exit / quit / Ctrl+D exits cleanly
Logging: CLI defaults to warn log level — startup logs are suppressed. Override with LOG_LEVEL=info pnpm echos. No daemon required. Both pnpm start (daemon) and pnpm echos (CLI) read from the same ./data/ directory. SQLite WAL mode makes concurrent access safe. Files changed:
  • packages/cli/src/index.ts — three-mode standalone CLI
  • packages/cli/package.json"bin": { "echos": "./dist/index.js" }
  • package.json"echos": "tsx --env-file=.env packages/cli/src/index.ts" script

Default model updated — claude-3-5-haiku-20241022claude-haiku-4-5-20251001

claude-3-5-haiku-20241022 reached end-of-life on February 19, 2026. The Anthropic API returns empty responses for this model from today. Files changed:
  • packages/shared/src/config/index.tsdefaultModel default updated
  • packages/core/src/agent/index.ts — in-code fallback updated; type cast cleaned up to Parameters<typeof getModel>[1]
Action required if you have DEFAULT_MODEL=claude-3-5-haiku-20241022 in .env: update it to claude-haiku-4-5-20251001.

February 19, 2026 — Web API Security & Experimental Interface Defaults

Web marked experimental, disabled by default

Web UI is now clearly marked as experimental in the setup wizard with a warning before the interface selection step. It defaults to off (it was already off in the config schema, but the wizard was pre-selecting Web UI if no existing .env was found).

New env var: WEB_API_KEY

All web API routes (except GET /health) now require Authorization: Bearer <WEB_API_KEY>.
# .env
WEB_API_KEY=your_64_char_hex_key   # generate: openssl rand -hex 32
The setup wizard generates and stores this key automatically when you enable the web interface. On re-runs it reuses the existing key from .env. If WEB_API_KEY is not set, the server starts but logs a warning and all routes are unauthenticated.

userId now validated against ALLOWED_USER_IDS in web routes

All /api/chat/* routes now verify the userId in the request body is in ALLOWED_USER_IDS. Unknown user IDs return 403 Forbidden. Previously the web API accepted any numeric userId.

Web server binds to 127.0.0.1 (was 0.0.0.0)

The Fastify server no longer listens on all interfaces. It is only reachable from localhost.

CORS restricted to localhost origins

origin: true (allow all) replaced with a function that only allows http(s)://localhost:* and http(s)://127.0.0.1:*.

Non-interactive wizard fix: enableWeb default

ENABLE_WEB in --non-interactive mode was defaulting to true unless explicitly set to 'false'. Fixed to false unless explicitly set to 'true', matching the config schema default. Files changed:
  • packages/shared/src/config/index.tswebApiKey field added
  • packages/web/src/index.ts — auth hook, localhost bind, restricted CORS
  • packages/web/src/api/chat.tsallowedUserIds param, 403 on unknown userId
  • scripts/setup.ts — experimental warning, fix defaults, generate/store WEB_API_KEY

February 19, 2026 — Model Presets & Cross-Provider Handoffs

New env vars: MODEL_BALANCED, MODEL_DEEP

Configure named model presets for on-the-fly switching during a session:
MODEL_BALANCED=claude-sonnet-4-5     # default balanced preset
MODEL_DEEP=claude-opus-4-5           # default deep preset
Format: plain model ID (provider inferred from prefix) or provider/model-id for explicit provider:
MODEL_BALANCED=openai/gpt-4o         # cross-provider: OpenAI
MODEL_DEEP=anthropic/claude-opus-4-5
Built-in defaults (used when env vars are not set):
PresetDefault model
fastclaude-3-5-haiku-20241022
balancedclaude-sonnet-4-5
deepclaude-opus-4-5
Switching mid-session (conversation history is preserved across model switches; thinking blocks from Claude are automatically converted to <thinking> tagged text for cross-provider compatibility):
  • Telegram: /model balanced
  • Web API: POST /api/chat/model { "preset": "balanced", "userId": 123 }
Files changed:
  • packages/shared/src/config/index.tsmodelBalanced, modelDeep fields
  • packages/core/src/agent/model-resolver.tsresolveModel(spec), MODEL_PRESETS, ModelPreset
  • packages/core/src/agent/index.tsmodelPresets? in AgentDeps
  • packages/core/src/index.ts — exports resolver + preset types
  • src/index.ts — passes presets into AgentDeps
  • packages/telegram/src/index.ts/model command
  • packages/web/src/api/chat.tsPOST /api/chat/model endpoint

February 19, 2026 — Configurable Thinking Level

New env var: THINKING_LEVEL

Controls the LLM reasoning depth. Defaults to off.
THINKING_LEVEL=medium pnpm start   # extended reasoning on every turn
THINKING_LEVEL=off pnpm start      # default — no reasoning, fastest
Valid values: off | minimal | low | medium | high | xhigh
Note: xhigh is only supported by OpenAI GPT-5.2/5.3 and Anthropic Opus 4.6 (where it maps to adaptive effort “max”). Setting it on an unsupported model will produce an error. Use medium or high as safe upgrades for Claude Haiku/Sonnet.
Files changed:
  • packages/shared/src/config/index.ts — added thinkingLevel Zod enum field
  • packages/core/src/agent/index.tsthinkingLevel? in AgentDeps; passed to Agent constructor; logged at startup
  • src/index.ts — passes config.thinkingLevel into AgentDeps

February 19, 2026 — LLM Payload Debug Logging

New env var: LOG_LLM_PAYLOADS

Set LOG_LLM_PAYLOADS=true to log the raw request payload sent to the LLM provider before each API call. Logged at Pino debug level under the key payload.
LOG_LLM_PAYLOADS=true pnpm start
Files changed:
  • packages/shared/src/config/index.ts — added logLlmPayloads field
  • packages/core/src/agent/index.ts — wraps agent.streamFn with onPayload hook when enabled
  • src/index.ts — passes config.logLlmPayloads into AgentDeps
Security note: Pino redaction (ANTHROPIC_API_KEY, Authorization headers) applies before any log output, so keys are not exposed even with this flag enabled. Do not enable in production unless actively debugging.

February 18, 2026 — Distribution & First-Run Setup Wizard

Setup Wizard (pnpm wizard / pnpm wizard:cli)

A new interactive setup wizard (scripts/setup.ts) replaces the manual .env editing workflow. Usage:
pnpm wizard                            # browser-based wizard (recommended)
pnpm wizard:cli                        # terminal wizard
pnpm wizard:check                      # prerequisite check only
pnpm wizard:cli --non-interactive      # CI/Ansible mode (reads env vars, writes .env)
pnpm wizard:cli --skip-validation      # skip live API key checks
What it does:
  1. Checks Node 20+, pnpm 10+, disk space
  2. Detects existing .env — offers update / replace / skip
  3. Collects and validates Anthropic key (required), OpenAI key (optional), Telegram token
  4. Configures interfaces (Telegram, Web UI) and ports
  5. Configures Redis scheduler with cron schedules
  6. Shows masked summary before writing
  7. Writes .env (mode 0600), backs up old .env as .env.backup.{timestamp}
  8. Creates data directories
  9. Offers to run pnpm build if no dist found
Security properties:
  • All keys entered via password() (masked *) — never visible in terminal
  • Keys are NOT accepted as CLI arguments (would appear in ps aux)
  • .env written with chmod 0600 immediately
  • Env file parsed with simple line-by-line reader — no eval(), no shell interpolation
  • API validation uses fetch() with AbortSignal.timeout(10000) — keys never logged
Non-interactive mode (CI/Ansible):
ANTHROPIC_API_KEY=sk-ant-... \
ALLOWED_USER_IDS=123456789 \
ENABLE_TELEGRAM=false \
ENABLE_WEB=true \
pnpm wizard:cli --non-interactive --skip-validation

Config schema fix — telegramBotToken now optional

packages/shared/src/config/index.ts: telegramBotToken changed from z.string().min(1) to z.string().optional(). Why: Web-only deployments were blocked by a required TELEGRAM_BOT_TOKEN even when ENABLE_TELEGRAM=false. The token is still validated at runtime in src/index.ts before the Telegram adapter is created.

First-run detection in src/index.ts

src/index.ts now exits with a helpful message if .env is missing:
No .env file found. Run: pnpm wizard (or pnpm wizard:cli)
Previously, missing config produced a cascade of confusing Zod validation errors.

Docker improvements

  • depends_on.redis.required: false — EchOS starts without Redis when scheduler is disabled
  • Healthcheck added to echos service (HTTP GET /health)
  • nginx and certbot services added under --profile nginx
  • docker/nginx.conf.template created with SSE-compatible proxy config and Let’s Encrypt instructions

install.sh (VPS one-liner)

curl -sSL https://raw.githubusercontent.com/albinotonnina/echos/main/install.sh | bash
Detects platform, checks/installs prerequisites (Node 20+, pnpm, git), clones repo, installs deps, launches wizard. Falls back gracefully when no TTY is available (piped curl).

February 15, 2026 - Initial Setup Fixes

Issues Fixed

  1. Workspace Package Resolution
    • Problem: tsx couldn’t resolve @echos/* workspace packages when running src/index.ts
    • Solution: Added TypeScript path mappings to root tsconfig.json
    • Files changed: tsconfig.json
    • Details: Added paths configuration mapping all @echos/* packages to their source locations
  2. LanceDB Native Module Compatibility
    • Problem: LanceDB 0.26.2 dropped support for Intel Macs (darwin-x64)
    • Solution: Downgraded to LanceDB 0.22.3
    • Files changed: packages/core/package.json
    • Details: Changed "@lancedb/lancedb": "^0.26.2" to "^0.22.3"
  3. Environment File Loading
    • Problem: Environment variables weren’t being loaded from .env file
    • Solution: Added --env-file flag to start script (Node 20.6+ feature)
    • Files changed: package.json
    • Details: Changed "start": "tsx src/index.ts" to "tsx --env-file=.env src/index.ts"

Configuration Changes

tsconfig.json (Root)

Added path mappings for workspace packages:
{
  "compilerOptions": {
    // ... existing config
    "paths": {
      "@echos/shared": ["./packages/shared/src/index.ts"],
      "@echos/shared/*": ["./packages/shared/src/*"],
      "@echos/core": ["./packages/core/src/index.ts"],
      "@echos/core/*": ["./packages/core/src/*"],
      "@echos/telegram": ["./packages/telegram/src/index.ts"],
      "@echos/web": ["./packages/web/src/index.ts"],
      "@echos/cli": ["./packages/cli/src/index.ts"],
      "@echos/scheduler": ["./packages/scheduler/src/index.ts"]
    }
  }
}
Why: tsx needs explicit path mappings to resolve pnpm workspace packages when running TypeScript files directly.

packages/core/package.json

Changed LanceDB version:
{
  "dependencies": {
    "@lancedb/lancedb": "^0.22.3"  // was "^0.26.2"
  }
}
Why: LanceDB 0.26.2 removed darwin-x64 (Intel Mac) native bindings. Version 0.22.3 is the latest with full macOS support.

package.json (Root)

Updated start script:
{
  "scripts": {
    "start": "tsx --env-file=.env src/index.ts"  // was "tsx src/index.ts"
  }
}
Why: Node.js 20.6+ supports native .env file loading via the --env-file flag, eliminating the need for dotenv packages.

Build Process

The correct startup sequence is:
  1. Install dependencies: pnpm install
  2. Build all packages: pnpm build (required on first run and after any package changes)
  3. Configure environment: Edit .env file with API keys
  4. Start application: pnpm start

Known Issues

Telegram Bot Conflicts

Symptom: GrammyError: Call to 'getUpdates' failed! (409: Conflict) Cause: Another instance of the bot is already running. Telegram allows only one instance to poll for updates. Fix:
pkill -f "tsx.*index.ts"
pnpm start
Prevention: For production, use webhooks instead of polling.

Documentation Updates

  • README.md: Added first-time setup notes and process management instructions
  • docs/DEPLOYMENT.md: Added troubleshooting section and process management guide
  • docs/TROUBLESHOOTING.md: New comprehensive troubleshooting guide
  • docs/SETUP_FIXES.md: This file

Environment Variables

Required variables (must be set in .env):
  • TELEGRAM_BOT_TOKEN - From @BotFather
  • ALLOWED_USER_IDS - Comma-separated Telegram user IDs
  • At least one LLM key: ANTHROPIC_API_KEY (Anthropic) or LLM_API_KEY (other providers)
Optional:
  • LLM_BASE_URL - Custom OpenAI-compatible endpoint; requires LLM_API_KEY
  • OPENAI_API_KEY - For embeddings and Whisper
  • WHISPER_LANGUAGE - ISO-639-1 code (e.g. en) to pin Whisper language detection
See .env.example for full list with defaults.

Verification Steps

To verify the setup is working:
  1. Check Node version: node --version (should be 20+)
  2. Check pnpm version: pnpm --version (should be 9+)
  3. Check packages built: ls packages/*/dist (should show compiled JS files)
  4. Check Redis running: redis-cli ping (should return PONG)
  5. Check env file: head .env (should show configured values, without secrets!)
  6. Start application: pnpm start (should start without errors)

Available Commands

  • pnpm start - Start daemon (Telegram + Web if enabled)
  • pnpm start:web-only - Start only the Web API (port 3000)
  • pnpm echos - Standalone CLI (interactive REPL, no daemon needed)
  • pnpm echos "query" - One-shot CLI query
  • pnpm dev - Watch mode for development (rebuilds on changes)
  • pnpm test - Run all tests
  • pnpm build - Build all workspace packages
See docs/INTERFACES.md for interface usage details.

Platform-Specific Notes

macOS (Intel)

  • Uses LanceDB 0.22.3 with darwin-x64 native bindings
  • Run pnpm install --force if native module errors occur

macOS (Apple Silicon)

  • Could use newer LanceDB versions if needed
  • Current version (0.22.3) works on both architectures

Linux

  • Should work with LanceDB 0.22.3 or newer
  • Native bindings auto-detected by platform

Windows

  • Not extensively tested but should work
  • May need WSL for better compatibility

Future Improvements

  • Consider using PM2 or similar for process management
  • Add webhook support for Telegram (production recommended)
  • Create systemd service file for Linux deployments
  • Add health check endpoint for monitoring
  • Document upgrade path for LanceDB when arm64 stability improves