Skip to content

ericrihm/sc-cpe

Repository files navigation

SC-CPE Certificate Sample
SC-CPE — Simply Cyber CPE Certificates
Automatic, cryptographically verifiable continuing-education certificates
for everyone who shows up to the Daily Threat Briefing.

Deploy Smoke CI Status: Live PAdES-T Signed MIT License

Verify a certificate · Leaderboard · Show links · Public profiles · Contribute

SC-CPE

Security professionals need CPE/CEU credits to keep their certifications active, but tracking attendance is manual, records are inconsistent, and there's no way for an auditor to independently verify a claim. SC-CPE fixes this — it watches the Simply Cyber Daily Threat Briefing YouTube live chat, matches per-user verification codes, and issues PAdES-T signed PDF certificates that are cryptographically verifiable offline, years later, without contacting the issuer.


What & Why

SC-CPE watches the Simply Cyber Daily Threat Briefing YouTube live chat, matches per-user verification codes, and issues signed PDF certificates worth 0.5 CPE / CEU per session. Every certificate is PAdES-T signed with an RFC-3161 timestamp and anchored to an append-only, hash-chained audit log — verifiable offline, years later, without contacting the issuer.

CPE tracking is a pain point for security professionals — manual logs, inconsistent records, and no way to independently verify attendance. Unlike traditional self-reported CPE systems, SC-CPE solves this by automating the entire pipeline from livestream attendance through signed certificate delivery, giving holders a credential that any auditor can check without trusting the issuer.

Tip

20 weekday briefings/month = 10 CPE. Enough to cover a significant chunk of most annual renewal requirements.


Supported Programs

Program Credit Per Session Submission Format
CompTIA (Security+, CySA+, CASP+, PenTest+, Network+ ...) CEU 0.5 CEU Proof-of-attendance: name, date(s), hours, provider, signature
ISC2 (CISSP, SSCP, CCSP ...) CPE 0.5 CPE (Group B) Same fields — upload under "Education"
ISACA (CISM, CISA, CRISC, CGEIT, CDPSE ...) CPE 0.5 CPE All 7 ISACA audit-evidence fields present

Acceptance is ultimately the certification body's decision — see Terms §5.

Important

ISACA 2027 update: Starting January 2027, ISACA splits CPE into certification-aligned (90 CPE min) and professional-aligned (30 CPE max). The Daily Threat Briefing covers threats, risk management, security operations, and governance — all certification-aligned domains. SC-CPE certificates already include the activity description field needed for domain-relevance verification.


Quickstart

1. Register    →  sc-cpe-web.pages.dev  (email + legal name + Turnstile)
2. Get code    →  check your email for SC-CPE{XXXX-XXXX}
3. Post code   →  paste it in YouTube live chat during the briefing
4. Get credit  →  shows on your dashboard within ~60 seconds
5. Get cert    →  per-session (~2h) or monthly bundle — your pick

Your dashboard link arrives by email from certs@signalplane.co. Lost it? Just visit /dashboard — an inline login form emails you a fresh link. You can also opt-in to "remember this device" so your dashboard loads without the URL token.


How It Works

How it works — registration through cert delivery

Detailed data flow
 1.  Register at sc-cpe-web.pages.dev
     → email + Turnstile. Dashboard link + SC-CPE{XXXX-XXXX} code arrive
       by email. The HTTP response never contains them — email possession
       is the activation gate.

 2.  Post your code in YouTube live chat during the stream.
     → The poller (every minute, 08:00-11:00 ET Mon-Fri) ingests chat,
       matches your code, and credits 0.5 CPE. Pre-stream chat and
       replays don't count. Dashboard tells you if you posted too early.

 3.  Pick cert style in the dashboard: per-session, bundled, or both.
     → Per-session certs arrive within ~2h. Bundled certs ship monthly.
       Both are PAdES-T signed.

 4.  Submit to your CE portal.
     → Upload the PDF under "webinars/seminars/training." The cert is
       the proof document.

 5.  Verify any cert at /verify.html
     → Drop the PDF on the page. SHA-256 is recomputed client-side and
       compared to the registered hash. Anyone — including auditors —
       can check without contacting us.

Architecture

