Findings are cheap.
A promoted issue is accountable.

That's the thesis behind the evidence ledger for cloud security: a self-hosted platform of five scanners that runs on your infrastructure. 2,391 checks across the platform; the 2,082 cloud CSPM checks (cloud security posture management) are each readable SQL, and every catalog is inspectable. The ledger records what each finding became (proven, decided, fixed, verified) in a database you own.

AI-assisted teams ship misconfigurations at machine speed. Every unscanned week is unrecorded risk.

Credentials come from a secrets manager, never stored in code or logs. Verified against your provider before anything is fetched.

you@your-vm:~$ kloudle ledger show ISSUE-████

Sample — redacted example,
not a customer record
ledger entry · ISSUE-████
S3 bucket Public Access Block is disabled
check: KSP10010 · S3.PublicAccessBlock · provider: AWS · S3
resource: s3://█████-assets-prod · account: ████████
severity: HIGH · frameworks: CIS 2.1.4
  1. 2026-07-28
    09:14:03Z
    01Observed Live today

    kloudle-aws-s3 reports public-access-block disabled. One line among hundreds returned by the scan — not yet a security issue.

  2. 2026-07-28
    09:14:41Z
    02Proven Live today

    evidence: bucket-policy.json acl.json public-access-block.json (snapshot at scan time)

  3. 2026-07-28
    09:31:19Z
    03Promoted Near-term

    severity: High · why: bucket reachable via public CloudFront distribution; objects written by a deploy role an agent uses

  4. 2026-07-28
    09:31:20Z
    04Recorded Near-term

    linked: KSP10179 (CloudFront origin → public-readable S3), deploy-role identity node · written to ledger

  5. 2026-07-29
    15:02:56Z
    05Human decision Near-term

    gate: allow-scoped-fix · agent scope: S3.PublicAccessBlock on this resource only · waiver alternative: 30d with reason

  6. 2026-07-29
    15:04:11Z
    06Acted from ledger Destination

    remediation: enable public-access-block · scope: this resource only · change-proof returned to ledger

  7. 2026-07-30
    03:00:37Z
    07Verified Near-term

    re-scan: S3.PublicAccessBlock → pass · status: resolved, verified

status: resolved · verified PASS append-only · recorded in your database

Every figure above is a number you can audit further down this page, or fetch /catalog.json and count for yourself.

Severity maps to CIS, NIST, and PCI-DSS benchmarks, not to a vendor score.

A dashboard is where findings go to age.

Four thousand alerts is not a security posture. It's a backlog with a color scheme.

Every CSPM can flood you with findings. None of them can tell you what happened next. What was proven. What was waived, by whom, with what expiry. What an agent was allowed to touch.

That history is the part an auditor asks for. It's the part a dashboard doesn't keep. Worse: in most tools, the record of your own risk lives in someone else's cloud, on someone else's retention policy.

A finding answers nothing. A record answers everything: what you saw, what you proved, what you decided, what you fixed, who signed off.

The ledger is the durable record of what you observed, what you proved, who decided, and what happened next.

any CSPM · findings feed4,000+ opensorted: newest
CRITS3 bucket is public to the internet · s3://█████████open · 201d
HIGHManagement ports (SSH/RDP) exposed to the world · sg-████████snoozed · 154d
HIGHS3 bucket Public Access Block is disabled · s3://█████████duplicate · 61d
MEDCloudFront origin points at public-readable S3 · ████████open · 118d
CRITGenAI agent running without a jailbreak guardrail · ████████unassigned · 97d
MEDS3 bucket is public to the internet · s3://█████████duplicate · 84d
HIGHManagement ports (SSH/RDP) exposed to the world · sg-████████snoozed · 90d
MEDCloudFront origin points at public-readable S3 · ████████open · 66d
HIGHS3 bucket Public Access Block is disabled · s3://█████████duplicate · 44d
CRITS3 bucket is public to the internet · s3://█████████open · 38d
HIGHGenAI agent running without a jailbreak guardrail · ████████unassigned · 22d
MEDManagement ports (SSH/RDP) exposed to the world · sg-████████open · 12d
what happened next: not recorded
Sample — redacted example,
not a customer record
ledger entry · ISSUE-████
Management Ports (SSH/RDP) exposed to the world in Security Groups
check: KSP10139 · provider: AWS · EC2
resource: sg-████████ (█████-staging-vpc) · account: ████████
  1. 2026-08-01
    02:00:12Z
    01Observed Live today

    kloudle-aws-ec2 reports 0.0.0.0/0 ingress on tcp/22.

  2. 2026-08-01
    02:00:58Z
    02Proven Live today

    evidence: security-group-rules.json (snapshot) — the rule itself, captured at scan time, stored in your database with pass/fail history.

