Skip to content

fix(dashmate): load ZeroSSL config in force mode - #4298

Open
thepastaclaw wants to merge 1 commit into
dashpay:v4.1-devfrom
thepastaclaw:tracker-1419-zerossl-force-csr
Open

fix(dashmate): load ZeroSSL config in force mode#4298
thepastaclaw wants to merge 1 commit into
dashpay:v4.1-devfrom
thepastaclaw:tracker-1419-zerossl-force-csr

Conversation

@thepastaclaw

@thepastaclaw thepastaclaw commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Issue being fixed or feature implemented

Fixes #3803
Fixes #4249

Tracking: https://github.com/thepastaclaw/tracker/issues/1419

dashmate ssl obtain --force skipped the ZeroSSL validation task that also initialized the task context. As a result, required values such as the API key, external IP, and certificate paths were undefined before key and CSR generation.

What was done?

  • Initialize the ZeroSSL configuration and certificate paths before the existing-certificate check, including in force mode.
  • Keep --force limited to bypassing existing-certificate validation and resetting prior certificate/artifact state so a fresh keypair, CSR, certificate, and bundle are generated.
  • Add focused regression coverage for force-mode context initialization, stale artifact state, and missing required configuration.

How Has This Been Tested?

  • yarn exec mocha --require ./test/bootstrap.js test/unit/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.spec.js test/unit/ssl/zerossl/validateZeroSslCertificateFactory.spec.js — 16 passing
  • yarn exec eslint src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js test/unit/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.spec.js
  • yarn workspace dashmate lint via the pre-commit hook
  • Independent exact-head pre-PR review — ship, 0 findings

Breaking Changes

None.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • Bug Fixes
    • Improved ZeroSSL certificate setup and validation.
    • Added more reliable handling of certificate files and SSL directories.
    • Fixed forced regeneration so stale certificate state is cleared correctly.
    • Improved error reporting when the API key or external IP is missing.
    • Ensured cleanup does not hide errors from the certificate generation process.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0b75a964-96c6-468f-b52a-804762d27cde

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The ZeroSSL task pipeline now initializes API configuration, external IP, certificate paths, and SSL directory state before certificate generation. Forced regeneration clears stale state and proceeds with fresh keypair, CSR, and certificate generation. Unit tests cover successful and invalid forced runs.

Changes

ZeroSSL certificate acquisition

