Sign inSign up

techblog/go-certi

By techblog

Updated 4 months ago

Image
0

542

techblog/go-certi repository overview

go-certi

go-certi is a Go rewrite and significant feature expansion of the original Python t0mer/certi project — an SSL Certificate Transparency log monitor that tracks certificates issued for domains you care about and alerts you when something new appears.


Overview

go-certi watches Certificate Transparency logs for your domains. Every time a new certificate is issued — by Let's Encrypt, a commercial CA, or anyone else — go-certi discovers it, stores it, and notifies you through the channel of your choice. Runs as a single statically-linked binary with an embedded React web UI.


Features

  • CT Log Monitoring — Fetches certificates from sslmate Cert Spotter (primary) with crt.sh as fallback. Supports anonymous access; an optional API key unlocks higher rate limits.
  • Scheduler — Per-domain or global cron-based scan schedule (@every 2h, @daily, standard cron expressions, etc.).
  • Configurable Notification Events — Choose which events trigger alerts per domain:
    • 🆕 New certificate issued — fires when a previously unseen certificate appears in CT logs
    • Expiring soon — fires when a cert will expire within a configurable number of days (default: 10)
    • Expired — fires when a cert has passed its expiry date
    • 🚫 Revoked — fires when a cert is marked as revoked in the CT log
    • 🔄 CA changed — fires when a newly issued cert comes from a different Certificate Authority than the previous one
  • Notification Channels — Pluggable, reusable targets that multiple domains can share:
  • Certificate Details — Stores and displays CN, SANs, issuer (friendly name + full DN), issue date, expiry date with countdown, revocation status, and CT source.
  • Web UI — Mobile-first React + Tailwind UI with light/dark/system theme. All data accessible without any tooling.
  • REST API — Full OpenAPI 3 / Swagger spec at /swagger/index.html.
  • Auth — Optional UI login (JWT, bcrypt) and optional API token protection. Both off by default for homelab use.
  • Single binary — Embedded SQLite database (no CGO — pure Go), embedded frontend. No external dependencies at runtime.
  • Multi-arch — Cross-compiled for linux/amd64, linux/arm64, linux/armv7, linux/armhf, linux/arm, windows/amd64, windows/arm64.
  • Docker — Multi-stage distroless image for linux/amd64, linux/arm64, linux/arm/v7.

Screenshots

Dashboard

Overview of monitored FQDNs, total certificates discovered, notification channels, and schedules. Recent certificates are listed with issuer and expiry.

Dashboard


FQDNs

Monitor multiple domains. Each domain shows the number of configured channels and active notification events. Click ⚙ to open the configuration panel, where you can select notification channels and choose which events trigger alerts.

FQDNs


FQDN Notification Configuration

Click the ⚙ gear button on any FQDN to configure:

  • Channels — select which notification channels to use for this domain
  • Notify on — choose any combination of: New certificate, Expiring soon (with configurable day threshold), Expired, Revoked, CA changed

Certificates

Paginated list of all discovered certificates. Shows subject CN, issuer (friendly name + full DN), issue date → expiry date with relative countdown, SANs, source, and a Revoked badge when applicable. Filter by FQDN or search by CN/SAN/CA.

Certificates


Notification Channels

Create and manage reusable notification channels. Each channel has a Test button to verify delivery, and a ✏ Edit button to update the name or configuration at any time.

Channels


Schedules

Define cron-based scan schedules. Supports robfig/cron syntax: @every 1h, @daily, 0 */4 * * *, etc. Mark one as the default — FQDNs without a custom schedule inherit it.

Schedules


Settings
  • Theme — Light / Dark / System
  • sslmate API key — leave blank to use crt.sh (no key required)
  • UI Authentication — Require login with username + password
  • API Token Protection — Require Authorization: Bearer <token> on all API requests; rotate token at any time

Settings


Login

Shown when UI authentication is enabled. Issues a signed JWT in an HttpOnly cookie on success.

Login


Swagger / API Docs

Interactive OpenAPI 3 documentation for all endpoints, available at /swagger/index.html.

Swagger


Quick Start

Binary