status: awaiting promotion · next stage: cross-plane context Near-term

Exhibit A — the dashboard (simulated) · severity, age, silence

Exhibit B — the ledger entry · what was seen, what was proved, and whose it is

Evidence-backed findings run today. Every lifecycle stage on this page carries its badge: live, near-term, destination.

See the unit →

Seven stages. One record that answers for itself.

Append-only, nothing overwritten: the entry is the audit trail.

Every issue in the ledger carries its whole life. Each stage row shows its name, status label, and maturity badge; the one-line description sits behind a per-stage expand.

Humans set the gates. Agents work the list. Every stage transition is appended with a timestamp. A waived issue is still an accountable one.

Two of these stages run today. Four are being built on the shipping engine. One is the destination. The badges are on the page because you'd ask anyway.

Sample — redacted example,
not a customer record
ledger entry · ISSUE-████
S3 bucket Public Access Block is disabled
check: KSP10010 · S3.PublicAccessBlock · provider: AWS · S3
resource: s3://█████-assets-prod · account: ████████
severity: HIGH · frameworks: CIS 2.1.4
  1. 2026-07-28
    09:14:03Z
    01Observed Live today

    scanner output; one line among hundreds; not yet a security issue.

    kloudle-aws-s3 reports public-access-block disabled. One line among hundreds returned by the scan — not yet a security issue.

  2. 2026-07-28
    09:14:41Z
    02Proven Live today

    raw config captured at scan time: files, not scores.

    evidence: bucket-policy.json acl.json public-access-block.json (snapshot at scan time)

  3. 2026-07-28
    09:31:19Z
    03Promoted Near-term

    ranked with context from across your cloud: what the resource touches, why it matters.

    severity: High · why: bucket reachable via public CloudFront distribution; objects written by a deploy role an agent uses

  4. 2026-07-28
    09:31:20Z
    04Recorded Near-term

    linked to related risk; written to the ledger you own.

    linked: KSP10179 (CloudFront origin → public-readable S3), deploy-role identity node · written to ledger

  5. 2026-07-29
    15:02:56Z
    05Human decision Near-term

    block, allow-scoped-fix, or waive with reason and expiry; agent scope set here.

    gate: allow-scoped-fix · agent scope: S3.PublicAccessBlock on this resource only · waiver alternative: 30d with reason

  6. 2026-07-29
    15:04:11Z
    06Acted from ledger Destination

    scoped remediation with change-proof returned.

    remediation: enable public-access-block · scope: this resource only · change-proof returned to ledger

  7. 2026-07-30
    03:00:37Z
    07Verified Near-term

    re-scan result recorded against the entry.

    re-scan: S3.PublicAccessBlock → pass · status: resolved, verified

status: resolved · verified PASS append-only · recorded in your database
Sample — redacted example,
not a customer record
ledger entry · ISSUE-████
S3 bucket is public to the Internet
check: KSP10011 · provider: AWS · S3
resource: s3://█████-www-static · account: ████████
severity: MEDIUM
  1. 2026-07-30
    02:00:09Z
    01Observed Live today

    scanner output; one line among hundreds; not yet a security issue.

    kloudle-aws-s3 reports bucket public to the Internet.

  2. 2026-07-30
    02:00:47Z
    02Proven Live today

    raw config captured at scan time: files, not scores.

    evidence: bucket-policy.json acl.json (snapshot)

  3. 2026-07-30
    02:17:33Z
    03Promoted Near-term

    ranked with context from across your cloud: what the resource touches, why it matters.

    severity: Medium · why: public reachability, no sensitive-writer identities in graph

  4. 2026-07-31
    10:42:18Z
    05Human decision Near-term

    block, allow-scoped-fix, or waive with reason and expiry; agent scope set here.

    gate: waive · reason: intentional public static-site bucket · expiry: 90d, re-review on expiry · agent scope: none

status: waived (expires ████-██-██) WAIVED
the waiver, its reason, and its author are part of the durable record — a waived issue is still an accountable one

2,391 checks. Here are two. Read them.

Cloud: AWS 842. GCP 424. Kubernetes 329. Azure 313. DigitalOcean 174. All 2,082 cloud checks are SQL queries against your own resource snapshots, with no black-box detections. The other four scanners speak their own languages. Below: one check from each side of the platform, verbatim.