Layer / File(s) Summary
Initialize ZeroSSL certificate context
packages/dashmate/src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js
The pipeline loads and validates the API key and external IP, derives SSL file paths, creates the SSL directory, and clears stale certificate state when --force is enabled.
Validate forced regeneration
packages/dashmate/test/unit/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.spec.js
Tests verify forced regeneration, path and configuration initialization, regeneration despite stale state, verification-server cleanup, and early failures for missing API key or external IP.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: shumkov, quantumexplorer

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes initialize ZeroSSL context and paths before force-mode regeneration, addressing the requirements in issues [#3803] and [#4249].
Out of Scope Changes check ✅ Passed The implementation and regression tests remain within the linked issues' scope of fixing ZeroSSL force-mode certificate renewal.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: loading ZeroSSL configuration when force mode is enabled.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions github-actions Bot added this to the v4.1.0 milestone Aug 5, 2026
@thepastaclaw

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw

thepastaclaw commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

✅ Final review complete — no blockers (commit 41dc4bf)

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@thepastaclaw

Copy link
Copy Markdown
Collaborator Author

The failing CodeRabbit or PastaClaw review check is unrelated to this PR's code changes. The gate ran when the PR opened, before either reviewer had submitted a formal GitHub review, and its log explicitly says it will rerun automatically when a review is submitted. CodeRabbit has since completed its analysis, and the PastaClaw review is queued separately. The ZeroSSL change is proper as-is; no branch change, rebase, empty commit, or CI retrigger is needed for this failure.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Preliminary review — Codex only

The PR fixes force mode's missing ZeroSSL configuration and path initialization, but the new regeneration path can persist a replacement certificate ID while leaving the previous certificate's key and CSR on disk. If renewal is interrupted and later retried without force, the pipeline can install a certificate bundle that does not match the retained private key.

Source: reviewers codex/general=gpt-5.6-sol(completed); verifier=codex/verifier=gpt-5.6-sol(completed); coordinator=openclaw-agent/cliproxy/gpt-5.6-sol(orchestration-only).

Validated blockers were found in the Codex precheck. Opus is deferred until a fresh Codex revalidation clears the blocker gate.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Opus: not run (deferred by blocker gate)

🔴 1 blocking

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/dashmate/src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js`:
- [BLOCKING] packages/dashmate/src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js:75-81: Keep forced renewal artifacts consistent with the persisted certificate ID
  Force mode clears only the in-memory presence flags; the previous `private.key`, `csr.pem`, and `bundle.crt` remain on disk. After generating a replacement key and CSR, the create task persists the new certificate ID at lines 180-185 before verification, download, or any artifact save occurs. If a later task fails, a subsequent non-force run loads that new pending certificate while `validateZeroSslCertificate` reports the old key and CSR as present; its pending-certificate branch clears only the bundle flag. Key/CSR generation and save are therefore skipped, while the new bundle is downloaded and written beside the old private key, producing a certificate/key mismatch. Stage the replacement ID, key, CSR, and bundle and commit them together after successful download, or roll back the persisted ID on failure so a retry cannot associate the old artifacts with the replacement certificate. Add a regression test covering a force run that fails after certificate creation followed by a non-force retry.

Note: GitHub does not allow me to request changes on my own PR, so this COMMENT review preserves the verified blocking finding and should still be treated as merge-blocking feedback.

@thepastaclaw
thepastaclaw force-pushed the tracker-1419-zerossl-force-csr branch from 91431c2 to e910329 Compare August 5, 2026 17:54
@thepastaclaw

Copy link
Copy Markdown
Collaborator Author

Addressed the blocking forced-renewal consistency finding in e9103295d1 (amended PR commit):

  • delay publishing the replacement ZeroSSL certificate ID until key/CSR/bundle download succeeds
  • stage and install artifacts together, rolling back artifacts/config if persistence fails
  • add regression coverage for a failed force run followed by a non-force retry, plus persistence failure paths

Validation: targeted unit spec (8 passing), changed-file ESLint, and git diff --check.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Preliminary review — Codex only

The carried-forward blocker, "Keep forced renewal artifacts consistent with the persisted certificate ID," is resolved: the current head delays publishing the replacement ID until the artifacts are ready and rolls back artifacts and configuration when persistence fails. The latest delta introduces a new blocking security regression because the staged replacement private key is installed with the process-default file mode rather than preserving owner-only access.

Source: reviewers codex/general=gpt-5.6-sol(completed); verifier=codex/verifier=gpt-5.6-sol(completed); coordinator=openclaw-agent/cliproxy/gpt-5.6-sol(orchestration-only).

Validated blockers were found in the Codex precheck. Sonnet is deferred until a fresh Codex revalidation clears the blocker gate.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet: not run (deferred by blocker gate)

🔴 1 blocking

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/dashmate/src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js`:
- [BLOCKING] packages/dashmate/src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js:338-357: Preserve restrictive permissions when installing the replacement private key
  The staged private key is created with Node's default mode (`0666` masked by the process umask), and `renameSync` replaces the destination inode instead of overwriting it. A forced renewal therefore changes an existing owner-only `0600` key to `0644` under the usual `0022` umask. Dashmate also creates its default and nested home directories without an explicitly restrictive mode, so on typical installations other local users can traverse the path and read the gateway TLS private key. Create the staged `private.key` with mode `0600` or explicitly chmod it before or after installation, while leaving the CSR and certificate bundle modes unchanged, and add a regression assertion for the installed key mode.

Note: GitHub does not allow me to request changes on my own PR, so this COMMENT review preserves the verified blocking finding and should still be treated as merge-blocking feedback.

@PastaPastaPasta
PastaPastaPasta marked this pull request as ready for review August 22, 2026 15:49

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Preliminary review — Codex only

The force-mode configuration initialization now works, and the prior private-key permission issue is fixed by staging the key with mode 0600. One blocker remains: replacing certificate files with renameSync disconnects successful renewals from the gateway's existing file-level bind mounts, so its SIGHUP hot restart continues serving the old certificate.
Source: reviewer codex/general=gpt-5.6-sol; final verifier codex/verifier=gpt-5.6-sol; orchestration-only and not reviewer evidence: openclaw-agent/cliproxy/gpt-5.6-sol.

Validated blockers were found in the Codex precheck. Opus is deferred until a fresh Codex revalidation clears the blocker gate.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet: not run (deferred by blocker gate)

🔴 1 blocking

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/dashmate/src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js`:
- [BLOCKING] packages/dashmate/src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js:358-361: Preserve bind-mounted certificate inodes during renewal
  The gateway mounts bundle.crt and private.key as individual files in docker-compose.yml lines 208-209. Replacing either destination with renameSync creates a new inode on the host, while the running container's bind mount remains attached to the old inode. The renewal scheduler then sends SIGHUP to PID 1, whose hot-restart supervisor forks another Envoy process in the same mount namespace, so that process also reads the old certificate. Normal expiration renewal replaces bundle.crt, and force mode replaces both mounted files; either path can publish the new certificate ID and report success while the gateway continues serving the expiring certificate. Overwrite existing destinations in place to preserve their mounted inodes, and use renameSync only for first-time installation.

Canonical verifier result: REQUEST_CHANGES. GitHub does not allow authors to approve or request changes on their own pull requests, so this review was submitted using COMMENT transport. The findings and blocker status above are unchanged.

dashmate ssl obtain --force skipped the ZeroSSL validation task that also initialized the task context, leaving the API key, external IP, and certificate paths undefined before key and CSR generation (dashpay#3803, dashpay#4249). Initialize configuration and paths unconditionally and keep --force limited to bypassing the existing-certificate check.

Replacement artifacts are staged first and installed transactionally: the new certificate ID is only published after key, CSR, and bundle succeed, with rollback of artifacts and config if persistence fails. The private key is staged with owner-only permissions, and existing destinations are overwritten in place so the gateway container's single-file bind mounts (which stay attached to the mounted inode) pick up renewals on SIGHUP; renameSync is used only for first-time installation.
@PastaPastaPasta
PastaPastaPasta force-pushed the tracker-1419-zerossl-force-csr branch from 9955519 to 41dc4bf Compare August 24, 2026 14:32

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Final validation — Codex/Sol only (Phase 2 disabled)

At exact head 41dc4bf, the prior bind-mount blocker is fixed: existing certificate artifacts are overwritten in place, while first-time installations still use rename, and regression coverage confirms the behavior. The targeted ZeroSSL suites pass with 21 tests, and ESLint passes for both changed files.
Source: reviewers codex/general=gpt-5.6-sol and codex/security-auditor=gpt-5.6-sol; final verifier codex/verifier=gpt-5.6-sol; openclaw-agent/cliproxy/gpt-5.6-sol is orchestration-only and not reviewer evidence.

Validated zero-blocker Codex/Sol precheck evidence was promoted to final because Phase 2 (Sonnet/Opus) is temporarily disabled. This is Codex/Sol-only final validation, not Codex + Sonnet/Opus coverage.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed), gpt-5.6-sol — security-auditor (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet/Opus: not run (Phase 2 disabled — temporary Codex/Sol-only final)
  • Secondary pass: disabled (temporary_phase2_sonnet_disable)

Canonical verifier result: APPROVE. GitHub does not allow authors to approve or request changes on their own pull requests, so this review was submitted using COMMENT transport. The findings and blocker status above are unchanged.

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.

2 participants