Architecture — Cloudflare Edge, External services, GitHub Actions
Component Tech What it does
Pages Functions Cloudflare Pages Registration, dashboard, verify, profiles, admin API, analytics, CSV export, links archive
Poller CF Worker · * * * * * Polls YouTube live chat (OAuth + API-key fallback), matches codes, credits attendance, updates streaks, extracts show links
Email Sender CF Worker · */2 * * * * Drains email_outbox via Resend
Purge CF Worker · 0 9 * * * Daily R2 chat GC, security digest, weekly digest, cert nudge, renewal nudges, link enrichment, monthly digest, Discord webhooks
Cert Signer Python 3.11 (GH Actions) WeasyPrint render + endesive PAdES-T with RFC-3161
Chat Rescan Python (GH Actions) · daily 16:00 UTC Recovers missed attendance from chat replays
D1 Cloudflare SQLite Single source of truth — schema in db/schema.sql
R2 Cloudflare Object Storage Raw chat JSONL (purges daily) + signed PDF certs + weekly backups
KV Cloudflare KV Rate-limit counters + circuit breaker state

Tech Stack

Layer Tech
Frontend Cloudflare Pages (static HTML + JS, CSP script-src 'self')
API Cloudflare Pages Functions (V8 isolates)
Workers Cloudflare Workers (poller, purge, email-sender)
Database Cloudflare D1 (SQLite)
Storage Cloudflare R2 (certs, chat, backups)
Caching Cloudflare KV (rate limits, circuit breakers)
Email Resend (DKIM + SPF; DMARC DNS pending)
Certs Python + WeasyPrint + endesive (PAdES-T)
CI/CD GitHub Actions (13 workflows)

Certificate Integrity

Certificate integrity — four layers of verification

Note

Anyone can generate a PDF that says "attended." SC-CPE certificates are different because each one is anchored to four independent, durable pieces of evidence.

1. Time-gated attendance — The poller only credits messages whose YouTube publishedAt falls inside the live window. Pre-stream chat and replays don't count. Rejected attempts are logged and surfaced on your dashboard.

2. Hash-chained audit log — Every state transition is recorded in an append-only, SHA-256 chained table with a UNIQUE INDEX on prev_hash. Chain forks fail at insert time. verify_audit_chain.py replays the full chain.

3. PAdES-T + RFC-3161 — Certs are signed with a dedicated CA-rooted key and bound to a trusted timestamp authority. The signature outlives the key's validity period. The signing cert fingerprint is on the face of every PDF.

4. Public verify URL — Each cert carries a /verify.html?t=... link anyone can open — no login required. Drop the PDF on the page and the browser recomputes its SHA-256 client-side against the registered hash.

user_registered → code_matched → attendance_credited → cert_issued → email_sent
       ▲                                                      ▲
       └──────── prev_hash = sha256(canonicalAuditRow(tip)) ──┘

Features

Cert Delivery Options

Option Best for Delivery
Per-session CompTIA (1 activity per submission) On demand, ~2h after request
Monthly bundle ISC2 / ISACA (annual rollup) Auto-generated end of month
Both Multiple certifications Per-session + monthly

Change your preference anytime from the dashboard.

Community & Engagement

Features — Dashboard, Community, Admin, and Communications

User dashboard — Attendance calendar, streak tracking (current + longest, weekday-aware), renewal countdown, annual summary, bulk cert download, appeal flow, inline sign-in, and device-memory opt-in.

Community — Opt-in leaderboard with streak column, public profiles (privacy-safe: first name + last initial), shareable SVG badges, show links archive with RSS feed.

Admin & ops — Analytics dashboard (growth, engagement, certs, system charts), CSV export (users / attendance / certs), appeal resolution queue, feature toggles, cert reissue/revoke/resend, manual attendance grants.

Communications — Monthly digest, weekly digest, cert nudges, renewal milestone emails (50% / 75% / 90% + 30-day warning), Discord webhooks (cert announcements + weekly leaderboard top-5).


Observability

Signal Source Cadence
Poller heartbeat D1 heartbeats Every minute (during stream window)
Purge / security / digest / cert nudge / renewal nudge D1 heartbeats Daily / weekly / monthly
Email sender D1 heartbeats Every 2 minutes
Synthetic canary GH Actions smoke.yml Hourly — pings prod, writes canary heartbeat
Watchdog GH Actions watchdog.yml Every 15 min — /api/health poll, Discord alerts
Audit chain GH Actions audit-chain-weekly.yml Weekly full chain walk
Schema drift GH Actions schema-drift.yml Weekly D1-vs-schema.sql diff
Chat rescan GH Actions rescan-chat.yml Daily 16:00 UTC — recovers missed attendance
D1 backup GH Actions backup.yml Weekly Sun 06:00 UTC → R2 + GitHub Artifact
CodeQL GH Actions codeql.yml Weekly + on push