Don't take the number's word for it. Below is KSP10011 ("S3 bucket is public to the internet," severity Critical), exactly as it ships. It joins your bucket's ACL grants, bucket policy, and public-access-block config, and returns pass or fail with the reason in plain text. Disagree with a result? The query is the argument.

Severity maps to CIS, NIST, and PCI-DSS benchmarks, not to a vendor score. And the catalog covers the cloud you actually have, including the AI in it: KSP10139, SSH and RDP open to the world in a security group. KSP11165, a DigitalOcean GenAI agent running without a jailbreak guardrail.

The cloud engine speaks SQL. The other scanners speak Python, JSON, Rust. Five scanners, five languages, and every catalog is inspectable the same way: fetch /catalog.json and count. The second card below is horizontal_authz ("Horizontal Authorization Abuse"), from the app-and-API scanner's catalog, exactly as it ships: the preconditions, the evidence signals it looks for, the tests it will attempt, and the proof it demands before anything becomes a finding.

checks/aws-s3/bucket-public-composite.sql
S3 bucket is public to the internet
check: KSP10011 · severity: CRITICAL · resource: s3_bucket · language: SQL · mapping: checks/ksp_mapping.json
-- checks/aws-s3/bucket-public-composite.sql
-- Check: KSP10011
-- Severity: CRITICAL
-- Description: S3 buckets should not be publicly accessible (composite: ACL + policy + public access block)

SELECT
    b.arn as resource_id,
    SPLIT_PART(b.arn, ':::', 2) as resource_name,
    CASE
        -- Pass if all public access is blocked
        WHEN pab.public_access_block_configuration IS NOT NULL
             AND jdecode(pab.public_access_block_configuration)->>'BlockPublicAcls' = 'true'
             AND jdecode(pab.public_access_block_configuration)->>'BlockPublicPolicy' = 'true'
             AND jdecode(pab.public_access_block_configuration)->>'IgnorePublicAcls' = 'true'
             AND jdecode(pab.public_access_block_configuration)->>'RestrictPublicBuckets' = 'true' THEN 'pass'
        -- Fail if ACL grants to AllUsers
        WHEN acl_public.bucket_arn IS NOT NULL THEN 'fail'
        -- Fail if policy allows public access (wildcard principal in an Allow statement)
        WHEN pol.policy IS NOT NULL AND EXISTS (
            SELECT 1 FROM jsonb_array_elements(
                CASE jsonb_typeof(jdecode(pol.policy)->'Statement')
                    WHEN 'array' THEN jdecode(pol.policy)->'Statement'
                    ELSE jsonb_build_array(jdecode(pol.policy)->'Statement')
                END
            ) AS stmt
            WHERE stmt->>'Effect' = 'Allow'
              AND (stmt->>'Principal' = '*' OR stmt->'Principal'->'AWS' @> '"*"'::jsonb)
        ) THEN 'fail'
        ELSE 'pass'
    END as status,
    CASE
        WHEN pab.public_access_block_configuration IS NOT NULL
             AND jdecode(pab.public_access_block_configuration)->>'BlockPublicAcls' = 'true'
             AND jdecode(pab.public_access_block_configuration)->>'BlockPublicPolicy' = 'true'
             AND jdecode(pab.public_access_block_configuration)->>'IgnorePublicAcls' = 'true'
             AND jdecode(pab.public_access_block_configuration)->>'RestrictPublicBuckets' = 'true'
            THEN 'All public access is blocked via public access block configuration'
        WHEN acl_public.bucket_arn IS NOT NULL
            THEN 'Bucket ACL grants access to AllUsers (internet) - enable public access block or remove AllUsers grants'
        WHEN pol.policy IS NOT NULL AND EXISTS (
            SELECT 1 FROM jsonb_array_elements(
                CASE jsonb_typeof(jdecode(pol.policy)->'Statement')
                    WHEN 'array' THEN jdecode(pol.policy)->'Statement'
                    ELSE jsonb_build_array(jdecode(pol.policy)->'Statement')
                END
            ) AS stmt
            WHERE stmt->>'Effect' = 'Allow'
              AND (stmt->>'Principal' = '*' OR stmt->'Principal'->'AWS' @> '"*"'::jsonb)
        )
            THEN 'Bucket policy allows public access (Principal: *) - restrict the policy or enable public access block'
        ELSE 'Bucket is not publicly accessible'
    END as reason
FROM aws_s3_buckets b
LEFT JOIN aws_s3_bucket_public_access_blocks pab
    ON b.arn = pab.bucket_arn
