nessie

Instalimi i Nessie

Ekzekutojeni Nessie në infrastrukturën që zgjidhni, nga një makinë e vetme deri te një cloud i menaxhuar.

Çfarë ju duhet para se të filloni

Nessie është një depo pnpm me shërbime Node dhe dy paketa statike. Në çfarëdo mënyre që ta ekzekutoni, këto katër gjëra duhet të ekzistojnë më parë.

  • PostgreSQL me pgvector. Nuk është opsionale dhe nuk zëvendësohet — migrimet ekzekutojnë CREATE EXTENSION vector dhe krijojnë kolona vector(1024). Prodhimi përdor pgvector/pgvector:pg17.
  • Node 22 dhe pnpm. Çdo Dockerfile në infrastructure/docker/ ndërtohet mbi node:22-slim, dhe depoja e fikson menaxherin e paketave në package.json të rrënjës (pnpm@10.22.0).
  • Një endpoint modeli i përputhshëm me OpenAI dhe një çelës për të. Agjentët dhe embedding-et kalojnë të dyja nëpërmjet tij. Shihni “Konfigurimi i inferencës” më poshtë.
  • Ruajtje objektesh e përputhshme me S3, për çdo vendosje që nuk është ajo lokale e zhvilluesit. Ngarkimet në diskun lokal refuzohen jashtë modalitetit local, dhe ky refuzim është dështim gjatë nisjes, jo një nisje e degraduar — shihni “Një makinë, ose një cloud i menaxhuar”.

Docker dhe një proxy i kundërt nevojiten për rrugën me kontejnerë të përshkruar më poshtë, ndërsa një domain dhe TLS nevojiten për çdo gjë që mund të arrihet nga interneti.

Madhësia e harduerit — CPU, memoria dhe disku minimal për një numër të caktuar njerëzish — nuk përcaktohet askund në depo, ndaj edhe kjo faqe nuk e përcakton.

Komponentët dhe si lidhen me njëri-tjetrin

KomponentiÇfarë ështëHyrja nga jashtë
api/API-ja HTTP/WebSocket e Fastify. Çdo klient flet me këtë dhe me asgjë tjetër.Po
worker/Orkestrimi i agjentëve dhe ekzekutimet në sfond. Pyet radhën e punëve dhe nuk lidh asnjë port.Jo
admin/Ndërfaqja e produktit — një paketë statike e ndërtuar nga Vite dhe e shërbyer nga nginx.Po
web/Faqja publike. Statike, opsionale dhe e pavarur nga ekzekutimi i produktit.Po
PostgreSQLBaza e të dhënave, radha e punëve dhe magjistrala e kohës reale, të tria bashkë.Jo
Ruajtja e objekteveBashkëngjitjet, avatarët dhe skedarët e bazës së njohurive. Të përputhshme me S3; MinIO në prodhim.Jo
gateway/Një rele opsionale shtytjeje për APNs dhe FCM. Është e çaktivizuar, përveç nëse e aktivizoni me qëllim.Po

API-ja dhe worker-i janë një imazh. Dy paketat janë të lidhura ngushtë, ndaj Dockerfile.app ndërton gjithçka një herë, dhe kontejneri i worker-it e mbishkruan komandën me node worker/dist/index.js. Në modalitetin local nuk ka fare kontejner worker: API-ja e ekzekuton worker-in brenda procesit të vet.

Nuk ka Redis. Radha e punëve dhe transporti i kohës reale mbështeten të dyja në Postgres, sipas një vendimi, jo nga mungesa e tij — struktura për Google Cloud i heq modulet e veta Pub/Sub dhe Redis për të njëjtën arsye.

Portet

Në zhvillimin lokal, si parazgjedhje API-ja është në 5454 dhe admin-i në 5455. Një checkout mund të marrë çiftin e vet duke vendosur NESSIE_API_PORT dhe NESSIE_ADMIN_PORT, në mjedis ose në .env të depos; çdo mjet i depos, përfshirë paketat e testimit në shfletues, lexon të njëjtat dy vlera. Zgjidheni çiftin përpara nisjes dhe mbajeni: zhvendosja e një shërbimi që po funksionon në një port tjetër e lë admin-in të flasë me API-n e gabuar.

