Skip to main content
Glama

Athena

Персональная вики, в которую пишет ваш ИИ, и вы можете просматривать её сами.

Athena размещает MCP-сервер перед Wiki.js. Ваш ассистент ищет в вики, читает страницы и сохраняет новые: заметки, документацию, целые разговоры. Всё, что он пишет, — это обычная Markdown-страница, которую вы можете открыть, редактировать и хранить долго после того, как какая-либо конкретная модель исчезнет.

Claude / ChatGPT / Cursor
        │  MCP over HTTPS
        ▼
   athena-mcp ──── search ──▶ Wiki.js (keyword) + Postgres (meaning)
        │          read ────▶ Wiki.js
        └────────  write ───▶ Wiki.js ──▶ athena-indexer ──▶ Postgres

Wiki.js хранит истину. Векторный индекс только помогает находить вещи и может быть удалён и перестроен в любое время.


Быстрый старт

Локально, примерно за пять минут. Для всего, что в интернете, сначала прочитайте Развернуть на сервере.

git clone https://github.com/jannismilz/athena.git
cd athena
cp .env.example .env
$EDITOR .env          # fill in every CHANGE_ME, one per secret:
                      #   openssl rand -hex 32
docker compose up -d
  1. Откройте Wiki.js и завершите мастер настройки.

  2. В Wiki.js: Администрирование → API, включите его, создайте токен и поместите его в .env как WIKI_API_TOKEN.

  3. Снова выполните docker compose up -d, чтобы он подхватился.

  4. Откройте панель управления и войдите с помощью DASHBOARD_TOKEN.

Данные записываются в каталог data/ рядом с копией репозитория, не внутри него, поэтому никакая операция git никогда не сможет их удалить. Измените ATHENA_DATA_DIR, если хотите другое расположение.

Ничто не публикует порт, поэтому обращайтесь к сервисам через ваш обратный прокси или добавьте временное сопоставление ports: при тестировании.

При первом запуске загружается модель эмбеддингов размером несколько сотен МБ. Индексатор повторяет попытки, пока она не будет готова, поэтому ожидается, что embeddings будет выглядеть нездоровым в течение минуты-двух при первой загрузке.


Related MCP server: wiki-js-mcp

Подключите свой ИИ

Всё обслуживается с MCP_PUBLIC_URL, который должен быть голым https://-источником без пути. Не /mcp.

Claude.ai → Settings → Connectors → Add custom connector

  • URL: https://athena-mcp.example.com/mcp

  • Оставьте client ID и secret пустыми. Athena регистрирует клиента сама.

  • Страница браузера запрашивает пароль. Это ваш MCP_TOKEN.

Cursor, Claude Desktop, and other header clients

{
  "mcpServers": {
    "athena": {
      "url": "https://athena-mcp.example.com/mcp",
      "headers": { "Authorization": "Bearer YOUR_MCP_TOKEN" }
    }
  }
}

Инструменты

Инструмент

Что делает

search_knowledge

Ключевой и семантический поиск, объединённые. Каждый результат содержит путь.

get_page

Полный Markdown одной страницы

get_page_structure

Структура заголовков, без тела

append_to_page

Добавить под заголовок, оставив остальное нетронутым

create_page

Новая страница Markdown

update_page

Заменить тело страницы

move_page

Переместить или переименовать

delete_page

Удалить и убрать из индекса

save_conversation

Сохранить разговор в conversations/YYYY/MM/

capture_note

Быстрая заметка в inbox/ для последующей сортировки

list_pages

Всё, с путями и временными метками

get_wiki_stats

Размер, структура и устаревание, чтобы ИИ мог ответить, чего не хватает

append_to_page — это тот, о котором стоит знать: добавление факта стоит одного абзаца, а не переписывания всей страницы.

Почему он хорошо ищет. Точные термины попадают в полнотекстовый индекс Wiki.js, расплывчатые вопросы — в векторный индекс, а результаты объединяются с помощью реципрокного рангового слияния, так что ни один источник не может подавить другой. Фрагменты записывают заголовки над ними, поэтому возвращаемые данные сохраняют свой контекст. Каждая страница, к которой прикасается ассистент, помечается тем, какой именно ассистент и когда, взято из аутентифицированного клиента, а не из того, что модель утверждает о себе.


