Skip to content
ConsoleMonster

TLS verdict

Follow a certificate chain from connection to proof, then act on a clear trust verdict.

Demos Reports Showcase

Use it when operators need to see where certificate trust breaks and what to repair.

  • Live
  • 120×52
  • PowerShell 5.1+
  • .NET 8.0+
TLS verdict 120×5224-bitFull

How it's built

The whole program in one file, with its sample data at the top. It drew the recording above; both languages draw the same cells.

PowerShelltls-verdict.ps1View on GitHub
# A deterministic TLS chain story. Data and timings are fictional; no hosts are contacted.
param(
    [ValidateSet('Healthy', 'ExpiredIntermediate')]
    [string] $Scenario = 'Healthy'
)
Import-Module ConsoleMonster

<# Real data plugs in here: map the output of your TLS checker to these rows.
   Keep connection results, certificate dates and revocation evidence separate.
   A successful handshake alone does not establish trust. #>
$expired = $Scenario -eq 'ExpiredIntermediate'
$chainStatus = if ($expired) { 'Fail' } else { 'Pass' }
$verdict = if ($expired) { 'UNTRUSTED' } else { 'TRUSTED' }
$hostName = if ($expired) { 'legacy.example.test' } else { 'portal.example.test' }
$steps = @(
    [pscustomobject] @{ Key = 'dns'; Label = 'Resolve DNS'; Status = 'Pass'; Detail = '203.0.113.42' }
    [pscustomobject] @{ Key = 'connect'; Label = 'Connect :443'; Status = 'Pass'; Detail = 'connected' }
    [pscustomobject] @{ Key = 'handshake'; Label = 'TLS handshake'; Status = 'Pass'; Detail = 'TLS 1.3' }
    [pscustomobject] @{ Key = 'chain'; Label = 'Validate chain'; Status = $chainStatus; Detail = $(if ($expired) { 'expired intermediate' } else { 'verified' }) }
    [pscustomobject] @{ Key = 'expiry'; Label = 'Expiry and revocation'; Status = $(if ($expired) { 'Skip' } else { 'Pass' }); Detail = $(if ($expired) { 'skipped' } else { 'valid / good' }) }
)
$context = @(
    [pscustomobject] @{ Key = 'Host'; Value = $hostName }
    [pscustomobject] @{ Key = 'IP'; Value = '203.0.113.42' }
    [pscustomobject] @{ Key = 'Protocol'; Value = 'TLS 1.3' }
    [pscustomobject] @{ Key = 'Cipher'; Value = 'AES_256_GCM_SHA384' }
    [pscustomobject] @{ Key = 'Issuer'; Value = 'Example Issuing CA' }
    [pscustomobject] @{ Key = 'Valid until'; Value = '2027-04-15 (leaf)' }
)
$nodes = @(
    [pscustomobject] @{ Kind = 'Node'; Key = 'client'; Label = 'client'; Status = 'Pass' }
    [pscustomobject] @{ Kind = 'Node'; Key = 'host'; Label = 'host:443'; Status = 'Pass' }
    [pscustomobject] @{ Kind = 'Node'; Key = 'leaf'; Label = 'leaf'; Status = 'Pass' }
    [pscustomobject] @{ Kind = 'Node'; Key = 'intermediate'; Label = 'intermediate'; Status = $chainStatus }
    [pscustomobject] @{ Kind = 'Node'; Key = 'root'; Label = 'root'; Status = $(if ($expired) { 'Skip' } else { 'Pass' }) }
)
$edges = @(
    [pscustomobject] @{ Kind = 'Edge'; Key = 'connect'; From = 'client'; To = 'host'; Label = ':443' }
    [pscustomobject] @{ Kind = 'Edge'; Key = 'tls'; From = 'host'; To = 'leaf'; Label = 'TLS' }
    [pscustomobject] @{ Kind = 'Edge'; Key = 'issuer'; From = 'leaf'; To = 'intermediate'; Label = 'issuer'; Status = $chainStatus; Cut = $expired }
    [pscustomobject] @{ Kind = 'Edge'; Key = 'anchor'; From = 'intermediate'; To = 'root'; Label = 'anchor' }
)
$flow = @($nodes; $edges) | New-ConsoleFlow -WidgetId chain
if ($expired) {
    $flow | Add-ConsoleItem {
        New-ConsoleFlowBranch -From intermediate -Label 'expired 2026-09-30' -Status Fail
    }
}
$proof = if ($expired) { 'Intermediate expired before 2026-10-25.' } else { 'All three certificates valid on 2026-10-25.' }
$action = if ($expired) { 'Renew the intermediate; serve the full chain.' } else { 'No repair needed; keep expiry monitoring on.' }
$exitCode = if ($expired) { 1 } else { 0 }

# demo: pause 1
New-ConsoleRows -Gap 1 {
    '[bold accent]TLS verdict[/]  [muted]Follow the chain. Verify the trust.[/]'
    New-ConsolePanel -Title 'Connection ledger' {
        $steps | New-ConsolePhases -StepCounter
    }
    New-ConsoleKeyValue -Columns 2 -KeyRole Accent -Content {
        foreach ($pair in $context) {
            New-ConsoleKeyValueItem -Key $pair.Key -Value $pair.Value
        }
    }
    $flow
    New-ConsoleRule -Title 'Trust decision'
    $verdictOptions = @{
        Title = $verdict
        Verdict = $true
        Status = $chainStatus
    }
    New-ConsolePanel @verdictOptions {
        New-ConsoleText -Text $(if ($expired) { 'Chain validation failed.' } else { 'Chain validation passed.' })
    }
    New-ConsoleKeyValue -KeyRole $chainStatus -Content {
        New-ConsoleKeyValueItem -Key 'Cause' -Value $(if ($expired) { 'Expired intermediate certificate.' } else { 'Trusted root and complete chain.' })
        New-ConsoleKeyValueItem -Key 'Proof' -Value $proof
        New-ConsoleKeyValueItem -Key 'Action' -Value $action
        New-ConsoleKeyValueItem -Key 'Exit' -Value "$exitCode · 184 ms"
    }
} | Show-Console
# demo: pause 6
C#Program.csView on GitHub
// A deterministic TLS chain story. Data and timings are fictional; no hosts are contacted.
using ConsoleMonster;
using ConsoleMonster.Layout;
using ConsoleMonster.Styling;
using ConsoleMonster.Widgets;

