Commit 24e5538
authored
feat(encryption): Stage 6D-6c-1 - Applier in-memory accessors for storage envelope state (#821)
## Summary
- Add `Applier.ActiveStorageKeyID() (uint32, bool)` and
`Applier.StorageEnvelopeActive() bool` backed by `atomic.Uint32` /
`atomic.Bool`. These are the per-Put closures main.go will thread into
`store.WithEncryption` and `store.WithStorageEnvelopeGate` in 6D-6c-2; a
`ReadSidecar`-on-every-Put would serialise the hot path through a JSON
parse + fsync barrier.
- Coherence kept by **durable write-then-cache** ordering: `NewApplier`
primes from `ReadSidecar` on construction, and `writeBootstrapSidecar` /
`writeRotationSidecar` / the fresh-success branch of
`applyEnableStorageEnvelope` call `refreshActiveStateCache(sc)` AFTER
`WriteSidecar` succeeds. The §2.1 #3 stale-DEKID and §2.1 #4
already-active no-op branches intentionally skip the refresh — they
don't change the mirrored fields.
- Operator-inert until 6D-6c-2 wires the method values into the storage
layer; 6D-6c-3 then wires the capability fan-out closure and the e2e
integration test.
Design doc updated:
[`docs/design/2026_05_18_partial_6d_enable_storage_envelope.md`](docs/design/2026_05_18_partial_6d_enable_storage_envelope.md)
tracks 6D-6c-1 as shipped and 6D-6c-2 / 6D-6c-3 as open.
## Why an in-memory mirror
The §6.2 storage gate and the §4.1 active storage DEK are read on the
storage hot path. The sidecar JSON is the durable source of truth, but
reading it per Put would dominate latency. `atomic.Uint32` +
`atomic.Bool` give a wait-free single-load read with no allocations and
no syscalls.
The two atomics are deliberately independent (not snapshot-atomic
together): the storage layer consults them independently —
`ActiveStorageKeyID` gates "do we encrypt at all" and
`StorageEnvelopeActive` gates "do we wrap in the §4.1 envelope". The
cross-invariant "envelope-active ⇒ DEK exists" is enforced by apply
ordering (cutover requires a bootstrapped sidecar), not by the cache.
## Crash recovery
Refresh runs AFTER the durable WriteSidecar. A crash between fsync and
atomic store is benign — disk-truth wins and the next startup's
`NewApplier` prime re-syncs the cache. A corrupt sidecar at construction
time surfaces back to the caller so a misconfigured node fails to start
instead of silently serving stale-zero state.
## Self-review (5 lenses)
1. **Data loss** — refresh is purely additive after the existing fsync;
no write path shortened, no error suppressed. Corrupt-sidecar reads at
construction fail the start instead of silently zeroing the cache.
2. **Concurrency / distributed failures** — atomic primitives + a
`-race`-clean concurrent-reads stress test (8 readers × 2000 reads
against a single applier goroutine). Two independent atomics is fine
because the storage layer consults them independently and the
cross-invariant is enforced by apply ordering.
3. **Performance** — hot Put path drops from JSON parse + potential
fsync to a single atomic load (~ns vs ~10–100µs).
4. **Data consistency** — durable write-then-cache ordering everywhere;
no-op apply branches skip the redundant refresh, keeping the invariant
"cache changes only when the mirrored sidecar field changes" explicit.
§2.1 / §6.4 semantics unchanged.
5. **Test coverage** — 5 functional tests (pre-bootstrap,
post-bootstrap, post-rotate-dek, post-cutover,
primed-from-existing-sidecar) + 1 race-stress test, all via the public
API.
## Test plan
- [x] `go test -race ./internal/encryption/...`
- [x] `go test -race ./internal/... ./store/...`
- [x] `golangci-lint --config=.golangci.yaml run
./internal/encryption/... ./store/...`
- [ ] CI green
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* Added comprehensive test coverage for storage encryption state
tracking across initialization, operational phases, startup recovery,
and concurrent access scenarios.
* **Chores**
* Updated design documentation to reflect current development milestone
progress.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/bootjp/elastickv/pull/821?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->3 files changed
Lines changed: 669 additions & 12 deletions
File tree
- docs/design
- internal/encryption
Lines changed: 44 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
74 | 100 | | |
75 | 101 | | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
82 | 119 | | |
83 | 120 | | |
84 | 121 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| |||
63 | 64 | | |
64 | 65 | | |
65 | 66 | | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
71 | 74 | | |
72 | 75 | | |
73 | 76 | | |
74 | 77 | | |
75 | 78 | | |
76 | 79 | | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
77 | 188 | | |
78 | 189 | | |
79 | 190 | | |
| |||
132 | 243 | | |
133 | 244 | | |
134 | 245 | | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
135 | 261 | | |
136 | 262 | | |
137 | 263 | | |
| |||
162 | 288 | | |
163 | 289 | | |
164 | 290 | | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
165 | 318 | | |
166 | 319 | | |
167 | 320 | | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
168 | 344 | | |
169 | 345 | | |
170 | 346 | | |
| |||
513 | 689 | | |
514 | 690 | | |
515 | 691 | | |
| 692 | + | |
516 | 693 | | |
517 | 694 | | |
518 | 695 | | |
| |||
773 | 950 | | |
774 | 951 | | |
775 | 952 | | |
| 953 | + | |
776 | 954 | | |
777 | 955 | | |
778 | 956 | | |
| |||
809 | 987 | | |
810 | 988 | | |
811 | 989 | | |
| 990 | + | |
812 | 991 | | |
813 | 992 | | |
814 | 993 | | |
| |||
0 commit comments