Skip to content

feat(consensus/keys): add keys import and keys export - #1198

Draft
leninmehedy wants to merge 1 commit into
01166-consensus-keys-generatefrom
01167-consensus-keys-import-export
Draft

leninmehedy wants to merge 1 commit into
01166-consensus-keys-generatefrom
01167-consensus-keys-import-export

Conversation

@leninmehedy

@leninmehedy leninmehedy commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Summary

Third PR in the stacked series for epic #1164 — Consensus Node Key & Secret Management. Stacked on #1197 (base branch 01166-consensus-keys-generate).

Adds typed Kubernetes Secret I/O and the import / export verbs, turning a folder of key material into the per-node secrets the solo-operator consumes (and back).

What's in it

  • kube client — ApplySecret (server-side apply; key bytes stay in memory, no temp file rendered) and GetSecretData.
  • internal/consensus/keys — Import builds the three per-node secrets from a folder; Export writes them back to a folder for backup. Each key secret is named by the key's own date and fingerprint: node<N>-gossip-keys-<date>-<fp8>, node<N>-grpc-tls-keys-<date>-<fp8>, node<N>-admin-pubkey-<fp8>, with neutral, index-free inner-keys. <date> is the certificate's Not before date (so reimporting the same folder after a cluster reset gives the same names) and <fp8> is the start of its SHA-384. New key material therefore creates new secrets beside the old ones and never replaces them. Each secret carries solo.hiero.org/node, key-type and cert-fingerprint labels plus SHA-384 and not-after annotations (see feat(consensus/keys): name key secrets by key date and fingerprint; stage rotations beside live keys #1282).
  • consensus keys import — --keys-dir, --source folder (vault tracked in feat(consensus/keys): ESO/vault source for 'keys import' #1171), --force, --gossip/--tls/--admin; --node-id required.
  • consensus keys export — --keys-dir, selectors; --node-id required.

Custody & safety invariants

  • import --secret-suffix <text> replaces the date part; export takes --fingerprint / --secret-suffix to choose one generation, and checks each secret's content against its name, so a renamed or edited secret is refused.
  • import --label key=value (repeatable) puts extra labels on every secret, for example a reference to an external system (example.com/external-ref=ticket-42). Kubernetes label syntax applies and weaver's own labels (solo.hiero.org/node, key-type, cert-fingerprint) are reserved. When a secret's content is already up to date, only its labels are patched: the key data is never sent again, so it cannot be overwritten.
  • Only the admin public key (admin.pub) is imported — the admin private key (admin.key) is never read or pushed to the cluster.
  • The admin secret holds the public key as raw bytes (32 for Ed25519, 33 for compressed secp256k1), because solo-operator hex-encodes the secret value itself when it builds genesis. import decodes the hex in admin.pub; export writes it back as hex. Storing the hex text would double-encode the key in genesis-network.json. Operator follow-up: hashgraph/solo-operator#1394 proposes accepting hex text too.
  • The gossip signing secret is the node's network identity, so a re-import is refused unless --force is passed.
  • Export cannot recover the admin private key (it isn't in the cluster).

Join flow this enables

keys generate (node-id-free) → submit nodeCreate off-cluster → network assigns the node-id → keys import --node-id <assigned> → consensus node install. solo-provisioner never submits the DAB transaction.

Files

File Purpose
internal/kube/client.go ApplySecret (SSA) + GetSecretData
internal/kube/secret_test.go GetSecretData tests
internal/consensus/keys/secrets.go SecretClient iface, SecretNamesFor, Import/Export
internal/consensus/keys/secrets_test.go import/export orchestration tests
cmd/cli/commands/consensus/keys/import.go import subcommand
cmd/cli/commands/consensus/keys/export.go export subcommand
cmd/cli/commands/consensus/keys/keys.go selector/namespace/node-id helpers; wiring; stubbable client
cmd/cli/commands/consensus/keys/import_export_test.go command tests
docs/commands/consensus.md import + export sections

Testing

Validated in the UTM VM:

go test -race -cover ./pkg/consensus/keys/... ./internal/consensus/keys/... ./internal/kube/... ./cmd/cli/commands/consensus/keys/...

All pass. The ApplySecret server-side-apply happy path is exercised via the orchestration tests (the fake dynamic client cannot create through SSA) and by UAT against a real cluster.

Closes #1167.

@leninmehedy
leninmehedy requested a review from a team as a code owner September 26, 2026 13:34
@swirlds-automation

swirlds-automation commented Sep 26, 2026 •

Copy link
Copy Markdown

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@leninmehedy
leninmehedy force-pushed the 01167-consensus-keys-import-export branch from 055af5d to ae5854f Compare September 26, 2026 13:48
@leninmehedy
leninmehedy force-pushed the 01167-consensus-keys-import-export branch from ae5854f to c16e1fb Compare September 26, 2026 14:06
@leninmehedy
leninmehedy force-pushed the 01167-consensus-keys-import-export branch from c16e1fb to 95d08a7 Compare September 26, 2026 14:13
@leninmehedy
leninmehedy force-pushed the 01167-consensus-keys-import-export branch from 95d08a7 to 54380e3 Compare September 26, 2026 14:14
@leninmehedy
leninmehedy force-pushed the 01167-consensus-keys-import-export branch 2 times, most recently from e3aaeef to 6d577dc Compare September 26, 2026 14:41
@leninmehedy
leninmehedy added this pull request to stack #1201 September 26, 2026 14:44
@leninmehedy
leninmehedy force-pushed the 01167-consensus-keys-import-export branch from 6d577dc to 06931e7 Compare September 29, 2026 05:12
@leninmehedy
leninmehedy force-pushed the 01167-consensus-keys-import-export branch from 06931e7 to d8d804f Compare October 13, 2026 06:27
@leninmehedy
leninmehedy force-pushed the 01167-consensus-keys-import-export branch from d8d804f to a61e70f Compare October 13, 2026 10:33
@leninmehedy
leninmehedy force-pushed the 01167-consensus-keys-import-export branch 3 times, most recently from fc84aff to e8a2f34 Compare October 13, 2026 12:37
Add typed Secret I/O and the import/export verbs over #1166:

- kube client: ApplySecret (server-side apply, key bytes kept in memory,
  no temp file) and GetSecretData.
- internal/consensus/keys: Import builds the three per-node secrets
  (node<N>-gossip-keys / -grpc-tls-keys / -admin-pubkey) from a folder;
  Export writes them back for backup. Only the admin public key is ever
  pushed to k8s; the gossip signing secret is guarded against overwrite
  unless --force (it is the node's network identity).
- commands: `consensus keys import` (--from, --source folder, --force,
  selectors; --node-id required) and `consensus keys export` (--out,
  selectors; --node-id required).

Vault-backed import is tracked separately (#1171).

Refs #1167

Signed-off-by: Lenin Mehedy <lenin.mehedy@hashgraph.com>
@leninmehedy
leninmehedy force-pushed the 01167-consensus-keys-import-export branch from e8a2f34 to de0c173 Compare October 13, 2026 12:52
@leninmehedy
leninmehedy marked this pull request as draft October 20, 2026 06:23
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.

feat(consensus/keys): typed Secret I/O — 'keys import' + 'keys export'

2 participants