string scenario = args.Length == 0 ? "Healthy" : args[0];
if (scenario != "Healthy" && scenario != "ExpiredIntermediate") throw new ArgumentException("Use Healthy or ExpiredIntermediate.");
bool expired = scenario == "ExpiredIntermediate";
StatusKind chainStatus = expired ? StatusKind.Fail : StatusKind.Pass;
string verdict = expired ? "UNTRUSTED" : "TRUSTED";
string hostName = expired ? "legacy.example.test" : "portal.example.test";
// Real data plugs in here: map your TLS checker's connection, date and revocation evidence
// to these records. A successful handshake alone does not establish trust.
Step[] steps = {
    new("dns", "Resolve DNS", StatusKind.Pass, "203.0.113.42"),
    new("connect", "Connect :443", StatusKind.Pass, "connected"),
    new("handshake", "TLS handshake", StatusKind.Pass, "TLS 1.3"),
    new("chain", "Validate chain", chainStatus, expired ? "expired intermediate" : "verified"),
    new("expiry", "Expiry and revocation", expired ? StatusKind.Skip : StatusKind.Pass, expired ? "skipped" : "valid / good")
};
Pair[] context = {
    new("Host", hostName), new("IP", "203.0.113.42"), new("Protocol", "TLS 1.3"),
    new("Cipher", "AES_256_GCM_SHA384"), new("Issuer", "Example Issuing CA"), new("Valid until", "2027-04-15 (leaf)")
};
FlowNode[] nodes = {
    new("client", "client", StatusKind.Pass), new("host", "host:443", StatusKind.Pass),
    new("leaf", "leaf", StatusKind.Pass), new("intermediate", "intermediate", chainStatus),
    new("root", "root", expired ? StatusKind.Skip : StatusKind.Pass)
};
FlowEdge[] edges = {
    new("connect", "client", "host", ":443"), new("tls", "host", "leaf", "TLS"),
    new("issuer", "leaf", "intermediate", "issuer", chainStatus, expired), new("anchor", "intermediate", "root", "anchor")
};
var flow = new Flow("chain");
foreach (FlowNode node in nodes) flow.Add(node);
foreach (FlowEdge edge in edges) flow.Add(edge);
if (expired) flow.WithBranch(new FlowBranch("intermediate", "expired 2026-09-30", Status: StatusKind.Fail));
var phases = new Phases { StepCounter = true };
foreach (Step step in steps) phases.Set(step.Key, step.Status, step.Label, step.Detail);
var facts = new KeyValueList { Columns = 2, KeyRole = ThemeToken.Accent };
foreach (Pair pair in context) facts.Add(pair.Key, pair.Value);
string proof = expired ? "Intermediate expired before 2026-10-25." : "All three certificates valid on 2026-10-25.";
string action = expired ? "Renew the intermediate; serve the full chain." : "No repair needed; keep expiry monitoring on.";
int exitCode = expired ? 1 : 0;
Terminal.Write(new Rows(
    new TextBlock("[bold accent]TLS verdict[/]  [muted]Follow the chain. Verify the trust.[/]"),
    new Panel(phases, "Connection ledger"), facts, flow, new Rule("Trust decision"),
    new Panel(new TextBlock(expired ? "Chain validation failed." : "Chain validation passed."), verdict) { Verdict = true, Status = chainStatus },
    new KeyValueList { KeyRole = chainStatus.ToToken() }
        .Add("Cause", expired ? "Expired intermediate certificate." : "Trusted root and complete chain.")
        .Add("Proof", proof).Add("Action", action).Add("Exit", exitCode + " · 184 ms")) { Gap = 1 });

record Step(string Key, string Label, StatusKind Status, string Detail);
record Pair(string Key, string Value);

Run it yourself

Three steps in PowerShell, four in C#. Every command copies with one click.

  1. Install the module

    Once. Windows PowerShell 5.1 or PowerShell 7 on Windows, macOS or Linux.

    Install-Module ConsoleMonster -Scope CurrentUser
  2. Save the program

    Download it, or copy it from above into tls-verdict.ps1.

  3. Run it

    From the folder you saved it in.

    ./tls-verdict.ps1
  1. Create a project

    A plain console app; .NET 8 or later.

    dotnet new console -n Tlsverdict; cd Tlsverdict
  2. Add ConsoleMonster

    The only package the program needs.

    dotnet add package ConsoleMonster
  3. Replace Program.cs

    Download it, or copy the program from above.

  4. Run it

    dotnet run

Or skip the copying. Every demo ships with the module and the project template.

Show-ConsoleDemo tls-verdict
Export-ConsoleDemo tls-verdict -Path ./tls-verdict.ps1
dotnet new install ConsoleMonster.Templates
dotnet new consolemonster --demo tls-verdict -n Tlsverdict

Make it yours

Start with these, then read the rest of the file.

  1. Map your TLS checker's output to the connection and certificate rows.

  2. Replace the simulated dates and revocation result with measured evidence.

  3. Choose the action and exit code from your own trust policy.

More reports

All demos