LEFT JOIN (
    SELECT DISTINCT bucket_arn
    FROM aws_s3_bucket_grants
    WHERE grantee_type = 'Group'
      AND jdecode(grantee)->>'URI' = 'http://acs.amazonaws.com/groups/global/AllUsers'
) acl_public ON b.arn = acl_public.bucket_arn
LEFT JOIN aws_s3_bucket_policies pol
    ON b.arn = pol.bucket_arn;

Collapsed to the first ~14 lines — the full query is a desktop read.

63 lines · rendered verbatim from the catalog

jdecode() is a Kloudle engine UDF — it decodes snapshot JSON columns to jsonb. Ships with the catalog in database/functions.sql.

Different surface, different language, same principle: read the logic.

product_security_agents/templates/horizontal_authz.json
Horizontal Authorization Abuse
check: horizontal_authz · scanner: spa-hacking-agent · surface: apps, APIs & SPAs · language: JSON
{
  "id": "horizontal_authz",
  "family": "horizontal_authz",
  "title": "Horizontal Authorization Abuse",
  "description": "Test whether users at the same privilege level can access or mutate each other's resources.",
  "source_slug": "webapp-horizontal-authz",
  "source_title": "Horizontal Authorization",
  "preconditions": [
    "At least two identities with the same nominal role exist.",
    "Resource identifiers or ownership markers are observable."
  ],
  "evidence_signals": [
    "User or account identifiers appear in routes, requests, or GraphQL ids.",
    "Biz Logic or authz artifacts mention ownership, profile, files, settings, or transactions."
  ],
  "required_artifacts": [
    "recon-manifest.json",
    "normalized_module_outputs.json"
  ],
  "reasoning_questions": [
    "Which resources appear user-owned rather than tenant-shared?",
    "Which mutation and read paths accept user identifiers directly?",
    "What alternate auth contexts already exist for replay?"
  ],
  "suggested_tests": [
    "Replay same-role requests across two user contexts.",
    "Swap user-owned identifiers in CRUD operations.",
    "Check GraphQL field and node access for other-user ids."
  ],
  "expected_proofs": [
    "Cross-user read succeeds with same-role alternate context.",
    "Cross-user mutation succeeds without explicit delegated permission."
  ],
  "termination_conditions": [
    "No same-role identities are available to compare.",
    "Relevant surfaces are already sufficiently tested with negative results.",
    "A deterministic proof or contradiction exists."
  ],
  "preferred_modules": [
    "authz_probe",
    "retest_regression"
  ]
}

Collapsed to the first ~14 lines — the full template is a desktop read.

43 lines · rendered verbatim from the catalog · shown in full

Snapshot first. Verdict second.

On demand or on schedule. Credentials verified against the live provider before a single resource is fetched.

A scan starts by proving it should. Credentials are checked against the provider itself before anything is fetched. They come from a secrets manager: never code, never logs.

Then discovery: your assets enumerated and stored as raw snapshots. Every expected snapshot is confirmed written before evaluation begins. Only then does the full catalog run.

Every stage emits an event and the UI updates as it happens: an append-only timeline of scan events, pushed live, no polling. Out the other end: PDF and JSON reports (executive summary, detailed findings, asset inventory, compliance, audit) with templates for SOC2, ISO27001, HIPAA, and PCI-DSS.

This is snapshot-based scanning, on demand or scheduled. You set the cadence, and you can see from the timeline exactly when each verdict was taken.

What lands in your database →
scan · 2026-08-01 · account ████████pushed live · no polling
  1. 02:00:00Z
    scan started

    on schedule · snapshot runner

  2. 02:00:03Z
    credentials verified

    against the live provider · pulled from your secrets manager

  3. 02:00:05Z
    creating tasks

    split into parallel per-service tasks

  4. 02:00:09Z
    tasks created
  5. 02:00:10Z
    scanning

    assets enumerated · raw snapshots stored

  6. 02:07:41Z
    scan completed

    every expected snapshot confirmed written

  7. 02:07:43Z
    evaluating

    full catalog against the snapshot

  8. 02:09:58Z
    generating reports
  9. 02:10:46Z
    report generated

    PDF · JSON ↓

out the other end · PDF + JSON
executive-summary.pdf detailed-findings.pdf asset-inventory.json compliance.pdf audit.pdf
templates: SOC2ISO27001HIPAAPCI-DSS

The nine status strings written to the scan record — the proof bar's “9 real scan states”, receipted

Your infrastructure. Your database. Your record.

