Skip to content

Latest commit

 

History

History
135 lines (78 loc) · 36.8 KB

ARCHITECTURE.md

File metadata and controls

135 lines (78 loc) · 36.8 KB

Архитектура

Подход

Модульная архитектура с направлением зависимостей внутрь: предметные правила → сценарии и порты → внешние адаптеры → точки сборки. Наличие TypeScript-интерфейса само по себе не делает код чистым: домен должен оставаться независимым от инфраструктуры и её форматов.

Базовый стек: TypeScript strict, Node.js 24 LTS, npm workspaces. Для HTTP используется Fastify, для каталога и устойчивых completion jobs — PostgreSQL. Консоль написана на TypeScript и обращается через SDK. Содержимое хранит LocalBlobStore на локальной файловой системе; отдельного HA blob-store, S3-backend и файловых шлюзов Nginx нет: HA одной площадки реплицирует весь том установки средствами DRBD (ADR 0072), раздачу масштабируют шлюзы чтения того же API (ADR 0009).

Направление зависимостей

Модуль Разрешённые внутренние зависимости Что запрещено
domain Нет HTTP, БД, Node/browser globals, внешние runtime-библиотеки
application domain Конкретные адаптеры, transport DTO, глобальная конфигурация
contracts Нет Домен, application, storage и HTTP server framework
infrastructure application, domain, extension-api, resolution Приложения, UI и SDK
sdk contracts Серверные пакеты и Node-only API
extension-api Нет Node/browser globals, внешние runtime-библиотеки, apps
signing extension-api Внешние runtime-библиотеки, apps, БД и сеть
extension-testkit extension-api Код продукта его не импортирует; только тесты
resolution Нет Внешние runtime-библиотеки, apps и пакеты репозитория
apps/api application, domain, contracts, infrastructure, extension-api, resolution Другие apps и SDK
apps/worker application, domain, infrastructure, resolution HTTP/Web/SDK
apps/scheduler application, domain, infrastructure HTTP/Web/SDK
apps/backup application, domain, infrastructure, resolution HTTP/Web/SDK и другие apps
apps/web sdk, contracts Все серверные пакеты и другие apps

Импорты типов также соблюдают границы. Межпакетный доступ — по workspace-имени через src/index.ts; даже разрешённый соседний пакет не открывает все свои внутренние файлы. Граф проверяется dependency-cruiser. Для domain/application дополнительно запрещены runtime-зависимости на сторонние пакеты и Node builtins; import type не должен использоваться для обхода модели.

В contracts допускаются библиотеки runtime-схем после выбора подхода; в SDK — browser-compatible зависимости по необходимости. JSON/wire DTO не обязаны совпадать с domain objects. Преобразования на границах явные.

Ответственность слоёв

Domain. Идентичность пакета, политика версий, допустимость перехода состояния, правила меток и удаления. Детерминированные функции/объекты. Время и случайность передаются извне.

Application. PublishPackage, CompleteUpload, ResolveDownload, UpdateMetadata, ScheduleTransfer. Сценарии оркестрируют порты и проверяют права. Не держат SQL-транзакцию на протяжении сетевой передачи. Только необходимые интерфейсы, без заготовок на все будущие операции.

Infrastructure. Адаптер PostgreSQL, backend blob-store, хеширование/архивы, event outbox, чтение метрик, идентификаторы и часы. Порты должны явно описывать устойчивость записи, отмену и ошибки; реализации проходят общий набор контрактных тестов.

Composition roots. Apps валидируют конфигурацию, создают адаптеры и сценарии, регистрируют lifecycle/shutdown. HTTP handlers переводят запрос в типизированный сценарий и результат в ответ. Не следует переносить SQL и правила публикации в routes.

Содержимое и управление

PostgreSQL хранит каталог, права, ревизии, состояния загрузок/задач, outbox. Blob-store хранит исходные байты; API не читает файлы целиком в память. Шлюз раздаёт разрешённый неизменяемый blob. Внешние приложения не являются промежуточными узлами передачи или обязательными серверами авторизации.

LocalBlobStore — профиль одного сервера; на Windows он не синхронизирует записи каталога (fsync каталога недоступен, ADR 0004). HA (ADR 0072) не вводит другого backend: PostgreSQL, LocalBlobStore и конфигурация лежат на одном томе DRBD, активный узел выбирает Pacemaker с обязательным fencing, а приложение лишь проверяет число копий перед подтверждением записи. Новая схема хранения или провайдер оформляется ADR до обещаний о доступности. Границы профилей: RELIABILITY.