Live status: /status.html (auto-refreshes every 30s)


CI/CD Pipeline

CI/CD pipeline — push to live in ~2 min

Branch protection on main: PRs required, Node test suite + Secret scan (gitleaks) must pass, no force-push. Auto-merge enabled — gh pr merge --auto --squash lands the PR the moment checks go green.

D1 migrations in db/migrations/ are applied automatically during deploy — the pipeline tracks applied files in _applied_migrations and only runs new ones.

PR previews — Every pull request gets an isolated preview deployment with its own D1, KV, and R2 bindings. The deploy-preview.yml workflow applies migrations, seeds test data, deploys, and comments the preview URL on the PR.


API

49 endpoints — expand for full table

Public

Path Auth Purpose
POST /api/register Turnstile Sign up
POST /api/recover Turnstile Recover dashboard link via email
GET /api/health public External watchdog poll
GET /api/verify/{token} public Cert verification data
GET /api/crl.json public Certificate revocation list
GET /api/leaderboard public Community leaderboard (top 20)
GET /api/links public Show links archive
GET /api/links/rss public Show links RSS feed
GET /api/profile/{token} public Public profile (privacy-safe stats)
GET /api/badge/{token} public SVG achievement badge
GET /api/download/{token} public Cert PDF download
GET /api/preflight/channel public YouTube channel pre-check
POST /api/csp-report public CSP violation reports

User (dashboard-token + CSRF)

Path Auth Purpose
GET /api/me/{token} dashboard-token User dashboard data
POST /api/me/{token}/prefs + CSRF Set cert style, nudge opt-out, leaderboard opt-in
POST /api/me/{token}/cert-per-session/{stream_id} + CSRF Request single-session cert
POST /api/me/{token}/cert-feedback + CSRF Report cert typo/error
POST /api/me/{token}/resend-code + CSRF Get a fresh verification code
POST /api/me/{token}/appeal + CSRF Appeal missed attendance credit
POST /api/me/{token}/delete + CSRF Account deletion (GDPR)
POST /api/me/{token}/rotate + CSRF Rotate dashboard token
GET /api/me/{token}/annual-summary dashboard-token Year-at-a-glance stats

Admin (bearer token)

Path Auth Purpose
GET /api/admin/heartbeat-status bearer Per-source staleness
GET /api/admin/audit-chain-verify bearer Full chain walk
GET /api/admin/ops-stats bearer Dashboard counts + warnings
GET /api/admin/cert-feedback bearer Non-ok feedback inbox
GET /api/admin/users bearer User search
GET /api/admin/user/{id}/certs bearer Certs for a specific user
GET /api/admin/attendance bearer Attendance records
GET /api/admin/appeals bearer Pending appeals
POST /api/admin/appeals/{id}/resolve bearer Resolve appeal
POST /api/admin/cert/{id}/reissue bearer Queue cert regeneration
POST /api/admin/cert/{token}/resend bearer Resend cert email
POST /api/admin/revoke bearer Revoke a certificate
GET /api/admin/export bearer CSV export (users, attendance, certs)
GET /api/admin/security-events bearer Security event log
GET /api/admin/streams bearer Recent streams with attendance counts
POST /api/admin/suspend bearer Suspend / unsuspend a user
GET/DELETE /api/admin/email-suppression bearer Email suppression list management
GET /api/admin/analytics/growth bearer User growth time series
GET /api/admin/analytics/engagement bearer Attendance engagement metrics
GET /api/admin/analytics/certs bearer Certificate issuance stats
GET /api/admin/analytics/system bearer System health metrics
POST /api/admin/canary-beat bearer Hourly smoke heartbeat
GET/POST /api/admin/toggles bearer Feature toggles
GET /api/admin/auth/login bearer Admin OAuth login
GET /api/admin/auth/callback bearer Admin OAuth callback
GET /api/admin/auth/logout bearer Admin logout
GET /api/watchdog-state bearer Watchdog health state