“Wiz and Datadog make money by centralizing your security data in their cloud. Kloudle makes money by keeping it in yours.”

You host every part of it. The scanner runs on your infrastructure. Every snapshot, every finding, and every future ledger entry lands in a database you administer.

When the results live in your database, retention is your policy and access is your call. Leave us, and you keep the record.

Your perimeter
Kloudle scanner
runs on your infrastructure · snapshot-based · on demand or scheduled
Your database
snapshots · findings · pass/fail history · every future ledger entry

Nothing crosses out. retention: your policy · access: your call

Your perimeter · a SaaS CSPM
Your cloud
your security data, collected —
Vendor's cloud
their database · their retention policy · their access

Self-hosted — the record stays in

Centralized SaaS — the record ships out

Book a demo — see it land in your database

Credentials come from a secrets manager, never stored in code or logs.

Humans set the gates. Agents work the list.

An MCP server (Model Context Protocol, the interface AI agents use to call tools), a CLI, and binaries ship today. Your agents read the same list your engineers do.

Your team ships at machine speed. Your misconfigurations arrive at the same speed. This is posture for that software factory. The same 2,082 cloud checks are reachable by your engineers in the UI and by your agents over MCP, CLI, and binaries, today.

The destination is further: agents acting from the promoted ledger, scoped to one resource, one fix, with change-proof returned to the record. Never from raw scanner noise. That stage carries its Destination badge: it isn't shipping yet, and the badge means you never have to guess.

Agents work the list over MCP server CLI binaries Live today
How the gate works →
ledger entry · ISSUE-████ · stages 05–06 · excerpt
05Human decision Near-term

gate: allow-scoped-fix · agent scope: this resource only

waiver alternative: 30d with reason · appended with a timestamp, part of the durable record

06Acted from ledger Destination

remediation: enable public-access-block · scope: this resource only · change-proof returned to ledger

acts from the promoted ledger · never from raw scanner noisehumans stay responsible

Five surfaces. One record.

Each labeled with what it is, not what you're being sold.

Four are live and scanning today. One is in development, with its check count printed anyway. Statuses audited 2026-08-05 against the catalogs themselves; when a status improved, the badge changed, and not before.

Questions with checkable answers.

What is an evidence ledger in cloud security?
An evidence ledger is a durable, append-only record of what a security scanner observed, what evidence supported promoting a finding to an issue, who decided what to do about it, and what happened next: fixed, waived, or verified. A dashboard shows current findings; a ledger keeps the history an auditor asks for. Kloudle coined the term for its platform: the ledger lives in a database you own.
What is self-hosted CSPM?
Self-hosted CSPM is cloud security posture management that runs on your own infrastructure instead of a vendor's cloud. The scanner, the resource snapshots, and the results all land in a database you administer, so retention is your policy and access is your call. Kloudle's self-hosted cloud scanner ships 2,082 checks for AWS, GCP, Azure, Kubernetes, and DigitalOcean, each one a readable SQL query.
How is Kloudle different from Wiz or Datadog?
"Wiz and Datadog make money by centralizing your security data in their cloud. Kloudle makes money by keeping it in yours." Kloudle runs on your infrastructure and writes every snapshot, finding, and ledger entry to your database. Leave, and you keep the record.
Can AI agents use Kloudle?
Yes. An MCP server (Model Context Protocol, the interface AI agents use to call tools), a CLI, and binaries ship today, so your agents read the same check list your engineers do. The destination is agents acting from the promoted ledger (scoped to one resource, one fix, with change-proof returned), never from raw scanner noise.
How do I verify Kloudle's claims?
Fetch /catalog.json. It is the machine-readable census of all five scanner catalogs: every check count on this page, per provider and per surface, with a counted_how receipt showing how each number was produced. A human can read it; so can an agent. Two checks are also printed verbatim on this page.

Audit us before you buy us.

You already read the SQL, and the JSON. The rest of the demo works the same way.

No logos. No testimonials. What you get instead, sitting where the badge wall usually goes, is evidence you can check yourself: 2,391 checks (two of them printed in full on this page), real check IDs, real scan states, a machine-readable /catalog.json, and a sample ledger entry that tells you it's a sample.

A demo walks a real scan's output: evidence files, PDF and JSON reports, and the ledger those results feed, badged stage by stage, so you know exactly what's live and what's next.

AI-assisted teams ship misconfigurations at machine speed. Every unscanned week is unrecorded risk.

Self-hosted. Credentials verified before anything is fetched, from a secrets manager, never code or logs.

07 Verified · re-scan: pass · status: resolved, verified