Download the latest release for your platform from the GitHub Releases page.

# Example: Linux amd64
curl -L https://github.com/t0mer/go-certi/releases/latest/download/go-certi_linux_amd64 -o go-certi
chmod +x go-certi
./go-certi

Open http://localhost:8111 in your browser.

Docker
docker run -d \
  --name go-certi \
  -p 8111:8111 \
  -v go-certi-data:/data \
  techblog/go-certi:latest
Docker Compose
services:
  go-certi:
    image: techblog/go-certi:latest
    ports:
      - "8111:8111"
    volumes:
      - go-certi-data:/data
    environment:
      GO_CERTI_SSLMATE_API_KEY: "your-key-here"  # optional
    restart: unless-stopped

volumes:
  go-certi-data:

Configuration

All settings can be provided as CLI flags or environment variables. Environment variables always win over flags — ideal for container deployments.

FlagEnv varDefaultDescription
--portGO_CERTI_PORT8111HTTP server port
--confGO_CERTI_CONF$XDG_CONFIG_HOME/go-certiConfig + database directory
--sslmate-api-keyGO_CERTI_SSLMATE_API_KEY(none)sslmate Cert Spotter API key
--reset-passwordGO_CERTI_RESET_PASSWORDGenerate a new password, print it, exit
--reset-api-tokenGO_CERTI_RESET_API_TOKENGenerate a new API token, print it, exit
--service <action>GO_CERTI_SERVICEManage as a system service. See Running as a System Service
--versionPrint version and exit

Config and the SQLite database are stored in the --conf directory:

<conf>/
├── config.json      # Runtime config
└── go-certi.db      # SQLite database (WAL mode)
Resetting credentials offline
# Reset the login password
./go-certi --conf /data --reset-password

# Reset the API token
./go-certi --conf /data --reset-api-token

Running as a System Service

go-certi can register itself as a native system service on Linux (systemd / SysV / Upstart), Windows (Service Control Manager), and macOS (launchd) via the --service flag. The service starts automatically on boot and restarts on failure.

Linux (systemd)
# Install — runs the binary at its current path with these args
sudo ./go-certi --service install --conf /var/lib/go-certi --port 8111

# Enable auto-start on boot
sudo systemctl enable go-certi

# Check status / logs
sudo systemctl status go-certi
sudo journalctl -u go-certi -f

# Uninstall
sudo ./go-certi --service uninstall
Windows

Open an Administrator PowerShell or CMD:

# Install (uses the binary's current location)
.\go-certi.exe --service install --conf C:\ProgramData\go-certi --port 8111

# Status
sc query go-certi

# Uninstall
.\go-certi.exe --service uninstall
Supported --service actions
ActionDescription
installRegister the service with the OS and start it. The current binary path and --conf / --port values are baked into the service definition.
uninstallStop and remove the service.
startStart the service.
stopStop the service.
restartRestart the service.
statusPrint whether the service is running, stopped, or unknown.
Notes
  • Move the binary first. The service definition stores the absolute path of the binary at install time. Move or rename the binary and the service will break — uninstall and reinstall to fix.
  • Permissions. install and uninstall require root on Linux/macOS and Administrator on Windows.
  • Service user. Defaults to root on Linux/macOS and LocalSystem on Windows. To run as a different user, edit the unit file after install or pre-create one manually.
  • Data directory. Make sure the --conf path is absolute and writable by the service user. The directory is created on first run if it doesn't exist.

Notification Events

Each FQDN can be configured to fire notifications on any combination of events:

EventDescriptionDedup window
new_certA certificate not previously seen has appeared in CT logsPer-cert (fires once per new cert)
expiring_soonA cert will expire within the configured threshold24 hours per cert
expiredA cert has passed its not_after date24 hours per cert
revokedA cert is marked revoked24 hours per cert
ca_changedA new cert uses a different Certificate Authority than the previous one24 hours per cert

The expiry threshold (default: 10 days) is configurable per domain from the ⚙ panel.

Notifications are best-effort — a failing channel is logged and never blocks a scan or takes down the scheduler.


Notification Channel Config