Prodhimi është një çështje tjetër dhe nuk ndryshon nga ai rregull. Kontejneri i API-së dëgjon brenda në 5554 (i fiksuar me NESSIE_API_PORT), ndërsa relja e shtytjes në 5556, të dyja pas proxy-it të kundërt.

Ekzekutimi në një makinë, për zhvillim

Depoja përmban një skedar Compose vetëm me një bazë të dhënash, dhe kjo është gjithçka që i nevojitet ciklit lokal.

pnpm install
docker compose -f infrastructure/compose/docker-compose.yml up -d postgres

Ai kontejner e publikon Postgres-in në 55432, me përdoruesin, fjalëkalimin dhe bazën e të dhënave të emërtuara të gjitha nessie. Vendoseni stringun e lidhjes në një .env në rrënjën e depos — skripti i zhvillimit të API-së e lexon — pastaj aplikoni migrimet dhe ndërtoni worker-in një herë, sepse në modalitetin lokal API-ja e ngarkon worker-in nga dist-i i ndërtuar:

DATABASE_URL=postgresql://nessie:nessie@localhost:55432/nessie

pnpm --filter @nessie/api prisma:migrate:deploy
pnpm --filter @nessie/worker build
pnpm dev

pnpm dev ekzekuton API-në dhe admin-in njëkohësisht, me hot reload, në 5454 dhe 5455. Kontrolloni që API-ja u përgjigj te GET /api/health.

Postgres dhe kërkesa e pgvector

Tre zgjerime krijohen vetë nga migrimet — vector, pg_trgm dhe pgcrypto — kështu që roli që i ekzekuton duhet të lejohet të krijojë zgjerime. Në një Postgres të menaxhuar, kjo është gjëja që duhet kontrolluar para çdo gjëje tjetër.

Gjerësia e vektorit është pjesë e skemës, jo një cilësim. thoughts.embedding, thought_recalls.query_embedding dhe knowledge_page_chunks.embedding janë kolona vector(N), dhe N deklarohet një herë si EMBEDDING_DIMENSIONS në packages/schemas/src/embedding.ts — aktualisht 1024, gjerësia natyrore e modelit të embedding-ut që përdor prodhimi. Çdo kërkesë embedding e dërgon atë gjerësi, kështu që një ofrues që do t'i përgjigjej me një tjetër dështon me zë të lartë, në vend që të shkruajë vektorë që baza e të dhënave i refuzon. Kalimi në një model embedding me gjerësi tjetër do të thotë të ndryshoni atë konstante, të shkruani një migrim që ndryshon tipin e tri kolonave, dhe të ndërtoni embedding-et sërish: vektorët me gjerësi të ndryshme nuk konvertohen, prandaj migrimi i vendos NULL në vend që t'i shkurtojë.

Nëse e ekzekutoni Nessie krahas aplikacioneve të tjera në një rrjet të përbashkët Docker, jepini kontejnerit të bazës së të dhënave një emër të veçantë. Shërbimi i Compose i projektit quhet nessie-postgres dhe kurrë postgres, sepse emri i shërbimit bëhet alias DNS në rrjetin e përbashkët, dhe një përplasje bën që postgres:5432 të alternojë mes dy bazave të të dhënave.

Konfigurimi

Konfigurimi i kohës së ekzekutimit është shtresuar: një skedar nessie.config.json i montuar vetëm për lexim në API dhe në worker, me variablat e mjedisit mbi të. Përputhja midis të dyjave është ConfigEnvMap në packages/config, që është lista autoritare.

NESSIE_MODE zgjedh formën e vendosjes: local (parazgjedhja dhe rruga e zhvilluesit), hosted ose selfHosted — e cila është ajo që vendos një instalim vetë-hostuar. Ky modalitet çaktivizon hyrjen e zhvillimit (dev login) dhe kërkon një listë CORS të lejuar.