Байты содержимого читаются только через порт BlobStore.read(range) с хранилищем из строки upload: разбор UPack (yauzl поверх BlobRangeReader), зеркала, копия и восстановление (поле backend в инвентаре точки), scrub и очистка. Пути blob строят только local-blobs.ts, blob-files.ts и раскладка vault; временные файлы raw, OCI и зеркал лежат в StagingArea тома данных. Это проверяет policy scripts/policy/blob-paths.mjs (ADR 0083, N9).

Хранилищ содержимого может быть несколько (ADR 0083, N11; ADR 0079, X2): default в ARKVORY_DATA_DIR и экземпляры встроенного модуля local-storage (storage-backend) из ARKVORY_MODULES_FILE, например второй том. Корни api, worker и backup собирают их в StorageBackends (infrastructure): у каждого экземпляра явные порт BlobStore, StorageLifecycle, режим full | read-only и acceptsWrites; вызов идёт ровно в хранилище строки upload, без перебора и без возврата к default при чтении. Хранилище, отличное от default, открывается после захвата владения и проверяется каждые 30 с, а перед каждой записью и удалением и после записи — заново читается его идентичность: его файл storage-id должен совпадать с записью arkvory_storage_backends, которую делает писатель при первом открытии. Хранилище новой загрузки выбирает application (PolicyPlacement по arkvory_storage_placement и правилам StorageRoutingRule домена) с возвратом в default, если выбранное не принимает данные; StorageService не переносит байты: незавершённый upload такого хранилища завершается в default, его части остаются на месте (parts_backend).

Правила изменений

Расширение авторизации сервисов проектируется внутри существующих слоёв: stable service account, credentials, action/resource bindings и общий application authorizer для HTTP API и worker. Новые границы и миграция старых read/write описаны в ADR 0016; широкая модель остаётся proposed, первый рабочий профиль принят в ADR 0017: root bootstrap, точные repository actions, managed credentials и повторная проверка mutations перед commit. Карта API отделяет работающие маршруты от планируемых.

  • Новый пакет создаётся только с конкретной ролью, владельцем правил и разрешёнными зависимостями.
  • Общее поведение извлекается после появления реального повторения; не создаём универсальный framework заранее.
  • Новые брокер/БД/сервис требуют обоснования: какую измеренную проблему они решают и как восстанавливаются.
  • Границы API, долговечность, операции удаления, топология HA и протокол очереди меняются через ADR и проверяемые сценарии.
  • Размер каталога, потоков и очередей должен быть ограничен конфигурацией и доступной ёмкостью.

Текущее состояние

Резервирование B1 — отдельный composition root apps/backup (операторский CLI, не HTTP-сервис). Domain хранит состояния job/фаз, решение по повторному ключу, gate схемы и валидацию manifest; application — CaptureBackup, VerifyPoint, RestoreToEmptyTarget и узкие порты; infrastructure — PostgreSQL jobs/pins/барьер допуска unlink, snapshot-экспорт по реестру таблиц, файловый vault и загрузку restore. Online GC и offline repair удаляют содержимое только через общий допуск unlink. Решение: ADR 0054, протокол и следующие этапы: ADR 0037, BACKUP_RECOVERY. Reader pins по-прежнему не заменяют backup pins. B2 (ADR 0056): тот же composition root запускает долгоживущий агент (lease/fencing в БД); domain — чистые расписание, retention и предупреждения, application — BackupAgent, ApplyBackupRetention, BackupControl и порты очереди, плана, каталога и блокировки vault; infrastructure — PostgreSQL-хранилища и prune слиянием inventory; API лишь ставит типизированные задания и читает состояние из БД.

Фоновая физическая очистка реализована отдельными domain/application/SQL модулями, API/SDK и UI. Неблокирующие per-object guards защищают активные uploads и shared content readers; writer запускает ограниченные порции. Миграции 16/17, динамическая политика и отсутствие SQL-транзакций вокруг filesystem I/O: ADR 0035, эксплуатация.

В infrastructure добавлен BandwidthGovernor — локальный исполнитель upload/download бюджета и квоты принципала. API подключает его к HTTP-потокам; application/storage и worker не зависят от HTTP pacing. AdmissionQueue ограничивает также активные операции принципала. Контракт и граница одного процесса: ADR 0008.

