uploader-bot/ARCHITECTURE.md

13 KiB
Raw Permalink Blame History

Обзор архитектуры системы

Этот документ — единый и актуальный источник информации по платформе: архитектура, протоколы, данные, конфигурация, сценарии, эксплуатация. Заменяет собой разрозненные и устаревшие документы.

Содержание

  • Компоненты и топология
  • Децентрализованный слой (членство, оценка размера сети, репликации, метрики)
  • Загрузка и конвертация контента
  • Просмотр и покупка контента (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:
    • PNCounter — количество просмотров;
    • HyperLogLog — уникальные ViewID (ViewID = blake3(ContentID || соль_устройства));
    • GCounter — watch_time, bytes_out, количество завершений.
  • Окно по часу (DHT_METRIC_WINDOW_SEC), ключ MetricKey = blake3(ContentID || WindowID).
  • Мерджи коммутативные, детерминированные.

Загрузка и конвертация контента

  1. Клиент грузит в tusd (resumable). Бэкенд получает HTTPhooks /api/v1/upload.tus-hook.
  2. Создается запись в БД для зашифрованного контента, воркеры размещают производные:
    • для медиа — preview/low/high;
    • для бинарей — оригинал (доступен только при наличии лицензии).
  3. /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: LWWSet; receipts: LWWSet;
    • hll: HyperLogLog; reports: карты локальных оценок N;
    • logical_counter: логический счётчик для LWWдоминации.
  • MetricKey(content_id, window_id) — метрики окна:

    • views: PNCounter; unique: HLL; watch_time, bytes_out, completions: GCounters.

Все записи подписываются и сливаются детерминированно: 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 kbuckets на текущем этапе не активированы в коде — заложены в дизайн и могут быть реализованы отдельно.


Наблюдаемость и метрики

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