From e7509ddad8558b3d366155f2ac544f640e25cb50 Mon Sep 17 00:00:00 2001 From: shankar0123 Date: Tue, 5 May 2026 06:15:35 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20factuality=20sweep=20=E2=80=94=20fix=20?= =?UTF-8?q?3=20broken=20links=20+=2012=20count=20claims=20(audit=20finding?= =?UTF-8?q?s=202026-05-05)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the cowork/docs-audit-2026-05-05/ end-to-end factuality audit (20 confirmed findings across 76 docs, 7 parallel subagents + audit-of-the-audit). Hot + Warm tier fixes ship here; STALE findings (qa-test-suite.md test-count snapshot) need 'make qa-stats' which is operator-side. BROKEN links repaired (3): - docs/reference/api.md L195: [Quick Start](quickstart.md) → ../getting-started/quickstart.md (404 pre-fix) - docs/reference/api.md L196: [Connector Guide](connectors.md) → connectors/index.md (Phase 4 rename, was 404 pre-fix) - docs/reference/protocols/scep-intune.md L377: [legacy-est-scep.md](legacy-est-scep.md) → scep-server.md (file was deleted in Phase 7 commit cb154a8) INCORRECT count claims repaired (12): - api.md L5 + L18-19 + L155: '78 API operations' / '# 78' / 'all 78 documented operations' → re-derive via grep -cE '^\s+operationId:' (actual at HEAD: 144) - architecture.md L66 (Mermaid label) + L502 + L1047 + L1253: '8 always-on + 4 optional loops' / '12-loop topology' → 9 always-on + 5 opt-in loops (14 total). Always-on/opt-in breakdown derived from cmd/server/main.go startup wiring: always-on are agentHealthCheck, crlGeneration, jobProcessor, jobRetry, jobTimeout, notificationProcess, notificationRetry, renewalCheck, shortLivedExpiryCheck (9); opt-in are networkScan, digest, healthCheck, cloudDiscovery, acmeGC (5). Re-derive count via grep -cE '^func \(s \*Scheduler\) [a-zA-Z]+Loop' internal/scheduler/scheduler.go. - configuration.md L31: '12 loops, 8 always-on + 4 opt-in' → '14 loops, 9 always-on + 5 opt-in'. Self-introduced regression from commit a3599ad (2026-05-05). - mcp.md L11 + L65: 'all 78 API endpoints' / '78 available tools' → re-derive via grep -cE 'mcp\.AddTool\(' (actual at HEAD: 87 MCP tools, 144 API operations). - connectors/index.md L111: '9 built-in' issuer connectors → '12 built-in', extending the inline enumeration to include Entrust, GlobalSign, EJBCA (which had been added since the L111 prose was written). Local-CA framing extended to mention tree mode + ADCS sub-CA mode-doc. - connectors/index.md L112: '14 built-in' target connectors → '15 built-in', adding AWS ACM target + Azure Key Vault target (which had been added since the L112 prose was written). - why-certctl.md L37 + the inline list: 'Nine issuer connectors ship today' → 'Twelve issuer connectors', adding AWS ACM PCA, Entrust, GlobalSign, EJBCA to the list and removing the misleading 'EST enrollment' bullet (EST is a protocol surface, not an issuer; clarified in trailing note). - why-certctl.md L66: '13 deployment targets' → '15', adding Kubernetes Secrets, AWS ACM, and Azure KV to the inline list. - why-certctl.md L92: 'supports 9 issuer types' → '12 issuer types'. - quickstart.md L135: '35 demo certificates across 5 issuers' → re-derive cert count via 'grep -oE "mc-[a-z0-9_-]+" migrations/seed_demo.sql | sort -u | wc -l' (actual: 32, matches README L86; quickstart was off-by-3). - quickstart.md L452 (Demo Data Reference table): Certificates '35' → '32' (matches the cert count from seed_demo.sql). Verification: - grep confirms no remaining stale refs across the touched files (8 files, 31 insertions / 28 deletions). - All 24 ci-guards/*.sh pass locally. - The audit's STALE findings (S-1, S-2 qa-test-suite.md Bundle-P snapshot) are operator-side: run 'make qa-stats' to refresh the Test Suite Health table. Companion: cowork/docs-audit-2026-05-05/RESULTS.md captures the full audit with subagent false positives and missed findings called out. --- docs/getting-started/quickstart.md | 4 ++-- docs/getting-started/why-certctl.md | 15 ++++++++++----- docs/reference/api.md | 13 ++++++------- docs/reference/architecture.md | 8 ++++---- docs/reference/configuration.md | 2 +- docs/reference/connectors/index.md | 4 ++-- docs/reference/mcp.md | 6 +++--- docs/reference/protocols/scep-intune.md | 7 +++---- 8 files changed, 31 insertions(+), 28 deletions(-) diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index b0fe838..4898fb8 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -132,7 +132,7 @@ Open **https://localhost:8443** in your browser. Your browser will warn about th > > **Key rotation:** `CERTCTL_AUTH_SECRET` accepts comma-separated keys (e.g., `CERTCTL_AUTH_SECRET=new-key,old-key`). Both keys are valid simultaneously, enabling zero-downtime rotation: add the new key, roll clients over, then remove the old key. -The dashboard comes pre-loaded with 35 demo certificates across 5 issuers, 8 agents, and 90 days of job history — expiring certs, expired certs, active certs, failed renewals, revocations, discovery scans, and approval workflows. A realistic snapshot of what certificate management looks like in a real organization. +The dashboard comes pre-loaded with demo data covering certificates across multiple issuers, agents, and 90 days of job history — expiring certs, expired certs, active certs, failed renewals, revocations, discovery scans, and approval workflows. A realistic snapshot of what certificate management looks like in a real organization. (Re-derive exact counts via `grep -oE 'mc-[a-z0-9_-]+' migrations/seed_demo.sql | sort -u | wc -l`.) ### What you're looking at @@ -449,7 +449,7 @@ Exposes the full REST API via MCP over stdio transport. Ask Claude: "What certif | Issuers | 5 | Local Dev CA, Let's Encrypt Staging, step-ca Internal, ZeroSSL (EAB), Custom OpenSSL CA | | Agents | 9 | 8 real agents (linux/darwin/windows, amd64/arm64) + server-scanner (network discovery) | | Targets | 8 | NGINX prod, NGINX staging, NGINX data, HAProxy, Apache, IIS, Traefik, Caddy | -| Certificates | 35 | Active, Expiring, Expired, Failed, Revoked, RenewalInProgress, Wildcard, S/MIME | +| Certificates | 32 | Active, Expiring, Expired, Failed, Revoked, RenewalInProgress, Wildcard, S/MIME | | Jobs | 50+ | 90 days of issuance, renewal, deployment jobs + 2 AwaitingApproval | | Discovered Certs | 12 | Unmanaged (filesystem + network), Managed (linked), Dismissed | | Discovery Scans | 8 | Historical + recent agent filesystem scans + network TLS scans | diff --git a/docs/getting-started/why-certctl.md b/docs/getting-started/why-certctl.md index 16085f7..886153f 100644 --- a/docs/getting-started/why-certctl.md +++ b/docs/getting-started/why-certctl.md @@ -34,17 +34,22 @@ This isn't a premium feature. It's the default behavior, free. Most alternatives ### 2. CA-Agnostic Issuer Architecture -certctl works with any certificate authority, not just ACME providers. Nine issuer connectors ship today, all free: +certctl works with any certificate authority, not just ACME providers. Twelve issuer connectors ship today, all free: - **ACME v2** (Let's Encrypt, ZeroSSL, Google Trust Services, Buypass) — HTTP-01, DNS-01, DNS-PERSIST-01 challenges, External Account Binding, ACME Renewal Information (RFC 9773), certificate profile selection - **HashiCorp Vault PKI** — `/v1/{mount}/sign/{role}` API, token auth - **DigiCert CertCentral** — async order model, OV/EV support - **Sectigo SCM** — async order model, DV/OV/EV support, 3-header auth - **Google Cloud CAS** — Certificate Authority Service, OAuth2 service account auth, CA pool selection +- **AWS ACM Private CA** — managed private CA on AWS, IAM-authenticated, SDK-waiter for issuance +- **Entrust Certificate Services** — Entrust CA Gateway with mTLS auth, approval-pending support +- **GlobalSign Atlas HVCA** — region-pinned commercial CA with dual mTLS + API key/secret auth +- **EJBCA / Keyfactor** — self-hosted open-source / Keyfactor enterprise CA, mTLS or OAuth2 - **step-ca** (Smallstep) — native /sign API with JWK provisioner auth -- **Local CA** — self-signed or sub-CA mode (chain to ADCS or any enterprise root) +- **Local CA** — self-signed or sub-CA mode (chain to ADCS or any enterprise root); supports multi-level CA tree mode - **OpenSSL / Custom CA** — delegate signing to any shell script -- **EST enrollment** (RFC 7030) — device certs for WiFi/802.1X, MDM, IoT + +EST (RFC 7030) and SCEP (RFC 8894) are protocol surfaces, not separate issuers — they dispatch to whichever issuer above is configured for the EST/SCEP profile. Every connector implements the same interface. Running multiple CAs in parallel — Let's Encrypt for public certs, Vault for internal services, your enterprise CA for legacy systems — is configuration, not code. @@ -58,7 +63,7 @@ A reload command can exit 0 while the certificate doesn't take effect — wrong The three differentiators above get the headlines, but the feature surface is wider than most paid platforms: -**13 deployment targets** — NGINX, Apache, HAProxy, Traefik, Caddy, Envoy, IIS (local PowerShell + remote WinRM), F5 BIG-IP (proxy agent + iControl REST), Postfix, Dovecot, SSH (agentless), Windows Certificate Store, and Java Keystore. All use a pluggable connector model. The control plane never initiates outbound connections — agents poll for work, meaning certctl works behind firewalls, across network zones, and in air-gapped environments. +**15 deployment targets** — NGINX, Apache, HAProxy, Traefik, Caddy, Envoy, IIS (local PowerShell + remote WinRM), F5 BIG-IP (proxy agent + iControl REST), Postfix/Dovecot (dual-mode), SSH (agentless), Windows Certificate Store, Java Keystore, Kubernetes Secrets, AWS Certificate Manager, and Azure Key Vault. All use a pluggable connector model. The control plane never initiates outbound connections — agents poll for work, meaning certctl works behind firewalls, across network zones, and in air-gapped environments. **Network certificate discovery** — active TLS scanning of CIDR ranges finds certificates you didn't know existed. Agents also scan local filesystems for PEM/DER files. Everything feeds into a triage workflow where you claim, dismiss, or import discovered certs into management. @@ -84,7 +89,7 @@ ACME clients solve one slice of the problem — issuance and renewal from ACME C ### vs. Agent-Based SaaS -The closest architectural competitors use the same agent model — local key generation, CSR submission, push-based deployment. Where certctl differs: it supports 9 issuer types (not just ACME), provides CRL/OCSP/revocation infrastructure (not just issuance), includes a policy engine and network discovery, and is source-available with no certificate limit. SaaS alternatives are typically proprietary, priced per certificate ($2+/cert/month), and cap their free tiers at 3-5 certificates. certctl is free for any number of certificates, forever. +The closest architectural competitors use the same agent model — local key generation, CSR submission, push-based deployment. Where certctl differs: it supports 12 issuer types (not just ACME), provides CRL/OCSP/revocation infrastructure (not just issuance), includes a policy engine and network discovery, and is source-available with no certificate limit. SaaS alternatives are typically proprietary, priced per certificate ($2+/cert/month), and cap their free tiers at 3-5 certificates. certctl is free for any number of certificates, forever. ### vs. Commercial PKI Platforms diff --git a/docs/reference/api.md b/docs/reference/api.md index b0d41d9..7d625c7 100644 --- a/docs/reference/api.md +++ b/docs/reference/api.md @@ -2,7 +2,7 @@ > Last reviewed: 2026-05-05 -certctl ships with a complete OpenAPI 3.1 specification at `api/openapi.yaml`. This spec documents all 78 API operations currently specified, every request/response schema, pagination conventions, authentication requirements, and error formats. It's the single source of truth for the documented REST API. (Note: The spec will be updated to include 7 additional certificate discovery endpoints from M18b.) +certctl ships with a complete OpenAPI 3.1 specification at `api/openapi.yaml`. The spec documents every operation (re-derive count via `grep -cE '^\s+operationId:' api/openapi.yaml`), every request/response schema, pagination conventions, authentication requirements, and error formats. It's the single source of truth for the documented REST API. This guide covers how to use the spec for API exploration, client SDK generation, and integration testing. @@ -14,9 +14,8 @@ The spec lives at `api/openapi.yaml` in the repository root. It's versioned alon # View the spec cat api/openapi.yaml -# Count operations -grep "operationId:" api/openapi.yaml | wc -l -# 78 (includes health + ready, 7 discovery endpoints pending spec update) +# Count operations (includes health + ready) +grep -cE '^\s+operationId:' api/openapi.yaml ``` ## Viewing with Swagger UI @@ -153,7 +152,7 @@ npx @apidevtools/swagger-cli validate api/openapi.yaml Import the spec directly into Postman: 1. Open Postman → Import → File → select `api/openapi.yaml` -2. Postman creates a collection with all 78 documented operations organized by tag +2. Postman creates a collection with every documented operation organized by tag 3. Set the `baseUrl` variable to `https://localhost:8443` (HTTPS-only as of v2.2) 4. Add an `Authorization: Bearer your-api-key` header to the collection 5. Import the demo stack CA bundle (`deploy/test/certs/ca.crt`) into Postman's Settings → Certificates → CA Certificates, or disable certificate verification for the `localhost` host (Settings → General → SSL certificate verification) @@ -193,6 +192,6 @@ This sends randomized valid requests to every endpoint and verifies the response ## What's Next - [MCP Server Guide](mcp.md) — AI-native access to the certctl API -- [Quick Start](quickstart.md) — Get certctl running locally -- [Connector Guide](connectors.md) — Build custom issuer and target connectors +- [Quick Start](../getting-started/quickstart.md) — Get certctl running locally +- [Connector Guide](connectors/index.md) — Build custom issuer and target connectors - [Architecture](architecture.md) — System design deep dive diff --git a/docs/reference/architecture.md b/docs/reference/architecture.md index a87a258..26a6ee1 100644 --- a/docs/reference/architecture.md +++ b/docs/reference/architecture.md @@ -63,7 +63,7 @@ flowchart TB API["REST API\n(Go net/http, :8443)"] SVC["Service Layer"] REPO["Repository Layer\n(database/sql + lib/pq)"] - SCHED["Background Scheduler\n8 always-on + 4 optional loops"] + SCHED["Background Scheduler\n9 always-on + 5 opt-in loops"] DASH["Web Dashboard\n(React SPA)"] end @@ -499,7 +499,7 @@ For incident-response events requiring fleet-wide revocation (key compromise, CA ### 4. Automatic Renewal -The control plane runs a scheduler with 8 always-on loops plus up to 4 optional loops (enabled by configuration). `internal/scheduler/scheduler.go:262-265` is the authoritative count. +The control plane runs a scheduler with 9 always-on loops plus up to 5 opt-in loops (enabled by configuration). Re-derive the count via `grep -cE '^func \(s \*Scheduler\) [a-zA-Z]+Loop' internal/scheduler/scheduler.go`; the opt-in gating lives in `cmd/server/main.go` startup wiring (`cfg.NetworkScan.Enabled`, `digestService != nil`, `healthCheckService != nil`, `cloudDiscoveryService != nil`, `cfg.ACMEServer.Enabled && cfg.ACMEServer.GCInterval > 0`). ```mermaid flowchart LR @@ -1044,7 +1044,7 @@ For deployments that need JWT/OIDC/mTLS, the standard pattern is to put an authe ### Concurrency Safety -The background scheduler uses `sync/atomic.Bool` idempotency guards on every loop (8 always-on plus up to 4 optional) — if a tick fires while the previous iteration is still running, it skips. A `sync.WaitGroup` tracks all in-flight goroutines. `WaitForCompletion(timeout)` blocks during shutdown until all work finishes or the timeout expires, preventing state corruption from mid-flight database operations during process exit. +The background scheduler uses `sync/atomic.Bool` idempotency guards on every loop (9 always-on plus up to 5 opt-in) — if a tick fires while the previous iteration is still running, it skips. A `sync.WaitGroup` tracks all in-flight goroutines. `WaitForCompletion(timeout)` blocks during shutdown until all work finishes or the timeout expires, preventing state corruption from mid-flight database operations during process exit. The job-processor tick fans the per-job work out across up to `CERTCTL_RENEWAL_CONCURRENCY` goroutines (default 25), gated by `golang.org/x/sync/semaphore.Weighted`. The cap is the operator's lever for "how many concurrent CA calls per scheduler tick" — operators with permissive upstream limits and large fleets (>10k certs) can bump to 100; operators with strict limits or async-CA-heavy fleets should stay at 25 or lower. Values ≤ 0 normalise to 1 (sequential). The Acquire is ctx-aware so a shutdown-driven ctx cancel interrupts the dispatch loop promptly; in-flight goroutines drain via Wait before the tick returns. Closes the #9 acquisition-readiness blocker from the 2026-05-01 issuer coverage audit (pre-fix the fan-out had no cap, so a 5,000-cert sweep tripped DigiCert / Entrust / Sectigo rate limits and the next tick re-fanned-out the same calls). @@ -1250,7 +1250,7 @@ flowchart TB 1. **Pluggable sources** — Each cloud provider implements the `DiscoverySource` interface (Name, Type, Discover, ValidateConfig). Three built-in sources: AWS Secrets Manager, Azure Key Vault, GCP Secret Manager 2. **CloudDiscoveryService orchestrator** — Iterates registered sources, calls `Discover()` on each, feeds reports into `ProcessDiscoveryReport()`. Errors from one source don't prevent other sources from running -3. **Scheduler integration** — opt-in cloud discovery scheduler loop (6h default; see `docs/architecture.md` 12-loop topology), runs immediately on startup, `atomic.Bool` idempotency guard +3. **Scheduler integration** — opt-in cloud discovery scheduler loop (6h default; one of the 14 loops in the scheduler topology — see the Background Scheduler section above), runs immediately on startup, `atomic.Bool` idempotency guard 4. **Sentinel agents** — Each source uses its own sentinel agent ID (`cloud-aws-sm`, `cloud-azure-kv`, `cloud-gcp-sm`) for dedup and triage filtering 5. **Source path format** — `aws-sm://{region}/{secret}`, `azure-kv://{cert-name}/{version}`, `gcp-sm://{project}/{secret}` 6. **No new schema** — Reuses existing `discovered_certificates` and `discovery_scans` tables. Sentinel agent IDs leverage existing `(fingerprint_sha256, agent_id, source_path)` dedup constraint diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index bf0c2f6..4dc3b5b 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -28,7 +28,7 @@ performance / contention tuning. | `CERTCTL_SCHEDULER_NOTIFICATION_PROCESS_INTERVAL` | `1m` | How often the notification-dispatcher loop fans out queued alerts to channels. | | `CERTCTL_SHORT_LIVED_EXPIRY_CHECK_INTERVAL` | `5m` | How often the short-lived-expiry loop watches certs whose TTL is less than 1h for imminent expiry. | -For the full scheduler topology (12 loops, 8 always-on + 4 opt-in) +For the full scheduler topology (14 loops, 9 always-on + 5 opt-in) see [`architecture.md`](architecture.md) "Scheduler topology". ## Job lifecycle diff --git a/docs/reference/connectors/index.md b/docs/reference/connectors/index.md index 4939f32..8904273 100644 --- a/docs/reference/connectors/index.md +++ b/docs/reference/connectors/index.md @@ -108,8 +108,8 @@ Target connectors: Three types of connectors: -1. **Issuer Connector** — Obtains certificates from CAs. 9 built-in: Local CA (self-signed + sub-CA), ACME v2 (HTTP-01, DNS-01, DNS-PERSIST-01, ARI, EAB, profile selection), step-ca, OpenSSL/Custom CA, Vault PKI, DigiCert CertCentral, Sectigo SCM, Google CAS, AWS ACM Private CA -2. **Target Connector** — Deploys certificates to infrastructure. 14 built-in: NGINX, Apache httpd, HAProxy, Traefik, Caddy, Envoy, Postfix, Dovecot, IIS (local + WinRM), F5 BIG-IP (proxy agent), SSH (agentless), Windows Certificate Store, Java Keystore, Kubernetes Secrets +1. **Issuer Connector** — Obtains certificates from CAs. 12 built-in: Local CA (self-signed + sub-CA + tree mode; ADCS sub-CA mode is documented separately), ACME v2 (HTTP-01, DNS-01, DNS-PERSIST-01, ARI, EAB, profile selection), step-ca, OpenSSL/Custom CA, Vault PKI, DigiCert CertCentral, Sectigo SCM, Google CAS, AWS ACM Private CA, Entrust Certificate Services, GlobalSign Atlas HVCA, EJBCA (Keyfactor) +2. **Target Connector** — Deploys certificates to infrastructure. 15 built-in: NGINX, Apache httpd, HAProxy, Traefik, Caddy, Envoy, Postfix/Dovecot (dual-mode), IIS (local PowerShell + WinRM proxy), F5 BIG-IP (proxy agent), SSH (agentless), Windows Certificate Store, Java Keystore (JKS / PKCS#12), Kubernetes Secrets, AWS Certificate Manager, Azure Key Vault 3. **Notifier Connector** — Sends alerts about certificate events (Email, Webhooks, Slack, Microsoft Teams, PagerDuty, OpsGenie implemented) All connectors accept JSON configuration at initialization, support config validation, and are registered in the service layer. Issuer connectors run on the control plane; target connectors run on agents. For network appliances where agents can't be installed, a **proxy agent** in the same network zone handles deployment — the server never initiates outbound connections. diff --git a/docs/reference/mcp.md b/docs/reference/mcp.md index ee81305..6b0f9d7 100644 --- a/docs/reference/mcp.md +++ b/docs/reference/mcp.md @@ -8,7 +8,7 @@ This guide covers setup, configuration, and usage with Claude, Cursor, and other ## What Is MCP? -MCP is an open protocol that connects AI assistants to external tools and data sources. Instead of copying and pasting API responses into a chat window, MCP lets the AI call your tools directly. The certctl MCP server exposes all 78 API endpoints as MCP tools — the AI sees typed schemas describing what each tool does, what parameters it accepts, and what it returns. +MCP is an open protocol that connects AI assistants to external tools and data sources. Instead of copying and pasting API responses into a chat window, MCP lets the AI call your tools directly. The certctl MCP server exposes the certctl API as MCP tools (re-derive count via `grep -cE 'mcp\.AddTool\(' internal/mcp/tools.go`) — the AI sees typed schemas describing what each tool does, what parameters it accepts, and what it returns. The MCP server is a separate binary (`cmd/mcp-server/`) that communicates via stdio transport. It's a stateless HTTP proxy: every MCP tool call becomes an HTTP request to the certctl REST API. No new state, no new database tables, no new attack surface beyond what the API already exposes. @@ -16,7 +16,7 @@ The MCP server is a separate binary (`cmd/mcp-server/`) that communicates via st You need: -1. A running certctl server (see [Quick Start](quickstart.md)) +1. A running certctl server (see [Quick Start](../getting-started/quickstart.md)) 2. The MCP server binary — either built from source or from a Docker image 3. An MCP-compatible AI client (Claude Desktop, Cursor, VS Code with Copilot, etc.) @@ -62,7 +62,7 @@ Add this to your Claude Desktop MCP configuration file (`~/Library/Application S } ``` -Restart Claude Desktop. You should see "certctl" appear in the MCP tools list with 78 available tools. +Restart Claude Desktop. You should see "certctl" appear in the MCP tools list (the available-tools count varies by certctl version; the exact set is enumerated in `internal/mcp/tools.go`). ## Setting Up with Cursor diff --git a/docs/reference/protocols/scep-intune.md b/docs/reference/protocols/scep-intune.md index 7d470bf..d26f729 100644 --- a/docs/reference/protocols/scep-intune.md +++ b/docs/reference/protocols/scep-intune.md @@ -374,10 +374,9 @@ the golden-file fixtures in `internal/scep/intune/testdata/`. ## Related docs -- [`legacy-est-scep.md`](legacy-est-scep.md) — the per-profile SCEP - setup guide + RFC 8894 reference + mTLS sibling route. Read this - first if you're not already running certctl SCEP for non-Intune - fleets. +- [`scep-server.md`](scep-server.md) — the per-profile SCEP setup + guide + RFC 8894 reference + mTLS sibling route. Read this first + if you're not already running certctl SCEP for non-Intune fleets. - [`architecture.md`](architecture.md) — overall control-plane architecture; Security Model section calls out the Intune trust anchor as a sensitive operator-configured surface.