The cloudsynth CLI · cloudsynth verify in CI

cloudsynth verify in CI

Take the intent file out of the playground and make it gate your own pipeline — three commands, no account, no network.

The playground shows you findings on code you paste. cloudsynth verify runs the same evaluator on your own stack, in your own pipeline, and fails the build when your infrastructure stops doing what you said it should.

It is a published npm package. There is no account, no API key, and no network call of any kind.

Three commands

npx cdk synth --all      # you already do this
npx cloudsynth init      # writes cloudsynth.intent.yml from what your stack already does
npx cloudsynth verify    # green, first try

init reads your synthesized templates and evaluates a catalog of checks against them. Rules that already hold are written as active checks — which is why the first verify passes. Rules that don't hold are written commented out, with a note saying what currently violates them:

# 2 resources currently violate this — uncomment to enforce
#   - id: buckets-block-public-access
#     description: Every bucket blocks all four forms of public access

That is your backlog, in your own file, in your own repo. Uncomment one at a time.

Wiring it up

GitHub Actions. Failures become annotations on the pull request — cloudsynth verify detects the runner and switches output format itself.

- run: npx cdk synth --all
- run: npx cloudsynth verify

Or, if a uses: step is what you reach for — it runs the same pinned CLI:

- run: npx cdk synth --all
- uses: cloudsynth-dev/verify-action@v0

GitLab CI

verify:
  script:
    - npx cdk synth --all
    - npx cloudsynth verify --format text

Any CI works. The exit code is the contract: 0 if every error-severity check holds, 1 otherwise.

Nothing leaves your runner

Synthesis happens in your pipeline and the templates are read from disk. Your infrastructure code never leaves your repository, and the tool keeps working offline, behind a proxy, and unchanged if CloudSynth disappears.

This is asserted, not promised: a test greps the built package for node:http, node:https, node:net, node:tls, node:dgram, node:dns, fetch( and WebSocket, and fails if any appear.

When a check is wrong for you

Two honest options, both of which leave a record. Deleting the check does not.

Exempt the one resource, with a reason.

Here is the real thing, from CloudSynth's own cloudsynth.intent.yml — the file that gates every deploy of this site. Four API endpoints genuinely must answer an unauthenticated request, and each says why in a sentence you can disagree with:

  - id: methods-have-auth
    description: Every non-OPTIONS API method requires authorization
    select: AWS::ApiGateway::Method
    where:
      HttpMethod:
        not: OPTIONS
    exempt:
      - match: RestApiServiceRestApistripewebhookPOST*
        reason: >-
          Stripe webhook. The caller is Stripe, which has no AWS credentials
          and no session; authenticity comes from the signature we verify on
          every request before reading the body.

      - match: RestApiServiceRestApiplaygroundrunPOST*
        reason: >-
          Anonymous playground synth. Requiring auth here would remove the
          try-before-signup path the product is built around; the handler runs
          with no IAM permissions and cannot reach anything.

      - match: RestApiServiceRestApiplaygroundshareshareIdGET*
        reason: >-
          Reading a shared playground by its unguessable id. The id is the
          capability — requiring a login to open a link someone sent you would
          defeat the feature.
    assert:
      AuthorizationType:
        not: NONE

Which reports:

PASS  Every non-OPTIONS API method requires authorization  (58 resources, 4 exempted)

Fifty-eight methods still judged, four excused on the record. Every other resource is still checked — an exemption excuses a resource, never the check. Past until the exemption stops applying and the check comes back; expiry that does not bite is decoration.

Worth knowing how we found these: until v0.3.0 this check passed against our own infrastructure, because every was combined existentially across templates and the admin API satisfying it hid the four public routes entirely. The fix is what surfaced them.

Or redefine the check in your own file, which replaces an inherited one of the same id.

Knowing whether a green run means anything

Every run reports how much of the stack any check actually looks at:

coverage: 41/80 resources examined across 6 type(s)

That number exists because "6/6 checks passed" is true of a file that examines twelve resources out of seven hundred. Name the types that must be fully covered and the gap becomes a failure rather than a footnote:

coverage:
  require:
    - AWS::Lambda::Function

The CDK is not required

Nothing in the evaluator is CDK-specific — it reads CloudFormation JSON. SAM templates and hand-written CloudFormation work identically.

Full reference: cloudsynth on npm.