Skip to content
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.

  1. 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 example DEVTLS_API_KEY. Only owners and admins can create keys.

  2. 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}'
  3. 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 pipefail
    API=https://devtls.fleeq.net/api
    DOMAIN_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); do
    sleep 4
    scan=$(curl -fsS "${auth[@]}" "$API/scans/$scan_id" | jq '.scan')
    status=$(jq -r '.status' <<<"$scan")
    [ "$status" = success ] || [ "$status" = failed ] && break
    done
    [ "$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")" ]; then
    echo "TLS grade $grade is below $MIN_GRADE"
    exit 1
    fi

    It exits 0 when the grade meets the bar, 1 when it’s below, and 2 when the scan itself failed.

  4. Run it from your pipeline — see below.

name: TLS check
on:
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: A
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_BRANCH

Add DEVTLS_API_KEY as a masked CI/CD variable.

  • 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.