Forge

docs/ / adr/0002-bridge-provider-keys-and-reopen.md · branch master

ADR-0002: Bridge provider keys — reopen чужого .witdb

Статус: принято
Дата: 2026-06-06
Репозиторий: AI-Guiders/pywitdb
Связано: ADR-0001
Upstream: WitDatabase ProviderMetadata (header bytes 48–99), StorageDetector, ConfigurationValidator


Решение (одной фразой)

witdb-bridge не реализует свой reopen-детект — делегирует открытие OutWit.Database.Core (header + ConfigurationValidator). В bridge обязательно подключены NuGet-пакеты, покрывающие Tier-1 provider keys прод-контура; чужие/custom keys → явная ошибка unknown_provider, не silent corruption.


Контекст

WitDatabase — provider-based архитектура (ProviderRegistry, IProvider). При создании .witdb в database header (100 байт, magic WitDB Format 1) сохраняется ProviderMetadata:

Поле в header Persisted Пример
StoreProviderKey да (16 байт UTF-8) btree, lsm
EncryptionProviderKey да aes-gcm, chacha20-poly1305, ""
ProviderFeatures да (flags byte) Encryption, Transactions, FileLocking, Mvcc
CacheProviderKey нет при reopen — default (clock)
JournalProviderKey нет при reopen — default builder

StorageDetector для файла читает header → ConfigurationValidator.GetOpeningHints. Если magic зашифрован — RequiresPassword=true, EncryptionProvider=unknown до успешного decrypt.

Критичные mismatch при reopen (ConfigurationValidator, non-strict):

  1. StoreProviderKey должен совпасть.
  2. Если store encrypted — нужен encryption provider; тип (EncryptionProviderKey) должен совпасть.

Cache/journal mismatch в non-strict не блокируют open.

Python/pywitdb не парсит header сам на MVP — bridge = thin wrapper над Core open path.


Tier-1: обязательная поддержка bridge (MVP)

Сценарии, которые должны открываться без доработок — типичный prod-контур door-to-singularity:

# Path shape Store key Encryption key Features (типично) NuGet в bridge
T1a файл *.witdb btree "" Transactions, FileLocking OutWit.Database.Core
T1b файл *.witdb btree aes-gcm Encryption + Transactions + FileLocking OutWit.Database.Core
T1c файл *.witdb btree aes-gcm + Mvcc OutWit.Database.Core

T1b/T1c — путь Intercom / CascadeIDE (intercom.witdb, UseWitDb + password). Это primary contract test для cross-lang CI.

Built-in keys (Core ProviderRegistration)

Bridge может полагаться на auto-registration Core:

Component Keys
IKeyValueStore btree, lsm, memory
ICryptoProvider aes-gcm
IStorage file, memory, encrypted
IPageCache clock (default), lru
ITransactionJournal wal, rollback

Tier-2: поддержка в v0.1 (явно в CI)

# Path shape Store key Encryption key NuGet
T2a директория (LSM) lsm "" или aes-gcm Core
T2b файл btree chacha20-poly1305 Core + OutWit.Database.Core.BouncyCastle

BouncyCastle-пакет регистрирует chacha20-poly1305 через [ModuleInitializer] — bridge обязан ссылаться на сборку, иначе ProviderNotFoundException при reopen WASM-compatible stores.


Tier-3: вне scope MVP (явный отказ)

Случай Поведение bridge
Custom ICryptoProvider / IKeyValueStore (сторонний NuGet) unknown_provider + stored key в ответе
indexeddb storage (Blazor) unsupported_backend — не файловый .witdb
memory store только ephemeral create; reopen файла N/A
SQL-only / EF schema mismatch out of scope KV bridge v0
Provider key длиннее 16 байт невозможен по формату header (upstream limit)

Не пытаться fallback на другой engine или игнорировать encryption mismatch.


Поведение witdb-bridge при open

Делегирование Core

open(path, password?)  →  WitDatabaseBuilder / WitDatabase.Create(existing)
                         →  StorageDetector.Detect(path)  [optional preflight]
                         →  Core build + ConfigurationValidator.ValidateOrThrow

Bridge не дублирует логику ConfigurationValidator в Python.

Ответ open (расширение NDJSON, ADR-0001)

Успех:

{
  "id": 1,
  "ok": true,
  "store": {
    "path": "intercom.witdb",
    "store_provider": "btree",
    "encryption_provider": "aes-gcm",
    "features": ["encryption", "transactions", "file_locking"]
  }
}

Ошибки (стабильные code):

code Когда
file_not_found path не существует и create=false
password_required encrypted, password не передан
wrong_password decrypt/auth failure
configuration_mismatch ConfigurationMismatchException
unknown_provider ProviderNotFoundException — нет регистрации для stored key
unsupported_backend indexeddb / non-file backends

Python pywitdb маппит коды в WitDbStoreError / BridgeNotAvailableError.


Зависимости bridge-проекта (зафиксировать)

bridge/WitDbBridge/WitDbBridge.csproj:

<PackageReference Include="OutWit.Database.Core" Version="…" />
<PackageReference Include="OutWit.Database.Core.BouncyCastle" Version="…" />

Версии — pin в Directory.Packages.props; bump только с прогоном contract tests.

Явный вызов BouncyCastleProviderRegistration.EnsureRegistered() в Program.cs bridge (на случай trimmed publish без ModuleInitializer).


Contract tests (обязательные до merge bridge PR)

Fixture Writer Reader Assert
fixtures/t1a-plain-btree.witdb C# Core, btree, no encryption pywitdb get/put round-trip KV
fixtures/t1b-aes-intercom-like.witdb C# + password, aes-gcm pywitdb same password KV + metadata keys
fixtures/t2a-lsm-dir/ C# LSM directory pywitdb open(dir) KV
fixtures/t2b-chacha.witdb C# + BouncyCastle pywitdb KV

Каждый fixture commit'ится в репо или генерируется в CI из tests/fixtures/cs/ (предпочтительно generate → не drift).


Native tier (v1) — следствие

Native C ABI не обязан реплицировать весь ProviderRegistry в Python. Минимум:

  • те же Tier-1 store+encryption combinations;
  • ошибки ABI по кодам, совместимым с bridge (unknown_provider, …).

Полный plugin host в Python — non-goal.


Последствия

  • Bridge binary чуть тяжелее (BouncyCastle ref) — acceptable для dev/CI.
  • Чужие custom providers требуют либо добавления их NuGet в bridge, либо native tier upstream — документируем в README.
  • Intercom/CascadeIDE .witdb — regression anchor для каждого релиза pywitdb.

Ссылки

  • WitDatabase: Sources/Core/OutWit.Database.Core/Pages/ProviderMetadata.cs
  • WitDatabase: Sources/Core/OutWit.Database.Core/Providers/ConfigurationValidator.cs
  • WitDatabase: Sources/Core/OutWit.Database.Core/Providers/StorageDetector.cs
  • WitDatabase: Sources/Core/OutWit.Database.Core/EXTENSIBILITY.md
View only · write via MCP/CIDE