Инсталирање на Nessie
Пуштете го Nessie на инфраструктура што ја избирате, од еден компјутер до управуван облак.
Што ви треба пред да започнете
Nessie е pnpm monorepo со 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 и клуч за неа. И агентите и вградувањата минуваат низ неа. Видете „Конфигурирање на инференцијата“ подолу.
- Складиште на објекти компатибилно со S3, за секое распоредување што не е локалното за програмерот. Прикачувањата на локален диск се одбиваат надвор од режимот
local, а тоа одбивање е неуспех при стартување, а не деградирано подигнување — видете „На една машина или во управуван облак“.
Docker и обратен прокси се потребни за контејнерскиот пат опишан подолу, а домен и TLS за сè што е достапно од интернет.
Големината на хардверот — минималниот процесор, меморија и диск за даден број луѓе — не е наведена никаде во репозиториумот, па ни оваа страница не ја наведува.
Компонентите и како се поврзуваат
| Компонента | Што е | Пристап однадвор |
|---|---|---|
api/ | HTTP/WebSocket API во Fastify. Секој клиент комуницира со него и со ништо друго. | Да |
worker/ | Организација на агентите и позадинските извршувања. Го проверува редот на задачи и не отвора порта. | Не |
admin/ | Административниот интерфејс — статички пакет изграден со Vite и послужуван од nginx. | Да |
web/ | Јавната веб-страница. Статична е, опционална и не е поврзана со работењето на производот. | Да |
| PostgreSQL | Базата на податоци, редот на задачи и реалновремениот канал — сите три. | Не |
| Складиште на објекти | Прикачувања, аватари и датотеки од базата на знаење. S3-компатибилно; во продукција MinIO. | Не |
gateway/ | Опционален релеј за push-пораки за APNs и FCM. Исклучен е, освен ако не го вклучите намерно. | Да |
API-то и работникот се една слика. Двата пакета се тесно поврзани, па Dockerfile.app го гради целиот тим еднаш, а контејнерот на работникот ја менува наредбата во node worker/dist/index.js. Во режимот local воопшто нема контејнер за работник: API-то го извршува работникот вграден во сопствениот процес.
Нема Redis. И редот на задачи и реалновременото пренесување се базирани на Postgres — по одлука, а не по пропуст. Дрвото на Terraform за 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-то ја чита — па примете ги миграциите и изградете го работникот еднаш, бидејќи во режимот local API-то го вчитува работникот од неговиот изграден 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-то и администраторскиот интерфејс паралелно, со hot reload, на 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, природната ширина на моделот за вградувања што го користи продукцијата. Секое барање за вградување ја испраќа таа ширина, па провајдер што би одговорил со друга ширина ќе падне гласно, наместо да запише вектори што базата ги одбива. Премин на модел за вградувања со друга ширина значи промена на таа константа, напишување миграција што го менува типот на трите колони и повторно вградување: вектори со различна ширина не се претвораат, па миграцијата ги става на null, наместо да ги скрати.
Ако го извршувате Nessie покрај други апликации на заедничка Docker-мрежа, дајте на контејнерот со базата посебно име. Услугата на проектот за Compose се вика nessie-postgres и никогаш postgres, бидејќи името на услугата станува DNS-алијас на заедничката мрежа, а судирот го прави postgres:5432 да се распределува наизменично (round-robin) помеѓу две бази.
Конфигурација
Конфигурацијата за време на извршување е по слоеви: датотека nessie.config.json монтирана само за читање во API-то и во работникот, со променливи на околината над неа. Мапирањето помеѓу двете е 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-то се одделни извори, а API-то мора да го дозволи изворот што го послужува. |
NESSIE_API_PUBLIC_URL, NESSIE_ADMIN_PUBLIC_URL | Се користи за создавање OAuth URI за пренасочување надвор од 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 и пополнете го, па подигнете ја базата, изградете, мигрирајте и стартувајте. Редоследот е важен: миграциите и почетните податоци се извршуваат врз база што веќе работи, пред која било услуга да почне да служи.
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-то создава еднократен токен за подигање и бележи URL за поставување. Токенот живее во 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, па инсталацијата останува на стариот издание додека некој рачно не го исчисти. Продукцијата на проектот еден ден ги одбиваше сите распоредувања на овој начин. Чистењето значи потврда дека неуспешната миграција навистина се вратила — ниту еден објект што таа го создава не треба да постои, а сè што менува треба да е во првичната форма — и дури тогаш да се реши како вратена. Миграција што била делумно применета не е кандидат.
Сигурносното копирање и враќањето за распоредувањето со сопствен хостинг и Compose не се документирани во репозиториумот, па оваа страница не опишува постапка за тоа.
На една машина или во управуван облак
Патот со една машина и Docker Compose е оној што е докажан: така работи продукцијата на проектот, на еден сервер, со базата, складиштето на објекти, API-то, работникот, администраторскиот интерфејс и страницата како контејнери зад заеднички обратен прокси.
Надминувањето на една машина менува три нешта, и сите се наметнати, а не препорачани. Надвор од режимот local, Nessie ги извршува API-то и работникот како одделни процеси и како повеќе копии од секој, па секоја можност што претпоставува дека еден процес го поседува дискот на машината се одбива: складиште на објекти на датотечен систем (неуспех при стартување), шаблони за извршното окружување со провајдерот docker (неактивни, а поставувањето паѓа гласно) и вградените алатки file_read, file_write и file_glob (неуспешен резултат од алатката што агентот може да го прочита). Секое одбивање ја именува поставката, режимот и она што треба да се користи наместо тоа.
За управуван облак, репозиториумот содржи дрво на Terraform во infrastructure/terraform/ за Google Cloud: API-то како услуга на Cloud Run со минимум една инстанца — тој држи постојан клиент на Postgres LISTEN и ги извршува одржувачките проверки, па ако се скали на нула, реалновременото испорачување запира — работникот како работен базен на Cloud Run, бидејќи нема порта и услугата никогаш не би била подготвена, задача за миграција што секое издание мора прво да ја помине, Cloud SQL за PostgreSQL 17 и GCS-bucket достапен преку S3-компатибилниот backend.
Тој пат е напишан, но не е испробан. Репозиториумот тоа го вели јасно, и оваа страница исто така: за него не постои проект на Google Cloud, ништо во дрвото не е планирано или применето врз вистински проект, патот на multipart-прикачувањето во GCS не е тестиран на вистински bucket, типот на ресурсот работен базен не е валидиран, а бројот на хопови на доверливиот прокси таму е привремен, за да се измери, а не измерен. Администраторскиот интерфејс и јавната страница таму воопшто не се моделирани. Третирајте го како почетна точка што ја проверувате, а не како поддржано распоредување.
Каде живеат податоците на распоредувањето, што софтверот ги чува и што намерно не го прави, е посебна страница: Направено во ЕУ и каде живеат вашите податоци.