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):
StoreProviderKeyдолжен совпасть.- Если 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