Skip to content

fix: validate release keys and smoke-run the genesis build on PRs - #254

Closed
mordamax wants to merge 3 commits into
masterfrom
fix/genesis-key-preflight
Closed

fix: validate release keys and smoke-run the genesis build on PRs#254
mordamax wants to merge 3 commits into
masterfrom
fix/genesis-key-preflight

Conversation

@mordamax

Copy link
Copy Markdown
Contributor

Description

v0.5.6-rc1's genesis step died on cast's bare Failed to decode private key — the first time anything ever consumed the DOTNS_ADMIN_KEY secret was on master, because PR CI ran only bash -n and the extractor's unit tests, never the script.

Two changes:

  • One credential, validated. DOTNS_ADMIN_KEY is now the only accepted owner key — the mnemonic fallback is gone (two credentials make the build guess which account owns everything, and the parity check validates addresses, not owners). A preflight rejects a malformed key before anvil starts and reports its shape — length, word count, a mnemonic hint with the cast wallet private-key derivation command — never the value, since a GitHub secret cannot be read back. Surrounding whitespace is stripped: a pasted trailing newline is the classic break, verified end to end.
  • genesis-build-smoke.yml runs the exact release command with the real secrets on every PR touching contracts, deploy or genesis scripts, manifests, or the publish workflows — same-repo PRs only, parity unstubbed, artifact shape asserted and uploaded.

The smoke run on this PR doubles as the probe for the broken secret: expect it red, read the shape from its log, re-paste the secret, re-run. Then re-dispatch the failed Publish Beta Package for v0.5.6-rc1.

Type

  • Bug fix
  • Feature
  • Breaking change
  • Documentation
  • Chore
  • Refactor
  • Security

Scope

  • Registration
  • Resolver
  • Store
  • Proof of Personhood
  • Deployment scripts
  • Tests

Related Issues

Follow-up to #253.

Fixes

Checklist

Code

  • Follows project style
  • forge build passes
  • forge test passes
  • No new compiler warnings

Testing

  • New tests added for changed behavior
  • Fuzz tests added where applicable
  • Invariant tests verified

Security

  • No new selfdestruct or delegatecall
  • Access control reviewed
  • No storage layout conflicts (for upgradeable contracts)

Documentation

  • NatSpec updated on changed interfaces
  • README updated if needed

Breaking Changes

  • No breaking changes
  • Breaking changes documented below

How to test

DOTNS_ADMIN_KEY="twelve word mnemonic ..." FACTORY_DEPLOYER_KEY=0x... bash scripts/genesis/build-genesis.sh   # shape diagnostic
gh workflow run genesis-build-smoke.yml

Exercised locally: all four malformed shapes fail fast without leaking the value; a newline-suffixed key completes the full build (33 accounts).

Notes

No Solidity changed; the contract checkboxes are inherited state.

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

CI Summary

Check Result
Deploy Contracts Reproduces the committed manifest; resume verified
PR Title PR Title Valid
Labels Unknown
Secret Scan Passed - No secrets detected

Deploy Contracts

Deployed addresses vs the committed manifest

Expected is the committed manifest; actual is this CI deployment of the same pipeline.

Contract Expected Actual Match
Create3Factory 0x8533c79E058c5a6489CAFeCA86dc600E029D75f5 0x8533c79E058c5a6489CAFeCA86dc600E029D75f5 match
DotnsContentResolver 0x7F74D7CD50f5a834270E2ad395a01b01891AB37d 0x7F74D7CD50f5a834270E2ad395a01b01891AB37d match
DotnsNameEscrow 0x4881Afb78e7C908cAe818168B926229D93376520 0x4881Afb78e7C908cAe818168B926229D93376520 match
DotnsNameWhitelist 0x420166cD67Ca0233094E492a4BbA67045eD7C38C 0x420166cD67Ca0233094E492a4BbA67045eD7C38C match
DotnsPopController 0xCC932348606cc1f3318cADeC5A5Cd2CA447f8a4b 0xCC932348606cc1f3318cADeC5A5Cd2CA447f8a4b match
DotnsPopLens 0xfe5A45f7fD58D1A6FE09455DB799405b1dcE9411 0xfe5A45f7fD58D1A6FE09455DB799405b1dcE9411 match
DotnsPopResolver 0xDaC984884EcA8Fc44011f1D6C49B27828390A72B 0xDaC984884EcA8Fc44011f1D6C49B27828390A72B match
DotnsProtocolRegistry 0xD19e3D0C97CF501125a04A97405e3e6592fa846E 0xD19e3D0C97CF501125a04A97405e3e6592fa846E match
DotnsRegistrar 0x4f06E818Ba3d987704fd91cf3d868E4b019106Ab 0x4f06E818Ba3d987704fd91cf3d868E4b019106Ab match
DotnsRegistrarController 0xBdaA01bD1bA67d709F2b1fF286Da0d854977EA30 0xBdaA01bD1bA67d709F2b1fF286Da0d854977EA30 match
DotnsRegistry 0xf34054fd76BbF85f216cf9908226D5f0A72E50CA 0xf34054fd76BbF85f216cf9908226D5f0A72E50CA match
DotnsResolver 0xbd1165E549DF96F083c0A16f61590927bC187009 0xbd1165E549DF96F083c0A16f61590927bC187009 match
DotnsReverseResolver 0xee3883d7eB60Ee9BCD7F3bcD8f2f05302A9Cc035 0xee3883d7eB60Ee9BCD7F3bcD8f2f05302A9Cc035 match
LabelStoreBeacon 0xb57Ebc2e7085616d4906D1fE49af1cE13f7dffeF 0xb57Ebc2e7085616d4906D1fE49af1cE13f7dffeF match
Multicall3 0xB4468000abD87D3c56cbFBd153161223D7b109e5 0xB4468000abD87D3c56cbFBd153161223D7b109e5 match
PopRules 0x747B456bE03aec0b42bd85C51513730FBD45DA31 0x747B456bE03aec0b42bd85C51513730FBD45DA31 match
RootGatewayDispatcher 0xa889CCA3Fb4B07b98a11cc54C10f13dDA20bc3db 0xa889CCA3Fb4B07b98a11cc54C10f13dDA20bc3db match
StoreFactory 0x709A027F446a9e2a4BB9cb9a9c754435b19e32B7 0x709A027F446a9e2a4BB9cb9a9c754435b19e32B7 match
UserStoreBeacon 0xb7C995601679840d36F37E86DB2d7dF30797eC5C 0xb7C995601679840d36F37E86DB2d7dF30797eC5C match

