Forge
markdownf8555a15
1# ADR-0002: Bridge provider keys — reopen чужого `.witdb`
2
3**Статус:** принято
4**Дата:** 2026-06-06
5**Репозиторий:** [AI-Guiders/pywitdb](https://github.com/AI-Guiders/pywitdb)
6**Связано:** [ADR-0001](0001-binding-architecture-bridge-and-native.md)
7**Upstream:** WitDatabase `ProviderMetadata` (header bytes 48–99), `StorageDetector`, `ConfigurationValidator`
8
9---
10
11## Решение (одной фразой)
12
13**`witdb-bridge` не реализует свой reopen-детект** — делегирует открытие `OutWit.Database.Core` (header + `ConfigurationValidator`). В bridge **обязательно подключены** NuGet-пакеты, покрывающие **Tier-1** provider keys прод-контура; чужие/custom keys → явная ошибка `unknown_provider`, не silent corruption.
14
15---
16
17## Контекст
18
19WitDatabase — provider-based архитектура (`ProviderRegistry`, `IProvider`). При создании `.witdb` в **database header** (100 байт, magic `WitDB Format 1`) сохраняется `ProviderMetadata`:
20
21| Поле в header | Persisted | Пример |
22|---------------|-----------|--------|
23| `StoreProviderKey` | да (16 байт UTF-8) | `btree`, `lsm` |
24| `EncryptionProviderKey` | да | `aes-gcm`, `chacha20-poly1305`, `""` |
25| `ProviderFeatures` | да (flags byte) | Encryption, Transactions, FileLocking, Mvcc |
26| `CacheProviderKey` | **нет** | при reopen — default (`clock`) |
27| `JournalProviderKey` | **нет** | при reopen — default builder |
28
29`StorageDetector` для **файла** читает header → `ConfigurationValidator.GetOpeningHints`. Если magic зашифрован — `RequiresPassword=true`, `EncryptionProvider=unknown` до успешного decrypt.
30
31**Критичные mismatch при reopen** (`ConfigurationValidator`, non-strict):
32
331. `StoreProviderKey` должен совпасть.
342. Если store encrypted — нужен encryption provider; тип (`EncryptionProviderKey`) должен совпасть.
35
36Cache/journal mismatch в non-strict **не** блокируют open.
37
38Python/pywitdb **не** парсит header сам на MVP — bridge = thin wrapper над Core open path.
39
40---
41
42## Tier-1: обязательная поддержка bridge (MVP)
43
44Сценарии, которые **должны** открываться без доработок — типичный prod-контур door-to-singularity:
45
46| # | Path shape | Store key | Encryption key | Features (типично) | NuGet в bridge |
47|---|------------|-----------|----------------|-------------------|----------------|
48| T1a | файл `*.witdb` | `btree` | `""` | Transactions, FileLocking | `OutWit.Database.Core` |
49| T1b | файл `*.witdb` | `btree` | `aes-gcm` | Encryption + Transactions + FileLocking | `OutWit.Database.Core` |
50| T1c | файл `*.witdb` | `btree` | `aes-gcm` | + Mvcc | `OutWit.Database.Core` |
51
52**T1b/T1c** — путь Intercom / CascadeIDE (`intercom.witdb`, `UseWitDb` + password). Это **primary contract test** для cross-lang CI.
53
54### Built-in keys (Core `ProviderRegistration`)
55
56Bridge может полагаться на auto-registration Core:
57
58| Component | Keys |
59|-----------|------|
60| `IKeyValueStore` | `btree`, `lsm`, `memory` |
61| `ICryptoProvider` | `aes-gcm` |
62| `IStorage` | `file`, `memory`, `encrypted` |
63| `IPageCache` | `clock` (default), `lru` |
64| `ITransactionJournal` | `wal`, `rollback` |
65
66---
67
68## Tier-2: поддержка в v0.1 (явно в CI)
69
70| # | Path shape | Store key | Encryption key | NuGet |
71|---|------------|-----------|----------------|-------|
72| T2a | **директория** (LSM) | `lsm` | `""` или `aes-gcm` | Core |
73| T2b | файл | `btree` | `chacha20-poly1305` | Core + **`OutWit.Database.Core.BouncyCastle`** |
74
75BouncyCastle-пакет регистрирует `chacha20-poly1305` через `[ModuleInitializer]` — bridge **обязан** ссылаться на сборку, иначе `ProviderNotFoundException` при reopen WASM-compatible stores.
76
77---
78
79## Tier-3: вне scope MVP (явный отказ)
80
81| Случай | Поведение bridge |
82|--------|------------------|
83| Custom `ICryptoProvider` / `IKeyValueStore` (сторонний NuGet) | `unknown_provider` + stored key в ответе |
84| `indexeddb` storage (Blazor) | `unsupported_backend` — не файловый `.witdb` |
85| `memory` store | только ephemeral create; reopen файла N/A |
86| SQL-only / EF schema mismatch | out of scope KV bridge v0 |
87| Provider key длиннее 16 байт | невозможен по формату header (upstream limit) |
88
89**Не** пытаться fallback на другой engine или игнорировать encryption mismatch.
90
91---
92
93## Поведение `witdb-bridge` при `open`
94
95### Делегирование Core
96
97```text
98open(path, password?) → WitDatabaseBuilder / WitDatabase.Create(existing)
99 → StorageDetector.Detect(path) [optional preflight]
100 → Core build + ConfigurationValidator.ValidateOrThrow
101```
102
103Bridge **не** дублирует логику `ConfigurationValidator` в Python.
104
105### Ответ `open` (расширение NDJSON, ADR-0001)
106
107Успех:
108
109```json
110{
111 "id": 1,
112 "ok": true,
113 "store": {
114 "path": "intercom.witdb",
115 "store_provider": "btree",
116 "encryption_provider": "aes-gcm",
117 "features": ["encryption", "transactions", "file_locking"]
118 }
119}
120```
121
122Ошибки (стабильные `code`):
123
124| code | Когда |
125|------|-------|
126| `file_not_found` | path не существует и `create=false` |
127| `password_required` | encrypted, password не передан |
128| `wrong_password` | decrypt/auth failure |
129| `configuration_mismatch` | `ConfigurationMismatchException` |
130| `unknown_provider` | `ProviderNotFoundException` — нет регистрации для stored key |
131| `unsupported_backend` | indexeddb / non-file backends |
132
133Python `pywitdb` маппит коды в `WitDbStoreError` / `BridgeNotAvailableError`.
134
135---
136
137## Зависимости bridge-проекта (зафиксировать)
138
139`bridge/WitDbBridge/WitDbBridge.csproj`:
140
141```xml
142<PackageReference Include="OutWit.Database.Core" Version="…" />
143<PackageReference Include="OutWit.Database.Core.BouncyCastle" Version="…" />
144```
145
146Версии — pin в `Directory.Packages.props`; bump только с прогоном contract tests.
147
148**Явный вызов** `BouncyCastleProviderRegistration.EnsureRegistered()` в `Program.cs` bridge (на случай trimmed publish без ModuleInitializer).
149
150---
151
152## Contract tests (обязательные до merge bridge PR)
153
154| Fixture | Writer | Reader | Assert |
155|---------|--------|--------|--------|
156| `fixtures/t1a-plain-btree.witdb` | C# Core, btree, no encryption | pywitdb `get`/`put` | round-trip KV |
157| `fixtures/t1b-aes-intercom-like.witdb` | C# + password, aes-gcm | pywitdb same password | KV + metadata keys |
158| `fixtures/t2a-lsm-dir/` | C# LSM directory | pywitdb `open(dir)` | KV |
159| `fixtures/t2b-chacha.witdb` | C# + BouncyCastle | pywitdb | KV |
160
161Каждый fixture commit'ится в репо или генерируется в CI из `tests/fixtures/cs/` (предпочтительно generate → не drift).
162
163---
164
165## Native tier (v1) — следствие
166
167Native C ABI **не** обязан реплицировать весь `ProviderRegistry` в Python. Минимум:
168
169- те же **Tier-1** store+encryption combinations;
170- ошибки ABI по кодам, совместимым с bridge (`unknown_provider`, …).
171
172Полный plugin host в Python — **non-goal**.
173
174---
175
176## Последствия
177
178- Bridge binary чуть тяжелее (BouncyCastle ref) — acceptable для dev/CI.
179- Чужие custom providers требуют либо добавления их NuGet в bridge, либо native tier upstream — документируем в README.
180- Intercom/CascadeIDE `.witdb` — regression anchor для каждого релиза pywitdb.
181
182---
183
184## Ссылки
185
186- WitDatabase: `Sources/Core/OutWit.Database.Core/Pages/ProviderMetadata.cs`
187- WitDatabase: `Sources/Core/OutWit.Database.Core/Providers/ConfigurationValidator.cs`
188- WitDatabase: `Sources/Core/OutWit.Database.Core/Providers/StorageDetector.cs`
189- WitDatabase: `Sources/Core/OutWit.Database.Core/EXTENSIBILITY.md`
190
View only · write via MCP/CIDE