From fc5802d82363ac820ff0568e374f1eb6dbdad8b8 Mon Sep 17 00:00:00 2001 From: Paul Payne Date: Mon, 29 Jun 2026 08:26:15 -0700 Subject: [PATCH] Add CA download to dashboard: GET /gateway/ca.crt + Gateway-panel button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the gateway serves internal-CA HTTPS (gateway.tls=internal), other devices must trust Caddy's root CA to accept *.lan HTTPS. Surface that from the dashboard instead of hand-copying root.crt around. - API: GET /gateway/ca.crt returns the public root cert (PEM) as a download, sourced from Caddy's admin API (/pki/ca/local — matches the running gateway) with an on-disk root.crt fallback. 404 unless tls=internal. GatewayInfo gains `tls` and `ca_fingerprint` (SHA-256, for out-of-band verification). The CA private key is never exposed — only the public root. - UI: a "CA cert" button in the Gateway panel, shown only when tls=internal, linking to the download with the fingerprint in its tooltip. - Tests for the tls=off defaults and the 404-without-internal-tls path; docs note the button + endpoint. --- app/src/components/GatewayPanel.tsx | 17 ++++++- app/src/types/index.ts | 2 + castle-api/src/castle_api/models.py | 5 ++ castle-api/src/castle_api/routes.py | 76 ++++++++++++++++++++++++++++- castle-api/tests/test_health.py | 11 +++++ docs/registry.md | 10 ++-- 6 files changed, 116 insertions(+), 5 deletions(-) diff --git a/app/src/components/GatewayPanel.tsx b/app/src/components/GatewayPanel.tsx index 5cec834..1f2a5bc 100644 --- a/app/src/components/GatewayPanel.tsx +++ b/app/src/components/GatewayPanel.tsx @@ -1,8 +1,9 @@ import { useMemo, useState } from "react" import { Link } from "react-router-dom" -import { Globe, RefreshCw, FileText } from "lucide-react" +import { Globe, RefreshCw, FileText, ShieldCheck } from "lucide-react" import type { GatewayInfo, HealthStatus } from "@/types" import { useGatewayReload, useCaddyfile } from "@/services/api/hooks" +import { apiClient } from "@/services/api/client" import { HealthBadge } from "./HealthBadge" interface GatewayPanelProps { @@ -33,6 +34,20 @@ export function GatewayPanel({ gateway, statuses }: GatewayPanelProps) {
+ {gateway.tls === "internal" && ( + + + CA cert + + )}