Skip to content

jepsen: run the existing suites against an encrypted cluster (§8.4) - #1232

Open
bootjp wants to merge 2 commits into
mainfrom
design/jepsen-encrypted-cluster
Open

bootjp wants to merge 2 commits into
mainfrom
design/jepsen-encrypted-cluster

Conversation

@bootjp

@bootjp bootjp commented Sep 10, 2026

Copy link
Copy Markdown
Owner

What §8.4 actually asks for

I went looking for an "encrypted Jepsen workload" and found the design says the opposite:

Encryption is consistency-transparent (same input bytes, different output bytes; FSM apply still deterministic), so no new Jepsen workload is required. A pass under the existing suite is the acceptance gate.

So the missing piece was never a workload — it was the ability to stand the cluster up encrypted. --encryption does that: it provisions the §5.1 KEK file on each node with owner-only permissions and starts the server with --encryption-enabled, --kekFile and --encryptionSidecarPath.

Decisions worth reviewing

All three flags travel together. A sidecar path alone only enables read-only capability probing; the mutating RPCs a bootstrap needs require --encryption-enabled and a KEK source. Two of the three yields either a refusal to start or, worse, a cluster that starts unencrypted.

The KEK is a fixed test value, not generated. Every node must unwrap the same sidecar — a per-node random KEK fails startup with ErrKEKMismatch, which is a much more confusing failure to debug than a hardcoded test key is to notice.

The flag lives in common-cli-opts so a future workload inherits the gate without opting in, and defaults off so existing runs measure exactly what they did before.

The test that wasn't testing anything

My first version of these tests passed with db.clj dropping the encryption flags entirely — i.e. it would have green-lit a --encryption run that produced an unencrypted cluster, reported PASS, and been recorded as evidence for this acceptance gate. That is strictly worse than having no gate.

Fixed by extracting server-args from start-node! as a pure function, purely so the flag set is assertable without SSH. Dropping the flags now fails 3 tests.

Test evidence

  • Full Jepsen suite: 157 tests, 353 assertions, 0 failures
  • Revert-checked, restores byte-exact:
    • workload drops the option → 1 failure
    • flag defaults on → 1 failure (it would silently change what every existing run measures)
    • db.clj drops the flags → 3 failures (this is the one that initially passed)

9 tests: flag availability and default, propagation through both workloads named in §8.4, ekdb/db carrying the option, the three-flag emission, absence when unrequested, and that enabling it disturbs no other argv entry.

Behavior change / risk

Test-harness only; no production code. Default off, so every existing run is byte-identical.

Not included: wiring --encryption through the remaining workloads (S3, SQS, multi-table). §8.4 names Redis and DynamoDB as the gate, and the flag is in common-cli-opts, so extending is a one-line change per workload if you want it.

Self-review (five passes)

  1. Data loss — none; harness code. The gate exists to detect it.
  2. Concurrency / distributed failures — the KEK is provisioned before start-node!, since startup guards refuse a missing KEK before anything a workload could observe.
  3. Performance — one small file write per node at setup.
  4. Data consistency — the point of the gate: the existing linearizability checkers now run against an encrypted cluster unchanged.
  5. Test coverage — 9 tests, 3 revert-checks. The load-bearing one is that the flags reach the server, since a silently-unencrypted PASS is the failure mode that matters.

https://claude.ai/code/session_013rNHooj7NF3giihWVba8QE

The encryption design's Jepsen gate is explicit that NO new workload is
required: encryption is consistency-transparent, so the acceptance gate
is the existing Redis and DynamoDB workloads passing against an
encrypted cluster. What was missing was the ability to stand that
cluster up.

--encryption provisions the §5.1 KEK file on each node with owner-only
permissions and starts the server with --encryption-enabled, --kekFile
and --encryptionSidecarPath. The three travel together because a
sidecar path alone only enables read-only capability probing, while the
mutating RPCs a bootstrap needs require the flag and a KEK source too.