Реализованы domain/application, нативный HTTP API, PostgreSQL с миграциями, LocalBlobStore, multipart/resume, каталог и история assets, SDK, консоль, completion worker и скачивание по ID артефакта, идентичности UPack и пути файла. Один API-процесс записи владеет БД. apps/scheduler остаётся каркасом без сценариев: распределённого scheduler нет; HA одной площадки и зеркала описаны ниже. Рабочие контракты и ограничения: CORE_RUNBOOK, LIFECYCLE_AND_CATALOG, ADR 0006.

Учётные записи, группы и сессии добавлены в тех же слоях: domain проверяет идентификаторы и точные repository grants, application определяет сценарий/порт, infrastructure реализует PostgreSQL и хеширование. API проверяет сессию и собирает principal при каждом запросе; UI использует SDK. Порядок и группировка списка UPack задаются чистой функцией application. ADR 0011.

Профиль read gateways сохраняет существующие границы: composition root API выбирает роль и связывает PostgresDownloadLease с доступностью локального governor. Domain/application не зависят от leases раздачи. Writer остаётся один, readers используют тот же согласованный root; миграция 5 и mode/maintenance barriers защищают конфигурацию. ADR 0009, эксплуатация.

Контрактный guard composition root сверяет зарегистрированные HTTP routes с OpenAPI перед готовностью сервера. Metadata живёт в contracts и не исполняет ACL; авторизация остаётся в application/infrastructure. Спецификация экспортируется сборкой и не включается в браузерный bundle. ADR 0018.

Ограниченный delegation профиль принят в ADR 0019: точный operator key → target account, отдельные admin actions и ceiling, без цепочек. Application валидирует команду и передаёт Principal порту; infrastructure повторно разрешает authority в той же транзакции, где меняет grants/keys/policy и пишет audit. Domain хранит чистые типы/actions. Контракт и обновление: SERVICE_DELEGATION.

Файловые страницы выделены в небольшие application/infrastructure asset-page модули. Параметры и prefix bounds чистые, SQL seek и scope cursor находятся в адаптере; каталог/HTTP только вызывают сценарий. Новый API сохраняет прежний assets list. ADR 0020.

Discovery репозиториев — чистая application projection актуального Principal, не новая таблица/RepositoryStore. Coarse permission mapping вынесен из HTTP в application с прежними semantics. Domain добавляет repository.read, transport и SDK используют отдельный wire contract. ADR 0021.

Клиентская очередь скачиваний живёт в SDK: DownloadQueue управляет расписанием, checkpointedDownload использует узкий DownloadStorage и существующий HTTP-клиент. OPFS/выбор конечного файла принадлежат web; domain/application/server не зависят от браузерного storage. ADR 0022. Области и видимость API описываются contracts registry; application operationVisible проецирует применимость по действующим правам без HTTP/БД. API объединяет её с ролью шлюза и выдаёт advisory-каталог. SDK namespaces делегируют существующему транспорту. ADR 0023, контракт.

Именованные вложения используют отдельные domain rules, application AttachmentStore/BuildAttachments и PostgreSQL-адаптер. Байты проходят прежний transfer stack. Migration 12 хранит append-only snapshots и FK-пины targets. Web разделяет annotation editor, attachments controller и общий worker hash. ADR 0024.

Границы и топологический порядок сборки теперь задаёт config/architecture.json. Проверяемые лимиты 500/300/100 и временные исключения описаны в ADR 0027; команды, расширение и требования к комментариям — в ENGINEERING_GATES. Существующий долг и порядок декомпозиции: аудит 2026-09-25.

SDK использует композицию: публичный ArkvoryClient делегирует группам операций, которые используют общий узкий HttpPort. HTTP-авторизация, ошибки и bounded parsing централизованы; transfer workflows сохраняют отдельные retry/checksum/abort-инварианты. Прежние flat methods и namespaces защищены проверкой строгого внешнего TS consumer. ADR 0028.

Маршруты upload/session/parts/completion выделены в upload-routes; UploadReceiver владеет ожиданием входящих bytes и deadline, получая bandwidth governor и диагностику через узкие порты. Эти механизмы остаются в HTTP-адаптере. LocalBlobStore отдельно обеспечивает точную длину завершённого чтения, включая Range и части. ADR 0029.

Декомпозиция API composition root завершена: server связывает runtime, application factories, security/context, диагностику, фоновые задачи и feature registrars. Владение ресурсами и rollback централизованы в ApiRuntime; очереди и потоки закрываются до фоновой работы и pools. Регистраторы получают узкие зависимости; полная композиция не передаётся обработчикам. ServerConfig содержит webDirectory, окружение читается только loadConfig. ARCH-016/017 сняты без новых исключений. ADR 0030.

