Files
Apple ef3473db21 snapshot: NODE1 production state 2026-02-09
Complete snapshot of /opt/microdao-daarion/ from NODE1 (144.76.224.179).
This represents the actual running production code that has diverged
significantly from the previous main branch.

Key changes from old main:
- Gateway (http_api.py): expanded from ~40KB to 164KB with full agent support
- Router: new /v1/agents/{id}/infer endpoint with vision + DeepSeek routing
- Behavior Policy: SOWA v2.2 (3-level: FULL/ACK/SILENT)
- Agent Registry: config/agent_registry.yml as single source of truth
- 13 agents configured (was 3)
- Memory service integration
- CrewAI teams and roles

Excluded from snapshot: venv/, .env, data/, backups, .tgz archives

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 08:46:46 -08:00

1096 lines
32 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="canonical" href="https://IvanTytar.github.io/microdao-daarion/cursor/02_architecture_basics/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.5.3, mkdocs-material-9.5.18">
<title>02 — MicroDAO Architecture Basics (MVP) - DAARION Documentation</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.66ac8b77.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#02-microdao-architecture-basics-mvp" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../.." title="DAARION Documentation" class="md-header__button md-logo" aria-label="DAARION Documentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
DAARION Documentation
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
02 — MicroDAO Architecture Basics (MVP)
</span>
</div>
</div>
</div>
<script>var media,input,key,value,palette=__md_get("__palette");if(palette&&palette.color){"(prefers-color-scheme)"===palette.color.media&&(media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']"),palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent"));for([key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
</button>
</nav>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="DAARION Documentation" class="md-nav__button md-logo" aria-label="DAARION Documentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
</a>
DAARION Documentation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../public/" class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../public/getting-started/" class="md-nav__link">
<span class="md-ellipsis">
Getting Started
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../public/architecture-overview/" class="md-nav__link">
<span class="md-ellipsis">
Architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../public/daiS_daos_overview/" class="md-nav__link">
<span class="md-ellipsis">
DAIS & DAOS
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" >
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="">
<span class="md-ellipsis">
Internal
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Internal
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_1" >
<label class="md-nav__link" for="__nav_5_1" id="__nav_5_1_label" tabindex="0">
<span class="md-ellipsis">
Infra
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5_1">
<span class="md-nav__icon md-icon"></span>
Infra
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../internal/infra/INFRA_AUTOMATION_PACK_V1/" class="md-nav__link">
<span class="md-ellipsis">
Infra Automation Pack v1
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../internal/infra/monitoring_overview/" class="md-nav__link">
<span class="md-ellipsis">
Monitoring Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../internal/infra/nodes_registry_v0/" class="md-nav__link">
<span class="md-ellipsis">
Nodes Registry v0
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_2" >
<label class="md-nav__link" for="__nav_5_2" id="__nav_5_2_label" tabindex="0">
<span class="md-ellipsis">
Specs
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5_2">
<span class="md-nav__icon md-icon"></span>
Specs
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../internal/specs/matrix_presence_aggregator/" class="md-nav__link">
<span class="md-ellipsis">
Matrix Presence Aggregator
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../internal/specs/city_map_spec/" class="md-nav__link">
<span class="md-ellipsis">
City Map Spec
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../internal/specs/node_join_protocol_draft/" class="md-nav__link">
<span class="md-ellipsis">
Node Join Protocol (Draft)
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#1" class="md-nav__link">
<span class="md-ellipsis">
1. Загальний огляд архітектури
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#2-mvp" class="md-nav__link">
<span class="md-ellipsis">
2. Стек MVP
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#3" class="md-nav__link">
<span class="md-ellipsis">
3. Основні модулі
</span>
</a>
<nav class="md-nav" aria-label="3. Основні модулі">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#31-auth-service" class="md-nav__link">
<span class="md-ellipsis">
3.1. Auth Service
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#32-teams-microdao-service" class="md-nav__link">
<span class="md-ellipsis">
3.2. Teams / MicroDAO Service
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#33-channels-service" class="md-nav__link">
<span class="md-ellipsis">
3.3. Channels Service
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#34-messaging-service" class="md-nav__link">
<span class="md-ellipsis">
3.4. Messaging Service
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#35-followups-service" class="md-nav__link">
<span class="md-ellipsis">
3.5. Followups Service
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#36-projects-tasks-service-kanban-lite" class="md-nav__link">
<span class="md-ellipsis">
3.6. Projects &amp; Tasks Service (Kanban-lite)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#37-agents-service" class="md-nav__link">
<span class="md-ellipsis">
3.7. Agents Service
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#38-search-service" class="md-nav__link">
<span class="md-ellipsis">
3.8. Search Service
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#4" class="md-nav__link">
<span class="md-ellipsis">
4. Дані та моделі
</span>
</a>
<nav class="md-nav" aria-label="4. Дані та моделі">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#41-postgresql" class="md-nav__link">
<span class="md-ellipsis">
4.1. База даних (PostgreSQL)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#42-message-bus-nats-jetstream" class="md-nav__link">
<span class="md-ellipsis">
4.2. Message Bus (NATS JetStream)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#43-meilisearch" class="md-nav__link">
<span class="md-ellipsis">
4.3. Пошукові індекси (Meilisearch)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#5-websockets" class="md-nav__link">
<span class="md-ellipsis">
5. WebSockets
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#6-public-confidential" class="md-nav__link">
<span class="md-ellipsis">
6. Приватність та режими (Public / Confidential)
</span>
</a>
<nav class="md-nav" aria-label="6. Приватність та режими (Public / Confidential)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#public-mode" class="md-nav__link">
<span class="md-ellipsis">
Public Mode
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#confidential-mode" class="md-nav__link">
<span class="md-ellipsis">
Confidential Mode
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#7-api" class="md-nav__link">
<span class="md-ellipsis">
7. API взаємодія (загальні правила)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#8-front-end" class="md-nav__link">
<span class="md-ellipsis">
8. Front-End архітектура
</span>
</a>
<nav class="md-nav" aria-label="8. Front-End архітектура">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#81" class="md-nav__link">
<span class="md-ellipsis">
8.1. Каталоги
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#82" class="md-nav__link">
<span class="md-ellipsis">
8.2. Рекомендовані патерни
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#9-mvp" class="md-nav__link">
<span class="md-ellipsis">
9. MVP Нефункціональні очікування
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#10-cursor" class="md-nav__link">
<span class="md-ellipsis">
10. Для Cursor
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="02-microdao-architecture-basics-mvp">02 — MicroDAO Architecture Basics (MVP)<a class="headerlink" href="#02-microdao-architecture-basics-mvp" title="Permanent link">&para;</a></h1>
<p>Цей документ дає Cursor і розробникам стисле уявлення про архітектуру MicroDAO, необхідне для реалізації перших функцій MVP.</p>
<h2 id="1">1. Загальний огляд архітектури<a class="headerlink" href="#1" title="Permanent link">&para;</a></h2>
<p>MicroDAO складається з:</p>
<ul>
<li><strong>Front-end SPA</strong> (React + TypeScript)</li>
<li><strong>API Gateway</strong> (<code>https://api.microdao.xyz/v1</code>)</li>
<li><strong>Core Services</strong> (Teams, Channels, Messages, Followups, Projects, Agents)</li>
<li><strong>PostgreSQL</strong> — основна база даних</li>
<li><strong>NATS JetStream</strong> — message bus (події, outbox-патерн)</li>
<li><strong>Meilisearch</strong> — індексація і пошук</li>
<li><strong>S3-compatible storage</strong> — файли</li>
<li><strong>WebSockets</strong> — оновлення повідомлень у реальному часі</li>
</ul>
<p>Джерела:
- Data Model &amp; Event Catalog
- Tech Spec / Технічний опис MicroDAO
- API Specification (OpenAPI 3.1)</p>
<h2 id="2-mvp">2. Стек MVP<a class="headerlink" href="#2-mvp" title="Permanent link">&para;</a></h2>
<ul>
<li><strong>Frontend:</strong> React 18, TypeScript, Vite або Next SPA-режим</li>
<li><strong>State:</strong> React Query / TanStack Query</li>
<li><strong>Design System:</strong> базовий UI-компонентний набір (кнопки, поля, layout)</li>
<li><strong>Backend:</strong> Go або Node (вже залежить від вашої реалізації — Cursor адаптується)</li>
<li><strong>Auth:</strong> Magic-link email (JWT)</li>
<li><strong>Transport:</strong> REST + WebSockets</li>
</ul>
<h2 id="3">3. Основні модулі<a class="headerlink" href="#3" title="Permanent link">&para;</a></h2>
<h3 id="31-auth-service">3.1. Auth Service<a class="headerlink" href="#31-auth-service" title="Permanent link">&para;</a></h3>
<ul>
<li>Відповідає за:</li>
<li><code>POST /auth/login-email</code></li>
<li><code>POST /auth/exchange</code></li>
<li>Видає JWT (користувач, локаль, tz).</li>
<li>Email з кодом / magic-link відправляє окремий SMTP-модуль.</li>
<li>Після входу SPA зберігає токен та ініціалізує сесію.</li>
</ul>
<h3 id="32-teams-microdao-service">3.2. Teams / MicroDAO Service<a class="headerlink" href="#32-teams-microdao-service" title="Permanent link">&para;</a></h3>
<ul>
<li>Створення спільноти — автоматично створює micro-DAO:</li>
<li><code>POST /teams</code></li>
<li><code>PATCH /teams/{id}</code> — public/confidential</li>
<li>Зберігає:</li>
<li>id спільноти</li>
<li>slug</li>
<li>режим (<code>public</code>, <code>confidential</code>)</li>
<li>Members / Guardians</li>
<li>Взаємодіє з Channels, Messages, Projects, Agents.</li>
</ul>
<h3 id="33-channels-service">3.3. Channels Service<a class="headerlink" href="#33-channels-service" title="Permanent link">&para;</a></h3>
<ul>
<li>Створення каналів:</li>
<li><code>POST /channels</code></li>
<li>Типи:</li>
<li><code>public</code> — доступні гостям (read-only)</li>
<li><code>group</code> — приватні групові канали</li>
<li>Channel data:</li>
<li>team_id</li>
<li>type</li>
<li>mode (public/confidential)</li>
</ul>
<h3 id="34-messaging-service">3.4. Messaging Service<a class="headerlink" href="#34-messaging-service" title="Permanent link">&para;</a></h3>
<ul>
<li>Головне ядро MVP.</li>
<li>API:</li>
<li><code>GET /channels/{id}/messages</code></li>
<li><code>POST /channels/{id}/messages</code></li>
<li><code>PATCH /messages/{id}</code></li>
<li><code>DELETE /messages/{id}</code></li>
<li>Зберігає:</li>
<li>текстові повідомлення</li>
<li>автора (user_id або agent_id)</li>
<li>E2EE шифротекст у confidential режимі</li>
<li>WebSocket транслює нові повідомлення в реальному часі.</li>
</ul>
<h3 id="35-followups-service">3.5. Followups Service<a class="headerlink" href="#35-followups-service" title="Permanent link">&para;</a></h3>
<ul>
<li>Легкий таскер, прив'язаний до повідомлень.</li>
<li>API:</li>
<li><code>POST /followups</code></li>
<li><code>GET /followups?assignee=...</code></li>
<li>Статуси:</li>
<li><code>open</code>, <code>in_progress</code>, <code>done</code></li>
<li>Використовується для персональних нагадувань і мікро-задач.</li>
</ul>
<h3 id="36-projects-tasks-service-kanban-lite">3.6. Projects &amp; Tasks Service (Kanban-lite)<a class="headerlink" href="#36-projects-tasks-service-kanban-lite" title="Permanent link">&para;</a></h3>
<ul>
<li>API:</li>
<li><code>POST /projects</code></li>
<li><code>GET /projects</code></li>
<li><code>POST /projects/{id}/tasks</code></li>
<li><code>GET /projects/{id}/tasks</code></li>
<li>Статуси задач:</li>
<li><code>backlog</code>, <code>in_progress</code>, <code>review</code>, <code>done</code></li>
<li>Проста Kanban-дошка для MVP.</li>
</ul>
<h3 id="37-agents-service">3.7. Agents Service<a class="headerlink" href="#37-agents-service" title="Permanent link">&para;</a></h3>
<ul>
<li>Зберігає приватних агентів користувача або команди.</li>
<li>API:</li>
<li><code>GET /agents</code></li>
<li><code>POST /agents</code></li>
<li>Для MVP:</li>
<li>один агент «Team Assistant»</li>
<li>мінімальний чат з LLM</li>
<li>Під капотом можна використовувати будь-який зовнішній LLM API.</li>
</ul>
<h3 id="38-search-service">3.8. Search Service<a class="headerlink" href="#38-search-service" title="Permanent link">&para;</a></h3>
<ul>
<li>На базі Meilisearch.</li>
<li>API:</li>
<li><code>GET /search?q=...&amp;scope=messages|docs|tasks</code></li>
<li>MVP:</li>
<li>індексація публічних повідомлень + задач.</li>
</ul>
<h2 id="4">4. Дані та моделі<a class="headerlink" href="#4" title="Permanent link">&para;</a></h2>
<h3 id="41-postgresql">4.1. База даних (PostgreSQL)<a class="headerlink" href="#41-postgresql" title="Permanent link">&para;</a></h3>
<p>Згідно з Data Model &amp; Event Catalog:</p>
<ul>
<li><code>users</code></li>
<li><code>teams</code>, <code>team_members</code></li>
<li><code>channels</code>, <code>messages</code>, <code>reactions</code></li>
<li><code>followups</code></li>
<li><code>projects</code>, <code>tasks</code></li>
<li><code>agents</code>, <code>agent_runs</code></li>
<li><code>files</code></li>
<li><code>audit_log</code></li>
<li>мінімальні індекси для пошуку повідомлень</li>
</ul>
<p><strong>ID формати:</strong> <code>ulid</code> або <code>ksuid</code> (обов'язково глобально унікальні).</p>
<h3 id="42-message-bus-nats-jetstream">4.2. Message Bus (NATS JetStream)<a class="headerlink" href="#42-message-bus-nats-jetstream" title="Permanent link">&para;</a></h3>
<p>Використовується не на всіх етапах MVP, але:</p>
<ul>
<li>дозволяє публікувати події:</li>
<li><code>message.created</code></li>
<li><code>followup.created</code></li>
<li><code>task.created</code></li>
<li>забезпечує надійний outbox pattern.</li>
</ul>
<h3 id="43-meilisearch">4.3. Пошукові індекси (Meilisearch)<a class="headerlink" href="#43-meilisearch" title="Permanent link">&para;</a></h3>
<p>Структури документів:</p>
<ul>
<li><strong>Messages</strong>: id, team_id, channel_id, created_at, body_plain (якщо public)</li>
<li><strong>Tasks</strong>: id, project_id, title, status, priority, labels</li>
<li><strong>Docs</strong> (можна не включати в MVP)</li>
</ul>
<h2 id="5-websockets">5. WebSockets<a class="headerlink" href="#5-websockets" title="Permanent link">&para;</a></h2>
<ul>
<li>Створений окремий WS endpoint.</li>
<li>Події які обробляє фронт:</li>
<li>нове повідомлення</li>
<li>оновлення повідомлення</li>
<li>реакція</li>
<li>В MVP достатньо канального namespace:</li>
<li><code>/ws/channels/{id}</code></li>
</ul>
<h2 id="6-public-confidential">6. Приватність та режими (Public / Confidential)<a class="headerlink" href="#6-public-confidential" title="Permanent link">&para;</a></h2>
<h3 id="public-mode">Public Mode<a class="headerlink" href="#public-mode" title="Permanent link">&para;</a></h3>
<ul>
<li>Канал доступний гостям на <code>/c/:slug</code>.</li>
<li>Повідомлення індексуються у Meilisearch.</li>
<li>Дані зберігаються у <code>messages.body_plain</code>.</li>
</ul>
<h3 id="confidential-mode">Confidential Mode<a class="headerlink" href="#confidential-mode" title="Permanent link">&para;</a></h3>
<ul>
<li>Повідомлення зберігаються як <code>body_enc</code> + <code>key_id</code>.</li>
<li>Клієнт розшифровує.</li>
<li>Не індексується, не надсилається в Meili.</li>
<li>Всі вкладення — шифротекст із pre-signed URL.</li>
<li>На фронті потрібно використовувати <strong>E2EE-хелпери</strong> (поза scope MVP — stub OK).</li>
</ul>
<h2 id="7-api">7. API взаємодія (загальні правила)<a class="headerlink" href="#7-api" title="Permanent link">&para;</a></h2>
<ul>
<li>Усі виклики захищені Bearer JWT.</li>
<li>Потрібно використовувати typed API-клієнт (можна автогенерувати зі спрощеної OpenAPI).</li>
<li>Обробка помилок:</li>
<li>400 → помилка користувача</li>
<li>403 → access denied</li>
<li>404 → ресурс не знайдено</li>
<li>429 → rate limit</li>
<li>500 → системна помилка</li>
</ul>
<h2 id="8-front-end">8. Front-End архітектура<a class="headerlink" href="#8-front-end" title="Permanent link">&para;</a></h2>
<h3 id="81">8.1. Каталоги<a class="headerlink" href="#81" title="Permanent link">&para;</a></h3>
<div class="codehilite"><pre><span></span><code>src/
api/
components/
features/
onboarding/
auth/
chat/
channels/
followups/
projects/
agents/
hooks/
layout/
routes/
store/
styles/
</code></pre></div>
<h3 id="82">8.2. Рекомендовані патерни<a class="headerlink" href="#82" title="Permanent link">&para;</a></h3>
<ul>
<li>React Query для запитів і кешу.</li>
<li>Zustand або Context для глобального стану онбордингу.</li>
<li>Мовна локалізація через простий i18n dictionary.</li>
<li>ErrorBoundary на рівні layout.</li>
</ul>
<h2 id="9-mvp">9. MVP Нефункціональні очікування<a class="headerlink" href="#9-mvp" title="Permanent link">&para;</a></h2>
<ul>
<li>Латентність чатів ≤ 300 мс (без LLM).</li>
<li>Одночасно 1050 активних користувачів.</li>
<li>Стабільна робота мобільної версії (мінімально).</li>
<li>Стійкий логін, без циклів і моклих лінків.</li>
</ul>
<h2 id="10-cursor">10. Для Cursor<a class="headerlink" href="#10-cursor" title="Permanent link">&para;</a></h2>
<p>Цей документ дає базу для:</p>
<ul>
<li>генерації React-компонентів,</li>
<li>створення нового маршруту <code>/onboarding</code>,</li>
<li>реалізації каналів і чатів,</li>
<li>інтеграції базового агента,</li>
<li>роботи з API без необхідності читати всю специфікацію.</li>
</ul>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"base": "../..", "features": ["navigation.sections", "navigation.instant", "content.code.copy"], "search": "../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
<script src="../../assets/javascripts/bundle.3220b9d7.min.js"></script>
</body>
</html>