The KEK is a fixed test value rather than a generated one: every node
must unwrap the same sidecar, and a per-node random KEK would fail
startup with ErrKEKMismatch — a far more confusing failure than a
hardcoded test key.

The flag lives in common-cli-opts, so a future workload inherits the
gate without opting in, and defaults off so existing runs measure the
same thing they did before.

server-args is extracted from start-node! as a pure function purely so
the flag set is testable without SSH. That matters more than it looks:
a --encryption run that silently produced an UNENCRYPTED cluster would
report PASS and be recorded as evidence for this gate, which is worse
than having no gate. The first version of these tests did not pin it —
dropping the flags from db.clj left them all green.

Full suite: 157 tests, 0 failures.

Claude-Session: https://claude.ai/code/session_013rNHooj7NF3giihWVba8QE
@bootjp

bootjp commented Sep 10, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@bootjp

bootjp commented Sep 10, 2026

Copy link
Copy Markdown
Owner Author

@claude review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T15:09:22.189751Z 395f7d6 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 36 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: a86c3246-4c53-4612-a117-cc0288940d67

📥 Commits

Reviewing files that changed from the base of the PR and between e3ac4cc and 2d280f2.

📒 Files selected for processing (13)
  • cmd/jepsen-encryption-setup/main.go
  • cmd/jepsen-encryption-setup/main_test.go
  • docs/design/2026_04_29_partial_data_at_rest_encryption.md
  • jepsen/src/elastickv/cli.clj
  • jepsen/src/elastickv/db.clj
  • jepsen/src/elastickv/dynamodb_multi_table_workload.clj
  • jepsen/src/elastickv/dynamodb_types_workload.clj
  • jepsen/src/elastickv/dynamodb_workload.clj
  • jepsen/src/elastickv/redis_workload.clj
  • jepsen/src/elastickv/redis_zset_safety_workload.clj
  • jepsen/src/elastickv/s3_workload.clj
  • jepsen/src/elastickv/sqs_htfifo_workload.clj
  • jepsen/test/elastickv/encrypted_cluster_test.clj

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude

claude Bot commented Sep 10, 2026

Copy link
Copy Markdown

Claude finished @bootjp's task in 3m 39s —— View job


Review: jepsen: run the existing suites against an encrypted cluster (§8.4)

  • Gather context (PR description, diff, CLAUDE.md conventions)
  • Read changed files (cli.clj, db.clj, workloads, design doc, new test)
  • Review --encryption flag wiring and server-args extraction
  • Review new encrypted_cluster_test.clj
  • Cross-check against main.go/design doc for what the three flags actually turn on
  • Post final review

The cluster this stands up is never actually encrypted

--encryption provisions the KEK file and starts every node with --encryption-enabled --kekFile --encryptionSidecarPath (jepsen/src/elastickv/db.clj:129-166), but per the design doc's own milestone table, those three flags only make a node capable of accepting the encryption-mutating admin RPCs — they don't turn encryption on. --encryption-enabled's own flag text says it opts into "encryption-mutating EncryptionAdmin RPCs" (main.go:233), and Stage 6's changelog is explicit that reaching an actually-encrypted state requires three further operator actions that this PR never performs:

  1. encryption bootstrap (Stage 5C/5E) — proposes BootstrapEncryption, creates the initial DEK/writer registry.
  2. enable-storage-envelope (Stage 6D) — cuts storage over from cleartext to ciphertext.
  3. enable-raft-envelope (Stage 6E) — cuts the raft log over.

None of these are invoked anywhere in this PR. upload-binaries! (jepsen/src/elastickv/db.clj:70-79) only builds/ships the elastickv and raftadmin binaries — elastickv-admin, the only binary with an encryption bootstrap/enable-storage-envelope/enable-raft-envelope subcommand, is never built, uploaded, or invoked anywhere in jepsen/. There's also no auto-bootstrap-on-startup path in main.go (--encryption-rotate-on-startup rotates an existing DEK; it doesn't create one).

