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 textAny 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: NONEWhich 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::FunctionThe 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.