Shoutrrr (Telegram, Slack, Discord, email, and more)
{ "url": "telegram://token@telegram?chats=123456789" }

See the Shoutrrr service docs for all supported services and URL formats.

GreenAPI (WhatsApp)
{
  "instance_id": "your-instance-id",
  "api_token_instance": "your-api-token",
  "chat_id": "[email protected]",
  "api_url": "https://api.green-api.com"
}
WaWeb (WhatsApp via go-whatsapp-web-multidevice)
{
  "base_url": "http://your-waweb-host:3000",
  "phone": "+972501234567",
  "auth": "basic dXNlcjpwYXNz"
}

API Reference

Full interactive documentation is available at /swagger/index.html.

Base path: /api/v1

MethodEndpointDescription
GET / POST/fqdnsList / create FQDNs
GET / PUT / DELETE/fqdns/:idGet / update / delete FQDN
POST/fqdns/:id/scanTrigger immediate CT scan
GET/certificatesList certificates (?fqdn=, ?page=, ?page_size=)
GET/certificates/:idGet certificate
GET/certificates/casList distinct certificate authorities
GET / POST/channelsList / create notification channels
GET / PUT / DELETE/channels/:idGet / update / delete channel
POST/channels/:id/testSend a test notification
GET / POST/schedulesList / create schedules
GET / PUT / DELETE/schedules/:idGet / update / delete schedule
GET / PUT/settingsGet / update application settings
POST/settings/api-token/rotateRotate the API token
POST/auth/loginLogin — returns JWT cookie + token
POST/auth/logoutClear session cookie
GET/auth/meCurrently authenticated username
GET/healthzLiveness probe (no auth, always 200)
GET/readyzReadiness probe (DB ping)
FQDN fields
FieldTypeDescription
fqdnstringDomain name to monitor
include_subdomainsboolAlso scan *.domain
enabledboolPause/resume monitoring
notifications_enabledboolMaster toggle for all notifications
channel_ids[]stringIDs of notification channels to use
notification_events[]stringEvents to notify on (see table above)
expiry_threshold_daysintDays before expiry to trigger expiring_soon
schedule_idstring?Override the global default scan schedule
Authentication

When API Token Protection is enabled, pass the token as a Bearer header:

Authorization: Bearer <token>

When UI Authentication is enabled, the JWT from /auth/login can also be used as a Bearer token for programmatic access.


Building from Source

Prerequisites: Go 1.25+, Node.js 20+

git clone https://github.com/t0mer/go-certi
cd go-certi

# Build frontend
cd web && npm install && npm run build && cd ..

# Build binary (embeds the frontend)
go build -o go-certi ./cmd/go-certi

# Run
./go-certi --conf ./data
Cross-compile all platforms
VERSION=1.0.0 BUILD_MODE=prod bash scripts/build.sh
# Binaries written to dist/
Run tests
go test ./... -race
Regenerate Swagger docs
go tool swag init -g cmd/go-certi/main.go -d .,internal/api -o docs/
Regenerate DB query code
go run github.com/sqlc-dev/sqlc/cmd/sqlc@latest generate

Tech Stack

LayerTechnology
LanguageGo 1.25+
HTTP frameworkGin
DatabaseSQLite via modernc.org/sqlite (pure Go, no CGO)
DB queriessqlc (type-safe generated code)
MigrationsHand-rolled embedded runner (go:embed)
Schedulerrobfig/cron/v3
Notificationscontainrrr/shoutrrr + GreenAPI HTTP + WaWeb HTTP
CT sourcessslmate Cert Spotter API + crt.sh fallback
Authgolang-jwt/jwt/v5 + golang.org/x/crypto/bcrypt
API docsswaggo/swag (OpenAPI 3 / Swagger UI)
FrontendReact 18 + Vite + TypeScript + Tailwind CSS + shadcn/ui
Logginglog/slog (structured, stdlib)

Credits

Inspired by the original t0mer/certi Python project by Tomer Klein.


License

See LICENSE.

Tag summary

Content type

Image

Digest

sha256:addd8df96

Size

13.6 MB

Last updated

4 months ago

docker pull techblog/go-certi