Net effect: a --encryption Jepsen run boots nodes that are encryption-capable but never cut over, so every key/value written during the run is stored in cleartext exactly as it would be without the flag. This is materially different from what §8.4 asks for ("Run the existing ... workloads against an encrypted cluster") and from what the design doc's own "Implemented" note now claims (docs/design/2026_04_29_partial_data_at_rest_encryption.md:2448-2455). It's also the same failure mode the PR is explicitly written to guard against — a --encryption run that silently produces an unencrypted cluster, reports PASS, and gets recorded as evidence for the gate — just one layer above where encrypted_cluster_test.clj checks (it pins that the flags reach argv, not that the storage/raft envelope is ever cut over).

Suggested fix: after start-node! brings the bootstrap node up (and after the join loop in setup!), shell out to elastickv-admin encryption bootstrap (build/upload that binary alongside elastickv/raftadmin), then enable-storage-envelope and enable-raft-envelope, before the workload begins issuing ops. Until that lands, --encryption and the "Implemented" note should be described as "starts an encryption-capable cluster" rather than "runs against an encrypted cluster."

Fix this →

Secondary: --encryption is silently inert on the workloads that don't wire it

The flag lives in cli/common-cli-opts (jepsen/src/elastickv/cli.clj:19-20), so s3_workload.clj, sqs_htfifo_workload.clj, dynamodb_multi_table_workload.clj, dynamodb_types_workload.clj, and redis_zset_safety_workload.clj all parse it into their CLI opts (via (into cli/common-cli-opts ...)) but never thread it into their ekdb/db call. Running any of those with --encryption parses cleanly and silently produces an unencrypted cluster — no error, no warning. The PR body calls this out as known/deferred scope, which is reasonable, but it's worth a one-line assert/warn in those workloads' option validation (or in cli.clj) given how much of this PR's own design is about not letting --encryption silently do nothing.

What looks solid

  • server-args extraction (jepsen/src/elastickv/db.clj:135-166) is a faithful pure-function refactor of the old inline args cond-> in start-node! — I diffed the two forms line-by-line and the non-encryption behavior is unchanged.
  • kek-test-bytes (32 raw k bytes) matches --kekFile's documented "32 raw bytes" requirement (main.go:248), and using a fixed value across all nodes is correct — a per-node random KEK would indeed hit ErrKEKMismatch since they all unwrap the same sidecar.
  • provision-kek! runs only in setup!, not in db/Kill's start!; since kill! doesn't wipe data-dir (only teardown! does), the KEK file survives nemesis-driven restarts, so it doesn't need re-provisioning there.
  • The three-flags-travel-together design (sidecar-alone = read-only probing) matches main.go:218-233's documented gating.
  • encrypted_cluster_test.clj's revert-checks are good practice, and the flag names/opts plumbing genuinely match what main.go expects.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 395f7d6259

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +251 to +252
(when (:encryption opts)
(provision-kek! node))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bootstrap and activate encryption before running workloads

When --encryption is used with a fresh Jepsen data directory, this branch only provisions a KEK before starting the nodes. The server's --encryption-enabled flag merely enables the encryption mutator RPCs, while buildEncryptionWriteWiring deliberately keeps writes cleartext until both BootstrapEncryption and EnableStorageEnvelope have applied; nothing in setup! invokes either operation. Consequently the Redis and DynamoDB acceptance runs can report PASS while all workload data was stored unencrypted. After cluster membership is established, the harness must bootstrap the DEKs, perform the storage-envelope cutover, and wait for it to apply before beginning the workload.

Useful? React with 👍 / 👎.

Comment on lines +20 to +21
[nil "--encryption" "Run against a cluster with data-at-rest encryption enabled."
:default false]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not advertise encryption to workloads that drop the option

Because this option is added to common-cli-opts, the S3, SQS, DynamoDB-types, DynamoDB-multi-table, and Redis-zset entrypoints all accept --encryption; however, their test constructors omit :encryption when building ekdb/db (a repo-wide search shows only redis_workload.clj and dynamodb_workload.clj propagate it). Those commands therefore silently launch an unencrypted cluster despite the CLI promise. Either propagate the option through every workload using the common options or reject/remove it from unsupported entrypoints.

