devtls
Gate CI on the grade
Any CI system that can run curl and jq can ask devtls for a fresh scan and fail when the grade is too low. You need a paid plan for API access.
-
Create an API key. In Settings → API keys, create a key named after where it’ll be used (
github-actions). The full key —dtls_…— is shown once: store it as a CI secret, for exampleDEVTLS_API_KEY. Only owners and admins can create keys. -
Find the domain’s ID by listing your domains:
Terminal window curl -fsS -H "authorization: Bearer $DEVTLS_API_KEY" \https://devtls.fleeq.net/api/domains | jq '.domains[] | {id, host, port, label}' -
Add the check script to your repository as
scripts/tls-check.sh:#!/usr/bin/env bash# Scan one devtls domain and fail when its grade is below MIN_GRADE.set -euo pipefailAPI=https://devtls.fleeq.net/apiDOMAIN_ID=${DEVTLS_DOMAIN_ID:?set DEVTLS_DOMAIN_ID}MIN_GRADE=${MIN_GRADE:-A}auth=(-H "authorization: Bearer ${DEVTLS_API_KEY:?set DEVTLS_API_KEY}")# Start a scan: devtls answers 202 with the queued scan.scan_id=$(curl -fsS "${auth[@]}" -X POST "$API/domains/$DOMAIN_ID/scan" | jq -r '.scan.id')# Wait for it to finish.for _ in $(seq 1 30); dosleep 4scan=$(curl -fsS "${auth[@]}" "$API/scans/$scan_id" | jq '.scan')status=$(jq -r '.status' <<<"$scan")[ "$status" = success ] || [ "$status" = failed ] && breakdone[ "$status" = success ] || { echo "Scan $status: $(jq -r '.error // "timed out"' <<<"$scan")"; exit 2; }grade=$(jq -r '.grade' <<<"$scan")echo "TLS grade: $grade ($(jq -r '.score' <<<"$scan")/100)"rank() { case "$1" in A+) echo 6;; A) echo 5;; B) echo 4;; C) echo 3;; D) echo 2;; *) echo 1;; esac; }if [ "$(rank "$grade")" -lt "$(rank "$MIN_GRADE")" ]; thenecho "TLS grade $grade is below $MIN_GRADE"exit 1fiIt exits
0when the grade meets the bar,1when it’s below, and2when the scan itself failed. -
Run it from your pipeline — see below.
GitHub Actions
Section titled “GitHub Actions”name: TLS checkon: deployment_status: schedule: - cron: '0 7 * * 1'jobs: tls: if: github.event_name == 'schedule' || github.event.deployment_status.state == 'success' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: bash scripts/tls-check.sh env: DEVTLS_API_KEY: ${{ secrets.DEVTLS_API_KEY }} DEVTLS_DOMAIN_ID: '42' MIN_GRADE: AGitLab CI
Section titled “GitLab CI”tls-check: stage: .post image: alpine:3.22 before_script: - apk add --no-cache bash curl jq script: - bash scripts/tls-check.sh variables: DEVTLS_DOMAIN_ID: '42' MIN_GRADE: A rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHAdd DEVTLS_API_KEY as a masked CI/CD variable.
Good to know
Section titled “Good to know”- Each CI scan counts toward your monthly allowance. On a busy repository, scan after deploys to production rather than on every push.
- Scans from one organization are limited to about 10 a minute; parallel jobs scanning many domains should stagger.
- Revoke a key in Settings → API keys at any time; it stops working immediately.