Модульная архитектура с направлением зависимостей внутрь: предметные правила → сценарии и порты → внешние адаптеры → точки сборки. Наличие 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.