Files
certctl/docs/demo-advanced.md
T
Shankar 9918f2f5cb Fix runtime bugs, implement service layer, and overhaul documentation
Runtime fixes:
- Fix env var mismatch (CERTCTL_DB_URL → CERTCTL_DATABASE_URL)
- Fix table name mismatches (certificates → managed_certificates, notifications → notification_events)
- Add renewal_policy_id to certificate queries
- Remove non-existent created_at from notification queries
- Add env var fallback for agent CLI flags
- Graceful degradation for missing notifiers/issuers in demo mode
- Copy web/ directory in Dockerfile for dashboard serving

Service layer:
- Implement handler-service interface pattern across all services
- Wire up certificate, agent, job, policy, team, owner, audit, notification services

Documentation:
- Add concepts.md: beginner-friendly guide to TLS, CAs, private keys
- Rewrite quickstart.md with accurate API examples matching actual handlers
- Add demo-advanced.md: interactive demo with cert issuance and automated script
- Update architecture.md with correct table names and connector interfaces
- Update connectors.md to match actual Go interface signatures
- Update demo-guide.md with cross-references to new docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 21:38:11 -04:00

356 lines
11 KiB
Markdown

# Advanced Demo: Certificate Lifecycle End-to-End
This demo goes beyond browsing pre-loaded data. You'll create a team, register an owner, set up an issuer, create a certificate, trigger renewal, and watch everything appear in the dashboard in real time. By the end, you'll understand the full certificate lifecycle as certctl manages it.
**Time**: 10-15 minutes
**Prerequisites**: certctl running via Docker Compose (see [Quick Start](quickstart.md))
## Setup
Make sure certctl is running:
```bash
docker compose -f deploy/docker-compose.yml up -d
# Wait for healthy status
docker compose -f deploy/docker-compose.yml ps
```
Open **http://localhost:8443** in your browser alongside your terminal. You'll watch changes appear in the dashboard as you make API calls.
Set up a base variable for convenience:
```bash
API="http://localhost:8443"
```
## Part 1: Build the Organization Structure
### Create a new team
```bash
curl -s -X POST $API/api/v1/teams \
-H "Content-Type: application/json" \
-d '{
"id": "t-demo",
"name": "Demo Team",
"description": "Team created during advanced demo walkthrough"
}' | jq .
```
### Register an owner
```bash
curl -s -X POST $API/api/v1/owners \
-H "Content-Type: application/json" \
-d '{
"id": "o-demo-user",
"name": "Demo User",
"email": "demo@example.com",
"team_id": "t-demo"
}' | jq .
```
Verify both exist:
```bash
curl -s $API/api/v1/teams/t-demo | jq .
curl -s $API/api/v1/owners/o-demo-user | jq .
```
## Part 2: Configure the Issuer
The demo ships with a Local CA issuer (`iss-local`) that can sign certificates immediately — no external CA needed. Let's verify it's available:
```bash
curl -s $API/api/v1/issuers/iss-local | jq .
```
You should see:
```json
{
"id": "iss-local",
"name": "Local Dev CA",
"type": "GenericCA",
"enabled": true
}
```
This Local CA generates real X.509 certificates using Go's `crypto/x509` library. The certificates are self-signed (not trusted by browsers in production), but structurally identical to production certificates — they have serial numbers, validity periods, SANs, key usage extensions, and a proper certificate chain.
## Part 3: Create a Managed Certificate
Now the main event. Let's create a certificate for a fictional internal API:
```bash
curl -s -X POST $API/api/v1/certificates \
-H "Content-Type: application/json" \
-d '{
"id": "mc-demo-api",
"name": "Demo API Certificate",
"common_name": "demo-api.internal.example.com",
"sans": ["demo-api.internal.example.com", "demo-api-v2.internal.example.com"],
"environment": "staging",
"owner_id": "o-demo-user",
"team_id": "t-demo",
"issuer_id": "iss-local",
"renewal_policy_id": "rp-default",
"status": "Pending",
"tags": {
"service": "demo-api",
"created_by": "advanced-demo",
"tier": "internal"
}
}' | jq .
```
**Check the dashboard now.** Click "Certificates" in the sidebar. You'll see your new "Demo API Certificate" with status "Pending" alongside the pre-loaded demo certificates. Click on it to see the full details: owner, team, environment, tags, and timeline.
### Verify via API
```bash
curl -s $API/api/v1/certificates/mc-demo-api | jq '{id, name, common_name, status, environment, owner_id, team_id}'
```
## Part 4: Trigger Certificate Renewal
In production, the scheduler automatically triggers renewal when certificates approach expiry. For this demo, we'll trigger it manually:
```bash
curl -s -X POST $API/api/v1/certificates/mc-demo-api/renew | jq .
```
Expected response:
```json
{
"status": "renewal_triggered"
}
```
This creates a renewal job. Check the jobs list:
```bash
curl -s "$API/api/v1/jobs" | jq '.data[] | select(.certificate_id == "mc-demo-api") | {id, type, status, certificate_id}'
```
**Check the dashboard.** Go to the "Jobs" view — you'll see the renewal job for your certificate.
## Part 5: Deploy the Certificate
Trigger deployment to see the deployment workflow:
```bash
curl -s -X POST $API/api/v1/certificates/mc-demo-api/deploy | jq .
```
Expected response:
```json
{
"status": "deployment_triggered"
}
```
Check for deployment jobs:
```bash
curl -s "$API/api/v1/jobs" | jq '.data[] | select(.certificate_id == "mc-demo-api")'
```
## Part 6: View the Audit Trail
Every action you've taken has been recorded. Check the audit trail:
```bash
curl -s $API/api/v1/audit | jq '.data[0:5]'
```
You'll see events for certificate creation, renewal trigger, and deployment trigger — each with actor, action, resource type, and timestamp.
**Check the dashboard.** The "Audit" view shows the full timeline of all actions across the system.
## Part 7: Check Notifications
Certctl sends notifications for certificate lifecycle events. Check what notifications were generated:
```bash
curl -s $API/api/v1/notifications | jq '.data[0:5]'
```
In demo mode, notifications are marked as "sent" even without a real email/webhook backend. In production, these would go out via SMTP or HTTP webhooks.
## Part 8: Create a Second Certificate and Compare
Let's create another certificate in production to see how the dashboard handles multiple environments:
```bash
curl -s -X POST $API/api/v1/certificates \
-H "Content-Type: application/json" \
-d '{
"id": "mc-demo-payments",
"name": "Demo Payments Gateway",
"common_name": "payments.example.com",
"sans": ["payments.example.com", "checkout.example.com"],
"environment": "production",
"owner_id": "o-demo-user",
"team_id": "t-demo",
"issuer_id": "iss-local",
"renewal_policy_id": "rp-default",
"status": "Active",
"expires_at": "2026-04-01T00:00:00Z",
"tags": {
"service": "payments",
"pci": "true",
"tier": "critical"
}
}' | jq .
```
This certificate expires in about 18 days from the demo date, so it should show up as "Expiring" in the dashboard when the scheduler runs. **Refresh the dashboard** — you'll see it in the certificate list.
Now filter the dashboard by environment or status to see how the filtering works with your new certificates mixed in with the demo data.
## Part 9: Policy Violations
Let's see what happens when a certificate doesn't meet policy requirements. Check existing policy rules:
```bash
curl -s $API/api/v1/policies | jq '.data[] | {id, name, type, enabled}'
```
The demo includes rules for required owner metadata, allowed environments, maximum certificate lifetime, and minimum renewal windows. Check existing violations:
```bash
curl -s "$API/api/v1/policies/pr-max-certificate-lifetime/violations" | jq .
```
**In the dashboard**, click "Policies" in the sidebar to see all active rules and which certificates are violating them.
## Full Automated Script
Here's a single script that runs the entire demo end-to-end. Save it as `demo.sh` and run it:
```bash
#!/bin/bash
set -e
API="http://localhost:8443"
BLUE='\033[0;34m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo -e "${BLUE}=== certctl Advanced Demo ===${NC}"
echo ""
# Step 1: Health check
echo -e "${YELLOW}Step 1: Checking server health...${NC}"
HEALTH=$(curl -s $API/health | jq -r '.status')
if [ "$HEALTH" != "healthy" ]; then
echo "Server is not healthy. Run: docker compose -f deploy/docker-compose.yml up -d"
exit 1
fi
echo -e "${GREEN}Server is healthy${NC}"
echo ""
# Step 2: Create team
echo -e "${YELLOW}Step 2: Creating demo team...${NC}"
curl -s -X POST $API/api/v1/teams \
-H "Content-Type: application/json" \
-d '{"id":"t-demo-auto","name":"Automated Demo Team","description":"Created by demo script"}' | jq -r '.id'
echo -e "${GREEN}Team created${NC}"
echo ""
# Step 3: Create owner
echo -e "${YELLOW}Step 3: Registering demo owner...${NC}"
curl -s -X POST $API/api/v1/owners \
-H "Content-Type: application/json" \
-d '{"id":"o-demo-auto","name":"Demo Script","email":"demo-script@example.com","team_id":"t-demo-auto"}' | jq -r '.id'
echo -e "${GREEN}Owner registered${NC}"
echo ""
# Step 4: Create certificate
echo -e "${YELLOW}Step 4: Creating managed certificate...${NC}"
CERT_ID="mc-demo-$(date +%s)"
curl -s -X POST $API/api/v1/certificates \
-H "Content-Type: application/json" \
-d '{
"id":"'$CERT_ID'",
"name":"Demo Auto Certificate",
"common_name":"auto-demo.internal.example.com",
"sans":["auto-demo.internal.example.com"],
"environment":"staging",
"owner_id":"o-demo-auto",
"team_id":"t-demo-auto",
"issuer_id":"iss-local",
"renewal_policy_id":"rp-default",
"status":"Pending",
"tags":{"created_by":"demo-script","automated":"true"}
}' | jq '{id, name, status}'
echo -e "${GREEN}Certificate created: $CERT_ID${NC}"
echo ""
# Step 5: Trigger renewal
echo -e "${YELLOW}Step 5: Triggering certificate renewal...${NC}"
curl -s -X POST $API/api/v1/certificates/$CERT_ID/renew | jq .
echo -e "${GREEN}Renewal triggered${NC}"
echo ""
# Step 6: Trigger deployment
echo -e "${YELLOW}Step 6: Triggering certificate deployment...${NC}"
curl -s -X POST $API/api/v1/certificates/$CERT_ID/deploy | jq .
echo -e "${GREEN}Deployment triggered${NC}"
echo ""
# Step 7: Check certificate status
echo -e "${YELLOW}Step 7: Checking certificate status...${NC}"
curl -s $API/api/v1/certificates/$CERT_ID | jq '{id, name, status, common_name, environment}'
echo ""
# Step 8: Check jobs
echo -e "${YELLOW}Step 8: Checking jobs...${NC}"
curl -s "$API/api/v1/jobs" | jq "[.data[] | select(.certificate_id == \"$CERT_ID\") | {id, type, status}]"
echo ""
# Step 9: View recent audit events
echo -e "${YELLOW}Step 9: Recent audit events...${NC}"
curl -s $API/api/v1/audit | jq '.data[0:3] | .[] | {action, resource_type, resource_id, timestamp}'
echo ""
# Step 10: Summary
echo -e "${BLUE}=== Demo Complete ===${NC}"
echo ""
echo "What happened:"
echo " 1. Created a team and owner for accountability"
echo " 2. Created a managed certificate tracked by certctl"
echo " 3. Triggered renewal (would contact the Local CA in production flow)"
echo " 4. Triggered deployment (would push to NGINX/F5/IIS targets)"
echo " 5. All actions recorded in the audit trail"
echo ""
echo -e "Open ${GREEN}http://localhost:8443${NC} to see everything in the dashboard."
echo "Look for certificate: $CERT_ID"
```
Make it executable and run:
```bash
chmod +x demo.sh
./demo.sh
```
## What to Show Stakeholders
If you're using this demo to present certctl to decision-makers, here's the narrative:
1. **Start with the dashboard** — "This is your certificate inventory. Every TLS certificate across your infrastructure, in one place."
2. **Point to expiring certs** — "These certificates would have caused outages. Certctl catches them automatically."
3. **Show the cert you just created** — "I just created this via the API. It's already tracked, assigned to a team, and will be renewed automatically."
4. **Show the audit trail** — "Complete traceability. Every action, every change, every deployment — timestamped and attributed."
5. **Show policies** — "Guardrails. We enforce that every certificate has an owner, uses approved CAs, and stays within allowed environments."
6. **Show agents** — "Private keys never touch the control plane. Agents handle cryptographic operations locally on your infrastructure."
7. **Show the API** — "Everything is API-first. The dashboard is just one consumer. You can integrate with CI/CD, Terraform, or custom tooling."
## Teardown
```bash
docker compose -f deploy/docker-compose.yml down -v
```