This commit is contained in:
claude code agent 227
2026-06-24 03:47:24 +03:00
parent 5c0f37f1b1
commit 0bff612c70
4 changed files with 0 additions and 956 deletions

310
AGENTS.md
View File

@@ -1,310 +0,0 @@
# AGENTS.md
This file guides AI agents (Claude Code, opencode, …) working in this
repository. It has two layers: **how to run a task end-to-end** (the
sections below), and **how the codebase is built** (the technical sections
further down, formerly in `CLAUDE.md`).
## Жизненный цикл задачи
### 1. Старт: синхронизация с develop
Перед началом **любой** работы обнови локальный `develop` и ветвись от него:
```bash
git checkout develop
git fetch gitea
git pull --ff-only gitea develop
git checkout -b <короткое-имя-фичи>
```
Никогда не пилит фичу прямо в `develop` и не ветвись от устаревшего
`develop` — иначе PR будет содержать лишние коммиты или конфликтовать.
### 2. Реализация
Веди задачу по workflow из системного промпта (Phase 1 анализ → Phase 3
реализация → Phase 4 review → Phase 5 верификация → Phase 6 отчёт). Большие
изменения делегируй в general subagent, ревьюй через review subagent.
**Worktree'ы создавай только внутри папки `.claude`** (например,
`.claude/worktrees/<имя>`). Создавать git worktree где-либо ещё — в корне
репозитория, в соседних каталогах или во временных папках — запрещено.
### 3. Коммит — ТОЛЬКО в Gitea и ТОЛЬКО от `claude_code`
Это правило без исключений:
- **Куда:** единственный remote для коммитов/пушей — **`gitea`**
(`gitea.vvzvlad.xyz`). **Никогда** не пушь в `origin` (GitHub-зеркало) и
тем более в `upstream` (оригинальный Docmost). GitHub-зеркало обновляется
CI-процессом владельца, не агентом.
- **От кого:** коммить **только** от агентского identity. Любой коммит,
у которого author или committer — `vvzvlad`, считается ошибкой и должен
быть переписан.
- **name:** `claude_code`
- **email:** `claude_code@vvzvlad.xyz`
Используй `--reset-author` при amend, иначе git оставит оригинального
автора (по умолчанию config на этой машине — `vvzvlad`, поэтому проверяй
после каждого коммита):
```bash
GIT_AUTHOR_NAME="claude_code" \
GIT_AUTHOR_EMAIL="claude_code@vvzvlad.xyz" \
GIT_COMMITTER_NAME="claude_code" \
GIT_COMMITTER_EMAIL="claude_code@vvzvlad.xyz" \
git commit --amend --no-edit --reset-author
```
Для обычного нового коммита достаточно один раз выставить локальный
config ветки и коммитить штатно:
```bash
git config user.name "claude_code"
git config user.email "claude_code@vvzvlad.xyz"
```
Проверка перед push:
```bash
git log -1 --format='Author: %an <%ae>%nCommitter: %cn <%ce>'
# обе строки должны показать claude_code <claude_code@vvzvlad.xyz>
```
### 4. Push и PR в develop
PR всегда в `develop`. Пароль `claude_code` лежит в macOS keychain как
**generic password** под service `gitea-claude-code` (не дублируй его как
internet-password для `gitea.vvzvlad.xyz` — это создаст конфликт с учёткой
владельца в git credential helper):
```bash
AGENT_PASS=$(security find-generic-password -s gitea-claude-code -w)
```
Push — через временную подстановку кредов в remote URL, после чего URL
обязательно возвращается в чистый вид (пароль не должен оседать в git
config / reflog):
```bash
ORIG_URL=$(git remote get-url gitea)
SAFE_PASS=$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1]))" "$AGENT_PASS")
git remote set-url gitea "https://claude_code:${SAFE_PASS}@gitea.vvzvlad.xyz/vvzvlad/gitmost.git"
git push -u gitea <branch>
git remote set-url gitea "$ORIG_URL"
unset AGENT_PASS SAFE_PASS
```
PR создаётся через Gitea REST API (Basic Auth от `claude_code`):
```bash
curl -s -X POST \
-u "claude_code:$(security find-generic-password -s gitea-claude-code -w)" \
-H "Content-Type: application/json" \
-d @pr_body.json \
"https://gitea.vvzvlad.xyz/api/v1/repos/vvzvlad/gitmost/pulls"
```
`base: develop`, `head: <branch>`. В теле PR — что сделано, что вне scope,
результаты верификации (tsc/lint/tests).
> Если push падает с `User permission denied for writing` — значит у
> `claude_code` нет коллабораторских прав на репо. Попроси владельца
> добавить (один раз, через Gitea UI или
> `PUT /api/v1/repos/vvzvlad/gitmost/collaborators/claude_code` с
> `{"permission":"write"}` от его учётки).
### 5. Мерж и cleanup
- **Мерж PR в develop делает пользователь** (не агент). Агент не жмёт
кнопку merge.
- **После реализации задачи удали её план из `docs/backlog/<task>.md`** —
это часть закрытия задачи, не пользовательская работа. Файлы в
`docs/backlog/` — это очередь работы, выполненное из неё вычищается.
Сделай это в отдельном коммите от того же `claude_code` в той же ветке
(или попроси пользователя удалить, если PR уже открыт и ты не хочешь
его перепушивать).
- Не закоммичен ли мусор в рабочем дереве? Проверь `git status` перед
финальным отчётом.
## Релизный цикл: набор на новую версию
Когда в `develop` накопилось достаточно изменений для релиза, запускается
**финальное ревью тремя скиллами-оркестраторами** перед мержем/тегом:
1. **test-orchestrator** (skill `code-review-orchestrator` с фокусом на
тестовом покрытии) — проверяет, что новый код покрыт тестами и нет
регрессий в существующих.
2. **review-orchestrator** (skill `code-review-orchestrator`) —
мульти-аспектный код-ревью: безопасность, стабильность, соответствие
конвенциям, регрессии, перегруженность.
3. **red-team-orchestrator** (red-team скилл) — адверсариальный анализ
атакующих сценариев на затронутые компоненты.
Порядок: оркестраторы возвращают списки находок → агент правит всё, что
они нашли (через subagent или сам, по правилам делегирования) → повторно
прогоняет ревью затронутых мест → режет тег по процедуре «Cutting a
release» ниже.
## Шпаргалка по учёткам и endpoint'ам
| Что | Значение |
| --- | --- |
| Единственный remote для коммитов | `gitea``https://vvzvlad@gitea.vvzvlad.xyz/vvzvlad/gitmost.git` |
| Агентский user (Gitea/git) | `claude_code` |
| Агентский email | `claude_code@vvzvlad.xyz` |
| Пароль в keychain | `security find-generic-password -s gitea-claude-code -w` |
| PR API | `https://gitea.vvzvlad.xyz/api/v1/repos/vvzvlad/gitmost/pulls` (тут `gitmost` — реальный slug репо на сервере) |
| Базовая ветка | `develop` |
| `origin` | GitHub-зеркало `vvzvlad/gitmost`**не пушить**, обновляется CI владельца |
| `upstream` | Оригинальный Docmost — **не пушить никогда** |
---
# Архитектура и кодовая база
## What this is
**Gitmost** is a community fork of [Docmost](https://github.com/docmost/docmost) — an open-source collaborative wiki / documentation app. The fork's defining constraint: **100% open, AGPL-only, with no Enterprise-Edition (EE) code**. The upstream `apps/server/src/ee`, `apps/client/src/ee` and `packages/ee` directories were deleted; there is no license gating or feature-flag wall. Features that upstream hides behind the enterprise license (comment resolution, the embedded `/mcp` server, the AI agent chat) are **re-implemented from scratch** on the community codebase.
**Naming gotcha:** only the *product* is rebranded. Internal identifiers are still `docmost` everywhere — npm package names (`docmost`, `@docmost/mcp`, `@docmost/editor-ext`), the default DB name, env-var prefixes (`MCP_DOCMOST_*`), and the TS path aliases (`@docmost/db/*`, `@docmost/transactional/*`). Do not "fix" these to `gitmost`; they are load-bearing for Docmost data/image compatibility (the DB schema is a strict superset of Docmost's, so an existing instance migrates by swapping images).
## Monorepo layout
pnpm workspace (`pnpm@10.4.0`) orchestrated by **Nx**. Four workspace packages:
| Path | Name | Stack | Role |
| --- | --- | --- | --- |
| `apps/server` | `server` | NestJS 11 + Fastify, Kysely (Postgres), Redis | Backend API, collaboration, AI |
| `apps/client` | `client` | React 18 + Vite + Mantine 8 + TanStack Query + Jotai | SPA frontend |
| `packages/editor-ext` | `@docmost/editor-ext` | Tiptap/ProseMirror | Shared Tiptap node/mark extensions, imported by both the client and the server |
| `packages/mcp` | `@docmost/mcp` | MCP SDK, Tiptap, Yjs | Standalone MCP server, also bundled into the server at `/mcp`. Does **not** import `editor-ext` — it keeps its own vendored mirror of the schema in `packages/mcp/src/lib/` |
`build` targets are Nx-cached and dependency-ordered (`dependsOn: ["^build"]`), so `editor-ext` builds before the apps. `nx.json` sets `affected.defaultBase: main`.
## Commands
Run from the repo root unless noted. The dev workflow needs **Postgres (with the `pgvector` extension) and Redis** reachable per `.env` (copy `.env.example``.env`).
```bash
pnpm install # install all workspaces (uses pnpm patches; see package.json `pnpm.patchedDependencies`)
pnpm dev # client (Vite) + server (Nest watch) concurrently — primary dev loop
pnpm client:dev # frontend only (Vite proxies /api to APP_URL)
pnpm server:dev # backend only (nest start --watch)
pnpm build # nx run-many -t build (all packages)
pnpm collab:dev # run the collaboration server process standalone (see "Two server processes")
```
**Lint** (per package — there is no root lint script):
```bash
pnpm --filter server lint # eslint --fix on server .ts
pnpm --filter client lint # eslint on client
```
**Tests** (per package — no root test script):
```bash
pnpm --filter server test # Jest, matches *.spec.ts under src
pnpm --filter server test -- ai-chat.service # single file by name pattern
pnpm --filter server test -- -t "resolves a comment" # single test by name
pnpm --filter client test # Vitest (vitest run)
pnpm --filter client test -- message-list # single Vitest file by name
pnpm --filter @docmost/mcp test # node --test (unit + mock)
pnpm --filter @docmost/mcp test:e2e # MCP end-to-end against a live instance
```
**Database migrations** (Kysely, run from `apps/server`; they auto-run on server startup too):
```bash
pnpm --filter server migration:create --name=my_change # new empty migration
pnpm --filter server migration:latest # apply all pending
pnpm --filter server migration:down # revert last
pnpm --filter server migration:codegen # regenerate src/database/types/db.d.ts from the live DB
```
Migration files live in `apps/server/src/database/migrations/` and are named `YYYYMMDDThhmmss-description.ts`. Fork-specific migrations only **add** tables (`page_embeddings`, `ai_chats`, `ai_chat_messages`, `ai_provider_credentials`, `ai_mcp_servers`, `page_template_references`) and columns (e.g. `pages.is_template`, a `NOT NULL DEFAULT false` boolean) — never drop/rewrite Docmost data.
**Migration ordering — always check when merging branches/features.** Kysely runs migrations in **alphabetical (= timestamp) order** and refuses to start if a *new* migration sorts **before** one already applied to the DB (`corrupted migrations: ... must always have a name that comes alphabetically after the last executed migration`). When you merge a branch or land a feature, verify your migration's timestamp still sorts **after every migration that may already be applied on the target** (`/bin/ls -1 apps/server/src/database/migrations | sort | tail`). Branches developed in parallel routinely break this: a feature branch adds `…T130000-…`, `main` meanwhile ships and deploys `…T150000-…`, and after the merge the older-timestamped file is rejected at boot. **Fix = rename your migration to a timestamp after the latest one already in the target** (content unchanged — the filename is the ordering key), then rebuild so the compiled `dist/database/migrations/` picks up the new name.
## Architecture — the big picture
### Two server processes
`apps/server` builds one codebase but runs as **two distinct entrypoints**, both required in production:
- **API server** — `dist/main` (`apps/server/src/main.ts`), the Fastify HTTP app (`AppModule`).
- **Collaboration server** — `dist/collaboration/server/collab-main` (`pnpm collab`), a Hocuspocus/Yjs WebSocket server (`apps/server/src/collaboration/`) handling real-time document editing, persistence, and page-history snapshots. It listens on `COLLAB_PORT` (default `3001`), separate from the API server's `PORT` (default `3000`), and shares state with the API server through Redis.
The API server is a Fastify app with a global `/api` prefix (`main.ts` excludes `robots.txt`, public share pages, and `mcp` from the prefix). A `preHandler` hook enforces that a resolved `workspaceId` exists for most `/api` routes (multi-tenant by hostname/subdomain via `DomainMiddleware`). Auth is JWT (cookie + bearer); authorization is **CASL** (`core/casl`) — every data access is scoped to the user's abilities.
### Module structure (server)
`AppModule` wires integration modules (`integrations/*`: storage [local/S3/Azure], mail, queue [BullMQ on Redis], security, telemetry, throttle, `mcp`, `ai`) plus `CoreModule`, `DatabaseModule`, and `CollaborationModule`. `CoreModule` (`core/*`) holds the domain modules: `page`, `space`, `comment`, `workspace`, `user`, `auth`, `group`, `attachment`, `search`, `share`, `ai-chat`, etc. Each domain module follows NestJS controller → service → repo layering; DB repos live under `database/repos` and are injected app-wide from the global `DatabaseModule`.
**EE removal artifact:** `app.module.ts` still contains a `try/require('./ee/ee.module')` stub. That path no longer exists, so the require fails and is swallowed (it only hard-exits when `CLOUD === 'true'`). Treat EE as gone — do not add code that depends on it.
### Persistence
- **Postgres via Kysely** (`nestjs-kysely`), typed by the generated `src/database/types/db.d.ts`. Use the camelCase Kysely query builder, not an ORM. After schema changes, write a migration *and* regenerate the DB types.
- **pgvector is mandatory** — the RAG feature stores embeddings in `page_embeddings`. `docker-compose.yml` uses `pgvector/pgvector:pg18` for this reason; the stock `postgres` image will fail the `CREATE EXTENSION vector` migration.
- **Redis** backs caching, the BullMQ queues, the WebSocket Socket.IO adapter, and collaboration sync.
### The two AI subsystems (the main fork additions)
1. **Embedded MCP server** (`integrations/mcp/` + `packages/mcp`). The standalone `@docmost/mcp` server (38 agent-native tools: per-block patch/insert/delete by id, scripted `(doc)=>doc` transforms with dry-run diff, table editing, version diff/restore, comments, images, shares) is bundled and served over HTTP at `/mcp`. It writes through Docmost's real-time-collaboration layer so concurrent human edits aren't clobbered. Each request authenticates **per-user** via the `Authorization` header — either HTTP Basic (`base64(email:password)`, the user's own Docmost login, validated through `AuthService`) or a Bearer access JWT (the user's `authToken`) — and the session acts under that user's permissions. `MCP_DOCMOST_EMAIL` / `MCP_DOCMOST_PASSWORD` are an **optional service-account fallback**, used only when a request carries neither Basic nor Bearer credentials (back-compat for CI/scripts). An admin enables MCP with a workspace toggle (Workspace settings → AI). Optionally protected by a shared `MCP_TOKEN`: when set, every `/mcp` request must carry a matching `X-MCP-Token` header (its own header, separate from `Authorization`, which now carries the per-user Basic/Bearer credentials). Note: this changed from the older `Authorization: Bearer <MCP_TOKEN>` scheme — see `.env.example` and the CHANGELOG Breaking Changes entry.
2. **AI agent chat** (`core/ai-chat/` server + `apps/client/src/features/ai-chat/` client). A built-in agent over the wiki using the Vercel **AI SDK** (`ai`, `@ai-sdk/*`) against any OpenAI-compatible provider configured per workspace (`integrations/ai/` — credentials encrypted at rest via `integrations/crypto`, stored in `ai_provider_credentials`). Key pieces:
- `core/ai-chat/tools/` — the agent's ~40 read+write tools. Every tool runs under the **calling user's** CASL permissions via a per-user loopback access token (`docmost-client.loader.ts`), so the agent can never exceed what the user could do. Only **reversible** operations are exposed (page history + trash; no permanent delete). Agent edits get an "AI agent" provenance badge in page history (`20260616T130000-agent-provenance` migration).
- `core/ai-chat/embedding/` — RAG indexer + a BullMQ consumer on `AI_QUEUE` that embeds pages into `page_embeddings` (vector search), complementing Postgres full-text search. Pages are (re)indexed on edit; `AI_EMBEDDING_TIMEOUT_MS` bounds a hung embeddings endpoint.
- `core/ai-chat/external-mcp/` — admins can attach external MCP servers (e.g. Tavily) to give the agent web access. **`ssrf-guard.ts` validates outbound MCP URLs against SSRF** — keep that guard in the path when touching external-MCP connection logic.
### Client structure
Vite SPA. Code is organized by feature under `apps/client/src/features/*` (mirrors the server domains: `page`, `space`, `comment`, `ai-chat`, `editor`, …). Conventions:
- **TanStack Query** for server state (one `queries/` file per feature), **Jotai** atoms for local/shared UI state, **Mantine 8** + CSS modules (`*.module.css`) + `postcss-preset-mantine` for UI.
- The editor is Tiptap; shared node/mark extensions live in `packages/editor-ext` and are imported by **both the client and the server** (collaboration, import/export) — editor schema changes often need to be made in `editor-ext`, not just the client. Note `packages/mcp` does *not* depend on `editor-ext`; it carries its own mirrored copy of the schema, so keep the two in sync manually when the document schema changes.
- API access goes through `apps/client/src/lib/api-client.ts` (axios). The `@` alias maps to `apps/client/src`.
- Runtime config is injected at build time by `vite.config.ts` via `define` (`APP_URL`, `COLLAB_URL`, `APP_VERSION`, …) — these come from the root `.env`, not from `import.meta.env`.
## Conventions
- **Code comments must be in English.**
- **Errors must never be swallowed or shown as generic messages.** Every caught error MUST (1) be logged in full to the console/logger — error name, message, stack, `cause`, and (for HTTP/provider failures) the status code and response body — and (2) be surfaced to the user with a *specific, human-readable explanation of what actually went wrong*, never a bare generic string like "Something went wrong" / "Could not start recording" / "Transcription failed". Include the real reason (the underlying error/provider message) in the user-facing text. On the server, wrap third-party/provider failures with `describeProviderError` (or equivalent) and rethrow as a meaningful HTTP status + message — never let them collapse into an opaque 500. On the client, `console.error(<context>, err)` the raw error AND show the extracted reason (e.g. `err.response?.data?.message`, or the error `name: message`) in the notification.
- The version string shown in the UI comes from `APP_VERSION` (CI/Docker) or `git describe --tags --always` (local), resolved in `vite.config.ts` — not from `package.json`.
- Server TS config is permissive (`noImplicitAny: false`, `strictNullChecks: false`, `no-explicit-any` lint disabled). Follow the existing relaxed style rather than tightening types broadly.
- Dependency versions are heavily pinned via `pnpm.overrides` and `pnpm.patchedDependencies` (`scimmy`, `yjs`) in the root `package.json`. Don't bump pinned/patched deps casually; the patches and overrides exist for compatibility/security reasons.
## CI / release
- `.github/workflows/develop.yml` — on push to `develop`, builds and pushes `ghcr.io/vvzvlad/gitmost:develop`.
- `.github/workflows/release.yml` — on `v*` tags (or manual dispatch), builds multi-arch (amd64 + arm64) images, pushes a manifest list to GHCR (`latest` + semver tags), and creates a draft GitHub Release with image tarballs. Uses the built-in `GITHUB_TOKEN` (not Docker Hub).
- The `Dockerfile` is a multi-stage pnpm build; `APP_VERSION` is passed as a build arg because `.git` isn't in the build context.
### Cutting a release
The git tag is the source of truth for the displayed version (UI reads `git describe --tags`); the `package.json` bump is metadata only. Steps:
1. Make sure `main` is clean and pushed (`git status`, `git push`).
2. Pick `vX.Y.Z` (SemVer): **minor** bump for a batch of features, **patch** for fixes only. Review what landed with `git log <last-tag>..HEAD --no-merges`.
3. Bump `"version"` to `X.Y.Z` in the **root** `package.json`, `apps/client/package.json`, and `apps/server/package.json` (keep all three in sync). Leave `packages/mcp` alone — it is versioned independently. Commit with the bare version as the subject, e.g. `0.91.0` (matches past bump commits).
4. Update `CHANGELOG.md` (Keep a Changelog format): add a `## [X.Y.Z] - YYYY-MM-DD` section summarising `git log vPREV..HEAD --no-merges` grouped by type (Breaking / Added / Changed / Fixed / Removed), and add the `compare/vPREV...vX.Y.Z` link at the bottom. Fold the bump + changelog into the release commit.
5. Tag the release commit with a **lightweight** tag (existing release tags are lightweight): `git tag vX.Y.Z`.
6. Push commit and tag: `git push origin main && git push origin vX.Y.Z`. Pushing the `v*` tag triggers `release.yml` (multi-arch GHCR images + a draft GitHub Release).
7. **Back-merge the release into `develop`** so develop builds report the new version: `git checkout develop && git merge --no-ff main && git push origin develop` (push to Gitea as well if that is the canonical remote).
#### Why develop keeps showing the *previous* version (and why step 7 matters)
The UI version is `git describe --tags --always` (see `vite.config.ts`), which walks **backwards from the current commit** and picks the **nearest tag reachable in that commit's ancestry**, then appends `-<commits-since-tag>-g<short-hash>`.
The release tag (`vX.Y.Z`) is created on **`main`'s release merge commit**, and that commit is **not** in `develop`'s history. So until the release is back-merged, `git describe` on `develop` cannot see the new tag and falls back to the *previous* reachable tag. Result: every develop build — and the `ghcr.io/vvzvlad/gitmost:develop` image — keeps reporting e.g. `v0.91.0-NNN-g<hash>` even though `main` is already tagged `v0.93.0`. This is the classic git-flow pitfall: the version on `develop` does **not** advance just because a release was tagged on `main`.
Back-merging `main → develop` (step 7) pulls the tagged release commit into `develop`'s ancestry, after which develop builds correctly show `vX.Y.Z-NNN-g<hash>`. If `develop` already drifted (release tagged but never back-merged), just run step 7 now — no new tag is needed.
##### The tag must also exist on the remote that CI builds from (multi-remote gotcha)
`git describe` names a tag **ref**, not just a commit — so the back-merge is *necessary but not sufficient*. The develop image is built by GitHub Actions (`develop.yml`, `actions/checkout` with `fetch-depth: 0`, then `git describe --tags --always`), so the version it prints depends on which tags exist **on the `github` remote**, not on your local clone or on `gitea`.
This repo has two writable remotes — `gitea` (canonical, where commits land) and `github` (where the `:develop` and release images are built) — plus `upstream` (docmost, never push). **`git push <branch>` does NOT push tags**; tags must be pushed explicitly and *to each remote separately*. A release tag that only lives on `gitea` is invisible to the GitHub Actions build: even with the tagged commit fully in `develop`'s history (step 7 done), `git describe` on the GitHub runner falls back to the previous tag it *does* have, so the develop image keeps showing e.g. `v0.91.0-NNN` while `git describe` locally already says `v0.93.0-NN`.
Fix / checklist when develop still shows the old version after a back-merge:
1. Confirm the tag is missing on github: `git ls-remote --tags github` (compare with `gitea`).
2. Push it there: `git push github vX.Y.Z` (and `git push gitea vX.Y.Z` if it is missing on gitea too). Note: pushing a `v*` tag to `github` also triggers `release.yml` (multi-arch GHCR images + draft Release) — expected, but be aware.
3. Re-run the develop build (`gh workflow run Develop`, or push any commit to `develop`) so `git describe` re-resolves with the tag now present.
(The `git push origin ...` in steps 6–7 above is shorthand — there is no `origin` remote here; substitute `gitea` **and** `github` as appropriate, and always push release tags to both.)
## Planning docs
`docs/*.md` hold design plans for in-progress / planned features (mobile app, offline sync, RAG improvements, voice dictation). Arbitrary HTML embed has **shipped** — it renders inside a sandboxed iframe and, when the `htmlEmbed` workspace toggle is on, is insertable by any member (no longer admin-only); turning the toggle off hides/stops serving existing embeds on public share pages. `docs/backlog/*.md` track known issues / follow-ups (e.g. AI-chat review follow-ups). Consult the relevant plan before working on one of those areas.

View File

@@ -1,209 +0,0 @@
# Changelog
All notable changes to this project are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
> Releases prior to `0.91.0` predate this changelog; see the
> [git tags](https://github.com/vvzvlad/gitmost/tags) for earlier history.
## [Unreleased]
### Changed
- **Public share AI: default per-workspace hourly assistant cap lowered
300 → 100.** The limiter falls back to this default whenever
`SHARE_AI_WORKSPACE_MAX_PER_HOUR` is unset, so a `0.93.0` deployment that
never set the env var has its anonymous public-share assistant hourly cap
cut from 300 to 100 on upgrade. Set `SHARE_AI_WORKSPACE_MAX_PER_HOUR` to
keep the previous limit. (#62)
## [0.93.0] - 2026-06-21
This release builds on the 0.91.0 AI foundation: admin-defined AI agent roles,
an anonymous AI assistant on public shares, server-side voice dictation, an
editor footnotes model, live page-template embeds, and sandboxed arbitrary-HTML
embeds — plus a large batch of security hardening and test coverage.
### Breaking Changes
- **MCP shared-token auth moved to its own header.** The `/mcp` shared guard
no longer reads `Authorization: Bearer <MCP_TOKEN>`; it now reads only the
`X-MCP-Token` header. The `Authorization` header is now reserved for per-user
HTTP Basic / Bearer access-JWT credentials, so each `/mcp` request
authenticates as a specific user (the `MCP_DOCMOST_*` service account is only
a fallback). Existing MCP clients (e.g. Claude Desktop) configured with
`Authorization: Bearer <MCP_TOKEN>` must be reconfigured to send
`X-MCP-Token: <MCP_TOKEN>` instead. See `MCP_TOKEN` in `.env.example`. As a
one-time aid, the server logs a single migration warning when it sees the
old-style header.
### Added
- **AI agent roles**: admin-defined assistant personas with an optional
per-role model override, selectable in chat.
- **Anonymous AI assistant on public shares**: public-share visitors can chat
with a selectable agent-role identity that reuses the internal chat
presentation, with per-request output-token caps and a fail-closed Redis
limiter.
- **Voice dictation (STT)**: server-side speech-to-text with a mic button in
the chat and the editor, OpenRouter STT support, an endpoint test, and real
provider-error surfacing.
- **Footnotes**: an editor footnotes model (inline references + a definitions
list).
- **Page templates**: live whole-page embed (MVP) with a template-marker icon
in the page tree and a working Refresh action.
- **Arbitrary HTML/CSS/JS embeds**: a sandboxed-iframe embed block gated by a
per-workspace toggle (default OFF); insertable by any member when the toggle
is on.
- Admin-only **"Analytics / tracker"** workspace setting: a raw HTML/JS snippet
injected into the `<head>` of public share pages only (for analytics such as
Google Analytics or Yandex.Metrika), kept separate from the member-facing
HTML-embed feature.
- **MCP**: a hierarchical tree mode for `list_pages`, and per-user auth for the
embedded `/mcp` endpoint.
- **Page tree**: Expand all / Collapse all for the space tree, and
server-authoritative realtime tree updates.
- **AI chat UX**: a `get_current_page` tool for proxy-robust page context, a
current-context-size readout, an agent step cap raised 8→20 with a forced
final text answer, and auto-collapse of the chat window on page focus.
- **AI settings**: a Clear control inside the API-key field and an endpoint
status dot bound to "configured × enabled".
- **Client**: an always-visible space grid replacing the space-switcher popover,
removal of the sidebar Overview item, tighter comments-panel density, and no
auto-open of the comments panel when adding a comment.
### Changed
- HTML embed blocks now render inside a sandboxed iframe (separate origin) and,
when the workspace HTML-embed toggle is on, can be inserted by any member
(previously admin-only). Turning the toggle off hides existing embeds and
stops serving them on public share pages.
- Remove the server-side role-based stripping of HTML-embed blocks from the
write paths (collab/REST/MCP, page create/duplicate, import, transclusion
unsync); sandboxing makes per-write gating unnecessary. The only remaining
server-side strip is the public-share read path, which still honors the
workspace HTML-embed toggle.
### Fixed
- AI chat: preserve scroll position during streaming, record chats that fail on
their first turn, and resolve the current page for agent context behind
proxies.
- AI roles: guard `update()` against concurrent soft-delete; harden the model
override, role-name uniqueness, and id validation; sandwich the safety
framework around the role persona.
- Auth: handle null-password (SSO/LDAP-only) accounts without a bcrypt throw.
- Footnotes: survive duplicate-id definitions without collab divergence.
- HTML embed: fix stale iframe height and damp the resize loop; strip embeds at
serve time on authenticated read paths and the plain page-create path.
- Page templates: import `ThrottleModule` so collab boots, never strand an
in-flight page-embed id, and add defense-in-depth workspace checks.
- Pages: `movePage` cycle guard with no phantom `PAGE_MOVED` event.
- Import: surface the real error cause from `/pages/import` instead of a generic
400.
### Security
- MCP: close an SSO/MFA bypass on Basic auth and stop minting non-init sessions;
close a brute-force limiter check-then-act race.
- Public share: block restricted descendants in the anonymous assistant, cap
per-request output, fail closed when Redis is unavailable, and reject non-text
message parts to close a size-cap bypass.
- Make `trustProxy` env-configurable with a safe default.
### Internal
- CI: gate the `develop` and release image builds on the test suite, run the
suites on push/PR, and build the `:develop` image on push to `develop`.
- Docs: replace `CLAUDE.md` with `AGENTS.md` codifying the agent workflow and
the release procedure, add migration-ordering guidance, and prune implemented
plans.
- A large batch of new server/client test coverage.
## [0.91.0] - 2026-06-18
Gitmost is a community-focused fork of Docmost. This release drops the
Enterprise-Edition code paths and introduces the in-app AI agent chat, a RAG
knowledge layer, an embedded MCP server, and the Gitmost rebrand.
### Breaking Changes
- Remove all frontend Enterprise-Edition code — the project now builds as a pure
community edition.
- AI agent: drop the `updateComment` tool from the agent toolset.
### Added
- **AI agent chat**: per-user in-app AI agent with a floating chat window.
Includes live streaming responses, open-page context awareness, a typing
indicator, a Stop control, and copy/export of a conversation as Markdown.
- **AI agent write tools & provenance**: reversible write tools (page
create/update/move/soft-delete, comment reply/resolve) enforced by Docmost
CASL, plus non-spoofable agent provenance signed into access/collab tokens and
recorded on pages and comments. No permanent/force delete.
- **RAG knowledge retrieval**: workspace bulk reindex with a manual "Reindex
now" action, hybrid RRF retrieval with heading-breadcrumb chunks and a merged
search tool, dimension-agnostic embeddings, and RAG indexing coverage shown in
AI settings.
- **MCP**: embedded community MCP server served at `/mcp`; an admin UI to
list/add/edit/delete external MCP servers with per-server enable toggle, Test,
write-only auth headers, a tool allowlist, and a Tavily preset; `insert_image`/
`replace_image` can now fetch sources from web URLs.
- **AI configuration**: dedicated AI provider settings with separate base URL and
API key for the chat vs. embedding model, and per-endpoint test buttons.
- **Branding**: Gitmost logo, favicon, and app name.
- **Collaboration**: comment resolution for the community build; agent edits are
separated from human edits in page history.
- **Editor / client**: page-tree open/closed state is persisted per
workspace+user; the brand logo shows the current `git describe` version.
### Changed
- Move AI settings to a dedicated `/settings/ai` page and redesign it with
per-endpoint test buttons.
- `edit_page_text` now returns verifiable mutation results and refuses
formatting-only edits; the agent tolerates Markdown in
`edit_page_text`/`insert_node` locators.
- Compact large tool outputs before persisting them.
- Reduce the chat window corner radius, shrink the chat message font size, and
shrink the default page-tree indentation from 16px to 8px.
### Fixed
- AI chat: stable streaming store id so optimistic and streamed messages render
immediately; provider errors stay visible and surface the real provider
status/message; the composer draft survives the new-chat id-adoption remount;
the workspace AI-chat enable toggle is restored for self-hosted.
- AI providers: use OpenAI Chat Completions for multi-turn requests; self-heal
the stored provider settings JSON; drop the hard output-token cap that
truncated complex tool calls.
- RAG: make the indexer observable and bound hung embedding calls; stop the
coverage bar from sticking below 100% on empty pages.
- Collaboration: use `-` instead of `:` in the agent page-history job id.
- Accessibility fixes (#2275) and table jitter on the edit/read toggle (#2252).
### Removed
- Non-functional DOCX / PDF / Confluence import buttons.
### Documentation
- README: rebrand to the Gitmost fork with EE-free positioning, an MCP
comparison, a grouped roadmap, a Russian translation, a "Migration from
Docmost" section, and AI agent chat documentation.
- Add plans for mobile app, voice dictation, arbitrary HTML/CSS/JS embeds, and
offline sync & PWA.
### Internal
- Add `.claude/worktrees/` to `.gitignore`.
- CI: add a `develop` workflow with `workflow_dispatch`; ignore cache errors in
the develop and release builds.
- Build: drop the private EE submodule, retarget CI to GHCR, and update the
Docker image to the GHCR registry.
[Unreleased]: https://github.com/vvzvlad/gitmost/compare/v0.93.0...HEAD
[0.93.0]: https://github.com/vvzvlad/gitmost/compare/v0.91.0...v0.93.0
[0.91.0]: https://github.com/vvzvlad/gitmost/compare/v0.90.1...v0.91.0

225
README.md
View File

@@ -1,225 +0,0 @@
<div align="center">
<h1><b>Gitmost</b></h1>
<p>
Open-source collaborative wiki and documentation software.
<br />
A fully-open community fork of <a href="https://github.com/docmost/docmost">Docmost</a>.
</p>
</div>
<br />
**English** · [Русский](README.ru.md)
## About this fork
**Gitmost** is a community fork of [Docmost](https://github.com/docmost/docmost), an open-source
collaborative wiki and documentation app.
The goal of the fork is a **100% open, AGPL-only build with no Enterprise-Edition (EE) code**:
- **No EE code at all.** All proprietary Enterprise-Edition sources were removed — the private
`apps/server/src/ee` submodule, the `apps/client/src/ee` directory (201 files) and the
`packages/ee` package are gone. There is no license gating: every feature is available to everyone.
- **Replacements are written from scratch.** Features that previously lived behind the enterprise
license (e.g. comment resolution, the AI agent chat, the `/mcp` server) were re-implemented from
scratch on top of the community codebase. No EE code is reused, and there is no
entitlement/feature-flag wall.
- **No upsell.** There are no "buy a license" / "upgrade to Enterprise" banners, trial nags, or
locked-feature placeholders anywhere in the UI.
- Authentication is plain email + password (no SSO/LDAP/cloud/billing flows).
## What's different from Docmost
| Change | Details |
| --- | --- |
| **EE code removed** | Stripped all client and server Enterprise-Edition code; ships as a clean community/AGPL build with no license checks. |
| **Comment resolution** | Re-implemented from scratch as a community feature (resolve / re-open with Open/Resolved tabs). No EE code reused, available to anyone who can comment. |
| **Embedded MCP server** | A community MCP server (`@docmost/mcp`, 38 tools) is served over HTTP at `/mcp` — no enterprise license required. Replaces the removed license-gated EE MCP. |
| **AI agent chat** | Built-in AI agent chat over your wiki, written from scratch as a community feature — no enterprise license. The agent reads and edits pages on your behalf (scoped to your permissions), with full-text + vector (RAG) search and optional web access via external MCP servers. |
| **Rebranding** | App logo / name changed from *Docmost* to *Gitmost*. |
| **Compact page tree** | Default page-tree indentation reduced from 16px to 8px per nesting level. |
| **Persistent page-tree state** | The sidebar page tree remembers which nodes you expanded/collapsed across reloads — saved in the browser (localStorage), scoped per workspace + user so accounts sharing a browser don't clash. Upstream Docmost forgets the tree on every reload. |
| **CI / images** | Release CI publishes container images to GHCR (`ghcr.io/vvzvlad/gitmost`) using the built-in `GITHUB_TOKEN` instead of Docker Hub. |
### Embedded MCP server
Gitmost has **our own MCP server** — [docmost-mcp](https://github.com/vvzvlad/docmost-mcp),
which we wrote — **built directly into the app** and served at `/mcp`. It exposes **38
agent-native tools**: surgical per-block edits (patch / insert / delete by id),
structure-preserving find/replace, scripted `(doc) => doc` transforms with a dry-run diff,
structured table editing, version history with diff / restore, comments, images and share
links — all applied through Docmost's real-time-collaboration layer, so a write never
clobbers a concurrent human edit.
**Better than Docmost's own MCP.** Docmost's built-in MCP is an enterprise feature, and its
tools are coarse — read a page as Markdown, create / move / delete pages, replace a whole
page. Ours is built around how an agent actually edits: address one block and patch it, or
*program* the change, instead of round-tripping a ~100 KB document through the model on
every little fix. And it needs no enterprise license.
| | **Gitmost `/mcp` (our docmost-mcp)** | Docmost's built-in MCP |
| --- | :---: | :---: |
| **Enterprise license** | Not required | Required |
| **Tools** | 38, agent-native | Coarse (read Markdown, page CRUD, replace whole page) |
| **Per-block edits / find-replace / scripted transforms** | ✅ | — |
| **Structured table editing, version diff / restore** | ✅ | — |
| **Comments, images, share links** | ✅ | — |
| **Safe real-time-collab writes (no clobber)** | ✅ | — |
**Same server as standalone docmost-mcp — just bundled.** This is the exact
[docmost-mcp](https://github.com/vvzvlad/docmost-mcp) you can also run on its own; embedding
it doesn't make it more capable, you simply don't have to install and run a separate
process. An admin flips one toggle in **Workspace settings → AI** and any MCP client
points at `${APP_URL}/mcp`.
### AI agent chat
Gitmost ships a **built-in AI agent chat** over your wiki — written from scratch as a
community feature, with no enterprise license. Open it from the page header; the agent can
**read and edit** your workspace on your behalf:
- **Full read + write toolset (~40 tools).** Search and read pages, make surgical per-block
and table edits, create / rename / move pages, diff and restore page history, and create /
resolve comments — every action runs under *your* permissions (Docmost CASL), so the agent
can never see or change anything you couldn't.
- **Safe by design.** The agent is given only **reversible** operations (page history +
trash); permanent deletion is never exposed. Agent edits are marked in page history with an
"AI agent" badge linking back to the chat.
- **Search over your content.** Full-text search plus optional vector (RAG) semantic search
across pages.
- **Web access via external MCP.** Admins can connect external MCP servers (e.g. Tavily) to
give the agent web search / internet access.
- **Bring your own model.** Configure an OpenAI-compatible endpoint — OpenAI, OpenRouter, a
local Ollama, or any self-hosted server — plus the model and API key in
**Workspace settings → AI**. The key is encrypted and never leaves the server.
## Roadmap
### Done
-**MCP server** — embedded community MCP server served at `/mcp`.
-**macOS app** — native macOS app ([gitmost-app](https://github.com/vvzvlad/gitmost-app)) that embeds the UI with multi-server tabs.
-**AI chat** — built-in AI agent chat over your wiki content (read + write, RAG search, configurable provider, optional web access via external MCP).
-**Voice dictation** — microphone button in the AI agent chat and the page editor; audio is transcribed server-side (Whisper / OpenAI-compatible STT) via the workspace AI provider, with an admin toggle to show/hide it.
-**Page templates** — flag a page as a template and embed its whole content live into other pages; edits to the template propagate to every place it is inserted (whole-page transclusion on top of the existing synced blocks).
-**Public-share AI assistant** — anonymous visitors of a shared page can ask the AI agent, scoped strictly to that share's page tree (read-only, share-scoped search), behind a workspace toggle.
-**Footnotes** — academic-style footnotes: a numbered superscript reference inline (read it in place via a hover popover), with the note text living as a real, editable block at the bottom of the page; auto-numbered, collaboration-safe, and round-trips through Markdown export/import and the AI agent / MCP.
### In progress
- 🚧 **Git synchronization** — two-way sync of pages with a Git repository.
### Planned
- 🔭 **Viewer comments** — let read-only viewers leave comments.
- 🔭 **Password-protected pages** — protect individual pages / shares with a password.
- 🔭 **Windows / Linux app** — native desktop app for Windows and Linux.
- 🔭 **Mobile app** — mobile apps (iOS first, Android to follow), reusing the existing responsive web UI and editor via a Capacitor wrapper, with offline planned for later. See [docs/mobile-app-plan.md](docs/mobile-app-plan.md).
- 🔭 **Offline mode** — offline sync & PWA support.
- 🔭 **Editor & UX improvements** — blocks inside tables (lists, to-do items), column layout, additional heading levels, highlight blocks, custom emoji in callouts, floating images, anchor links for page mentions, toggles (shared-page width, aside/sidebar, spellcheck, ligatures), sanitized space-tree export, and mentions in breadcrumbs.
## Getting started
Gitmost follows the upstream Docmost setup. See the Docmost
[documentation](https://docmost.com/docs) for self-hosting and development instructions; replace the
`docmost/docmost` image with `ghcr.io/vvzvlad/gitmost` where applicable.
## Migration from Docmost
Gitmost's database schema is a **strict superset** of Docmost's. Every Gitmost-specific migration
only **adds** new tables (`page_embeddings`, `ai_chats`, `ai_chat_messages`,
`ai_provider_credentials`, `ai_mcp_servers`) and **nullable** columns — it never drops or rewrites
existing Docmost data. Migrations run automatically on startup, so migrating an existing Docmost
instance is essentially **two image swaps**.
The only hard requirement is the database image: the AI agent's RAG storage needs the
[pgvector](https://github.com/pgvector/pgvector) extension (`CREATE EXTENSION vector`), which the
stock `postgres` image does not ship. Swap it for `pgvector/pgvector:pgNN` — the same vanilla
Postgres plus pgvector bundled, built on the official `postgres` image and fully data-compatible
with it.
### From a current Docmost on Postgres 18
If your Docmost already runs `postgres:18`, it's a clean **in-place** swap — no dump/restore needed,
the existing data directory is reused as-is:
```diff
services:
docmost:
- image: docmost/docmost:latest
+ image: ghcr.io/vvzvlad/gitmost:latest
...
db:
- image: postgres:18
+ image: pgvector/pgvector:pg18
```
`APP_SECRET`, `DATABASE_URL`, `REDIS_URL` and the storage volume stay unchanged. On the first
start the new migrations apply on top of your existing schema (`CREATE EXTENSION vector` plus the
`page_embeddings` and AI tables); watch the logs for `Migration "..." executed successfully`.
> ⚠️ **Never change `APP_SECRET` after setup.** It does double duty: it signs JWTs *and* derives the
> AES-256-GCM key that encrypts stored AI-provider credentials (API keys). Rotating it makes every
> saved AI API key undecryptable (you'd have to re-enter them in AI settings) and invalidates all
> existing sessions. Pick it once, keep it stable, and back it up together with your database.
### Notes
- **Back up first.** Take a `pg_dump` before swapping — migrations apply in place, and the
container exits if a migration fails.
- **Volume layout is identical.** `pgvector/pgvector` is built on the official `postgres` image and
uses the same `PGDATA`, so keep your existing data volume and its mount path unchanged — the swap
reuses the directory as-is.
- **Match the Postgres major.** A Postgres data directory is not compatible across major versions.
If your Docmost runs an older major (e.g. Postgres 16), use the matching `pgvector/pgvector:pg16`
to keep the in-place swap, or move the data with `pg_dump` / `pg_restore` into the new instance.
- **Managed Postgres.** If you don't use the bundled `db` container, make sure pgvector is available
and your database role is allowed to run `CREATE EXTENSION vector`.
- **AI is opt-in.** The `page_embeddings` table stays empty until you configure an AI provider;
existing pages are indexed on their next edit. pgvector is still required for the migration to
apply at all.
## Features
- Real-time collaboration
- Diagrams (Draw.io, Excalidraw and Mermaid)
- Spaces
- Permissions management
- Groups
- Comments (with resolve / re-open)
- Page history
- Search
- File attachments
- Embeds (Airtable, Loom, Miro and more)
- Translations (10+ languages)
- Embedded MCP server (`/mcp`)
- AI agent chat over your wiki (read + write, RAG search, external MCP / web access)
### Screenshots
<p align="center">
<img alt="AI agent chat" src="docs/screenshots/ai-chat.png" width="70%">
<img alt="home" src="https://docmost.com/screenshots/home.png" width="70%">
<img alt="editor" src="https://docmost.com/screenshots/editor.png" width="70%">
</p>
### License
Gitmost is licensed under the open-source AGPL 3.0 license.
Unlike upstream Docmost, this fork contains **no Enterprise-Edition code** — the `apps/server/src/ee`,
`apps/client/src/ee` and `packages/ee` directories have been removed, so there are no files governed
by an enterprise license.
### Credits
Gitmost is based on [Docmost](https://github.com/docmost/docmost) by the Docmost team. Huge thanks to
them for the original open-source project.
<img width="100" alt="Crowdin" src="https://github.com/user-attachments/assets/a6c3d352-e41b-448d-b6cd-3fbca3109f07" />
[Crowdin](https://crowdin.com/) for providing access to their localization platform.
<img width="48" alt="Algolia-mark-square-white" src="https://github.com/user-attachments/assets/6ccad04a-9589-4965-b6a1-d5cb1f4f9e94" />
[Algolia](https://www.algolia.com/) for providing full-text search to the docs.

View File

@@ -1,212 +0,0 @@
<div align="center">
<h1><b>Gitmost</b></h1>
<p>
Совместная вики и система документации с открытым исходным кодом.
<br />
Полностью открытый community-форк <a href="https://github.com/docmost/docmost">Docmost</a>.
</p>
</div>
<br />
[English](README.md) · **Русский**
## Об этом форке
**Gitmost** — это community-форк [Docmost](https://github.com/docmost/docmost),
открытой системы для совместной работы над вики и документацией.
Цель форка — **сборка на 100% открытая, только под AGPL, без кода Enterprise-редакции (EE)**:
- **Никакого EE-кода.** Весь проприетарный код Enterprise-редакции удалён — приватный
сабмодуль `apps/server/src/ee`, каталог `apps/client/src/ee` (201 файл) и пакет
`packages/ee`. Никаких лицензионных ограничений: все функции доступны всем.
- **Замены написаны с нуля.** Функции, которые раньше были скрыты за enterprise-лицензией
(например, резолв комментариев, чат с AI-агентом, сервер `/mcp`), переписаны с нуля поверх
community-кодовой базы. EE-код не переиспользуется, нет проверок entitlement и feature-флагов.
- **Никакого навязывания.** В интерфейсе нигде нет плашек «купите лицензию» / «перейдите на
Enterprise», напоминаний о триале и заглушек на месте заблокированных функций.
- Аутентификация — обычная пара email + пароль (без SSO/LDAP/облака/биллинга).
## Чем отличается от Docmost
| Изменение | Подробности |
| --- | --- |
| **Удалён EE-код** | Вырезан весь код Enterprise-редакции на клиенте и сервере; это чистая community/AGPL-сборка без лицензионных проверок. |
| **Резолв комментариев** | Переписан с нуля как community-функция (резолв / переоткрытие с вкладками «Открытые» / «Решённые»). EE-код не используется, доступно любому, кто может комментировать. |
| **Встроенный MCP-сервер** | Community MCP-сервер (`@docmost/mcp`, 38 инструментов) отдаётся по HTTP на `/mcp` — без enterprise-лицензии. Заменяет удалённый лицензируемый EE MCP. |
| **Чат с AI-агентом** | Встроенный чат с AI-агентом по содержимому вики, написанный с нуля как community-функция — без enterprise-лицензии. Агент читает и редактирует страницы от вашего имени (в рамках ваших прав), с полнотекстовым + векторным (RAG) поиском и опциональным доступом в интернет через внешние MCP-серверы. |
| **Ребрендинг** | Логотип / название приложения изменены с *Docmost* на *Gitmost*. |
| **Компактное дерево страниц** | Отступ дерева страниц по умолчанию уменьшен с 16px до 8px на уровень вложенности. |
| **Сохранение состояния дерева страниц** | Дерево страниц в сайдбаре запоминает, какие узлы вы раскрыли/свернули, между перезагрузками — состояние хранится в браузере (localStorage) отдельно для каждой пары воркспейс + пользователь, чтобы аккаунты в одном браузере не пересекались. В оригинальном Docmost дерево сбрасывается при каждой перезагрузке. |
| **CI / образы** | Release-CI публикует контейнерные образы в GHCR (`ghcr.io/vvzvlad/gitmost`) через встроенный `GITHUB_TOKEN` вместо Docker Hub. |
### Встроенный MCP-сервер
В Gitmost есть **наш собственный MCP-сервер** — [docmost-mcp](https://github.com/vvzvlad/docmost-mcp),
который мы написали сами, — **встроенный прямо в приложение** и доступный на `/mcp`. Он даёт
**38 agent-native инструментов**: точечное редактирование по блокам (patch / insert / delete
по id), find/replace с сохранением структуры, скриптовые трансформации `(doc) => doc` с
предпросмотром диффа, структурное редактирование таблиц, история версий с диффом /
восстановлением, комментарии, изображения и ссылки на шаринг — всё применяется через слой
real-time-коллаборации Docmost, поэтому запись никогда не затирает параллельную правку
человека.
**Лучше, чем родной MCP у Docmost.** Встроенный MCP у Docmost — enterprise-функция, и его
инструменты примитивные: прочитать страницу как Markdown, создать / переместить / удалить
страницу, заменить страницу целиком. Наш сделан под то, как агент реально редактирует:
адресовать один блок и пропатчить его или *запрограммировать* изменение, а не гонять
документ на ~100 КБ через модель ради каждой мелкой правки. И enterprise-лицензия не нужна.
| | **`/mcp` в Gitmost (наш docmost-mcp)** | Родной MCP у Docmost |
| --- | :---: | :---: |
| **Enterprise-лицензия** | Не нужна | Нужна |
| **Инструменты** | 38, agent-native | Примитивные (Markdown, CRUD страниц, замена целиком) |
| **Правки по блокам / find-replace / скриптовые трансформации** | ✅ | — |
| **Структурное редактирование таблиц, дифф / восстановление версий** | ✅ | — |
| **Комментарии, изображения, ссылки на шаринг** | ✅ | — |
| **Безопасная запись через real-time-коллаборацию (без затирания)** | ✅ | — |
**Это тот же сервер, что и отдельный docmost-mcp, — просто встроенный.** Это ровно тот самый
[docmost-mcp](https://github.com/vvzvlad/docmost-mcp), который можно запускать и отдельно;
от встраивания он не становится «мощнее» — просто не нужно ставить и держать отдельный
процесс. Админ включает его одним переключателем в **Настройки воркспейса → AI**, а
любой MCP-клиент указывает на `${APP_URL}/mcp`.
### Чат с AI-агентом
В Gitmost есть **встроенный чат с AI-агентом** по содержимому вики — написанный с нуля
как community-функция, без enterprise-лицензии. Открывается из шапки страницы; агент умеет
**читать и редактировать** ваш воркспейс от вашего имени:
- **Полный набор инструментов чтения + записи (~40 штук).** Поиск и чтение страниц,
точечные правки по блокам и в таблицах, создание / переименование / перемещение страниц,
дифф и восстановление версий, создание / резолв комментариев — каждое действие
выполняется под *вашими* правами (Docmost CASL), поэтому агент не видит и не меняет
ничего, чего не могли бы вы сами.
- **Безопасность по умолчанию.** Агенту доступны только **обратимые** операции (история
версий + корзина); перманентное удаление не экспонируется. Правки агента помечаются в
истории версий бейджем «AI-агент» со ссылкой на чат.
- **Поиск по вашему контенту.** Полнотекстовый поиск плюс опциональный векторный (RAG)
семантический поиск по страницам.
- **Доступ в интернет через внешние MCP.** Админ может подключить внешние MCP-серверы
(например, Tavily), чтобы дать агенту веб-поиск / доступ в интернет.
- **Своя модель.** OpenAI-совместимый эндпоинт — OpenAI, OpenRouter, локальный Ollama или
любой self-hosted-сервер — плюс модель и API-ключ настраиваются в
**Настройки воркспейса → AI**. Ключ шифруется и никогда не покидает сервер.
## Дорожная карта
### Готово
-**MCP-сервер** — встроенный community MCP-сервер на `/mcp`.
-**Приложение для macOS** — нативное приложение для macOS ([gitmost-app](https://github.com/vvzvlad/gitmost-app)), встраивающее UI с вкладками для нескольких серверов.
-**AI-чат** — встроенный чат с AI-агентом по содержимому вики (чтение + запись, RAG-поиск, настраиваемый провайдер, опциональный доступ в интернет через внешние MCP).
-**Голосовая диктовка** — кнопка-микрофон в чате AI-агента и в редакторе страниц; аудио распознаётся на сервере (Whisper / OpenAI-совместимый STT) через AI-провайдер воркспейса, с тумблером админа для показа/скрытия.
-**Шаблоны страниц** — пометить страницу шаблоном и вставлять её содержимое живой ссылкой в другие страницы; правки шаблона распространяются на все места вставки (whole-page-транслюзия поверх существующих synced-блоков).
-**AI-ассистент на публичных шарах** — анонимный зритель расшаренной страницы может спросить AI-агента, который ищет строго по дереву этой шары (read-only, share-scoped поиск), за тумблером воркспейса.
-**Сноски** — сноски академического вида: нумерованная ссылка-надстрочник прямо в тексте (читается на месте во всплывающем окне по наведению), а текст сноски живёт реальным редактируемым блоком внизу страницы; авто-нумерация, безопасна для совместного редактирования, переживает экспорт/импорт Markdown и доступна AI-агенту / MCP.
### В процессе
- 🚧 **Синхронизация с Git** — двусторонняя синхронизация страниц с Git-репозиторием.
### В планах
- 🔭 **Комментарии зрителей** — возможность комментировать для пользователей с доступом только на чтение.
- 🔭 **Защищённые паролем страницы** — защита отдельных страниц / шар паролем.
- 🔭 **Приложение для Windows / Linux** — нативное десктоп-приложение для Windows и Linux.
- 🔭 **Мобильное приложение** — мобильные приложения (iOS обязательно, Android как пойдёт) на базе существующей адаптивной веб-версии и редактора через обёртку Capacitor; оффлайн запланирован на будущее. См. [docs/mobile-app-plan.md](docs/mobile-app-plan.md).
- 🔭 **Офлайн-режим** — офлайн-синхронизация и поддержка PWA.
- 🔭 **Улучшения редактора и UX** — блоки внутри таблиц (списки, чек-листы), колоночная вёрстка, дополнительные уровни заголовков, highlight-блоки, кастомные эмодзи в callout-ах, плавающие изображения, anchor-ссылки на упоминания страниц, тоглы (ширина шары, aside/сайдбар, spellcheck, лигатуры), санитизация экспорта дерева спейса и mentions в хлебных крошках.
## С чего начать
Gitmost повторяет процесс установки upstream-Docmost. Инструкции по self-hosting и разработке
смотрите в [документации](https://docmost.com/docs) Docmost; где это применимо, заменяйте образ
`docmost/docmost` на `ghcr.io/vvzvlad/gitmost`.
## Миграция с Docmost
Схема БД Gitmost — это **строгий superset** схемы Docmost. Все Gitmost-специфичные миграции только
**добавляют** новые таблицы (`page_embeddings`, `ai_chats`, `ai_chat_messages`,
`ai_provider_credentials`, `ai_mcp_servers`) и **nullable**-колонки — они никогда не удаляют и не
переписывают существующие данные Docmost. Миграции применяются автоматически при старте, поэтому
миграция существующего инстанса Docmost — это по сути **замена двух образов**.
Единственное жёсткое требование — образ БД: RAG-хранилище AI-агента использует расширение
[pgvector](https://github.com/pgvector/pgvector) (`CREATE EXTENSION vector`), которого нет в
стоковом образе `postgres`. Замените его на `pgvector/pgvector:pgNN` — это тот же ванильный
Postgres со встроенным pgvector, собранный на базе официального образа `postgres` и полностью
data-совместимый с ним.
### С текущего Docmost на Postgres 18
Если ваш Docmost уже работает на `postgres:18`, это чистая замена in-place — без
dump/restore, существующий каталог данных переиспользуется как есть:
```diff
services:
docmost:
- image: docmost/docmost:latest
+ image: ghcr.io/vvzvlad/gitmost:latest
...
db:
- image: postgres:18
+ image: pgvector/pgvector:pg18
```
`APP_SECRET`, `DATABASE_URL`, `REDIS_URL` и том сторейджа остаются прежними. При первом запуске
новые миграции применяются поверх вашей схемы (`CREATE EXTENSION vector` плюс таблицы
`page_embeddings` и AI-таблицы); следите в логах за строками `Migration "..." executed successfully`.
> ⚠️ **Никогда не меняйте `APP_SECRET` после установки.** Он выполняет двойную роль: подписывает JWT
> *и* служит материалом для ключа AES-256-GCM, которым шифруются сохранённые ключи AI-провайдеров
> (API-ключи). Смена секрета сделает все сохранённые AI-ключи нерасшифровываемыми (придётся вводить
> их заново в настройках AI) и инвалидирует все текущие сессии. Задайте его один раз, держите
> неизменным и бэкапьте вместе с базой данных.
## Возможности
- Совместная работа в реальном времени
- Диаграммы (Draw.io, Excalidraw и Mermaid)
- Пространства (Spaces)
- Управление правами доступа
- Группы
- Комментарии (с резолвом / переоткрытием)
- История страниц
- Поиск
- Вложения файлов
- Встраивания (Airtable, Loom, Miro и другие)
- Переводы (10+ языков)
- Встроенный MCP-сервер (`/mcp`)
- Чат с AI-агентом по вики (чтение + запись, RAG-поиск, внешние MCP / доступ в интернет)
### Скриншоты
<p align="center">
<img alt="Чат с AI-агентом" src="docs/screenshots/ai-chat.png" width="70%">
<img alt="home" src="https://docmost.com/screenshots/home.png" width="70%">
<img alt="editor" src="https://docmost.com/screenshots/editor.png" width="70%">
</p>
### Лицензия
Gitmost распространяется под открытой лицензией AGPL 3.0.
В отличие от upstream-Docmost, этот форк **не содержит кода Enterprise-редакции** — каталоги
`apps/server/src/ee`, `apps/client/src/ee` и `packages/ee` удалены, поэтому файлов под
enterprise-лицензией здесь нет.
### Благодарности
Gitmost основан на [Docmost](https://github.com/docmost/docmost) от команды Docmost. Огромное
спасибо им за оригинальный открытый проект.
<img width="100" alt="Crowdin" src="https://github.com/user-attachments/assets/a6c3d352-e41b-448d-b6cd-3fbca3109f07" />
[Crowdin](https://crowdin.com/) — за доступ к их платформе локализации.
<img width="48" alt="Algolia-mark-square-white" src="https://github.com/user-attachments/assets/6ccad04a-9589-4965-b6a1-d5cb1f4f9e94" />
[Algolia](https://www.algolia.com/) — за полнотекстовый поиск по документации.