Skip to content

Add 'aspire certs' command for certificate management#15117

Merged
davidfowl merged 9 commits intorelease/13.2from
danegsta/doctorCerts
Mar 12, 2026
Merged

Add 'aspire certs' command for certificate management#15117
davidfowl merged 9 commits intorelease/13.2from
danegsta/doctorCerts

Conversation

@danegsta
Copy link
Copy Markdown
Member

@danegsta danegsta commented Mar 11, 2026

Description

Adds a new top-level aspire certs command with clean and trust subcommands for managing HTTPS development certificates. This provides a user-friendly way to resolve certificate issues without needing to manually run dotnet dev-certs commands, particularly for polyglot users who may not be familiar with .NET tooling.

Certificate operations use the local CertificateManager directly (native calls, no subprocess), so the certificates are functionally identical to those created by dotnet dev-certs.

Command Structure

aspire certs clean    # Remove all HTTPS development certificates
aspire certs trust    # Trust the certificate, creating one if necessary

Doctor Integration

aspire doctor now provides context-specific certificate fix recommendations:

  • No certificate or untrusted certificate: suggests aspire certs trust
  • Multiple certificates or outdated versions: suggests running aspire certs clean then aspire certs trust
  • Partially trusted (Linux SSL_CERT_DIR): suggests the appropriate SSL_CERT_DIR environment variable configuration

Other Changes

  • ICertificateToolRunner is now fully synchronous (CheckHttpCertificate, TrustHttpCertificate, CleanHttpCertificate) since all operations use in-process CertificateManager
  • DevCertsCheck no longer depends on ICertificateToolRunner — it only reads certificates for diagnostics
  • Removed unused TrustHttpCertificateAsync / CleanHttpCertificateAsync from IDotNetCliRunner
  • All user-facing strings are localizable via resource files

Fixes #15036

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add `` and <code/> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?

Copilot AI review requested due to automatic review settings March 11, 2026 00:24
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 11, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 15117

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 15117"

@davidfowl
Copy link
Copy Markdown
Contributor

The only issue I have with fix is that we can't always fix the problem and agents can do a way better job.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new aspire doctor fix subcommand to automatically repair environment issues (starting with HTTPS dev certificate problems), and updates aspire doctor output to recommend running these fixes instead of dotnet dev-certs commands.

Changes:

  • Introduces DoctorFixCommand with dynamic subcommands/actions driven by a new IHealableEnvironmentCheck interface.
  • Extends DevCertsCheck to provide fix actions (clean, trust) and updates doctor output to suggest aspire doctor fix certificates.
  • Adds certificate clean support to the native certificate runner and expands JSON output models/source-gen support, plus new tests and localized strings.

Reviewed changes

Copilot reviewed 44 out of 46 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/Aspire.Cli.Tests/Utils/DevCertsCheckTests.cs Adds coverage for “no certificates” warning behavior.
tests/Aspire.Cli.Tests/Utils/DevCertsCheckHealTests.cs Adds unit tests for heal action recommendation + heal execution paths.
tests/Aspire.Cli.Tests/Utils/CliTestHelper.cs Registers DevCertsCheck as both IEnvironmentCheck and IHealableEnvironmentCheck; registers DoctorFixCommand.
tests/Aspire.Cli.Tests/TestServices/TestCertificateToolRunner.cs Adds CleanHttpCertificateAsync support to the test runner.
tests/Aspire.Cli.Tests/Commands/DoctorFixCommandTests.cs Verifies help/exit-code behavior for doctor fix command surface.
tests/Aspire.Cli.Tests/Certificates/CertificateServiceTests.cs Updates test stub to satisfy new ICertificateToolRunner API.
src/Aspire.Cli/Utils/EnvironmentChecker/IHealableEnvironmentCheck.cs Introduces healable-check contract (EvaluateAsync/HealAsync) and action/result models.
src/Aspire.Cli/Utils/EnvironmentChecker/EnvironmentCheckResult.cs Adds JSON DTOs for doctor fix output (DoctorFixResponse, etc.).
src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs Implements healing for dev-certs; updates doctor fix suggestions and categorization.
src/Aspire.Cli/Commands/DoctorFixCommand.cs Adds doctor fix command with --all and per-check/action subcommands + JSON output.
src/Aspire.Cli/Commands/DoctorCommand.cs Wires DoctorFixCommand under aspire doctor.
src/Aspire.Cli/Certificates/ICertificateToolRunner.cs Adds CleanHttpCertificateAsync.
src/Aspire.Cli/Certificates/NativeCertificateToolRunner.cs Implements native clean operation via CertificateManager.CleanupHttpsCertificates().
src/Aspire.Cli/JsonSourceGenerationContext.cs Adds source-gen metadata for new doctor fix JSON types.
src/Aspire.Cli/Program.cs Registers DoctorFixCommand + healable checks and updated DevCertsCheck DI pattern.
src/Aspire.Cli/DotNet/DotNetCliRunner.cs Removes TrustHttpCertificateAsync method (now handled via native runner abstraction).
src/Aspire.Cli/Resources/DoctorFixCommandStrings.resx Adds localizable strings for doctor fix UX.
src/Aspire.Cli/Resources/DoctorFixCommandStrings.Designer.cs Adds resource accessor for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/DoctorCommandStrings.resx Adds localizable strings for certificate healing messages used by DevCertsCheck.
src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs Updates resource designer for new healing strings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.cs.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.de.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.es.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.fr.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.it.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.ja.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.ko.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.pl.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.pt-BR.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.ru.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.tr.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.zh-Hans.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorFixCommandStrings.zh-Hant.xlf Localization scaffolding for DoctorFixCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.cs.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.de.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.es.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.fr.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.it.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ja.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ko.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.pl.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.pt-BR.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ru.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.tr.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.zh-Hans.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.zh-Hant.xlf Localization scaffolding for new healing strings in DoctorCommandStrings.
Files not reviewed (2)
  • src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Language not supported
  • src/Aspire.Cli/Resources/DoctorFixCommandStrings.Designer.cs: Language not supported

You can also share your feedback on Copilot code review. Take the survey.

@danegsta
Copy link
Copy Markdown
Member Author

The only issue I have with fix is that we can't always fix the problem and agents can do a way better job.

I tried to balance it by having both a "fix any issues that doctor detects and we're able to fix" flow while keeping CLI commands for the individual fix actions. The alternative approach would be a dedicated aspire certificates command with list, clean, and trust sub-commands.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 11, 2026

🎬 CLI E2E Test Recordings

The following terminal recordings are available for commit b1b1646:

Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_DefaultSelection_InstallsSkillOnly ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View Recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
CertificatesClean_RemovesCertificates ▶️ View Recording
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate ▶️ View Recording
CertificatesTrust_WithUntrustedCert_TrustsCertificate ▶️ View Recording
CreateAndDeployToDockerCompose ▶️ View Recording
CreateAndDeployToDockerComposeInteractive ▶️ View Recording
CreateAndPublishToKubernetes ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateAndRunTypeScriptStarterProject ▶️ View Recording
CreateEmptyAppHostProject ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DescribeCommandResolvesReplicaNames ▶️ View Recording
DescribeCommandShowsRunningResources ▶️ View Recording
DetachFormatJsonProducesValidJson ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View Recording
RestoreGeneratesSdkFiles ▶️ View Recording
RunWithMissingAwaitShowsHelpfulError ▶️ View Recording
SecretCrudOnDotNetAppHost ▶️ View Recording
SecretCrudOnTypeScriptAppHost ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording
StopAllAppHostsFromAppHostDirectory ▶️ View Recording
StopAllAppHostsFromUnrelatedDirectory ▶️ View Recording
StopNonInteractiveMultipleAppHostsShowsError ▶️ View Recording
StopNonInteractiveSingleAppHost ❌ Upload failed
StopWithNoRunningAppHostExitsSuccessfully ▶️ View Recording
TypeScriptAppHostWithProjectReferenceIntegration ▶️ View Recording

📹 Recordings uploaded automatically from CI run #22991558337

return Task.FromResult<IReadOnlyList<EnvironmentCheckResult>>([new EnvironmentCheckResult
{
Category = "sdk",
Category = "environment",
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.

Should category be an enum? I assume we resolve it at some point to a description. It would be better if there was a constrained enum rather than a string

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That's a larger consideration; if we did want to make the change I'd do it in a separate PR as it would change the overall doctor API.

danegsta and others added 4 commits March 11, 2026 09:57
Introduce IHealableEnvironmentCheck interface that environment checks
can implement to support automated fix actions. DevCertsCheck is the
first implementor, providing 'clean' and 'trust' actions that use the
native CertificateManager fork instead of subprocess calls.

Key changes:
- IHealableEnvironmentCheck with HealAction/HealResult records and
  smart state-based evaluation via EvaluateAsync
- DoctorFixCommand with dynamic sub-commands per healable check,
  --all flag, --format (table/json) output support
- DevCertsCheck heal implementation using ICertificateToolRunner
  with shared code between check and heal paths
- Doctor results updated to suggest 'aspire doctor fix certificates'
  instead of 'dotnet dev-certs' commands
- Localizable resource strings for all user-facing text
- Structured JSON output matching aspire doctor response format

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix ProgressDescription doc to say 'status message' not 'spinner'
- Align EvaluateHealActions with doctor: multiple all-trusted certs
  no longer triggers destructive trust action
- Route InteractionService output to stderr in dynamic sub-commands
  when --format json is requested (BaseCommand routing doesn't apply)
- Use HealCommandName instead of HealCommandDescription in category
  header to avoid awkward 'Applying Fix ... fixes' text
- Fix category inconsistency: single untrusted cert now uses
  'environment' category like all other dev-cert results

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Five E2E tests covering the doctor fix command:
- Fix certificates with untrusted cert (evaluates and repairs)
- Fix certificates with already trusted cert (reports no issues)
- Fix --all with untrusted cert (evaluates all checks)
- Explicit clean sub-command (removes certificates)
- JSON format output (validates structured response)

All tests use Docker terminal automation with SequenceCounter
for deterministic prompt detection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…localize strings

- Drop async from ICertificateToolRunner: all methods are synchronous
  (CertificateManager runs in-process, no subprocess needed)
- Remove DevCertTrustLevel string constants, use CertificateManager.TrustLevel enum
- Remove dead DotNetCliRunner.CheckHttpCertificateMachineReadableAsync
- SSL_CERT_DIR fix: detect system OpenSSL cert dir via 'openssl version -d'
  instead of hardcoding /etc/ssl/certs; always prepend $SSL_CERT_DIR
- Localize all DevCertsCheck result strings via DoctorCommandStrings
- Keep CLI commands as format args (never localized)
- Fix elevation permission strings for consistency
- Update all test doubles to match new sync interface

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danegsta danegsta force-pushed the danegsta/doctorCerts branch from 0ff5aa5 to 564b8a2 Compare March 11, 2026 20:28
danegsta and others added 2 commits March 11, 2026 15:24
The DoctorFixCertificates_WithTrustedCert_ReportsNoIssues test was
searching for 'No issues found' but the actual output is
'No fixable issues were identified.' from DoctorFixCommandStrings.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add CertificatesCommand with clean and trust subcommands
- Remove DoctorFixCommand and IHealableEnvironmentCheck infrastructure
- Remove DoctorFixCommandStrings resources and 13 XLF files
- Remove HealCertificates* entries from DoctorCommandStrings
- Update DevCertsCheck to recommend 'aspire certificates clean/trust'
- Remove ICertificateToolRunner dependency from DevCertsCheck
- Add CertificatesCommandStrings resources with XLF files
- Replace DoctorFixCommandTests with CertificatesCommandTests
- Replace DevCertsCheckHealTests with DevCertsCheckFixRecommendationTests
- Update E2E tests to use aspire certificates clean/trust commands
- All 1591 CLI tests pass

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danegsta danegsta changed the title Add 'aspire doctor fix' sub-command for automated environment repair Add 'aspire certificates' command for certificate management Mar 12, 2026
@davidfowl
Copy link
Copy Markdown
Contributor

Can we call it aspire certs and make it possible to see the current state of cert trust (I'm assuming we can fix all of the problems that occur with dev certs trust in this impl since we are also controlling what happens inside the apphost).

@JamesNK any thoughts on the ux?

JamesNK added 2 commits March 12, 2026 14:18
- Extract CleanCommand and TrustCommand from nested classes to top-level
  CertificatesCleanCommand and CertificatesTrustCommand, registered in DI
  following the standard subcommand pattern (matching Secret, Telemetry, etc.)
- Rename the CLI command from 'certificates' to 'certs'
- Update doctor fix recommendations to reference 'aspire certs' commands
- Update all tests and xlf localization notes
- Replace (bool, string?) tuple with CertificateCleanResult class
- Add [MemberNotNullWhen(false, nameof(ErrorMessage))] so ErrorMessage
  is guaranteed non-null on failure
- Surface exception details in clean failure output
@JamesNK JamesNK changed the title Add 'aspire certificates' command for certificate management Add 'aspire certs' command for certificate management Mar 12, 2026
…ove unused types

- Extract CertificateHelpers with shared IsSuccessfulTrustResult and OpenSSL detection
- Handle UserCancelledTrustStep in trust command with specific warning message
- Detect Windows cert store dialog cancellation (HResult 0x800704C7) in clean command
- Add WasCancelled property to CertificateCleanResult
- Remove unused DoctorFix types (DoctorFixResponse, DoctorFixActionResult, DoctorFixSummary)
- Make CertificateServiceException internal
- Add TrustCancelled and CleanCancelled resource strings with localization
@JamesNK
Copy link
Copy Markdown
Member

JamesNK commented Mar 12, 2026

I made a bunch of changes and reduced duplication. And I added support for detecting cancellation of the dialog on Windows (I think there is a bug in the code from ASP.NET Core...)

Trust cancellation:
image

Trust success:
image

Clean cancellation:
image

Clean success:
image

@JamesNK
Copy link
Copy Markdown
Member

JamesNK commented Mar 12, 2026

@danegsta Double check my changes tomorrow when you get online. Merge if it is good to go.

@danegsta
Copy link
Copy Markdown
Member Author

I made a bunch of changes and reduced duplication. And I added support for detecting cancellation of the dialog on Windows (I think there is a bug in the code from ASP.NET Core...)

Trust cancellation: image

Trust success: image

Clean cancellation: image

Clean success: image

Looks good (it'd be nice to open a PR on aspnetcore to upstream your cancellation detection improvement). I did a little experimenting to see if we could handle MacOS cancellation, but there cancelling or sending Ctrl+C just results in an InvalidOperationException, so there's not really a way to differentiate cancellation from failure.

@davidfowl davidfowl merged commit 75eca36 into release/13.2 Mar 12, 2026
255 checks passed
@davidfowl davidfowl deleted the danegsta/doctorCerts branch March 12, 2026 17:36
@dotnet-policy-service dotnet-policy-service bot added this to the 13.2 milestone Mar 12, 2026
@danegsta
Copy link
Copy Markdown
Member Author

Can we call it aspire certs and make it possible to see the current state of cert trust (I'm assuming we can fix all of the problems that occur with dev certs trust in this impl since we are also controlling what happens inside the apphost).

@JamesNK any thoughts on the ux?

I'm going to open a follow up PR that adds extra certificate metadata to the aspire doctor --format json output.

@danegsta
Copy link
Copy Markdown
Member Author

Follow up to expose details on identified certificates in aspire doctor json output: #15184

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.

5 participants