Useful? React with 👍 / 👎.

Two review findings, both of which made the §8.4 gate report success on a
cleartext cluster — the exact failure this suite's own docstring calls
"worse than no gate at all, because it would be recorded as evidence".

P1 — setup! provisioned a KEK and started the nodes, and nothing else.
--encryption-enabled only opens the EncryptionAdmin mutator RPCs;
buildEncryptionWriteWiring deliberately keeps the store's envelope gate
closed until BOTH BootstrapEncryption and EnableStorageEnvelope have
applied (the gate reads cache.StorageEnvelopeActive). Every "encrypted"
Redis and DynamoDB run therefore stored cleartext and could still PASS.

setup! now bootstraps the DEKs and performs the §7.1 Phase-1 storage
cutover after membership is established — after, because the writer batch
needs a registry entry per member (§5.6 step 1a) and the cutover's
capability gate requires every voter to report capable. It then polls
every node until it reports storage_envelope_active and FAILS SETUP if
any node does not, so the workload can never start against a cluster that
is still writing cleartext.

Bootstrap takes the KEK-wrapped DEK bytes as an argument, which an
operator gets from their KMS and the harness has no way to produce. The
new cmd/jepsen-encryption-setup generates a DEK, wraps it under the
harness KEK file, and prints only the wrapped form — the plaintext never
leaves the process. It is harness tooling on purpose: putting DEK
generation into elastickv-admin would add operator-facing handling of raw
key material, which needs its own design and review. The orchestration
itself uses the shipped elastickv-admin subcommands, so the run exercises
the real operator path.

P2 — --encryption lives in common-cli-opts so that, as this suite already
claimed, "a future workload gets the gate without opting in". Five
entrypoints (s3, sqs-htfifo, dynamodb-types, dynamodb-multi-table,
redis-zset-safety) accepted the flag and then dropped it when building
ekdb/db, so the CLI advertised a guarantee it did not provide. All five
now thread it, and one table-driven test drives every workload
constructor so the next one fails here instead of shipping a silent lie.

Claude-Session: https://claude.ai/code/session_013rNHooj7NF3giihWVba8QE
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@bootjp

bootjp commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

Both findings are correct and fixed in 2d280f2. The P1 is the failure mode this suite's own namespace docstring names — "a run that reported PASS while the cluster was never actually encrypted would be worse than no gate at all, because it would be recorded as evidence" — so it is exactly the thing the PR was supposed to prevent and didn't.

P1 — Bootstrap and activate encryption before running workloads. Confirmed by tracing it rather than taking it as read:

  • main_encryption_write_wiring.go:208 passes store.WithStorageEnvelopeGate(w.cache.StorageEnvelopeActive), so the store's envelope gate is driven by the sidecar state.
  • StorageEnvelopeActive only becomes true once EnableStorageEnvelope applies, and that RPC requires a prior BootstrapEncryption.
  • setup! called provision-kek! and start-node! and nothing else.

So every "encrypted" Redis and DynamoDB run stored cleartext and could still pass.

setup! now, on the bootstrap node after the joins complete, bootstraps the DEKs and performs the §7.1 Phase-1 storage cutover, then polls every node until it reports storage_envelope_active and fails setup if any node does not. After rather than before, for the reason you gave: the writer batch needs one registry entry per member (§5.6 step 1a) and the cutover's capability gate requires every voter to report capable, so running it against a one-node cluster would register a single writer and then refuse the cutover once the peers joined. Polling every node rather than just the proposer, because a node that has not applied the cutover is still writing cleartext and the workload would be measuring a half-encrypted cluster.

Bootstrap takes the KEK-wrapped DEK bytes as an argument — an operator gets them from their KMS, and the harness had no way to produce them. Nothing in the repo wraps a DEK from a KEK file. New cmd/jepsen-encryption-setup generates a DEK, wraps it under the harness KEK, and prints only the wrapped form; the plaintext never leaves the process. Keeping it as harness tooling is deliberate: adding an elastickv-admin subcommand that generates key material would expand operator-facing surface in a way that needs its own design and security review. The orchestration uses the shipped elastickv-admin subcommands, so the run still exercises the real operator path.

