A self-hosted intelligent-agent platform. Give a language model durable memory, real tools, multiple chat channels, and an operations plane — all in one binary you can run on your own hardware, audit end-to-end, and govern with human-in-the-loop approvals.
Live deployment reference: https://corlinman.cornna.xyz. 中文介绍章节见文末 "中文速览"。
What's new in 1.10.0 — audit rounds 4–9 + the first fully-green CI gate (ruff + mypy + import-linter), an unauthenticated
/v1/voiceWebSocket closed, Anthropic/Bedrock multi-round + parallel tool calling fixed, the gateway now runs as an unprivileged user on native installs, and a durable SQLite voice session store. SeeCHANGELOG.md. 1.10.0 更新概览见 更新日志。
curl -fsSL https://raw.githubusercontent.com/sweetcornna/corlinman/main/deploy/install.sh | bash升级到最新版本(保留所有数据):
bash deploy/install.sh --upgrade完整说明(preflight、health gate、China mirrors、QQ sidecar、native vs docker)见 下面的 Quickstart 与 Install paths 两节。
Most LLM infrastructure today is either a thin API wrapper (you send prompts, you read tokens, you integrate nothing) or a workflow toolbox (drag and drop nodes, marketplace plugins, zero opinion on how they compose).
corlinman takes a third stance: the agent is the product. The reasoning loop, the tools it calls, the memory it retains across turns, the channels it hears from, and the operator surface that governs it — all live in one coherent system that is opinionated about correctness, observability, and safety.
What you get out of the box:
- One agent loop, many providers. OpenAI tool-call semantics on top of Anthropic, OpenAI, Google, DeepSeek, Qwen, or GLM — with per-model aliases and hot-swap without restart.
- Tools are real plugins, not prompt templates. Sync, async, and long-lived "service" tools over JSON-RPC 2.0 stdio or gRPC, with optional Docker sandboxing for untrusted code and a human-approval gate for dangerous actions.
- Memory that survives conversations. Per-session message history in
SQLite; a SQLite FTS5 (BM25) knowledge base today, with HNSW dense
vectors + RRF fusion + cross-encoder rerank on the roadmap
(see
docs/PLAN_PORT_COMPLETION.md). - Channels are first-class agent I/O. Production adapters for QQ (OneBot v11) and Telegram, a scheduler for cron-driven tasks, an OpenAI-compatible HTTP/SSE endpoint for your own clients.
- An admin plane that treats operations seriously. A warm-amber
glass web console (Tidepool design system, day + night themes) for
plugin management, RAG inspection, live log streaming, approval
queues, config live-reload, and model routing — plus OTel traces,
Prometheus metrics, and a
corlinman doctorsmoke command.
If you want something you can hand your teammate a URL to, then audit on Sunday morning without reverse-engineering twenty repos — that's corlinman.
Use the one-line installer at the top of this README. Behind the scenes that single command does the rest:
- Preflight — checks disk (≥ 5 GB), RAM (≥ 1 GB), port
6005, docker version, required tools. Bails early with a clear✗ port 6005 held by PID …if anything's off. - Image —
docker pull ghcr.io/sweetcornna/corlinman:latest(multi-arch amd64/arm64, ~30 s). Falls back to a localdocker buildx buildif the registry is unreachable. - Boot —
docker compose up -dwith the bundled compose file. - Health gate — polls
/healthuntil 200 (≤ 60 s; override withCORLINMAN_HEALTH_TIMEOUT). - Done — prints the URL to open and the seed credentials:
✅ corlinman is live: http://localhost:6005/login
default login: admin / root ← change immediately at /account/security
data dir: /opt/corlinman/data
upgrade later: bash deploy/install.sh --upgrade
Sign in with admin / root, get redirected to Account & Security,
rotate the password — done. Want a real LLM? Walk /onboard from the UI
or run corlinman init (works headless on a server without a browser).
Want to start chatting immediately on the bundled mock provider? Hit
Skip in /onboard.
Security: first-boot credentials are
admin/rootand are explicitly intended for local development. The UI forces a password rotation on first login and stamps a banner until you change them;corlinman doctorwill keep warning until the default is gone.
Upgrade later: bash deploy/install.sh --upgrade — auto-detects
docker vs native, pulls the new image (or re-syncs the venv), restarts
the service, runs a fresh /health probe, never touches the data dir.
For multi-agent setups, deeper provider config, and the self-evolution curator, see:
- Profiles — isolated agent instances with their own persona/memory/skills
- Credentials — provider keys + EnvPage UI
- Evolution & Curator — how the agent grows with you
┌────────────────────────────────────┐
HTTP + SSE ──────▶ │ corlinman-gateway │ ◀─── Next.js admin UI
(clients, UI, │ Python · FastAPI · uvicorn · │ (static export,
channels) │ listens on :6005; routes /v1, │ served by nginx)
│ /admin, /health, /metrics, │
│ /plugin-cb, /v1/voice WS) │
└──┬──────────┬──────────┬──────────┘
│ │ │
in-process │ in-proc │ in-proc │ JSON-RPC / gRPC
▼ ▼ ▼
┌────────┐ ┌────┐ ┌───────────┐
│ agent │ │emb │ │ plugin │
│ Python │ │(py)│ │ runtimes │
│ loop │ └────┘ │ (py / node/
│ + LLM │ │ bash + │
│ SDKs │ │ docker) │
└───┬────┘ └───────────┘
▼
┌──────────────────────┐
│ upstream providers │
│ Anthropic · OpenAI · │
│ Google · DeepSeek · │
│ Qwen · GLM · custom │
└──────────────────────┘
Side-bus:
• corlinman-channels ── QQ / OneBot v11 · Telegram ──▶ internal ChatRequest
• corlinman-server.scheduler ── croniter ───────────▶ gateway AppState
• corlinman-embedding.vector ─ SQLite FTS5 (BM25) ──────────▶ /admin/rag
One language, one process. corlinman is a pure Python stack: FastAPI +
uvicorn gateway, grpc.aio for the agent sidecar, docker-py for plugin
sandboxes, watchdog-driven hot reload, SQLite FTS5 (BM25) for keyword
search, structlog, signal-correct shutdown. The agent reasoning loop, all
LLM provider SDKs (anthropic, openai, google-genai, etc.), embedding,
plugin runtime, channel adapters, CLI and gateway all live under
python/packages/ and share one venv.
Deep dive: docs/architecture.md.
Two ways in. Humans pick the one-liner; AI agents read deploy/AI_DEPLOY.md.
| Path | One-liner | Notes |
|---|---|---|
| Docker (recommended) | curl -fsSL https://raw.githubusercontent.com/sweetcornna/corlinman/main/deploy/install.sh | bash -s -- --mode docker |
Pulls ghcr.io/sweetcornna/corlinman:latest (multi-arch amd64+arm64), falls back to a local build if the registry is unreachable. Needs Docker Engine 24+ with the compose v2 plugin. |
| Native (uv + systemd) | curl -fsSL https://raw.githubusercontent.com/sweetcornna/corlinman/main/deploy/install.sh | bash -s -- --mode native |
Installs uv, clones the repo to /opt/corlinman/repo, syncs the workspace, registers a systemd unit. No container runtime needed. |
| In-place upgrade | bash deploy/install.sh --upgrade (any mode) |
Auto-detects docker vs native, pulls/rebuilds the new image or re-syncs the venv, restarts the service, re-probes /health. Never touches $DATA_DIR. Re-run with --version vX.Y.Z to pin a specific release tag. |
| 🇨🇳 China network | append --china to either fresh-install command above |
Switches PyPI → Tsinghua, Docker Hub → DaoCloud, github.com → gh-proxy.com, npm → npmmirror. Auto-enabled when pypi.org TTFB > 3s. See China-region deployment below. |
| 🤖 QQ bot sidecar | append --with-qq to the docker fresh-install command |
Layers docker-compose.qq.yml so NapCat (OneBot v11) boots alongside corlinman. The installer materialises .env from deploy/.env.template on first run and prompts you to fill in QQ_* / OPENAI_API_KEY before re-running. Docker mode only — NapCat is a container. |
Every fresh install starts with a preflight (disk ≥ 5 GB, RAM ≥ 1 GB,
port 6005 free, docker/curl/git on PATH, supported OS), then ends with a
health gate that polls /health until 200 before printing success
— so the URL you click is guaranteed to respond.
Both paths converge on http://localhost:6005/login. Sign in with
admin / root, rotate the password on the Account & Security page
you're redirected to, then optionally walk /onboard to wire a real LLM
provider (or skip and use the bundled mock provider). After that the
admin UI lives at http://localhost:6005/admin.
For headless servers without a browser, corlinman init is the
interactive CLI equivalent — walks the same admin password change +
provider key paste + model alias write that the web wizard does, then
restarts the gateway.
中国大陆部署的瓶颈是 PyPI / Docker Hub / raw.githubusercontent.com 的跨境
延迟。--china 自动切换到一组 2026-04 实测仍稳定的镜像:
| 用途 | 镜像 | 实测 TTFB (Tencent Cloud Tianjin) |
|---|---|---|
| PyPI | https://pypi.tuna.tsinghua.edu.cn/simple (清华 TUNA) |
0.24s |
| PyPI 备 | https://mirrors.aliyun.com/pypi/simple/ (阿里云) |
0.08s TTFB / 5s 全量 |
| GitHub clone | https://gh-proxy.com/https://github.com/... |
0.53s |
| GitHub raw | https://gh-proxy.com/https://raw.githubusercontent.com/... |
0.53s |
| Docker Hub | https://docker.m.daocloud.io (DaoCloud) |
0.12s |
| Docker Hub 备 | https://docker.1ms.run |
0.17s |
| npm | https://registry.npmmirror.com (前 taobao) |
0.91s |
| Debian apt | mirrors.tuna.tsinghua.edu.cn |
< 0.1s |
部分 BGP 网络(如腾讯云 Tianjin)反而能直连 github.com——--china
模式会先尝试代理 URL,失败时自动回落到直连,二选一不需要操作员判断。
已停用 / 移除(曾被推荐但 2026 已死或限速严重):ghproxy.com /
mirror.ghproxy.com / github.moeyy.xyz / dockerhub.icu /
docker.kubesphere.io / jsdelivr CDN 对 raw.github 的代理。
手动覆盖单项镜像(不需要重写整个 --china):
# 例:用阿里云 PyPI + 自己自建的 docker registry mirror
CN_PIP_INDEX=https://mirrors.aliyun.com/pypi/simple/ \
CN_DOCKER_MIRROR=https://your.mirror/ \
curl -fsSL https://gh-proxy.com/https://raw.githubusercontent.com/sweetcornna/corlinman/main/deploy/install.sh \
| bash -s -- --mode docker --china可覆盖的变量:CN_PIP_INDEX / CN_GH_PROXY(设为空字符串关闭 GitHub
代理) / CN_DOCKER_MIRROR。
已经装好之后想换镜像? PyPI 在 ~/.config/uv/uv.toml(或环境变量
UV_INDEX_URL),Docker 在 /etc/docker/daemon.json 的 registry-mirrors
数组。改完 systemctl restart corlinman / systemctl restart docker 即可。
真离线场景(VPS 没有外网):先在能联网的机器上 docker save 镜像 +
uv pip download 全部 wheel 到本地仓库,scp 过去再装。docker save ghcr.io/sweetcornna/corlinman:dev | ssh vps "docker load" 是最快的搬运姿势。
Paste deploy/AI_DEPLOY.md into Claude Code / Cursor /
Aider and tell it your VPS host + mode. The prompt covers 7 phases
(inventory → stop old → install → restore config → verify → upgrade →
cleanup) with explicit stop conditions. The install.sh that the AI
invokes already runs preflight + health gate on its own, so the AI's job
is mostly orchestration + verification, not babysitting the bash.
CORLINMAN_VERSION (git ref / branch, default main),
CORLINMAN_PREFIX (install root, default /opt/corlinman),
CORLINMAN_DATA_DIR (data dir, default $CORLINMAN_PREFIX/data),
CORLINMAN_PORT (gateway port, default 6005),
CORLINMAN_HEALTH_TIMEOUT (post-boot /health poll cap in seconds, default 60),
CORLINMAN_TAG (compose image tag, default latest — pin to vX.Y.Z for prod).
# Container path — pull the prebuilt image (or set CORLINMAN_TAG=local
# to force a local build via the bundled compose file).
git clone https://github.com/sweetcornna/corlinman && cd corlinman
docker compose -f docker/compose/docker-compose.yml pull
docker compose -f docker/compose/docker-compose.yml up -d
# Optional: enable Docker-backed plugin sandboxing on trusted hosts.
docker compose -f docker/compose/docker-compose.yml \
-f docker/compose/docker-compose.sandbox.yml up -d
# Visit http://127.0.0.1:6005/health then http://127.0.0.1:6005/login
# (default admin / root — change on first login). For LLM provider setup,
# either click through the in-UI /onboard wizard or run the CLI:
docker exec -it corlinman corlinman initRequirements: Python 3.12, uv, Node 20+, pnpm, protoc. The
dev-setup.sh script now checks all of these up front and prints a
per-platform install hint if anything's missing.
./scripts/dev-setup.sh # prereq check + deps + proto + hooks + doctor smoke
uv sync --all-packages --frozen
pnpm -C ui install && pnpm -C ui build
uv run corlinman init # interactive setup wizard (recommended)
uv run corlinman-gateway # gateway (FastAPI/uvicorn)
uv run corlinman-python-server # agent sidecar (grpc.aio)
uv run corlinman doctor # 9-check smoke (config, providers, port, etc.)Data lives in ~/.corlinman/ by default; override with --data-dir or
CORLINMAN_DATA_DIR. See docs/runbook.md for the
production deployment playbook (nginx reverse proxy + DNS-01 TLS via
acme.sh + systemd or docker-compose).
An agent in corlinman is a Python reasoning_loop wrapped around a
provider SDK. It takes a message history, emits tokens + tool calls,
consumes tool results, and iterates until the model signals stop. The
Python gateway owns the transport, multiplexes channels onto it, persists
sessions, and enforces governance (rate limits, approvals, timeouts).
Agents are defined as frontmatter-headed Markdown
(~/.corlinman/agents/<name>.md), hot-editable from the admin UI's
Monaco editor, and routed by the model field or per-channel binding.
Tools are not prompts-in-a-template. Every tool corlinman exposes is a real program that runs in its own sandbox, communicates over JSON-RPC 2.0 on stdio (or gRPC for long-lived "service" plugins), and publishes a JSON Schema that the agent sees directly via OpenAI tool_call semantics. Three plugin types:
| Type | Transport | Lifetime | Use case |
|---|---|---|---|
sync |
JSON-RPC stdio | Spawned per call | Calculator, HTTP fetch, shell one-shots |
async |
JSON-RPC stdio + /plugin-callback |
Spawn → return task_id → webhook back | Long jobs (image gen, LLM sub-calls) |
service |
gRPC over UDS | Long-lived supervised child | Stateful integrations (DB pools, Git) |
Plugins can be written in any language (Python, Node, Go, Rust, bash,
…) because the contract is stdio/gRPC + JSON, not a Python import hook.
Optional Docker sandboxing (docker-py-driven) enforces memory, CPU,
read-only root, network isolation, and capability drops. Untrusted
plugins can demand a human approval before every call.
Full authoring guide: docs/plugin-authoring.md.
Two layers of persistence, both auditable:
- Conversation memory. Per-session append-only message history in
SQLite (
sessions.sqlite), trimmed to a configurable message cap, keyed by channel binding or client-suppliedsession_key. - Knowledge memory (RAG). Retrieval today: SQLite FTS5 (BM25)
keyword search via
/admin/rag(stats, debug-query, FTS5 rebuild; seecorlinman_server/gateway/routes_admin_b/rag.py). Filter by tag, debug-query from the admin UI, rebuild from source via the CLI. HNSW dense vectors + Reciprocal Rank Fusion + cross-encoder rerank (bge-reranker-v2-m3) are on the roadmap — seedocs/PLAN_PORT_COMPLETION.md.
Neither is a black box: every chunk has a source_path, every message
has a timestamp, every retrieval scores through the UI.
A channel is any producer of ChatRequest. corlinman ships with:
- HTTP + SSE — OpenAI-compatible
/v1/chat/completions(stream and non-stream),/v1/embeddings,/v1/models. - QQ (OneBot v11) — forward WebSocket bridge with image/audio multimodal forwarding, keyword filtering, per-group / per-sender rate limits, NapCat "正在输入..." indicator + heartbeat watcher, file uploads via NapCat extension actions, durable inbox so a crash mid- reply leaves a breadcrumb.
- Telegram — long-poll bot adapter for private + group chats with a
real-time "is typing…" indicator, a mutable spinner placeholder
that edits in place as tool calls land (
🧠 思考中... → 🔧 调用工具: write_file → 📎 已发送文件 → ✍️ 生成回复中... → final reply), and thesend_attachmentbuiltin tool so the agent can reply with files (HTML / PDF / images / voice) instead of dumping raw text. - Discord / Slack / Feishu — text channels with the same routing + rate-limit + chat-service plumbing as QQ + Telegram (no status spinner yet — Telegram only).
- Corlinman (in-app
/chat) — Claude.ai-grade conversation window at/admin/chatdriven by the same hermes loop as every other channel. Implemented as a first-classChannelProtocol member (corlinman_channels.corlinman.CorlinmanChannel, id"corlinman"), gated byCORLINMAN_CHANNEL_ENABLED=1so existing telegram/qq deployments stay bit-for-bit identical until you flip it on. Owns per-sessionasyncio.Queueso a browserPOSTand an assistant token stream meet on the same thread. Exposes/api/channels/corlinman/{send,events,typing,edit,delete,react}. - Scheduler —
croniter-driven cron runner that fires an agent at a cron expression with a canned prompt template (for daily digests, alerting bots, etc.).
Each channel shares the same agent loop — switch models mid-flight with
a config reload, no channel restart. Every channel exposes a unified
ChannelBinding to the reasoning loop so per-turn resume, audit logs,
and the permission gate all key on the same session_key.
- Approvals. Configurable per tool:
allow/deny/prompt.promptparks the tool call, pushes a notification via the SSE broadcast, and waits for a human click in the admin UI (or a 5-min timeout → auto-deny). - Rate limits. Per-group and per-sender token buckets on channel adapters.
- Config live-reload.
POST /admin/configaccepts a TOML body, validates it, and atomically swaps in the new config without restart (restart-required fields are flagged in the response). - Observability. OTel OTLP export (traces + logs with
traceparentpropagation across gateway / agent / plugins), Prometheus/metricswith 7 metric families covering QPS, latency, tool-call rate, backoff, stream inflight, RAG stage timings, and plugin execution duration. A bundled Grafana dashboard lives inops/dashboards/corlinman.json. - Doctor.
corlinman doctor(alsomake doctor) runs 9 local checks — data dir writability, config TOML parseability, Python version, required packages, runtime config loader, provider registry, runtime wiring (P1+P2 boot sim),must_change_password(warns until you rotate the seedadmin/root), andport_bindable(catches "port 6005 already held" before the gateway tries to start).--jsonmode is CI-friendly: every check returnsok/warn, neverfail.
| Provider | Chat | Streaming | Tool calls | Embeddings | Status |
|---|---|---|---|---|---|
| Anthropic | ✅ | ✅ | ✅ | n/a | production |
| OpenAI | ✅ | ✅ | ✅ | ✅ | production |
| ✅ | ✅ | ✅ | ✅ | production | |
| DeepSeek | ✅ | ✅ | ✅ | n/a | production |
| Qwen | ✅ | ✅ | ✅ | n/a | production |
| GLM | ✅ | ✅ | ✅ | n/a | production |
| OpenAI-compatible (local vLLM, Ollama, SiliconFlow, any gateway speaking the spec) | ✅ | ✅ | ✅ | ✅ | works via providers.openai.base_url |
Migrating from newapi: existing
kind: newapientries are silently routed through theopenai_compatibleadapter (migration shim incorlinman_providers/specs.py); setkind: openai_compatiblein new configs. The dedicated/admin/newapisurface and onboard wizard integration have been removed.
Custom providers are a ~200-line Python class: subclass
corlinman_providers.base.CorlinmanProvider, register a model-name
prefix in registry.py, and you're in the agent loop. See
python/packages/corlinman-providers/.
The headline operator surface (added in 1.8.0). Renders a Claude.ai-grade conversation window driven by the existing hermes agent backend:
- Streaming with full loop visibility — token-by-token assistant text, collapsible Claude-style reasoning blocks, tool-call cards (running / ok / error with args + result panes), nested sub-agent cards, inline approval prompts (Deny / Approve once / Always-session).
- Composer — multiline auto-grow textarea, Enter to send / Shift+Enter
newline, drag-drop + paste file attachments (50 MB cap),
/slash commands (/clear,/reset,/model,/persona),@-mention picker for agents and skills, reply-with-quote chip above the textarea, model + persona pills. - Conversation sidebar — time-grouped list (Pinned / Today / Yesterday / Previous 7 / 30 / Older / Archived), fuzzy search, rename / pin / archive / delete-with-undo.
- Artifact panel — code blocks (≥ 25 lines or
html/svg/mermaid/markdown) surface in a resizable side panel with sandboxed iframe preview for HTML, inline SVG render, source view, version history, copy + download. - Message-level actions — copy, regenerate, edit-in-place for user messages (re-runs the turn after truncating history), branch fork into a new session pre-loaded with the slice up to that point, reply-quote, jump-to-message.
- Token + cost meter — header chip aggregates input/output tokens + estimated cost across the entire session.
- In-conversation search — Cmd / Ctrl + F overlay walks matches with Enter / Shift+Enter.
- Resume any session —
/admin/sessionsnow exposes a "Continue" button per row that routes to/admin/chat/{sessionKey}and auto-hydrates the full historical transcript viareplaySession()before the composer accepts input. Telegram / qq / scheduled persona runs are all resumable in the browser.
A Next.js 15 static-export bundle served by nginx (or directly from the
gateway at /). Tidepool design system — warm-amber glass with
day + night themes (sun/moon pill in the top nav), Instrument Serif
hero display over Geist sans/mono, ⌘K command palette, framer-motion
page transitions, live SSE dashboards.
Ten pages covering the full control plane:
- Dashboard (
/) — stat cards + live activity feed (SSE from/admin/logs/stream) + 7-check system health panel. - Plugins — list with status dots, detail with a schema-driven
"Test invoke" form that hits
POST /admin/plugins/:name/invoke. - Agents — list + Monaco editor for agent Markdown with frontmatter validation.
- RAG — stats cards, debug query box with score bars, confirm-gated rebuild trigger.
- Channels — per-adapter status lights, connection reset button, inline keyword editor, recent-message transcript.
- Scheduler — job table with live next-trigger countdown, manual trigger button, execution history modal.
- Approvals — pending tab (SSE live) + history tab.
- Models — provider cards with enabled toggle, inline alias CRUD.
- Config — Monaco TOML editor with section nav, JSON-schema hints, validation issues panel sliding in from the bottom.
- Logs — virtualized list of SSE events with level + subsystem filters and a pause-stream toggle.
Every page plays nice with the keyboard and passes WCAG AA contrast in both themes. Internationalisation (zh-CN / en) ships in 0.1.3.
corlinman boots from $CORLINMAN_DATA_DIR/config.toml (annotated
example: docs/config.example.toml). A
minimum production config looks like:
[server]
port = 6005
bind = "0.0.0.0"
data_dir = "/data"
[admin]
username = "admin"
# Generate via the onboard wizard, or:
# echo -n 'your-password' | argon2 "$(openssl rand -hex 8)" -id -m 15 -t 2 -p 1 -l 32 -e
password_hash = "$argon2id$v=19$m=32768,t=2,p=1$..."
# Providers are a free-form `BTreeMap<String, ProviderEntry>`. The table
# key is operator-chosen; `kind` selects the wire shape. Full reference:
# docs/providers.md (14 supported kinds + recipes).
[providers.openai]
kind = "openai"
api_key = { env = "OPENAI_API_KEY" }
base_url = "https://api.openai.com/v1"
enabled = true
[providers.anthropic]
kind = "anthropic"
api_key = { env = "ANTHROPIC_API_KEY" }
enabled = true
# Need a CN endpoint or a niche aggregator? Add an OpenAI-compat entry
# with a chosen name — no code changes required. See docs/providers.md §3.
# [providers.openrouter]
# kind = "openai_compatible"
# api_key = { env = "OPENROUTER_API_KEY" }
# base_url = "https://openrouter.ai/api/v1"
# enabled = true
[models]
default = "gpt-4o-mini"
[models.aliases]
smart = "claude-opus-4-7"
cheap = "gpt-4o-mini"
# Optional: QQ bot channel
# [channels.qq]
# enabled = true
# forward_ws_url = "ws://127.0.0.1:6700"
# group_allowlist = [123456789]Everything is hot-reloadable via POST /admin/config or the Config
page in the admin UI. Restart-required fields (bind address, port,
channel enablement) return requires_restart: true and are flagged in
the response.
The deployment reference setup, as used in the hosted demo:
Internet ──[HTTPS]──▶ Cloudflare (CDN + edge TLS + DDoS)
│
▼
┌─────────────────────────┐
│ nginx on the VM │
│ TLS: LE ECC via acme.sh │
│ DNS-01 (no port 80 │
│ exposed to ACME) │
│ │
│ location /admin|/v1... │── 127.0.0.1:6005 ──▶ corlinman container
│ location / │── /opt/corlinman/ui-static/ (static files)
└─────────────────────────┘
- TLS lives at both the Cloudflare edge (universal SSL) and the origin (Let's Encrypt ECC, auto-renewed by acme.sh via a Cloudflare API token — DNS-01 challenge, so you never need to punch port 80 out for HTTP-01).
- Static bundle is served directly by nginx from
/opt/corlinman/ui-static/(rsync target fromui/out/on the build host). The gateway never fights nginx for static bytes. - Upgrade path for the UI: rebuild locally, rsync, done — no
container restart. For the gateway: rebuild the image, transfer via
docker save | ssh docker load,docker compose up -d.
Full runbook with nginx config, acme.sh commands, healthcheck wiring,
and rollback procedure: docs/runbook.md.
# Clone + set up hooks, deps, and proto generation.
./scripts/dev-setup.sh
# Run the whole stack in dev mode with hot reload.
corlinman dev
# Full gate (what CI + pre-commit run).
uv run ruff check .
uv run mypy python/packages/
uv run pytest -m "not live_llm and not live_transport"
pnpm -C ui typecheck
pnpm -C ui lint
pnpm -C ui build
bash scripts/gen-proto.sh && git diff --exit-code python/packages/corlinman-grpc/src/corlinman_grpc/_generated/
# Quick local smoke (config + providers + port-bindable + must_change_password).
make doctor # → uv run corlinman doctorCoding expectations, branch + commit conventions, live-lane tests, and
boundary checks all live in CONTRIBUTING.md.
python/packages/* Python packages (gateway / agent / providers / embedding / vector / channels / mcp / canvas / plugins / cli / ...)
proto/ Protocol Buffers (gRPC IDL between agent sidecar and gateway)
ui/ Next.js admin console (static export)
qa/scenarios/ Executable YAML test scenarios
docker/ Compose profiles (sandbox Dockerfile for plugins)
docs/ Architecture / plugin authoring / runbook / roadmap
.git-hooks/ pre-commit (FAST_COMMIT=1 escape hatch)
scripts/ dev-setup.sh, gen-proto.sh
ops/ Grafana dashboard + observability compose
- Quickstart — 60-second boot + first-login + skip-to-mock walkthrough
- Profiles — isolated agent instances (persona + memory + skills)
- Credentials — provider keys + EnvPage UI
- Evolution & curator — self-evolution loop ported from hermes-agent
- Architecture — message flow, crate/package graph, gRPC bus
- Providers reference — 14 supported
kinds + recipes (Ollama / OpenRouter / SiliconFlow / Groq) - Plugin authoring — write your own sync / async / service plugin
- Skills, agents & the variable cascade — author
skills/,agents/,TVStxt/* - v0.1 → v0.2 migration — manifest v2, vector v6, new config sections, block protocol
- Runbook — production deployment + incident handling
- Milestones — per-milestone status
- Roadmap — sprint-level plan beyond 1.0
- Changelog — release-by-release
- Performance baseline — p50/p99 numbers
v1.10.0 (current) — audit rounds 4–9 + a CI-greening pass + a
voice-store feature. The whole Python CI gate (ruff + mypy +
import-linter) is green for the first time; an unauthenticated
/v1/voice WebSocket was closed (token moved off the query string);
Anthropic/Bedrock multi-round + parallel tool calling fixed; the
gateway now runs as an unprivileged corlinman user on native installs;
and a durable SQLite voice session store landed (the transcript→chat
bridge is still deferred). No data migration required. Released
2026-05-29, tagged v1.10.0. Full notes (incl. upgrade notes) in
CHANGELOG.md.
v1.1.0 — channel parity (QQ official bot + WeChat 公众号
land alongside existing channels), Claude-Code-style task UX (live
todo-list view + summary-based context compaction + mid-turn user
message injection), and admin UI simplification (sidebar trimmed to
10 operator pages with a Developer Settings toggle for the rest).
Released 2026-05-24, tagged v1.1.0. Full notes in
CHANGELOG.md.
v1.0.0 — full Python port, multi-channel chat with status streaming
- file replies, multi-gateway HA via shared Postgres journal, hook
event bus, context-aware permissions, on-demand skill reload,
production-hardened security (SSRF + sandbox + symlink escape) and
reliability (reactive 401 refresh across every provider). Released
2026-05-24, tagged
v1.0.0. Post-1.0 work is tracked indocs/milestones.mdanddocs/roadmap.md.
Shipped in 1.1 (on top of 1.0):
- ✅ QQ 官方机器人 channel + 微信公众号 channel
- ✅ Discord / Slack / Feishu mutable-spinner parity (the four
edit-capable channels now share Telegram's
_status.pycore) - ✅ Live task-list rendering (
📋 任务清单 ☑/▣/☐) from thetodo_writebuiltin - ✅ Claude-Code-style summary-based context compaction (≥ 95 % budget triggers a sub-call summary; failure degrades to elision)
- ✅ Mid-turn user-message injection
(
ReasoningLoop.inject_user_message) — a follow-up message to a busy session merges into the live turn instead of queueing - ✅ Sessions admin page wired to the journal + Delete / Clear-all controls
- ✅ Admin sidebar trimmed to 10 operator items with a Dev Settings toggle
Shipped in 1.0 (vs the 0.6.x line):
- ✅ Telegram + Discord + Slack + Feishu channels
- ✅ Mutable-spinner status streaming + typing indicator +
send_attachmenttool - ✅ Multi-gateway HA via Postgres journal (race-safe
ON CONFLICT) - ✅ Hook event bus (
UserPromptSubmit/PreToolDispatch/ToolCalled/TurnComplete/TurnErrored) - ✅ Context-aware permission gate (per tool × model × session × user_id)
- ✅ Dynamic skill reload (
*.mdper turn, no restart) - ✅ Per-turn journal resume after gateway / agent restart
- ✅ SSRF + run_shell rlimit + symlink escape hardening
Near-term (P1):
- Plugin SDK packages on npm / PyPI / crates.io
- MCP (Model Context Protocol) compatibility layer — expose corlinman plugins as MCP tools to Claude Desktop / Cursor
- OIDC login (replace basic auth)
- Discord / Slack / Feishu status streaming parity (today: Telegram-only)
- Redis journal backend (Postgres landed in 1.0)
- 24 h soak test + fuzz corpus in CI
Longer-term (P2):
- Multi-tenant (data_dir per tenant + RBAC)
- External vector DB backends (Qdrant / Milvus)
- Canvas renderer + voice I/O
- VS Code / JetBrains / Tauri desktop clients
Contributions welcome — see CONTRIBUTING.md for the
architecture invariants (e.g. no Anthropic-specific types leaking
into the provider trait), test-lane conventions, and the pre-commit
hooks you'll install. Issues on GitHub for bugs / features.
MIT. See LICENSE.
1.10.0 新特性:审计第 4–9 轮 + 一次 CI 转绿 + 语音存储功能。整条 Python CI 门禁(ruff + mypy + import-linter)首次全绿;关闭了未鉴权的
/v1/voiceWebSocket(令牌移出查询串);修复 Anthropic/Bedrock 多轮 + 并行工具调用;原生安装下 gateway 现以非特权corlinman用户运行;新增 持久化的 SQLite 语音会话存储(转写→聊天桥接仍延后)。无需数据迁移。 完整说明(含升级须知)见 更新日志。
corlinman 是一个可自托管的智能体平台。 不只是 LLM 的 API 代理,也不是拖拽工作流的工具箱——它是一套有主张的运行时:让语言模型拥有持久记忆、真实工具、多通道接入、可审计的运维面板,全部跑在你自己的机器上。
核心能力:
- 一个 agent 循环,多家 provider:在 Anthropic / OpenAI / Google / DeepSeek / Qwen / GLM 上跑 OpenAI 标准 tool_call 语义;配置热重载、按模型别名路由。
- 真工具,不是 prompt 模板:同步 / 异步 / 常驻三种插件类型,统一 JSON-RPC 2.0 stdio 或 gRPC 通信,可选 Docker 沙箱 + 人工审批闸。
- 跨会话的记忆:SQLite 会话历史 + SQLite FTS5(BM25)关键词检索经
/admin/rag暴露;HNSW 稠密向量 + RRF 融合 + cross-encoder rerank 在路线图上(见docs/PLAN_PORT_COMPLETION.md)。 - 通道作为一等公民:QQ (OneBot v11) / Telegram / Discord / Slack / Feishu / 定时任务 / OpenAI 兼容 HTTP/SSE 并行接入,共享同一 agent 循环。Telegram 端有实时「正在输入...」指示器 + 工具调用流式状态条 +
send_attachment文件发送工具;QQ 通过 NapCat 扩展拿到同样的输入状态 + 文件上传能力。 - 严肃的运维面板:Tidepool 暖橙玻璃风格 Next.js 管理界面(日 / 夜双主题,插件 / 知识库 / 日志 / 审批 / 配置 / 调度器 / 模型路由),OTel + Prometheus 埋点,9 项
doctor体检。
在线 demo:https://corlinman.cornna.xyz
架构:纯 Python 单语言栈 —— FastAPI/uvicorn gateway + grpc.aio agent sidecar + 插件 runtime + SQLite FTS5(BM25)检索 + CLI + provider SDK + reasoning loop + embedding 全部在 python/packages/ 下,共享一个 venv;W3C traceparent 贯穿全链路。
快速开始(60 秒):
# 一行装好,全自动 preflight → 拉镜像 → 启动 → 等 /health 200 → 打印 URL
curl -fsSL https://raw.githubusercontent.com/sweetcornna/corlinman/main/deploy/install.sh | bash
# 国内网络加 --china(清华 PyPI / gh-proxy / DaoCloud),TTFB > 3 秒会自动开
curl -fsSL https://raw.githubusercontent.com/sweetcornna/corlinman/main/deploy/install.sh | bash -s -- --china
# 想顺便起 NapCat QQ 机器人就再加 --with-qq(docker 模式)
curl -fsSL https://raw.githubusercontent.com/sweetcornna/corlinman/main/deploy/install.sh | bash -s -- --china --with-qq装完打开 http://<服务器>:6005/login,用 admin / root 登录后会被
强制跳到 账户安全 改密码。想给一个真 LLM?走 UI 里的 /onboard 或
在服务器上跑 corlinman init(headless 交互式向导,免浏览器)。
升级:bash deploy/install.sh --upgrade,自动识别 docker 还是
native 模式,拉新镜像/重 sync venv,重启服务,重跑 /health。不会动数据目录。
自 1.10.0 起,native 模式下 gateway 以非特权 corlinman 用户运行(此前为
root),升级时会自动重生并重载 systemd unit——无需人工操作;若你定制过该
unit,请把覆盖项放进 systemd drop-in 以免被覆盖。
数据默认落在 ~/.corlinman/,通过 CORLINMAN_DATA_DIR 覆盖。完整生产部署(nginx + acme.sh DNS-01 + Cloudflare)见 docs/runbook.md,架构细节见 docs/architecture.md。