Управление установкой выделено в apps/deploy; единственная внутренняя зависимость — переносимый packages/contracts для протокола обновлений. UpdatePort отделяет переключение/откат от supervisor, GitHub и файловой системы. Привилегированный updater отделён от HTTP API защищённым mailbox: сервер принимает только check/configure/apply и читает статус. ADR 0031, ADR 0038, обновления.

Локальный мастер удалённой установки также принадлежит apps/deploy: HTTP session/формы, orchestration, SSH, команды платформ и туннель разделены. Клиентский пакет включает отдельный bundle; apps/cli и публичный API не получают SSH-credentials и не приобретают зависимость от deploy. Приватный туннель не является сетевой публикацией сервиса. ADR 0036.

Комплекты запуска делегируют тому же deploy CLI. Релизный pipeline разделяет read-only сборку/приёмку и привилегированную публикацию уже проверенных байтов; состав и хеши assets проверяются перед публикацией. ADR 0032.

Каждая поставляемая точка входа (apps/api, apps/worker, apps/backup, apps/deploy, apps/cli) первым импортом подключает confine.js своего приложения: пакет resolution ограничивает разрешение модулей Node.js каталогом релиза и встроенными модулями, бандлы — только встроенными, пока код не назовёт релиз. ADR 0031, дополнение 2026-10-18.

Удалённый arkvoryctl находится в apps/cli, зависит только от SDK/contracts (и resolution для ограничения своей загрузки) и не имеет доступа к внутренним слоям сервера. CLI владеет профилями и локальными чекпойнтами; протокол, авторизация и сетевые повторы остаются в SDK. Устанавливается отдельными клиентскими пакетами с private runtime. ADR 0034, справка.

Единая идентичность Arkvory и чистая дорелизная установка описаны в ADR 0040. Все имена конфигурации, SQL, сервисных ключей и локального состояния используют Arkvory; нормализатор альтернативных имён и зависимость worker от contracts удалены.

Восстановление download intents остаётся в web (OPFS/sessionStorage/Web Locks); SDK принимает paused jobs через явный restore. Staging сегментирован по 8 MiB. CLI packages publish оркестрирует upload/register с прежним receipt; metadata search реализован отдельным SQL-модулем через BrowseStore. ADR 0041.

Владение PostgreSQL-сессией выделено из каталога: StorageOwnership ограничивает период подтверждения locks, необратимо закрывает потерянное соединение и не допускает повторный claim. Это не межсерверный fencing и не репликация: ADR 0042.

Объектные locks uploads/GC и динамические content pins используют тот же инфраструктурный монитор SQL-сессии. PostgresJobLease отдельно связывает heartbeat/finish reservation с конечным локальным окном; worker отслеживает потери и ограничивает shutdown. ADR 0043.

Ранний проект репликации (ADR 0044, REPLICATION, API, UI) не реализован в этом виде и сохранён как история; действующие решения ниже.

Зеркала и импорт по стадиям (ADR 0058): у зеркала свой каталог, ключи и права; worker забирает ленту изменений (R/changes, журнал arkvory_audit) и содержимое через публичный API источника. Application владеет mirror-sync, mirror-import, mirror-conflicts и портами источника и состояния; адаптеры источника и цели — в apps/worker, состояние и отложенные конфликты — PostgreSQL в infrastructure. Запись в зеркальный репозиторий отклоняется сценариями (mirror_read_only).

HA одной площадки (ADR 0072): переключение и fencing — Pacemaker/DRBD вне приложения. Служба arkvory-replica (apps/deploy, root) читает состояние ядра DRBD; API читает его через порт application ReplicaState и адаптер SocketReplicaState и в replica-guard отказывает в подтверждении записи (503 replication_degraded), пока копий меньше требуемого. Команды arkvory cluster-* принадлежат apps/deploy.

Очередь скачиваний (ADR 0076): правила сессий и очереди — domain DownloadSessions, исполнитель DownloadScheduler и подписанные талоны — infrastructure, допуск на HTTP-потоке — apps/api. Очередь живёт в памяти процесса записи; у шлюзов чтения своя очередь на процесс, общей очереди в PostgreSQL нет.