Панель управления

Отдельный сервис, на порту 8082. Вход с помощью DASHBOARD_TOKEN; токена нет ни в одном URL. Для скриптов используйте заголовок Bearer:

curl -H "Authorization: Bearer $DASHBOARD_TOKEN" \
  https://wiki.example.com/dashboard/api/metrics?days=30

Панель

Ответы

Контент

страницы, слова, по областям, самые большие, устаревающие

Активность ИИ

вызовов в день, какие инструменты, какой ассистент, чтение vs запись

Поиски, ничего не нашедшие

то, на что ваша вики не смогла ответить

Здоровье индекса

сохранённые чанки, проиндексированные страницы, насколько отстаёт

Резервное копирование

когда завершился последний запуск, размер, куда пошло

Третья строка — та, которая оправдывает своё место. Каждая запись — это страница, которую стоит написать.

Она дважды только для чтения: она никогда не пишет, и подключается к Postgres как athena_readonly — роль, имеющая только SELECT и ничего больше. Показатели агрегируются в Postgres и кэшируются, поэтому обновление почти ничего не стоит.


Развернуть на сервере

VPS с 4 ГБ ОЗУ запускает всё, включая модель эмбеддингов на CPU.

1. Хост и брандмауэр

sudo ufw default deny incoming && sudo ufw default allow outgoing
sudo ufw allow 22/tcp && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp
sudo ufw enable

Установите Docker, затем создайте пользователя, владеющего развёртыванием:

sudo useradd --create-home --shell /bin/bash athena
sudo usermod -aG docker athena

Запускайте compose от имени этого пользователя, никогда с sudo, иначе привязки монтирования окажутся принадлежащими root. Членство в группе docker эквивалентно root на хосте, поэтому держите её небольшой.

2. DNS

Две A-записи, указывающие на хост:

Имя

Обслуживает

wiki.example.com

Wiki.js и панель управления по адресу /dashboard/

athena-mcp.example.com

конечную точку MCP

3. Размещение и настройка

Всё, что пишет Athena, проходит через одну настройку, ATHENA_DATA_DIR, поэтому вся установка может жить в одном каталоге. Используйте два подкаталога с разными жизненными циклами:

/athena
├── app/     the git repository   replaceable, thrown away on every upgrade
└── data/    postgres, state,     irreplaceable, never touched by git
             uploads, backups

Они являются соседями, а не вложенными, и в этом вся суть. data/ находится в .gitignore, а git clean -xdf удаляет игнорируемые файлы, поэтому данные внутри копии репозитория находятся на расстоянии одной рутинной команды от уничтожения без подтверждения и отмены. Соседний каталог недоступен для любой операции git.

sudo mkdir -p /athena && sudo chown athena:athena /athena
cd /athena
git clone https://github.com/jannismilz/athena.git app
cd app
cp .env.example .env
chmod 600 .env        # it holds every secret

ATHENA_DATA_DIR по умолчанию равен ../data, который разрешается относительно каталога, содержащего файл compose. Клонируйте в /athena/app, как указано выше, и данные окажутся в /athena/data без необходимости настройки. Установите секреты:

POSTGRES_PASSWORD=...
MCP_TOKEN=...
DASHBOARD_TOKEN=...
DASHBOARD_DB_PASSWORD=...
MCP_PUBLIC_URL=https://athena-mcp.example.com
WIKI_PUBLIC_URL=https://wiki.example.com

Compose создаёт /athena/data и его подкаталоги при первом запуске. Выполняйте все команды docker compose из /athena/app.

/athena/data
├── postgres/     the wiki, users, settings, uploads, activity log, vectors
├── wikijs/       Wiki.js config, cache, upload cache
├── mcp/          oauth-state.json, the tokens issued to AI clients
├── indexer/      index bookkeeping, rebuilt automatically if lost
├── embeddings/   the downloaded model
└── backups/      local dumps plus status.json

