Инсталиране на Nessie
Пускайте Nessie на инфраструктура по ваш избор – от един компютър до управляван облак.
Какво ви трябва, преди да започнете
Nessie е pnpm монорепо с Node услуги и два статични пакета. Независимо как го пускате, първо трябва да имате едни и същи четири неща.
- PostgreSQL с
pgvector. Задължително е и не може да се замени с друго — миграциите изпълняватCREATE EXTENSION vectorи създават колониvector(1024). Продукцията работи сpgvector/pgvector:pg17. - Node 22 и pnpm. Всеки Dockerfile в
infrastructure/docker/се изгражда върхуnode:22-slim, а хранилището фиксира мениджъра на пакети в кореннияpackage.json(pnpm@10.22.0). - Крайна точка за модел, съвместима с OpenAI, и ключ за нея. Агентите и векторните вграждания (embeddings) минават през нея. Вижте „Конфигуриране на инференс“ по-долу.
- Обектно хранилище, съвместимо с S3, за всяко внедряване, което не е локалната среда за разработка. Качванията на локален диск се отхвърлят извън режима
local, а този отказ е грешка при стартиране, а не деградиран старт — вижте „Една машина или управляван облак“.
Docker и обратен прокси са нужни за пътя с контейнери, описан по-нататък, а домейн и TLS — за всичко, достъпно от интернет.
Изискванията към хардуера — минималният процесор, памет и диск за даден брой хора — не са посочени никъде в хранилището, затова и тази страница не ги посочва.
Компонентите и как се свързват
| Компонент | Какво е | Входящ достъп |
|---|---|---|
api/ | HTTP/WebSocket API на Fastify. Всички клиенти общуват само с него. | Да |
worker/ | Оркестрация на агентите и фонови изпълнения. Чете опашката от задачи и не отваря порт. | Не |
admin/ | Интерфейсът на продукта — статичен пакет, изграден с Vite и сервиран от nginx. | Да |
web/ | Публичният сайт. Статичен, незадължителен и не е свързан с работата на продукта. | Да |
| PostgreSQL | Базата данни, опашката от задачи и реалтайм шината — и трите. | Не |
| Обектно хранилище | Прикачени файлове, аватари и файлове от базата знания. Съвместимо с S3; в продукция — MinIO. | Не |
gateway/ | Незадължителен релей за push известия към APNs и FCM. Изключен, докато не го включите съзнателно. | Да |
API и worker са един образ. Двата пакета са тясно свързани, затова Dockerfile.app изгражда целия екип веднъж, а контейнерът на worker-а презаписва командата с node worker/dist/index.js. В режим local няма отделен контейнер за worker: API изпълнява worker-а, вграден в собствения си процес.
Няма Redis. Опашката от задачи и транспортът за реално време работят и двата върху Postgres — решение, а не пропуск. Облачното дърво за Google Cloud изтрива модулите си за Pub/Sub и Redis по същата причина.
Портове
В локалната разработка по подразбиране API е на 5454, а администраторският интерфейс — на 5455. Едно копие на хранилището може да вземе собствена двойка, като зададете NESSIE_API_PORT и NESSIE_ADMIN_PORT в средата или във .env на хранилището; всеки инструмент в хранилището, включително браузърните тестове, чете същите две стойности. Изберете двойката, преди да стартирате, и я запазете: преместването на работеща услуга на друг порт оставя администраторския интерфейс да говори с грешния API.
Продукцията е различна и правилото по-горе не я засяга. Контейнерът на API слуша вътрешно на 5554 (фиксиран с NESSIE_API_PORT), а релеят за push — на 5556, и двата зад обратния прокси.
Пускане на една машина за разработка
Хранилището включва Compose файл само с база данни, което е всичко, от което се нуждае локалният цикъл.
pnpm install
docker compose -f infrastructure/compose/docker-compose.yml up -d postgresТози контейнер публикува Postgres на 55432, като потребителят, паролата и базата данни се казват nessie. Поставете низа за връзка във файл .env в корена на хранилището — скриптът за разработка на API го чете — след това приложете миграциите и изградете worker-а веднъж, защото в локален режим API зарежда worker-а от компилираната му папка dist:
DATABASE_URL=postgresql://nessie:nessie@localhost:55432/nessie
pnpm --filter @nessie/api prisma:migrate:deploy
pnpm --filter @nessie/worker build
pnpm devpnpm dev пуска API и администраторския интерфейс паралелно с горещо презареждане, на 5454 и 5455. Проверете дали API отговаря с GET /api/health.
Postgres и изискването за pgvector
Три разширения се създават от самите миграции — vector, pg_trgm и pgcrypto, — затова ролята, която ги пуска, трябва да има право да създава разширения. При управляван Postgres това е първото нещо, което трябва да проверите.
Широчината на векторите е част от схемата, а не настройка. thoughts.embedding, thought_recalls.query_embedding и knowledge_page_chunks.embedding са колони vector(N), а N е записано веднъж като EMBEDDING_DIMENSIONS в packages/schemas/src/embedding.ts — в момента 1024, родната широчина на embedding модела, който използва продукцията. Всяка заявка за вграждане изпраща тази широчина, така че доставчик, който би отговорил с друга, се проваля шумно, вместо да записва вектори, които базата данни отхвърля. Преминаването към embedding модел с друга широчина означава да промените тази константа, да напишете миграция, която преоформя трите колони, и да извършите ново вграждане: вектори с различна широчина не се преобразуват, затова миграцията ги нулира, вместо да ги съкращава.
Ако пускате Nessie до други приложения в обща Docker мрежа, дайте на контейнера с базата данни различно име. Услугата в Compose на проекта се казва nessie-postgres и никога postgres, защото името на услугата става DNS псевдоним в общата мрежа, а сблъсък кара postgres:5432 да разпределя заявките по кръг (round-robin) между две бази данни.
Конфигурация
Конфигурацията при изпълнение е на слоеве: файл nessie.config.json, монтиран само за четене в API и worker, а над него — променливи на средата. Съответствието между двете е ConfigEnvMap в packages/config и то е авторитетният списък.
NESSIE_MODE избира вида внедряване: local (по подразбиране и пътят за разработчици), hosted или selfHosted — което задава самостоятелно инсталиране. Той изключва входа за разработка и изисква списък с разрешени CORS адреси.
Това са настройките, без които внедряване с selfHosted няма да стартира или няма да работи:
| Променлива | Защо |
|---|---|
DATABASE_URL / NESSIE_DB_URL | Низът за връзка към Postgres. |
NESSIE_AUTH_SECRET | 32 байта в шестнадесетичен вид (openssl rand -hex 32). Подписва сесиите и токените за първоначално настройване. Стартирането прекъсва без нея извън режим local, а стойността трябва да е една и съща на всяка реплика и при всяко внедряване, иначе всяка сесия умира при всяко издание. |
NESSIE_ENCRYPTION_KEY_RING, NESSIE_ENCRYPTION_ACTIVE_KEY_VERSION | JSON ключов пръстен и етикетът, който избира активния корен; задължителни извън режим local. Това е отделен корен от подписващата тайна — ключовият пръстен шифрова всяка трайна тайна, която Nessie съхранява. Не използвайте един и същ за двете. |
NESSIE_STORAGE_PROVIDER | Трябва да е s3. filesystem е стойността по подразбиране и е отхвърлена в режимите hosted и selfHosted, така че внедряване, което нищо не задава тук, не стартира, вместо да записва качванията на диск, който друг контейнер не може да прочете. |
NESSIE_CORS_ORIGINS | Адресът на администраторския интерфейс. Администраторският интерфейс и API са отделни origin-и, а API трябва да включи в списъка си този, който обслужва. |
NESSIE_API_PUBLIC_URL, NESSIE_ADMIN_PUBLIC_URL | Използва се за създаване на OAuth адреси за пренасочване извън HTTP заявка. Без първия от тях API хвърля грешка при заявка. |
NESSIE_MODEL_PROVIDER, _BASE_URL, _API_KEY | Къде отива инференсът. Вижте по-долу. |
infrastructure/compose/.env.prod.example показва точния вид на всички тях, а незадължителните блокове — обектно хранилище, трезор за тайни, SSO, свързана поща — са закоментирани на място. Пълният каталог, включително всяка незадължителна настройка, е docs/deployment/configuration.md.
Внедряване с Docker Compose
Копирайте .env.prod.example като .env до docker-compose.prod.yml и го попълнете, след това вдигнете базата данни, изградете, мигрирайте и стартирайте. Редът има значение: миграциите и посевните данни (seeds) се изпълняват срещу база, която вече работи, преди някоя услуга да обслужва заявки.
COMPOSE="docker compose -f infrastructure/compose/docker-compose.prod.yml"
bash infrastructure/compose/ensure-encryption-key-ring.sh infrastructure/compose/.env
$COMPOSE up -d nessie-postgres
$COMPOSE build nessie-api nessie-admin nessie-web
$COMPOSE run --rm --no-deps nessie-api pnpm --filter @nessie/api prisma:migrate:deploy
$COMPOSE run --rm --no-deps nessie-api pnpm --filter @nessie/api seed:connectors
$COMPOSE run --rm --no-deps nessie-api pnpm --filter @nessie/api seed:apps
$COMPOSE run --rm --no-deps nessie-api pnpm --filter @nessie/api reconcile
$COMPOSE up -dПродукционните Dockerfile-ове пускат линтъра на всеки пакет преди изграждането му, така че грешка при линтване е грешка при изграждането.
Стъпката reconcile не е незадължителна
reconcile засажда стандартните правила на политиката за всяка организация и попълва пропуснатите предоставяния на защитените MCP инструменти и на личния асистент. API преди правеше това при стартиране на всяка реплика; сега само се свързва, слуша и нищо друго. Затова без тази стъпка първата организация няма правила на политиката и всяко свързване на агент се отхвърля. Стъпката е идемпотентна, така че пускането ѝ при всяко внедряване е безплатно — а надграждане, приложено ръчно, също трябва да я пусне.
Първият акаунт на собственик
Новата инсталация selfHosted няма потребители, затова API създава еднократен токен за първоначално настройване и записва в журнала адрес за настройка. Токенът се пази в Postgres, а не в процес, така че всички реплики са съгласни за него.
docker compose -f infrastructure/compose/docker-compose.prod.yml logs nessie-api 2>&1 | grep bootstrapОтворете https://<your-admin-host>/bootstrap?token=<token> и създайте акаунта на собственика. Токенът изтича след 15 минути; за нов рестартирайте услугата API, за да се създаде нов.
Ако конфигурирате SSO, изобщо няма стъпка за първоначално настройване. Режимът на първоначално настройване се изключва в момента, в който се включи външен доставчик на удостоверяване — иначе би подменил екрана за вход — а първият човек, който влезе през SSO, създава стандартния екип и става негов собственик.
Конфигуриране на инференс
Чатът е съвместим с OpenAI. NESSIE_MODEL_BASE_URL е адресът на крайната точка, NESSIE_MODEL_API_KEY — bearer токенът, а NESSIE_MODEL_PROVIDER назовава адаптера. Nessie компилира адаптери за openai, kimi и deepseek, а към всичко друго се свързва през общия конектор, съвместим с OpenAI. NESSIE_MODEL_NAME задава моделът по подразбиране за внедряването, който все пак отстъпва пред избора на модел за отделния агент.
Вгражданията се насочват отделно, защото чат доставчикът може изобщо да не предлага вграждания — например DeepSeek няма крайна точка за вграждания. Всяко незададено поле NESSIE_EMBEDDING_* наследява чат доставчика, така че внедряване, което не задава нито едно от тях, вгражда през крайната точка на чата. Задайте NESSIE_EMBEDDING_PROVIDER, NESSIE_EMBEDDING_MODEL и — при прокси, което насочва по услуга — сегмента на услугата, когато чат доставчикът не може да вгражда. Грешката тук е тиха, а не шумна: паметта при извличане и търсенето в базата знания преминават към лексикален канал и продължават, като записват в журнала kb_search: query embedding failed.
Собственото счетоводство на Nessie за разходите — дневникът на токените за всяка организация и контролът на бюджета — не зависи от това към коя крайна точка го насочвате и работи еднакво, независимо дали е конфигурирано подписано атрибутиране на всяко извикване.
Вход в системата
Nessie удостоверява хората през UnlikeOtherAI, който управлява и структурата на организациите и екипите. Страницата за вход в администраторския интерфейс показва един бутон „Вход / Регистрация“.
Интеграцията е по схемата config-JWT, а не по стандартен OIDC: API отдава подписан RS256 конфигурационен документ на GET /api/auth/sso/config и съответния JWKS на GET /.well-known/jwks.json, и двата на хоста на API. Настройването е еднократна последователност — генерирайте RSA-2048 двойка ключове и задайте UOA_CONFIG_JWT_PRIVATE_KEY_B64, UOA_CONFIG_JWT_KID, UOA_DOMAIN, UOA_CONFIG_URL, UOA_JWKS_URL, UOA_REDIRECT_URL и UOA_CONTACT_EMAIL; натиснете бутона веднъж, което създава заявка за интеграция; изчакайте тя да бъде одобрена, което изпраща еднократен линк с клиентската тайна към адреса за контакт; задайте UOA_CLIENT_SECRET и рестартирайте API. kid трябва да е уникален за всеки домейн.
Пускане зад домейн, с TLS
Внедряването обслужва три имена: публичния сайт, администраторския интерфейс и API. Адресите на администраторския интерфейс и на API не са взаимозаменяеми. Dockerfile.admin вгражда VITE_API_BASE_URL в пакета по време на изграждане, така че всеки изграден администраторски артефакт — включително десктоп версия, която го вгражда — трябва да е изграден срещу адреса на API. Ако го изградите срещу собствения адрес на администраторския интерфейс, /api/auth/providers се разрешава до HTML обвивката на администраторския интерфейс и входът остава блокиран на „Зареждане на доставчиците...“.
Продукцията прекратява TLS в Caddy, който получава сертификати за всяко име на хост чрез HTTP-01 и не се нуждае от приставка за DNS доставчик за това. Насочете прокси-то към контейнерите на администраторския интерфейс и сайта на порт 80, а към API — на вътрешния му порт.
Задайте NESSIE_API_TRUSTED_PROXY_HOPS на броя прокси пред API — 1 зад един обратен прокси. По подразбиране е 0, което напълно игнорира X-Forwarded-For. Това е едно решение с две последствия: то също определя ключа за ограниченията на честотата при удостоверяване, така че твърде ниска стойност означава, че всеки клиент споделя брояча на прокси-то, а твърде висока позволява на клиента да подправи собствения си адрес. Задавайте ниско и измервайте.
Обслужването на всяка организация и екип на собствен поддомейн е възможно и е изключено по подразбиране. Управлява се от NESSIE_TEAM_HOST_BASE_DOMAIN; ако не е зададена, клиентите изобщо не се насочват по име на хост, а проверката за издаване на сертификати NESSIE_TLS_CHECK_KEY отказва всичко, докато не е зададена — което е правилното по подразбиране за внедряване, което не ползва тази функция.
Проверки на състоянието
| Крайна точка | На какъв въпрос отговаря |
|---|---|
GET /api/health | Жив ли е този процес? Отказва само след като е започнал да се изпразва. |
GET /api/health/ready | Може ли тази реплика да приема нови заявки? Това е крайната точка, към която да насочите балансьора на натоварването. |
GET /api/ops/health | Как е внедряването? Отчет за човек — сигнали от работниците, дължина на опашката, мъртви задачи — а не сонда. |
При SIGTERM API се изпразва, вместо да спре веднага. NESSIE_SHUTDOWN_TIMEOUT_MS ограничава това изпразване и по подразбиране е 25 секунди; дръжте го под гратисния период, който оркестраторът дава на контейнера, иначе средата убива процеса, преди изпразването да е свършило каквото и да е.
Надграждане
Надграждането е prisma migrate deploy срещу съществуващата база, после посевните данни, после reconcile. Същите четири команди като при първото внедряване.
Пътят е тестван за маршрута, който реално следва самостоятелният потребител, а не върху нова база: CI възстановява снимка, направена двадесет миграции назад, мигрира я до текущия връх, проверява схемата и пуска димна проверка върху основните таблици. Всяко издание в този следящ прозорец е доказан източник за надграждане. Папките с миграции са неизменими, след като са добавени в хранилището, а стъпка за линтване проваля изграждането, ако някоя бъде преименувана, преномерирана, изтрита или променена, защото всяко от тези нарушава migrate deploy за база, която вече е записала стария ред.
Една неуспешна миграция спира всяко следващо внедряване. migrate deploy спира при първата неуспешна и отказва всяка следваща миграция с P3009, така че инсталацията остава на старото издание, докато някой не я освободи ръчно. Продукцията на самия проект прекара един ден, отхвърляйки всяко внедряване по този начин. За да освободите една, трябва да потвърдите, че неуспешната миграция наистина е била върната назад — нито един от обектите, които създава, не трябва да съществува, а всичко, което променя, трябва да е в първоначалния си вид — и едва тогава да я отбележите като върната назад. Миграция, която е приложена наполовина, не е кандидат.
Резервното копиране и възстановяването за самостоятелното внедряване с Docker Compose не са документирани в хранилището, затова тази страница не описва процедура за тях.
Една машина или управляван облак
Пътят с един хост и Docker Compose по-горе е този, който е доказан: на него работи продукцията на проекта — на един сървър, с базата данни, обектното хранилище, API, worker-а, администраторския интерфейс и сайта като контейнери зад споделен обратен прокси.
Преминаването отвъд една машина променя три неща и всички са налагани, а не препоръчвани. Извън режим local Nessie пуска API и worker като отделни процеси и в няколко копия на всеки, така че всяка възможност, която предполага, че един процес притежава диска на машината, се отказва: файловото обектно хранилище (грешка при стартиране), шаблоните на изпълнителната среда с доставчик docker (неактивни, а осигуряването се проваля шумно) и вградените инструменти file_read, file_write и file_glob (неуспешен резултат от инструмент, който агентът може да прочете). Всеки отказ назовава настройката, режима и какво да се използва вместо нея.
За управляван облак хранилището съдържа дърво Terraform в infrastructure/terraform/, насочено към Google Cloud: API като услуга Cloud Run с минимум една инстанция — той държи постоянен клиент на Postgres LISTEN и изпълнява поддържащи сканирания, така че мащабирането до нула спира доставката в реално време — worker-ът като worker pool в Cloud Run, защото не отваря порт и услуга никога не би станала готова, задача за миграция, която се изпълнява преди всяко внедряване, Cloud SQL for PostgreSQL 17 и GCS кошница, достъпвана през съвместимия със S3 бекенд.
Този път е написан, но не е изпробван на практика. Хранилището го казва ясно и тази страница също: няма Google Cloud проект за него, нищо в дървото не е планирано или приложено върху реален проект, пътят за многочастно качване в GCS не е тестван върху реална кошница, типът ресурс worker pool не е валидиран, а броят хопове на доверения прокси в него е заместител, който трябва да се измери, а не измерена стойност. Администраторският интерфейс и публичният сайт изобщо не са моделирани там. Разглеждайте го като отправна точка, която проверявате, а не като поддържано внедряване.
Къде живеят данните на внедряването, какво съхранява софтуерът и какво умишлено не съхранява, е отделна страница: Създадено в ЕС и къде се намират вашите данни.