Единая консоль площадок (ADR 0077, этапы F1–F4): реестр площадок, сопряжение и опрос — application fleet-control и PostgreSQL-адаптеры; управляющая площадка ходит к другим по закреплённому сертификату с ключом fleet-key, который ограничен белым списком маршрутов. Изменения на площадке исполняет её собственный API или агент площадки (apps/deploy).

Модули (ADR 0079, этап X1; форма API — этап Y1 ADR 0083): переносимый пакет extension-api — публичный контракт модулей (манифест, ModuleContext и HOST_SERVICES, жизненный цикл, чистая planModules, снимок публичных деклараций api-report.d.ts); domain, application, contracts и web его не импортируют. Список встроенных модулей (upack, raw — ядро, oci, lfs, npm) и план с выбором оператора из ARKVORY_MODULES_FILE — packages/infrastructure/src/builtin-modules.ts и module-plan.ts: план один на установку, его считают API, шлюзы чтения и worker. Общий хост packages/infrastructure/src/module-host.ts, которым владеют корни API, worker и backup: план до старта, старт в порядке плана, откат и остановка в обратном порядке до закрытия пулов. Маршруты выключенного протокола не регистрируются, guard контракта строит исключения из префиксов включённых модулей. Процесс записи хранит отпечатки плана в arkvory_module_plan; шлюз чтения и worker с другим отпечатком совместимости (без версий ядра и встроенных модулей) не готовы (module_plan_mismatch), полный отпечаток с версиями — справочный. Подписанные модули (этап X4) загружает packages/infrastructure (module-loader.ts, module-installation.ts): права каталога modules/, повторная проверка подписи и файлов, список отзыва и роли — до любого кода; план со встроенными модулями, выключены до выбора оператора; import() с вычисляемым аргументом — только module-import.ts, который после проверок добавляет каталог пакета в ограничение разрешения процесса (allowResolutionUnder, ADR 0031) и сам разрешает коду пакета только его файлы. Отказ — на модуль (system/modules показывает failed, revoked или unavailable с кодом); выполняют их API, шлюзы чтения, worker и backup по объявленным ролям; службы выдаются отдельно для роли процесса. Миграций схемы самих модулей пока нет. GET /api/v1/system/modules показывает план глобальному администратору, capabilities.modules — включённые модули шлюза.

Наблюдаемость остаётся в infrastructure без внешних зависимостей: DiagnosticLogger (JSON-строки, уровни, поля процесса, неблокирующий учёт потерь) и MetricsRegistry (Prometheus text). Composition roots создают один логгер на процесс, ставят crash-only обработчики и регистрируют /health/metrics. Correlation ID передаётся явно копией Principal на запрос и сохраняется адаптерами задач и аудита; domain/application его не интерпретируют, глобального контекста нет: ADR 0052.

Лента событий для внешних систем (ADR 0080, этапы E1–E2): domain events — каталог типов, отображение строки журнала в событие, ограничение data и правило удаления префикса позиций; application EventFeed — видимость по Principal, ограниченный просмотр и курсор с эпохой; infrastructure — outbox (appendCatalogEvents вызывает единственный писатель журнала appendCatalogAudit в транзакции изменения) и PostgresEventLog (чтение в одном снимке, секвенсор и удаление под блокировкой строки состояния). Секвенсор и удаление по сроку работают циклом worker; HTTP-маршруты и CloudEvents-конверт — apps/api и contracts, клиенты — SDK events и arkvoryctl events tail.

Службы модулей в выпущенном ядре

Ядро 0.6.3 предоставляет extension API 0.13 (схема 52). Помимо загрузки пакетов, API передаёт маршрутам область прав вызывающего и отмену; хост выдаёт staged completion, change feed, storage и compute по ролям. derived-cache управляет поколениями производных данных и их reservations/pins; module-state — ограниченным атомарным состоянием установки; resource-catalog — общими владельцами, ссылками, поколениями файлов и допуском GC. Эти службы не раскрывают модулям пул БД или право произвольного удаления. Recovery каталогов и измерение файлов выполняются с ограниченной параллельностью; сбой измерения сохраняет dirty-состояние квот. Решения: ADR 0084, ADR 0086, ADR 0089, ADR 0090, ADR 0091.

Pro подключает эти общие контракты вне публичного дерева ядра. Backup читает опубликованные байты через модуль в read-only роли; logical restore с --to-default-storage переносит их в обычные blobs. Производные каталоги и внутренний регистр координации не включаются в логическую копию и не заменяют опубликованные данные. Полный перечень исключений — packages/infrastructure/src/backup-tables.ts; эксплуатация — CURRENT_STATE.