Только postgres/ незаменим, и контейнер резервного копирования дампит его каждый час. Всё остальное либо регенерируется автоматически, либо стоит одного переподключения.

Если вы предпочитаете следовать соглашению об иерархии файловой системы, поместите данные в /srv/athena, а копию репозитория в /opt/athena. Расположение с единым корнем, описанное выше, проще на машине, выполняющей одну задачу, и любой вариант работает: решает только ATHENA_DATA_DIR.

4. Обратный прокси

Ни один контейнер не публикует порт. Сервисы находятся в двух сетях:

  • athena, внутренняя. Postgres, модель эмбеддингов и индексатор живут только здесь, поэтому скомпрометированный прокси не может добраться до базы данных.

  • athena-edge, к которой подключается ваш обратный прокси. Только три сервиса ниже находятся на ней.

Маршрутизируйте их:

Хост

Куда

Примечания

wiki.example.com

wikijs:3000

Обновление WebSocket, лимит тела 100M

wiki.example.com/dashboard/

dashboard:8082

athena-mcp.example.com

mcp:8080

не должен буферизовать, потоки MCP

Передавайте X-Forwarded-For: логины ограничиваются по адресу, и без него каждая попытка выглядит так, как будто пришла от прокси.

Запустите nginx как контейнер, подключённый к сети athena-edge, как показано ниже, или на хосте с сопоставлением ports:, привязанным к 127.0.0.1. Подключение к edge-сети означает, что прокси может достичь Wiki.js, MCP-сервера и панели управления, и ничего больше.