A related gap this surfaced, not in scope here: there is no documented way for an operator using --kekFile to obtain the wrapped DEK bytes encryption bootstrap requires. Worth its own issue.

P2 — Do not advertise encryption to workloads that drop the option. Confirmed; the audit matches yours exactly — only redis_workload.clj and dynamodb_workload.clj propagated it, and s3 / sqs-htfifo / dynamodb-types / dynamodb-multi-table / redis-zset-safety dropped it. I chose propagation over rejection because the design's position is that "encryption is consistency-transparent, so the gate is a cluster-setup switch", and this suite already asserted that putting the flag in common-cli-opts means "a future workload gets the gate without opting in" — the wiring simply did not honour what the test claimed. All five now thread it, and one table-driven test drives every workload constructor so the next workload fails there instead of shipping a silent lie.

Verification

  • lein test elastickv.encrypted-cluster-test — 18 tests, 46 assertions, 0 failures
  • go test ./cmd/jepsen-encryption-setup/ — ok; the wrapped DEK is round-tripped back through kek.FileWrapper.Unwrap so the harness cannot hand bootstrap bytes a node cannot unwrap, and a distinctness test covers the storage/raft pair (bootstrap rejects equal ids)
  • golangci-lint — 0 issues
  • Revert-checks, each confirming the named test fails with the property reverted and the file restores byte-exact:
    • storage_envelope_active parsed as "present means active" → status-parsing-treats-an-inactive-envelope-as-inactive FAILS. This is the load-bearing one: a false reading as true would release the workload against a cleartext cluster.
    • --discover-from narrowed to the proposer → bootstrap-discovers-the-writer-batch-from-every-member FAILS on all three assertions
    • default-group endpoint resolved with (first (keys raft-groups)) instead of the sorted group-idsencryption-admin-targets-the-default-raft-group FAILS ("n1:50061" vs "n1:50062"); Clojure array-maps preserve insertion order, so unsorted keys genuinely pick the wrong group
    • any one workload dropping :encryptionevery-workload-accepting-encryption-propagates-it FAILS naming that workload

One honest limitation: the unit tests cover the argv construction and the status parsing, not the SSH orchestration — setup! needs a real Jepsen environment, and scripts/run-jepsen-local.sh starts its cluster directly rather than going through db/DB. What the tests do pin is every decision the orchestration makes; the orchestration itself is three c/exec calls and a poll.

@codex review
@claude review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@claude

claude Bot commented Sep 12, 2026

Copy link
Copy Markdown

Claude finished @bootjp's task in 4m 52s —— View job


Review: 2d280f2 (fix for the two prior findings)

  • Gather context (PR description, prior review findings, trigger comment)
  • Review commit 2d280f24 fix for P1 (bootstrap + storage envelope activation before workloads)
  • Review commit 2d280f24 fix for P2 (encryption propagation to all workloads)
  • Review new cmd/jepsen-encryption-setup tool
  • Check test evidence claims (unit tests, revert-checks)
  • Post final review

Verified by tracing the code, not by re-running the harness (see limitation note at the bottom).

P1 — bootstrap + storage-envelope cutover: fixed

activate-encryption! (jepsen/src/elastickv/db.clj:316-353) now runs on the bootstrap node, only after the peer-join loop completes (db.clj:398-404), and does the right three things in the right order:

  1. elastickv-admin encryption bootstrap with --discover-from for every node in (:nodes test) (including the bootstrap node itself), so the §5.6 writer batch is discovered via GetCapability rather than hand-written. I checked cmd/elastickv-admin/encryption_mutators.go:522-643--discover-from and --writer are correctly mutually exclusive, and the discovered batch is validated for non-zero/distinct full_node_ids before BootstrapEncryption is called.
  2. elastickv-admin encryption enable-storage-envelope using the full_node_id/local_epoch read back from encryption status on the bootstrap node itself — this is the correct proposer identity, since that node is also in the discovered writer batch from step 1.
  3. Polls storage_envelope_active on every node (not just the proposer) via util/await-fn with a 60s timeout, and parse-encryption-status (db.clj:292-303) treats an absent or false field as inactive — so the polling loop fails closed rather than false-positiving on a half-cutover cluster.