Këto janë cilësimet pa të cilat një vendosje selfHosted nuk do të nisë, ose nuk do të funksionojë:

VariabliPse
DATABASE_URL / NESSIE_DB_URLStringu i lidhjes me Postgres.
NESSIE_AUTH_SECRET32 bajt në hex (openssl rand -hex 32). Nënshkruan sesionet dhe token-et e nisjes. Nisja hedh gabim pa të jashtë modalitetit local, dhe duhet të jetë e njëjta vlerë në çdo replikë dhe gjatë çdo vendosjeje, përndryshe çdo sesion vdes në çdo publikim.
NESSIE_ENCRYPTION_KEY_RING, NESSIE_ENCRYPTION_ACTIVE_KEY_VERSIONNjë unazë çelësash JSON dhe etiketa që zgjedh rrënjën aktive, të kërkuara jashtë modalitetit local. Kjo është një rrënjë e veçantë nga sekreti i nënshkrimit — unaza enkripton çdo sekret të qëndrueshëm që Nessie ruan. Mos i ripërdorni njëra si tjetra.
NESSIE_STORAGE_PROVIDERDuhet të jetë s3. filesystem është parazgjedhja dhe refuzohet në modalitetet hosted dhe selfHosted, kështu që një vendosje që nuk vendos asgjë këtu dështon të nisë, në vend që të shkruajë ngarkimet në një disk që asnjë kontejner tjetër nuk mund ta lexojë.
NESSIE_CORS_ORIGINSOrigjina e admin-it. Admin-i dhe API-ja janë origjina të ndara, dhe API-ja duhet ta vendosë në listën e lejuar atë që shërben.
NESSIE_API_PUBLIC_URL, NESSIE_ADMIN_PUBLIC_URLPërdoren për të krijuar URI-të e ridrejtimit OAuth jashtë një kërkese HTTP. API-ja hedh gabim në kohën e kërkesës pa të parën.
NESSIE_MODEL_PROVIDER, _BASE_URL, _API_KEYKu shkon inferenca. Shihni më poshtë.

infrastructure/compose/.env.prod.example ka formën e saktë të të gjitha, me blloqet opsionale — ruajtjen e objekteve, kasafortën e sekreteve, SSO-n, postën e lidhur — të komentuara në vend. Katalogu i plotë, duke përfshirë çdo cilësim opsional, është docs/deployment/configuration.md.

Vendosja me Docker Compose

Kopjoni .env.prod.example në .env pranë docker-compose.prod.yml dhe plotësojeni, pastaj ngrini bazën e të dhënave, ndërtoni, migroni dhe nisni. Rendi ka rëndësi: migrimet dhe seed-et ekzekutohen kundër një baze të dhënash që është tashmë e ngritur, përpara se ndonjë shërbim të jetë duke shërbyer.

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-at e prodhimit ekzekutojnë lint-in e secilës paketë përpara se ta ndërtojnë, kështu që një dështim i lint-it është dështim i ndërtimit.

Hapi reconcile nuk është opsional

reconcile mbjell rregullat e parazgjedhura të politikës së çdo organizate dhe plotëson lejet e mjeteve të mbrojtura MCP dhe të Asistentit Personal. API-ja e bënte këtë në nisje, në çdo replikë; tani vetëm lidhet, dëgjon dhe nuk bën asgjë tjetër, kështu që pa këtë hap, organizata e parë nuk ka rregulla politike dhe çdo lidhje agjenti refuzohet. Është idempotent, kështu që ekzekutimi i tij në çdo vendosje nuk kushton asgjë — dhe një përditësim i aplikuar me dorë duhet ta ekzekutojë edhe ai.

Llogaria e parë e pronarit

Një instalim i ri selfHosted nuk ka përdorues, kështu që API-ja nxjerr një token nisjeje për një përdorim të vetëm dhe regjistron një URL konfigurimi. Token-i ruhet në Postgres dhe jo në një proces, kështu që çdo replikë bie dakord për të.

docker compose -f infrastructure/compose/docker-compose.prod.yml logs nessie-api 2>&1 | grep bootstrap

Hapni https://<your-admin-host>/bootstrap?token=<token> dhe krijoni llogarinë e pronarit. Token-i skadon pas 15 minutash; rinisni shërbimin e API-së për të nxjerrë një të ri.

Nëse konfiguroni SSO, nuk ka fare hap nisjeje. Modaliteti i nisjes pezullohet në çastin kur aktivizohet një ofrues i jashtëm autentikimi — përndryshe do të rrëmbente ekranin e hyrjes — dhe personi i parë që hyn përmes SSO e inicializon ekipin e parazgjedhur dhe bëhet pronari i tij.

Konfigurimi i inferencës

Chat-i është i përputhshëm me OpenAI. NESSIE_MODEL_BASE_URL është endpoint-i, NESSIE_MODEL_API_KEY bearer-i, dhe NESSIE_MODEL_PROVIDER emëron adapterin — Nessie ka adaptera të përpiluar për openai, kimi dhe deepseek, dhe arrin çdo tjetër nëpërmjet lidhësit të përgjithshëm të përputhshëm me OpenAI. NESSIE_MODEL_NAME vendos modelin parazgjedhur të vendosjes, të cilin përzgjedhja e modelit të vetë agjentit e mbizotëron.

Embedding-et dirigjohen veçmas, sepse ofruesi i chat-it mund të mos i shërbejë fare — DeepSeek, për shembull, nuk ka endpoint embedding-esh. Çdo fushë NESSIE_EMBEDDING_* që nuk vendoset trashëgon ofruesin e chat-it, kështu që një vendosje që nuk vendos asnjërën embedon nëpërmjet endpoint-it të chat-it. Vendosni NESSIE_EMBEDDING_PROVIDER, NESSIE_EMBEDDING_MODEL dhe, në një proxy që dirigjon sipas shërbimit, segmentin e shërbimit, kur ofruesi i chat-it nuk mund të embedojë. Gabimi në këtë fushë dështon në heshtje, jo me zë të lartë: rikujtimi i kujtesës dhe kërkimi në bazën e njohurive bien në një kanal leksikor dhe vazhdojnë, duke regjistruar kb_search: query embedding failed ndërsa veprojnë.

Kontabiliteti i shpenzimeve të vetë Nessie — ledger-i i tokenëve për organizatë dhe porta e buxhetit — nuk preket nga endpoint-i ku e drejtoni, dhe funksionon njëlloj pavarësisht nëse është konfiguruar atribuimi i nënshkruar për çdo thirrje apo jo.

Hyrja

Nessie i autentikon njerëzit nëpërmjet UnlikeOtherAI, i cili menaxhon edhe strukturën e organizatës dhe të ekipeve. Faqja e hyrjes në admin shfaq një buton të vetëm “Login / Register”.

Integrimi është një rrjedhë config-JWT, jo OIDC standarde: API-ja shërben një dokument konfigurimi të nënshkruar me RS256 te GET /api/auth/sso/config dhe JWKS-in përkatës te GET /.well-known/jwks.json, që të dyja në hostin e API-së. Ngritja e tij është një sekuencë që bëhet një herë — gjeneroni një çift çelësash RSA-2048 dhe vendosni UOA_CONFIG_JWT_PRIVATE_KEY_B64, UOA_CONFIG_JWT_KID, UOA_DOMAIN, UOA_CONFIG_URL, UOA_JWKS_URL, UOA_REDIRECT_URL dhe UOA_CONTACT_EMAIL; klikoni butonin një herë, gjë që ngre një kërkesë integrimi; prisni që ajo të miratohet, gjë që dërgon te adresa e kontaktit një lidhje për një përdorim të vetëm me sekretin e klientit; vendosni UOA_CLIENT_SECRET dhe rinisni API-në. kid-i duhet të jetë unik për çdo domain.

Vendosja pas një domain-i, me TLS

Një vendosje shërben tri emra: faqen publike, admin-in dhe API-në. Origjinat e admin-it dhe të API-së nuk janë të ndërrueshme. Dockerfile.admin e ngulit VITE_API_BASE_URL në paketë gjatë ndërtimit, kështu që çdo artefakt i ndërtuar i admin-it — përfshirë një ndërtim desktop që e përfshin — duhet të ndërtohet kundër origjinës së API-së. Nëse ndërtohet kundër origjinës së vetë admin-it, /api/auth/providers zgjidhet në guaskën HTML të admin-it, dhe hyrja mbetet e ngecur te “Loading providers...”.

Prodhimi e mbyll TLS te Caddy, i cili merr certifikata për çdo emër hosti përmes HTTP-01 dhe nuk ka nevojë për shtojcë të ofruesit të DNS-it për këtë. Drejtojeni proxy-n drejt kontejnerëve të admin-it dhe të faqes në portin 80, dhe drejt API-së në portin e saj të brendshëm.

Vendosni NESSIE_API_TRUSTED_PROXY_HOPS në numrin e proxy-ve përpara API-së — 1 pas një proxy-i të kundërt të vetëm. Parazgjedhja është 0, që injoron plotësisht X-Forwarded-For. Ky është një vendim me dy pasoja: është gjithashtu ajo mbi të cilën bazohen kufijtë e shpejtësisë së autentikimit, kështu që nëse është shumë i ulët çdo klient ndan kovën e proxy-t, ndërsa nëse është shumë i lartë një klient mund ta falsifikojë adresën e vet. Preferoni vlerën e ulët dhe matni.

Shërbimi i secilës organizatë dhe ekipi në nëndomain-in e vet është i mundshëm dhe i çaktivizuar si parazgjedhje. Qeveriset nga NESSIE_TEAM_HOST_BASE_DOMAIN; nëse nuk është vendosur, organizatat nuk dirigjohen fare sipas emrit të hostit, dhe porta e lëshimit të certifikatave NESSIE_TLS_CHECK_KEY refuzon çdo gjë kur nuk është vendosur — gjë që është parazgjedhja e duhur për një vendosje që nuk e përdor këtë veçori.

Kontrollet e shëndetit

EndpointPyetja që përgjigjet
GET /api/healthA është gjallë ky proces? Dështon vetëm pasi ka filluar të zbrazet.
GET /api/health/readyA mund ta marrë kjo replikë kërkesa të reja? Ky është ai ku duhet të drejtoni një balancues ngarkese.
GET /api/ops/healthSi po shkon vendosja? Një raport për njeriun — rrahjet e zemrës së worker-ëve, thellësia e radhës, punët e vdekura — jo një probë.

Në SIGTERM API-ja zbraz punën në vend që të vdesë aty ku është. NESSIE_SHUTDOWN_TIMEOUT_MS e kufizon atë zbrazje dhe parazgjedhja është 25 sekonda; mbajeni nën periudhën e grace-it që i jep orkestruesi kontejnerit, përndryshe runtime-i e vret procesin para se zbrazja të sjellë ndonjë dobi.

Përditësimi

Një përditësim është prisma migrate deploy kundër bazës ekzistuese të të dhënave, pastaj seed-et, pastaj reconcile. Të njëjtat katër komanda si vendosja e parë.

Rruga është testuar për itinerarin që e ndjek vërtet një vetë-hostues, jo kundër një baze të re të dhënash: CI rikthen një pamje të marrë njëzet migrime më parë, e migron në kokën aktuale, validon skemën dhe kryen një kontroll tymi mbi tabelat kryesore. Çdo publikim brenda asaj dritareje të fundit është një burim i provuar për përditësim. Dosjet e migrimeve janë të pandryshueshme pasi commit-ohen, dhe një hap lint dështon ndërtimin nëse një prej tyre riemërohet, ri-numërohet, fshihet ose modifikohet, sepse të katër këto e prishin migrate deploy për një bazë të dhënash që ka regjistruar tashmë rreshtin e vjetër.

Një migrim i dështuar ndalon çdo vendosje pas tij. migrate deploy ndalon te dështimi i parë dhe refuzon çdo migrim të mëvonshëm me P3009, kështu që instalimi mbetet në publikimin e vjetër derisa dikush ta pastrojë me dorë. Prodhimi i vetë projektit kaloi një ditë duke refuzuar çdo vendosje në këtë mënyrë. Pastrimi kërkon konfirmimin që migrimi i dështuar vërtet u kthye mbrapsht — asnjë nga objektet që krijon nuk duhet të ekzistojë, dhe çdo gjë që ndryshon duhet të jetë ende në formën e saj origjinale — dhe vetëm pas kësaj ta shënoni si të kthyer mbrapsht. Një migrim që u aplikua pjesërisht nuk është kandidat.

Rezervimi dhe rikthimi për vendosjen vetë-hostuese me Docker Compose nuk janë dokumentuar në depo, kështu që kjo faqe nuk përshkruan një procedurë për to.

Një makinë, ose një cloud i menaxhuar

Rruga e vetme me Docker Compose në një host, më lart, është ajo që është e provuar: është ajo me të cilën funksionon prodhimi i vetë projektit, në një server të vetëm, me bazën e të dhënave, ruajtjen e objekteve, API-në, worker-in, admin-in dhe faqen si kontejnerë pas një proxy-i të kundërt të përbashkët.

Shkallëzimi përtej një makine ndryshon tri gjëra, dhe të gjitha zbatohen me detyrim, jo si këshilla. Jashtë modalitetit local, Nessie e ekzekuton API-në dhe worker-in si procese të veçanta, dhe si disa kopje të secilit, kështu që çdo aftësi që supozon se një proces zotëron diskun e makinës refuzohet: ruajtja e objekteve në skedarë (dështim gjatë nisjes), shabllonet e mjedisit të ekzekutimit me ofruesin docker (joaktive, dhe sigurimi dështon me zë të lartë), dhe mjetet e integruara file_read, file_write dhe file_glob (një rezultat i dështuar mjetesh që agjenti mund ta lexojë). Çdo refuzim emëron cilësimin, modalitetin dhe atë që duhet përdorur në vend të tij.

Për një cloud të menaxhuar, depoja mban një strukturë Terraform në infrastructure/terraform/ që synon Google Cloud: API-ja si një shërbim Cloud Run me minimum një instancë — ajo mban një klient të qëndrueshëm Postgres LISTEN dhe kryen sweep-e mirëmbajtjeje, kështu që zvogëlimi i saj në zero ndalon shpërndarjen në kohë reale — worker-i si një worker pool i Cloud Run, sepse nuk lidh asnjë port dhe një Service nuk do të arrinte kurrë gjendjen gati; një punë migrimi që kalon çdo lëshim; Cloud SQL për PostgreSQL 17; dhe një bucket GCS i arritur përmes backend-it të përputhshëm me S3.

Ai rrugë është shkruar, por jo e provuar. Depoja e thotë këtë troç, dhe kjo faqe e thotë po ashtu: nuk ekziston asnjë projekt Google Cloud për të, asgjë në strukturë nuk është planifikuar ose aplikuar kundër një projekti të vërtetë, rrjedha e ngarkimit me shumë pjesë drejt GCS është e pavërtetuar kundër një bucket-i të vërtetë, lloji i burimit worker-pool nuk është validuar, dhe numri i kërcimeve të besuara të proxy-it është një vend-mbajtës që duhet matur, jo një matje. Admin-i dhe faqja publike nuk janë modeluar fare atje. Trajtojeni si një pikënisje që duhet verifikuar, jo si një vendosje e mbështetur.

Ku ndodhen të dhënat e një vendosjeje, çfarë mban programi dhe çfarë qëllimisht nuk mban, është një faqe e veçantë: Prodhuar në BE, dhe ku ndodhen të dhënat tuaja.