View full logs

Labels

other

@github-actions github-actions Bot added the other label Aug 28, 2026
@mordamax
mordamax requested a review from re-gius August 28, 2026 16:49
@mordamax
mordamax marked this pull request as ready for review August 28, 2026 18:31

@re-gius re-gius left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall the preflight and the mnemonic removal look good to me.

However, I am not convinced of putting FACTORY_DEPLOYER_KEY in reach of every PR that touches scripts/genesis/**, since it's the same key dotns-releases uses and it can't be cheaply rotated

I would rather have it in an environment with required reviewers, or the pull_request trigger dropped in favour of push: master.

jobs:
genesis-build-smoke:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: add timeout-minutes: 45 or similar

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Moreover, we should add

env:
  DOTNS_TLD: testnet

as from #255 and always reference ${{ env.DOTNS_TLD }} below

# cut. The extractor's unit tests and `bash -n` cannot catch either — v0.5.6-rc1 died on a
# key `cast` could not decode, after every PR check had passed.
#
# Skipped on fork PRs: secrets are withheld there.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This hands a production key to any PR author, e.g. through a PR that edits build-genesis.sh to extract it. Moreover, this key is expensive to rotate since every DotNS address derives from its hash.
Instead, we could create a GitHub deployment environment (e.g., genesis-smoke) in this repository with a "Required reviewers" protection rule, so that only named reviewers can approve it, and move FACTORY_DEPLOYER_KEY to its environment secrets.
Otherwise, drop the pull_request trigger entirely and keep push: master

push:
branches: [master]
paths:
- "scripts/genesis/**"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should add some others here:

  • "scripts/deploy/**" the stages the genesis is built from
  • "deployments/**" what the check asserts against
  • "bun.lock" since it may break the build
  • "package.json" updates may break the build
  • ".github/workflows/publish-release.yml"
  • ".github/workflows/publish-prerelease.yml"

# build would guess which account owns everything, and nothing downstream would
# notice a wrong guess — the parity check validates addresses, not owners.
# Not DOTNS_MNEMONIC (the whitelist workflows' operational credential) and not
# DEPLOYER_KEY (dotns-releases' own secret) for the same reason.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: why the same reason? I don't get it

# ---- Key shape preflight ----
# `cast` rejects a malformed key with a bare "Failed to decode private key" — no variable
# name, no shape — and a GitHub secret cannot be read back to inspect. Describe the problem
# (length, word count) without ever printing the value. Surrounding whitespace is stripped:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: ALL whitespace is stripped, not just surrounding

#
# Skipped on fork PRs: secrets are withheld there.
on:
pull_request:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The preflight's fine, keep it. It's the smoke workflow I'd drop, starting with this pull_request trigger.

The release already does this. publish-release.yml builds the genesis with these same secrets on the tag, so a bad key fails the release before anything publishes. That is how v0.5.6-rc1 surfaced. The smoke runs the same check earlier, at PR time.

Moving it to PR time has a downside worth weighing. This job runs build-genesis.sh with DOTNS_ADMIN_KEY and FACTORY_DEPLOYER_KEY in env, and the paths filter includes the scripts and contracts a PR edits. So any same-repo PR can change the script (or, with ffi = true, any executed .sol) to print the keys, and the job runs it before review. The if: guards forks, not a write-access member or a leaked token. DOTNS_ADMIN_KEY owns the registry, resolvers, registrar and beacons, and cannot be rotated out of a shipped genesis.

And this is what dotns-releases is for. It's the private repo that owns deploy and release, and these credentials already live there behind its access controls. Putting the same production keys into the public contracts repo's PR CI widens where they live and hands them to PR-authored code from any contributor. A keyed rehearsal belongs there, run deliberately, not on every PR here.

The fix the incident calls for is the preflight, not a new workflow: it turns the bare decode error into a readable one, and dropping the mnemonic fallback removes the which-key ambiguity. I'd land that half, ideally as the first step of the publish workflows so a bad key fails fast, and drop the smoke.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

feel free to drop this PR, as i mentioned in matrix - it's optional

@mordamax

Copy link
Copy Markdown
Contributor Author

Closing as having that kind of CI brings risks for OS repo

@mordamax mordamax closed this Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants