13 KiB
13 KiB
Обзор архитектуры системы
Этот документ — единый и актуальный источник информации по платформе: архитектура, протоколы, данные, конфигурация, сценарии, эксплуатация. Заменяет собой разрозненные и устаревшие документы.
Содержание
- Компоненты и топология
- Децентрализованный слой (членство, оценка размера сети, репликации, метрики)
- Загрузка и конвертация контента
- Просмотр и покупка контента (UI/UX требования)
- API (ключевые эндпойнты и полезная нагрузка)
- Ключи и схемы данных (DHT)
- Конфигурация и значения по умолчанию
- Наблюдаемость и метрики
- Диаграммы последовательностей (Mermaid)
- Сборка и тестирование
Компоненты и топология
- Backend API: сервис на Sanic (Python) с бота́ми Telegram; база данных PostgreSQL (SQLAlchemy + Alembic).
- Хранилище: локальная ФС (uploads/derivatives); IPFS (kubo) для ретривания/пининга; tusd (resumable upload).
- Конвертеры: воркеры (ffmpeg) в контейнерах —
convert_v3,convert_process. - Frontend: SPA (Vite + TypeScript), отдается nginx-контейнером.
- Децентрализованный слой: встроенный DHT (в процессе) — членство, лизы реплик, метрики контента.
flowchart LR
Client -- TWA/HTTP --> Frontend
Frontend -- REST --> API[Backend API]
API -- tus hooks --> tusd
API -- SQL --> Postgres
API -- IPC --> Workers[Converters]
API -- IPFS --> IPFS
API -- DHT --> DHT[(In-Process DHT)]
DHT -- CRDT Merge --> DHT
Децентрализованный слой
Идентификаторы и версии
- NodeID = blake3(Ed25519 публичного ключа) — шестнадцатеричная строка (256 бит).
- ContentID = blake3(зашифрованного блоба) — неизменяемый идентификатор контента.
- schema_version = v1 — фиксируется во всех DHT-ключах/записях.
Членство (membership)
- Рукопожатие
/api/v1/network.handshake— запрос подписан Ed25519; верифицируется на стороне получателя. Без корректной подписи — 400 BAD_SIGNATURE. - Полезная нагрузка включает: сведения о ноде (версия, возможности, IPFS), метрики, массив известных публичных нод, квитанции достижимости (reachability_receipts: issuer, target, ASN, timestamp, signature).
- Состояние членства — CRDT LWW-Set (добавления/удаления) с TTL (
DHT_MEMBERSHIP_TTL=600сек), плюс HyperLogLog для оценки мощности (N_local). - Фильтрация «островов»: ноды с
reachability_ratio < q(по умолчаниюq=0.6) исключаются при вычислении N_estimate и выборе реплик. - Итоговая оценка
N_estimate = max(валидных N_local от пиров).
sequenceDiagram
participant A as Узел A
participant B as Узел B
A->>B: POST /network.handshake {nonce, ts, node, receipts, signature}
B->>B: верификация ts/nonce, подписи
B->>B: upsert member; merge(receipts)
B-->>A: {node, known_public_nodes, n_estimate, server_signature}
A->>A: merge; N_estimate = max(N_local, полученные)
Репликации и лизы
- Выбор префикса:
p = max(0, round(log2(N_estimate / R_target))), гдеR_target ≥ 3(по умолчанию 3). - Ответственные ноды: чьи первые
pбит NodeID совпадают с первымиpбит ContentID. - Лидер — минимальный NodeID среди ответственных.
- Лидер выдаёт
replica_leases(TTL=600 сек), соблюдая разнообразие: не менее 3 разных первых октетов IP и, если доступно, 3 разных ASN. - Ранжирование кандидатов — rendezvous score
blake3(ContentID || NodeID). - Сердцебиение (heartbeat) держателей — каждые 60 сек; 3 пропуска → признать down и переназначить ≤180 сек.
- Недобор/перебор фиксируются в
conflict_logи прометеус‑метриках.
stateDiagram-v2
[*] --> Discover
Discover: Рукопожатия + квитанции
Discover --> Active: TTL & кворм ASN
Active --> Leader: Выбор лидера префикса p
Leader --> Leased: Выдача лизов (diversity)
Leased --> Monitoring: Heartbeat 60s
Monitoring --> Reassign: 3 пропуска
Reassign --> Leased
Метрики (окна)
- На событии просмотра формируются дельты CRDT:
- PN‑Counter — количество просмотров;
- HyperLogLog — уникальные ViewID (ViewID = blake3(ContentID || соль_устройства));
- G‑Counter — watch_time, bytes_out, количество завершений.
- Окно по часу (
DHT_METRIC_WINDOW_SEC), ключMetricKey = blake3(ContentID || WindowID). - Мерджи коммутативные, детерминированные.
Загрузка и конвертация контента
- Клиент грузит в
tusd(resumable). Бэкенд получает HTTP‑hooks/api/v1/upload.tus-hook. - Создается запись в БД для зашифрованного контента, воркеры размещают производные:
- для медиа — preview/low/high;
- для бинарей — оригинал (доступен только при наличии лицензии).
/api/v1/content.viewвозвращаетdisplay_optionsи агрегированное состояние конвертации/загрузки.
sequenceDiagram
participant C as Клиент
participant T as tusd
participant B as Бэкенд
participant W as Воркеры
participant DB as PostgreSQL
C->>T: upload
T->>B: hooks (pre/post-finish)
B->>DB: create content
B->>W: очередь конвертации
W->>DB: derive/previews
C->>B: GET /content.view
B->>DB: resolve derivatives
B-->>C: display_options + status
Просмотр и покупка (UI/UX)
/api/v1/content.view/<content_address>определяет доступные отображения:- бинарный контент без превью — оригинал только при наличии лицензии;
- аудио/видео — для неавторизованных preview/low, для имеющих доступ — decrypted_low/high.
- В процессе конвертации фронтенд показывает статус «processing», без фальшивых ссылок.
- Обложка (cover):
- фиксированный квадратный слот; изображение «вписывается» без растягивания/искажения;
- пустые области не заполняются чёрным — фон совпадает с фоном страницы.
- Кнопки «Купить за TON/Stars»: всегда в одной строке (без горизонтального/вертикального скролла контента на малых экранах).
flowchart LR
View[content.view] --> Resolve[Определение деривативов]
Resolve --> Ready{Готово?}
Ready -- Нет --> Info[Статус: processing/pending]
Ready -- Да --> Options
Options -- Бинарь + нет лицензии --> HideOriginal[Скрыть оригинал]
Options -- Медиа + нет лицензии --> PreviewLow[preview/low]
Options -- Есть лицензия --> Decrypted[decrypted low/high|original]
API (ключевые)
GET /api/system.version— актуальность сервиса.POST /api/v1/network.handshake— обмен членством (обязательная Ed25519‑подпись запроса). Пример запроса:
{
"version": "3.0.0",
"schema_version": "v1",
"public_key": "<base58 ed25519 pubkey>",
"node_id": "<blake3(pubkey)>",
"public_host": "https://node.example",
"node_type": "public|private",
"metrics": {"uptime_sec": 123, "content_count": 42},
"capabilities": {"accepts_inbound": true, "is_bootstrap": false},
"ipfs": {"multiaddrs": ["/ip4/.../tcp/4001"], "peer_id": "..."},
"known_public_nodes": [],
"reachability_receipts": [],
"timestamp": 1710000000,
"nonce": "<hex>",
"signature": "<base58 ed25519 signature>"
}
GET /api/v1/content.view/<content_address>—display_options,status,conversion.GET /api/v1.5/storage/<file_hash>— отдача файла.GET /metrics— экспозиция метрик Prometheus (либо fallback‑дамп счётчиков).
Ключи и схемы DHT
-
MetaKey(content_id)— метаданные репликаций:replica_leases: карта{lease_id -> {node_id, issued_at, expires_at, asn, ip_first_octet, heartbeat_at, score}};leader: NodeID лидера;revision: номер ревизии;conflict_log: массив событийUNDER/OVER/LEASE_EXPIREDи т.п.
-
MembershipKey(node_id)— членство:members: LWW‑Set;receipts: LWW‑Set;hll: HyperLogLog;reports: карты локальных оценок N;logical_counter: логический счётчик для LWW‑доминации.
-
MetricKey(content_id, window_id)— метрики окна:views: PN‑Counter;unique: HLL;watch_time,bytes_out,completions: G‑Counters.
Все записи подписываются и сливаются детерминированно: CRDT‑логика + LWW‑доминация (logical_counter, timestamp, node_id).
Конфигурация и значения по умолчанию
- Сеть/рукопожатия:
NODE_PRIVACY,PUBLIC_HOST,HANDSHAKE_INTERVAL_SEC,NETWORK_TLS_VERIFY, IPFS‑пиры/бустрапы. - DHT:
DHT_MIN_RECEIPTS=5,DHT_MIN_REACHABILITY=0.6,DHT_MEMBERSHIP_TTL=600;DHT_REPLICATION_TARGET=3,DHT_LEASE_TTL=600,DHT_HEARTBEAT_INTERVAL=60,DHT_HEARTBEAT_MISS_THRESHOLD=3;DHT_MIN_ASN=3,DHT_MIN_IP_OCTETS=3,DHT_METRIC_WINDOW_SEC=3600.
- Конвертация: квоты
CONVERT_*,MAX_CONTENT_SIZE_MB.
Примечание: PoW‑допуски и Kademlia k‑buckets на текущем этапе не активированы в коде — заложены в дизайн и могут быть реализованы отдельно.
Наблюдаемость и метрики
Prometheus:
dht_replication_under_total,dht_replication_over_total,dht_leader_changes_total;dht_merge_conflicts_total;dht_view_count_total,dht_unique_view_estimate,dht_watch_time_seconds.
Логи: структурированные ошибки HTTP (с id), conflict_log по репликациям, события регистрации нод.
Диаграммы последовательностей (сводные)
Обновление N_estimate
sequenceDiagram
participant Peer
participant Membership
participant DHT
Peer->>Membership: handshake(payload, receipts)
Membership->>Membership: merge LWW/receipts
Membership->>Membership: update HLL и N_local
Membership->>DHT: persist MembershipKey
Membership->>Membership: N_estimate = max(valid reports)
Выбор лидера и выдача лизов
sequenceDiagram
participant L as Leader
participant R as Responsible
L->>L: p = round(log2(N_est/R))
L->>R: rank by rendezvous(ContentID, NodeID)
L->>L: assign leases (diversity)
R-->>L: heartbeat/60s
L->>L: reassign on 3 misses
Публикация метрик окна
sequenceDiagram
participant C as Client
participant API as Backend
participant M as Metrics
participant D as DHT
C->>API: GET content.view?watch_time,bytes_out
API->>M: record_view(delta)
M->>D: merge MetricKey(ContentID, window)
API-->>Prom: /metrics
Сборка и тестирование
# Старт окружения (пример для /home/configs)
docker compose -f /home/configs/docker-compose.yml --env-file /home/configs/.env up -d --build
# Тесты слоя DHT
cd uploader-bot
python3 -m unittest discover -s tests/dht