Design Decisions

  • Hash-chained audit log over simple event table — append-only with SHA-256 prev_hash ensures tampering is detectable years later without a trusted third party. A UNIQUE INDEX on prev_hash serialises concurrent writers at the database level.

  • Dashboard token over password auth — a single bearer URL means no password resets, no session management, no cookie consent banners. Trade-off: URL sharing leaks access, mitigated by a separate badge_token for public-facing URLs.

  • PAdES-T over simple PDF signing — RFC-3161 timestamps make certificates verifiable even after the signing key expires. More complexity, but the cert is the product — it must stand on its own.

  • Email-queue pattern over direct send — decouples email delivery from the request path, enabling retry with idempotency, backpressure visibility via the outbox table, and a clean cursor-advance-on-success contract.


Who Runs This

Simply Cyber LLC (United States). Fully open-source at github.com/ericrihm/sc-cpe — every line that decides who gets credit, every policy doc, every deploy workflow. Branch protection + required CI + auto-deploy means the deployed code is the exact SHA on main.

Domain Purpose
sc-cpe-web.pages.dev Web + API (canonical origin)
cpe.simplycyber.io Reserved — future DNS wiring
signalplane.co Email domain (Resend DKIM + SPF; DMARC DNS pending)

Security disclosure: security.txt or email certs@signalplane.co with [SECURITY] in the subject.


Testing & Development

scripts/install_hooks.sh                 # pre-push hook → runs test suite
bash scripts/test.sh                     # pure-logic tests (388 tests)
scripts/check_schema.sh                  # diff live D1 schema vs repo
ADMIN_TOKEN=... ORIGIN=https://sc-cpe-web.pages.dev \
  scripts/smoke_hardening.sh             # read-only probe of deployed origin

Deploying

Auto-deploy on every merge to main via deploy-prod.yml: tests → D1 migrations → Pages → Workers (parallel) → post-deploy smoke. ~2 min on a warm runner.

git checkout -b fix/whatever
git commit -m "fix(scope): description"
git push -u origin fix/whatever
gh pr create --fill && gh pr merge --auto --squash

Caution

Break-glass only: enforce_admins: false allows admin direct-push to main. The push trigger still fires deploy-prod with full test suite. Re-engage protection immediately after.


Repo Map

pages/                 Cloudflare Pages Functions — public web surface
  functions/api/       JSON API (register, dashboard, admin, analytics, verify, links, profiles)
  _lib.js              Shared helpers (audit, rate-limit, email, crypto)
  _heartbeat.js        Staleness predicates for heartbeat monitoring
  _middleware.js        Security headers (CSP, HSTS, COOP, CORP)
  *.html / *.js / *.css  15 pages, extracted JS/CSS (CSP-safe, no inline scripts)
workers/
  poller/              Per-minute livestream chat poller (OAuth + API-key fallback)
  purge/               Daily R2 GC + digests + nudges + link enrichment + Discord webhooks
  email-sender/        Drains email_outbox via Resend
services/certs/        Python PDF issuer (PAdES-T + RFC-3161)
db/
  schema.sql           Authoritative schema
  migrations/          Append-only numbered migrations (auto-applied on deploy)
  seed-preview.sql     Test data for PR preview environments
scripts/               Smoke, schema check, audit verifier, tests, backups, chat rescan
.github/workflows/     13 workflows: CI, deploy (prod + preview), smoke, watchdog,
                       cert crons, schema drift, backups, chat rescan, CodeQL
docs/
  DESIGN.md            Architecture decisions
  RUNBOOK.md           Operator procedures
  LTV.md               Legal/compliance reasoning (GDPR Art. 17(3)(e))
  VERIFIER_GUIDE.md    Third-party cert verification guide
  PITCH.md             Simply Cyber team pitch

Contributing

Built for the Simply Cyber community. Contributions welcome — see CONTRIBUTING.md.

Found a bug or have feedback? Open an issue or email certs@signalplane.co.

License

MIT — see LICENSE for details. Cert artefacts are retained under GDPR Art. 17(3)(e) as evidentiary records.

About

Auto-issues CPE/CEU certificates to Simply Cyber Daily Threat Briefing YouTube livestream attendees. PAdES-T signed PDFs, hash-chained append-only audit log, Cloudflare-native (Pages Functions + D1 + R2 + Workers).

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors