rm all 7
This commit is contained in:
310
AGENTS.md
310
AGENTS.md
@@ -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.
|
||||
209
CHANGELOG.md
209
CHANGELOG.md
@@ -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
225
README.md
@@ -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.
|
||||
212
README.ru.md
212
README.ru.md
@@ -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/) — за полнотекстовый поиск по документации.
|
||||
Reference in New Issue
Block a user