Skip to the content.

User Manual

This manual covers day-to-day usage of tlsanalyzer.

Installation

The easiest path is to download a binary from the GitHub releases page.

Linux users can also install the .deb or .rpm packages from the same release page. These packages install the binary and the tlsanalyzer(1) man page:

sudo dpkg -i tlsanalyzer_*.deb
sudo rpm -i tlsanalyzer-*.rpm
man tlsanalyzer

Release tags also publish a minimal multi-arch container image to GitHub Container Registry:

docker run --rm ghcr.io/olelbis/tlsanalyzer:v0.31.3 --host example.com --no-clear
docker run --rm ghcr.io/olelbis/tlsanalyzer:latest --host example.com --policy modern --no-clear

The image supports linux/amd64 and linux/arm64, includes CA certificates for normal certificate validation and is published with registry SBOM and provenance attestations.

Homebrew and Windows package-manager support are not published yet. For now, macOS users should use the release binaries or the container image, and Windows users should use the release .exe assets. A dedicated Homebrew tap and Scoop or WinGet manifests are the next installation-channel candidates.

You can also build from source:

CGO_ENABLED=0 go build -v -ldflags="-X 'github.com/olelbis/tlsanalyzer/build.Version=$(cat VERSION)' -X 'github.com/olelbis/tlsanalyzer/build.BuildUser=Team tlsanalyzer' -X 'github.com/olelbis/tlsanalyzer/build.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)'" -o tlsanalyzer .

Or use the build script:

./scripts/build.sh
./scripts/build.sh --all

Release Verification

Each release includes checksums.txt, an SPDX SBOM and GitHub artifact attestations.

Verify downloaded files with SHA256:

sha256sum --ignore-missing -c checksums.txt

Verify provenance attestations with the GitHub CLI:

gh attestation verify tlsanalyzer-linux-amd64 --repo olelbis/tlsanalyzer

Container provenance is attached to the GHCR image by the release workflow.

Go Package Usage

The CLI is backed by the reusable analyzer package:

opts := analyzer.DefaultOptions("example.com")
opts.MinVersion = tls.VersionTLS12
opts.PolicyConfig = policy.Config{Name: policy.NameModern}

result, err := analyzer.Run(opts, analyzer.Hooks{})

Use analyzer.DefaultOptions for CLI-like defaults, then override host, SNI, timeout, minimum TLS version and policy settings as needed. Operational target failures are represented in result.Results with scan statuses such as network_error, timeout and handshake_error; Run returns an error only for caller hook failures, wrapped as *analyzer.HookError.

The preview tlsprobe package exposes the raw TLS 1.3 cipher probe:

results, err := tlsprobe.ProbeTLS13CipherSuites(context.Background(), tlsprobe.Options{
	Address:    net.JoinHostPort("example.com", "443"),
	ServerName: "example.com",
	Timeout:    5 * time.Second,
}, tlsprobe.SupportedTLS13CipherSuites())

tlsprobe sends ClientHello-only probes and classifies the first useful server response. It does not complete full TLS handshakes. The package also exposes selected key share group, HelloRetryRequest retry and TLS alert-code metadata when available. See TLS probe Go package for status values and current limits.

Basic Usage

tlsanalyzer --host example.com

Scan a different port:

tlsanalyzer --host example.com --port 8443

Scan an IP address or load balancer while sending a specific SNI name:

tlsanalyzer --host 203.0.113.10 --sni example.com

Start from TLS 1.2:

tlsanalyzer --host example.com --min-version 1.2

Use a JSON config file:

tlsanalyzer --config tlsanalyzer.json --target production --profile modern-ci

Flags

  --config string
        JSON config file
  --target string
        Named target from --config
  --profile string
        Named policy profile from --config
  --host string
        Hostname or server IP to scan (required unless provided by --config)
  --port string
        TLS server port (default "443")
  --sni string
        TLS Server Name Indication and certificate validation name
  --targets-file string
        JSON file with targets to scan in batch mode
  --concurrency int
        Maximum concurrent targets in batch mode, capped at 64 (default 4)
  --retries int
        Retry count for transient network failures
  --retry-backoff int
        Base retry backoff in seconds (default 1)
  --timeout int
        Connection timeout in seconds (default 5)
  --min-version string
        Minimum TLS version to test: 1.0, 1.1, 1.2 or 1.3 (default "1.0")
  --cert
        Print certificate chain
  --output string
        File to save PEM output to; only used with --cert
  --checkcert
        Print days until certificate expiration
  --force-ciphers
        Force cipher suites during TLS 1.0, 1.1 and 1.2 scans
  --skip-verify
        Skip certificate validation and report TLS handshake support only
  --json
        Write scan result as JSON to stdout
  --no-clear
        Do not clear the terminal before scanning
  --compact
        Use compact human-readable console output
  --version
        Print version information and exit
  --policy string
        Policy to evaluate: modern
  --fail-on string
        Comma-separated checks that fail the run: legacy-tls, weak-cipher, invalid-cert, expired-cert
  --require-tls string
        Comma-separated TLS versions that must be supported, such as 1.3
  --forbid-tls string
        Comma-separated TLS versions that must not be supported, such as 1.0,1.1
  --require-alpn string
        Comma-separated ALPN protocols that supported handshakes must negotiate
  --forbid-alpn string
        Comma-separated ALPN protocols that must not be negotiated
  --min-cert-key-bits int
        Minimum certificate public key size in bits
  --min-cert-days int
        Minimum number of days before certificate expiry
  --markdown string
        Write scan result to a Markdown file
  --sarif string
        Write policy findings to a SARIF file
  --junit string
        Write scan and policy results to a JUnit XML file

Common Workflows

Human-readable scan

tlsanalyzer --host example.com --no-clear

Use --no-clear when running in terminals, logs or CI systems where clearing the screen is unwanted.

The verbose per-version output prints the negotiated cipher suite once and labels it as the cipher selected in that handshake. Additional cipher lists are shown only when the scanner has probed, raw-probed or observed extra cipher evidence. The console output ends with a compact summary covering supported TLS versions, protocol findings, certificate validation status and cipher findings. Cipher findings include the evidence mode, such as negotiated, probed, raw-probed, observed or mixed evidence. When TLS 1.3 raw probe evidence is available, the summary also reports the supported raw-probed cipher count and makes clear that the evidence is ClientHello-only ServerHello evidence, not a full TLS handshake. Cipher severity is version-aware, so CBC cipher suites negotiated on TLS 1.0 or TLS 1.1 are reported as legacy CBC findings.

Use --compact for shorter human-readable output while preserving the final summary:

tlsanalyzer --host example.com --compact

tlsanalyzer offers h2 and http/1.1 through ALPN to observe the negotiated TLS application protocol. It does not send HTTP requests or evaluate non-TLS HTTP behavior.

See output-examples.md for small examples that explain negotiated, probed, raw-probed and observed evidence.

JSON config file

tlsanalyzer can read repeatable scan settings from a JSON file without adding runtime dependencies:

{
  "target": "production",
  "profile": "modern-ci",
  "timeout": 5,
  "min_version": "1.2",
  "json": true,
  "sarif": "tlsanalyzer.sarif",
  "junit": "tlsanalyzer.xml",
  "no_clear": true,
  "targets_file": "targets.json",
  "concurrency": 4,
  "retries": 2,
  "retry_backoff": 1,
  "targets": {
    "production": {
      "host": "example.com",
      "port": "443",
      "sni": "example.com"
    }
  },
  "profiles": {
    "modern-ci": {
      "policy": "modern",
      "require_tls": "1.3",
      "forbid_tls": "1.0,1.1",
      "min_cert_days": 30
    }
  }
}

Run it with:

tlsanalyzer --config tlsanalyzer.json

Use --target or --profile to select a different named entry from the same file:

tlsanalyzer --config tlsanalyzer.json --target staging --profile baseline

CLI flags override values loaded from the config file:

tlsanalyzer --config tlsanalyzer.json --target production --host override.example.com

Unknown JSON fields are rejected so configuration typos fail early.

Batch scans

Use --targets-file to scan multiple endpoints in one run. The file can be a JSON array:

[
  {"host": "example.com"},
  {"host": "203.0.113.10", "port": "443", "sni": "example.com"}
]

It can also be wrapped in a targets object:

{
  "targets": [
    {"host": "example.com", "port": "443"}
  ]
}

Run a batch scan with bounded concurrency and retry controls:

tlsanalyzer --targets-file targets.json --concurrency 4 --retries 2 --retry-backoff 1 --json

Batch mode supports human summaries, aggregate JSON output, SARIF and JUnit XML:

tlsanalyzer --targets-file targets.json --policy modern --sarif tls.sarif --junit tls.xml

Retries are only applied when a target produces transient network_error or timeout scan statuses. Backoff is linear, so --retry-backoff 2 waits 2 seconds before the first retry, 4 seconds before the second retry and so on.

If a target omits port, the global --port value is used. If a target omits sni, the global --sni value is used when present.

Unknown fields in target files are rejected so typos fail early. Batch concurrency is capped at 64 workers to keep local resource usage predictable.

--cert and --markdown are currently single-target features and are rejected together with --targets-file.

CI report formats

Write SARIF output for security dashboards:

tlsanalyzer --host example.com --policy modern --sarif tlsanalyzer.sarif

SARIF output uses version 2.1.0 and reports enabled policy failures and scan execution errors as SARIF results. A passing policy with no scan execution errors still produces a valid SARIF file with no results.

Write JUnit XML output for CI test report views:

tlsanalyzer --host example.com --policy modern --junit tlsanalyzer.xml

JUnit output includes one testcase per scanned TLS version. Network, timeout and handshake execution errors are reported as JUnit errors, while policy failures are reported as JUnit failures.

Markdown report

tlsanalyzer --host example.com --markdown example.com.md

Markdown reports include:

See example-report.md for a sample report.

JSON output

tlsanalyzer --host example.com --json

JSON output is intended for scripts and automation. It includes:

The current JSON schema version is 1.1. See json-schema-v1.md for the field contract. Additive fields may be introduced in later minor releases. Removing or renaming existing fields should be treated as a breaking schema change.

Certificate chain

Print the certificate chain:

tlsanalyzer --host example.com --cert

Save the PEM chain to a file:

tlsanalyzer --host example.com --cert --output example.pem

The saved file is prefixed by TLS version, for example TLS1.2_example.pem.

When using JSON output, --cert requires --output so PEM data does not mix with JSON on stdout:

tlsanalyzer --host example.com --json --cert --output example.pem

Certificate expiration

tlsanalyzer --host example.com --checkcert

This prints days until certificate expiration in the human-readable output and includes expiration data in Markdown and JSON reports.

Certificate validation behavior

By default, certificate validation is enabled and reported separately from TLS protocol support.

This means a server can be reported as supporting TLS 1.2 while its certificate is reported as invalid.

Use --skip-verify only when you intentionally want to inspect the TLS handshake without validating certificate trust:

tlsanalyzer --host expired.example.com --skip-verify

When certificate policy checks are enabled with --policy modern or --fail-on invalid-cert, skipped or unavailable certificate validation fails the policy.

Cipher probing

tlsanalyzer --host example.com --force-ciphers

For TLS 1.0, 1.1 and 1.2, tlsanalyzer can force individual cipher suites to probe support.

By default, tlsanalyzer reports the cipher suite negotiated by the normal TLS handshake. Full cipher probing runs when --force-ciphers is used, or when a policy check needs cipher evidence, such as --policy modern or --fail-on weak-cipher.

Go does not allow forcing individual TLS 1.3 cipher suites through tls.Config.CipherSuites. When cipher probing is enabled for TLS 1.3, tlsanalyzer uses its preview tlsprobe raw probe to send minimal ClientHello messages with one TLS 1.3 cipher suite at a time. The probe reads ServerHello, HelloRetryRequest, alert, close, timeout or inconclusive outcomes and records per-cipher status without completing the full TLS handshake.

If the raw probe cannot confirm support, tlsanalyzer falls back to observed handshakes so the report still includes the negotiated TLS 1.3 evidence.

JSON and Markdown output distinguish:

Raw probe status rows may include:

Policy mode

Evaluate a built-in policy:

tlsanalyzer --host example.com --policy modern

The modern policy fails the run when it detects:

Because the modern policy includes weak cipher checks, it automatically enables cipher probing.

Because the modern policy includes certificate checks, certificate validation must succeed. Invalid, skipped or unavailable validation fails the policy.

Use targeted checks without a named policy:

tlsanalyzer --host example.com --fail-on legacy-tls,weak-cipher

The weak-cipher check also enables cipher probing.

Available checks are:

Use configurable policy gates for stricter environments:

tlsanalyzer --host example.com --require-tls 1.3 --forbid-tls 1.0,1.1 --require-alpn h2 --min-cert-key-bits 2048 --min-cert-days 30

Configurable policy flags are evaluated together with --policy and --fail-on when they are present:

Interpreting Scan Status

Each TLS version has a scan status:

Certificate validation is reported separately:

Exit Behavior

Code Meaning
0 Scan completed successfully and enabled policy checks passed.
1 Invalid input, scan setup failure, report write failure, certificate output failure or target-level scan execution failure.
2 CLI flag parsing failed.
3 Scan completed but enabled policy checks failed.

Unsupported TLS versions are scan results, not CLI failures.

For single-target scans, input validation reports all detected input problems in one response when possible, while preserving the documented exit codes. The special --host is required case still prints usage help.

A target-level scan execution failure means every attempted TLS version ended with network_error, timeout or handshake_error. Mixed results are still reported as scan evidence; use policy gates when a specific TLS posture must fail the run.

See compatibility-policy.md for the preview stability promises around exit codes, JSON schema v1, policy behavior and TLS 1.3 evidence labels.

Operational Notes