Skip to content

Commit 5ff6ce9

Browse files
feat(doctor): X6 (part) — trust readiness probe in the doctor matrix (spec 05/13) (#33)
Add a `trust (mkcert)` probe to `devstack doctor`: it reports local-CA readiness (mkcert on PATH, CAROOT rootCA.pem, certutil for Firefox/NSS) with the exact remediation, as a WARNING (local HTTPS is opt-in, so a missing CA never fails doctor). This is the decision-#3 self-verification for the sudo-gated trust feature (N2) and a step toward the full X6 doctor matrix + safe --fix. Test: doctor --json lists the trust probe. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2da2159 commit 5ff6ce9

2 files changed

Lines changed: 29 additions & 0 deletions

File tree

internal/cli/doctor.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/open-source-cloud/devstack/internal/docker"
1111
"github.com/open-source-cloud/devstack/internal/state"
12+
"github.com/open-source-cloud/devstack/internal/trust"
1213
"github.com/open-source-cloud/devstack/internal/xdg"
1314
)
1415

@@ -134,6 +135,20 @@ func runDoctor(cmd *cobra.Command) []docker.Check {
134135
checks = append(checks, docker.Check{Name: "state ledger", Status: docker.StatusOK, Detail: fmt.Sprintf("schema v%d @ %s", v, ctxName)})
135136
}
136137

138+
// Local-CA trust (spec 05) — opt-in, so never fatal: report readiness as a
139+
// warning with the exact remediation when not fully set up.
140+
ts := trust.New().Status(ctx)
141+
if ts.OK() {
142+
checks = append(checks, docker.Check{Name: "trust (mkcert)", Status: docker.StatusOK, Detail: "local CA installed (" + ts.CARoot + ")"})
143+
} else {
144+
checks = append(checks, docker.Check{
145+
Name: "trust (mkcert)",
146+
Status: docker.StatusWarn,
147+
Detail: fmt.Sprintf("mkcert=%v CA=%v firefox=%v", ts.MkcertFound, ts.CAInstalled, ts.FirefoxTrust),
148+
Remediation: ts.Remediation,
149+
})
150+
}
151+
137152
return checks
138153
}
139154

internal/cli/gc_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,17 @@ func TestDoctorRebuildStateFlag(t *testing.T) {
4545
t.Error("doctor is missing the --rebuild-state flag")
4646
}
4747
}
48+
49+
func TestDoctorIncludesTrustProbe(t *testing.T) {
50+
var out strings.Builder
51+
root := NewRootCmd(Options{})
52+
root.SetArgs([]string{"doctor", "--json"})
53+
root.SetOut(&out)
54+
root.SetErr(&out)
55+
// doctor exits non-zero only on a hard FAIL; the trust probe is a warning, so
56+
// in CI (no mkcert) doctor still succeeds and the JSON lists the probe.
57+
_ = root.Execute()
58+
if !strings.Contains(out.String(), `"trust (mkcert)"`) {
59+
t.Errorf("doctor --json missing the trust probe:\n%s", out.String())
60+
}
61+
}

0 commit comments

Comments
 (0)