server {
    listen 80;
    server_name wiki.example.com;

    location / {
        proxy_pass http://wikijs:3000;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        "upgrade";
        client_max_body_size 100M;
        proxy_read_timeout 120s;
    }

    location /dashboard/ {
        proxy_pass http://dashboard:8082/;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name athena-mcp.example.com;

    location / {
        proxy_pass http://mcp:8080;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # MCP streams responses. Without these, long tool calls appear to hang.
        proxy_set_header Connection "";
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 300s;
    }
}

Затем выпустите сертификаты с помощью certbot или завершайте TLS там, где вы это уже делаете.

Здесь нет ничего специфичного для платформы. PaaS, которая запускает Compose и предоставляет собственный прокси, нуждается в трёх настройках, все в .env:

ATHENA_DATA_DIR=../files          # Dokploy's persistent directory
ATHENA_EDGE_NETWORK=dokploy-network
ATHENA_EDGE_EXTERNAL=true

ATHENA_DATA_DIR наиболее важен: Dokploy очищает абсолютные пути привязки монтирования при повторном развёртывании, поэтому абсолютный путь там уничтожит базу данных. Относительный путь к каталогу приложения сохраняется.

Затем добавьте домены в интерфейсе платформы, указывая на сервис и его порт:

Домен

Сервис

Порт

wiki.example.com

wikijs

3000

athena-mcp.example.com

mcp

8080

wiki.example.com/dashboard

dashboard

8082

Платформа генерирует собственные метки маршрутизации и обрабатывает TLS, поэтому полностью пропустите раздел nginx. Всё остальное, включая файл compose, без изменений.

Вам не нужно публиковать образы в реестре: Dokploy собирает из репозитория. Сборка четырёх образов конкурирует с Postgres и моделью эмбеддингов за память, поэтому на маленьком хосте вы можете предпочесть сборку в CI и загрузку вместо этого.

5. Запустите, затем заблокируйте вики

docker compose up -d && docker compose ps

Завершите мастер Wiki.js немедленно. Пока вы этого не сделаете, любой, кто найдёт хост, может получить администраторскую учётную запись. Затем в Wiki.js:

  • Группы → Гости: удалите доступ на чтение, если вы не хотите, чтобы вики была публичной.

  • Аутентификация: отключите самостоятельную регистрацию.

  • API: включите его и создайте токен для WIKI_API_TOKEN.

6. Проверка

curl -s https://athena-mcp.example.com/health

# Must reject unauthenticated calls:
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://athena-mcp.example.com/mcp
# expected: 401

Резервное копирование

Один pg_dump — это полная резервная копия. Wiki.js хранит страницы, историю, пользователей, разрешения, настройки и байты каждого загруженного файла в Postgres. Загрузки находятся в таблице assetData; файлы в data/wikijs/uploads — только кэш. Журнал активности Athena и векторы поиска находятся во второй базе данных на том же сервере.

Данные

В резервной копии

Страницы, история, пользователи, настройки

да

Загруженные изображения и файлы

да

Журнал активности и векторы поиска

да

Учётные данные индекса, регистрации OAuth

нет, перестраивается или переподключается

.env

нет, храните копию в менеджере паролей

Контейнер backup запускается каждый час. Каждый запуск дампит обе базы данных, проверяет, что каждый дамп читаем, сохраняет локальную копию, отправляет в ваш пункт назначения rclone, проверяет соответствие загрузки и только затем удаляет старые. Неудачный запуск никогда не сможет удалить вашу последнюю хорошую резервную копию.

docker compose run --rm backup now            # take one now
docker compose run --rm backup restore list   # see what exists
docker compose logs -f backup                 # watch the schedule

Настройте всё в .env. Подходит любой rclone-назначение: S3, Backblaze, Wasabi, MinIO, Hetzner. Оставьте BACKUP_REMOTE пустым, чтобы хранить резервные копии только на хосте.

Добавьте crypt-удалённый ресурс и укажите BACKUP_REMOTE на него. Тогда назначение будет получать только шифротекст, включая имена файлов.

BACKUP_REMOTE=crypt:
RCLONE_CONFIG_CRYPT_TYPE=crypt
RCLONE_CONFIG_CRYPT_REMOTE=s3:my-bucket/athena
RCLONE_CONFIG_CRYPT_PASSWORD=<rclone obscure ...>
RCLONE_CONFIG_CRYPT_PASSWORD2=<rclone obscure ...>

Храните оба пароля в своём менеджере паролей. Без них резервные копии нечитаемы, в том числе вами.

Восстановление

Потренируйтесь в этом до того, как понадобится. Восстановление, которое никто не запускал, — это догадка.

docker compose run --rm backup restore list
docker compose stop wikijs mcp indexer dashboard
docker compose run --rm backup restore run 2026-08-18T115529Z
docker compose start wikijs mcp indexer dashboard

Оно попросит ввести имя базы данных для подтверждения. restore fetch <stamp> загружает резервную копию без восстановления и сообщает, читаем ли каждый дамп.

Поисковый индекс восстанавливается сам после этого: индексатор перечитывает каждую страницу и заново встраивает всё, содержимое чего изменилось.


Конфигурация

Всё берётся из окружения. Каждый сервис проверяет свою конфигурацию при запуске и завершается со списком того, что не так, так что опечатка проявляется сразу, а не в три часа ночи.

Пять секретов, все созданы вами. Никакие учётные данные, принадлежащие Claude, OpenAI или кому-либо ещё, никогда не хранятся в .env.

Секрет

Кому принадлежит

Защищает

POSTGRES_PASSWORD

postgres, mcp, indexer

полный доступ к базе данных

WIKI_API_TOKEN

mcp, indexer

API Wiki.js

MCP_TOKEN

mcp

конечную точку MCP

DASHBOARD_TOKEN

dashboard

вход в панель управления

DASHBOARD_DB_PASSWORD

dashboard, mcp, indexer

роль базы данных только на SELECT

Что запускается

Сервис

Порт

Что это такое

postgres

internal

Данные Wiki.js, журнал активности и векторы через pgvector

wikijs

3000

Вики, которую вы читаете и редактируете

embeddings

internal

Модель встраивания, на CPU

mcp

8080

То, к чему подключается ваш ИИ

indexer

8081

Синхронизирует векторный индекс с вики

dashboard

8082

Метрики

backup

none

Ежечасный дамп, проверка, отправка

Отдельной векторной базы данных нет. Векторы живут в Postgres, так что одна резервная копия покрывает всё.

На ARM-хостах образ embeddings опубликован только для linux/amd64 и не будет работать нативно. Укажите EMBEDDINGS_PROVIDER=openai на совместимую с OpenAI конечную точку, например Ollama.

Переменная

По умолчанию

Примечания

ATHENA_DATA_DIR

../data

Корень всех bind-монтирований, рядом с checkout

ATHENA_INSTANCE_NAME

Athena

Отображается на странице входа и панели управления

ATHENA_EDGE_NETWORK

athena-edge

Сеть, к которой подключается ваш reverse proxy

ATHENA_EDGE_EXTERNAL

false

true, если платформа предоставляет эту сеть

ATHENA_LOG_LEVEL

info

debug, info, warn, error

TZ

UTC

Метки происхождения и пути с датами

POSTGRES_DB

wiki

База данных Wiki.js

ATHENA_DB

athena

Журнал активности и векторы, создаётся автоматически

WIKI_LOCALE

en

Язык содержимого

WIKI_PUBLIC_URL

http://localhost:3000

Используется для ссылок в панели управления

MCP_PUBLIC_URL

обязательно

Голый https-источник, без пути

METRICS_CACHE_SECONDS

60

Как долго повторно используются данные панели

EMBEDDINGS_MODEL

intfloat/multilingual-e5-small

Изменение приводит к переиндексации всего

EMBEDDINGS_PROVIDER

tei

tei или openai для совместимой конечной точки

INDEX_INTERVAL_SECONDS

300

Интервал полной синхронизации

CHUNK_MAX_CHARS

1200

Максимальный размер чанка

BACKUP_*

см. .env.example

Расписание, хранение, rclone-назначение

Изменение EMBEDDINGS_MODEL меняет ширину вектора, а векторы от двух моделей нельзя сравнивать, поэтому индексатор перестраивает таблицу и заново встраивает каждую страницу. Содержимое Wiki.js не затрагивается.


Безопасность

Каждый контейнер получает только те учётные данные, которые использует. Панель управления не получает ни POSTGRES_PASSWORD, ни WIKI_API_TOKEN, так что её компрометация даёт доступ на чтение и не более. Проверяйте в любое время:

docker compose exec dashboard env | grep -iE 'PASSWORD|TOKEN'
  • Неаутентифицированные MCP-запросы получают 401 без объяснения.

  • Оба пути входа ограничивают после 5 неудач на адрес; ссылка для входа сгорает после 3 попыток.

  • Сессии панели управления — это подписанные куки с сроком действия и nonce, никогда не токен. HttpOnly, SameSite=Strict, межсайтовые POST-запросы отклоняются.

  • Сравнение секретов выполняется за постоянное время.

  • Заголовки прокси доверяются только с loopback, так что удалённый клиент не может подделать свой адрес, чтобы обойти ограничение.

  • Контейнеры работают от непривилегированного пользователя.

Намеренно отсутствует: разрешения по инструментам. Любой аутентифицированный клиент может вызывать все инструменты, включая delete_page. Wiki.js хранит историю страниц, так что удаление обратимо, но относитесь к MCP_TOKEN как к полному доступу на запись к вашей вики. Athena также предполагает одного владельца; у Wiki.js есть свои пользователи для чтения вики.

MCP_TOKEN работает двумя способами, потому что ИИ-клиенты аутентифицируются двумя способами.

Клиенты с заголовком, такие как Cursor и Claude Desktop, отправляют Authorization: Bearer <MCP_TOKEN>. Это весь механизм.

Claude.ai в браузере так не может. Его пользовательские коннекторы поддерживают только OAuth, а спецификация MCP требует динамической регистрации клиента, поэтому сервер, принимающий браузерный Claude, должен быть сервером авторизации. Athena реализует один:

  1. Claude регистрируется и получает сгенерированный client id. Никакой ваш секрет не задействован.

  2. Claude отправляет вас на страницу входа на вашем собственном сервере.

  3. Вы вводите MCP_TOKEN как пароль. Это шаг одобрения человеком.

  4. Athena выдаёт Claude токены, которые Athena создала сама.

Эти токены записываются в data/mcp/oauth-state.json, никогда в .env. Отзовите их с помощью:

rm data/mcp/oauth-state.json && docker compose restart mcp

Если вы никогда не используете браузерный Claude, игнорируйте всё это. Путь с bearer-токеном его не затрагивает.


Эксплуатация

docker compose logs -f mcp
curl -s localhost:8081/stats | python3 -m json.tool

# Force a full reconciliation
docker compose exec -T indexer bun -e 'await fetch("http://127.0.0.1:8081/sync",{method:"POST"})'

Обновление. Всегда сначала делайте резервную копию: Wiki.js запускает свои собственные миграции при старте, и их нельзя отменить остановкой контейнера.

docker compose run --rm backup now
git pull && docker compose build && docker compose up -d

Симптом

Причина

Сервис завершается при запуске, выводя конфиг

Отсутствует обязательная переменная или всё ещё CHANGE_ME

Claude не может подключиться, нет страницы входа

MCP_PUBLIC_URL содержит путь или не https

Вход отклоняет правильный пароль

Ограничение после 5 неудач, подождите минуту

Нет результатов семантического поиска

embeddings всё ещё загружается, проверьте его логи

Панель управления показывает отстающие страницы

Индексатор догоняет, проверьте его логи

Вызовы инструментов возвращают 401

Файл состояния очищен или токен изменён, переподключите клиент

Postgres завершается, "database files are incompatible"

Основная версия образа изменилась под существующими данными

Postgres не прочитает каталог данных, записанный другой основной версией. Сделайте дамп, очистите, восстановите:

docker compose run --rm backup now             # on the OLD version
docker compose down
mv data/postgres data/postgres.old             # keep until you are happy
# edit the image tag in docker-compose.yml and the FROM line in
# docker/backup/Dockerfile to the same new major version
docker compose build backup
docker compose up -d postgres
docker compose run --rm backup restore run <stamp>   # once per database
docker compose up -d

Векторный индекс восстанавливается вместе со всем остальным, так что ничего не перевстраивается.


Разработка

bun install
bun test          # 145 tests
bun run check     # typecheck, lint, test

Пакет

Что это такое

packages/core

Клиент Wiki.js, разбиение на чанки, слияние поиска, векторы, аутентификация, конфигурация

packages/mcp

MCP-сервер, OAuth-сервер авторизации, инструменты

packages/indexer

Цикл синхронизации, встраивания, запись векторов, внутренний поисковый API

packages/dashboard

Интерфейс метрик

docker/backup

Контейнер резервного копирования и восстановления

website/

Одностраничный сайт

themes/wikijs/

Опциональные CSS и JS для Wiki.js

Bun запускает TypeScript напрямую, поэтому этапа сборки нет, и контейнеры работают с исходным кодом. bun run --cwd packages/dashboard preview создаёт preview.html с примерными данными.

Как это работает вместе:

  • Индексатор инкрементальный. Он снимает отпечаток каждой страницы и пропускает всё неизменённое, так что проход по нетронутой вики ничего не стоит.

  • Каждый сервис с административными учётными данными подготавливает базу данных при запуске под advisory lock, поэтому порядок запуска не важен.

  • Панель управления — это серверный HTML с встроенными SVG-диаграммами. Никакого клиентского JavaScript, никакой библиотеки диаграмм, никакого этапа сборки.

Публикация сайта. website/index.html развёртывается на GitHub Pages при каждом пуше, который его затрагивает. Включите Pages один раз вручную: Settings → Pages → Build and deployment → Source: GitHub Actions. Это нельзя автоматизировать, потому что создание сайта Pages требует токена с правами администратора, а GITHUB_TOKEN их не имеет.


Лицензия

Apache-2.0. См. LICENSE.

Related MCP Connectors

Related MCP Servers