I cross-checked this against adapter/encryption_admin.go: BootstrapEncryption/RotateDEK are requireLeader-gated with no auto-forward to the real leader — a request to a non-leader returns an error rather than silently no-opping, so if this ever races a leader change, c/exec sees a non-zero exit and setup! fails loudly (consistent with "fail setup rather than proceed"). That's an inherent fragility window (bootstrap node might not still be leader by the time this runs), but it's the same class of assumption join-node!/raftadmin add_voter already makes a few lines above, and it fails closed rather than silently passing, so I'm not flagging it as blocking — just worth knowing if this step ever flakes in CI.

cmd/jepsen-encryption-setup/main.go is small and does what the harness needs: generates a fresh AES-256 DEK per invocation, wraps it via the same kek.FileWrapper the server uses, prints only the wrapped base64 form. main_test.go round-trips the wrapped output back through Unwrap and checks two calls produce distinct DEKs — good coverage for what this binary does.

P2 — five workloads silently dropping --encryption: fixed

Confirmed s3_workload.clj, sqs_htfifo_workload.clj, dynamodb_types_workload.clj, dynamodb_multi_table_workload.clj, and redis_zset_safety_workload.clj all now pass :encryption (:encryption opts) into their ekdb/db call. every-workload-accepting-encryption-propagates-it in encrypted_cluster_test.clj:181-193 drives all 7 workload constructors from one table, asserting both that --encryption propagates and that omitting it doesn't spuriously enable encryption — that second half is what would have caught a workload accidentally hardcoding :encryption true.

One thing not addressed: the design doc's "Implemented" note is now stale

docs/design/2026_04_29_partial_data_at_rest_encryption.md:2448-2455 (added in 395f7d6, not touched by 2d280f2) still says:

--encryption, which provisions the §5.1 KEK file on each node and starts the server with --encryption-enabled, --kekFile and --encryptionSidecarPath.

That's exactly the pre-fix behavior Codex flagged as producing a cleartext cluster. It doesn't mention elastickv-admin encryption bootstrap/enable-storage-envelope, the per-node storage_envelope_active poll, or cmd/jepsen-encryption-setup — so a reader relying on the doc (which CLAUDE.md's design-doc convention treats as the as-implemented record) would still believe the gate is just three server flags, i.e. the doc now under-describes what shipped and doesn't reflect that the original risk it names was fixed. Worth a follow-up edit to this note before treating §8.4 as fully closed.

Fix this →

Minor nit

cmd/jepsen-encryption-setup/main_test.go:47-49's comment says "bootstrap requires them to differ" about the two DEKs' key material — bootstrap actually requires the two dek_ids to differ (validateBootstrapDEKIDs in encryption_mutators.go:537-554), not the key bytes themselves. The test (asserting 8 distinct DEKs from crypto/rand) is still good hygiene, just the comment overstates what's enforced server-side.

Test evidence

The encrypted_cluster_test.clj deftest count matches the PR's claim (18 deftest forms, up from 9). I could not execute lein test or go test/go vet in this review sandbox — go/lein invocations require interactive approval that isn't available in this automated review context, and /tmp/lein isn't reachable from the allowed working directory. Everything above was verified by static tracing: reading adapter/encryption_admin.go's RPC gating, cmd/elastickv-admin's flag parsing, and matching it line-by-line against the Clojure argv-construction and regex-parsing functions, rather than by re-running the suite. If you want the actual lein test / go test ./cmd/jepsen-encryption-setup/ output verified, that'll need to run outside this sandbox.

Bottom line: both prior findings are substantively fixed with correct ordering and fail-closed behavior. The one loose end is the stale design-doc note, which is worth a follow-up edit so the doc doesn't contradict what actually shipped.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant