Skip to content

feat(cli): gRPC/SSL/Traceroute typed result rendering + IaC constructs#1362

Open
danielpaulus wants to merge 9 commits into
mainfrom
feat/cli-pertype-result-rendering
Open

feat(cli): gRPC/SSL/Traceroute typed result rendering + IaC constructs#1362
danielpaulus wants to merge 9 commits into
mainfrom
feat/cli-pertype-result-rendering

Conversation

@danielpaulus

@danielpaulus danielpaulus commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

This branch now covers two related pieces of CLI support for the new uptime monitor types (gRPC, SSL, Traceroute):

1. Typed result/report rendering (original scope)

  • Renders typed result output for gRPC, SSL, and traceroute checks in CLI reporters.
  • Adds per-type output formatting and tests for the new diagnostics payloads.

2. IaC constructs (added)

Implements the missing checkly/constructs classes so users can author and deploy the new monitor types, and so backend CLI export templates that import { GrpcMonitor, SslMonitor, TracerouteMonitor } from 'checkly/constructs' compile.

  • GrpcMonitor (grpc-monitor.ts, grpc-request.ts, grpc-assertion.ts + codegen) — checkType: 'GRPC', top-level degradedResponseTime/maxResponseTime (≤30000). Request mirrors the public API: url, port, ipFamily, skipSSL, timeout, grpcConfig { mode, tls, storeResponseBody, serviceDefinition, method, protoContent, message, service, metadata }, assertions. GrpcAssertionBuilder: responseTime, statusCode, healthCheckStatus, responseMessage, responseMetadata.
  • SslMonitor (ssl-monitor.ts, ssl-request.ts, ssl-assertion.ts + codegen) — checkType: 'SSL'. Response-time limits live inside sslConfig (degradedResponseTimeMs/maxResponseTimeMs), not at the top level. Models serverName, skipChainValidation, handshakeTimeoutMs, alertDaysBeforeExpiry, securityBaseline, clientCertificateMode + sslClientCertificateId. SslAssertionBuilder covers cert expiry/trust/hostname/TLS version/cipher/key size/etc.
  • TracerouteMonitor (traceroute-monitor.ts, traceroute-request.ts, traceroute-assertion.ts + codegen) — checkType: 'TRACEROUTE', top-level degradedResponseTime/maxResponseTime (≤30000). Request: url, protocol, port, ipFamily, maxHops, maxUnknownHops, ptrLookup, timeout, assertions. port is dropped from generated code for ICMP probes (matches the backend strip). TracerouteAssertionBuilder: responseTime, hopCount, packetLoss.

Wiring:

  • Exports added to constructs/index.ts.
  • Import/codegen wired in constructs/check-codegen.ts (GRPC → GrpcMonitorCodegen, SSL → SslMonitorCodegen, TRACEROUTE → TracerouteMonitorCodegen).
  • GRPC/SSL/TRACEROUTE added to constants.CheckTypes.

Request shapes were verified against checkly-go-sdk types and the terraform-provider resource schemas, and the synthesized payloads are byte-identical to the previously parity-validated create bodies.

3. Deploy fix for uptime-only projects (T65)

  • Adds Bundler.isEmpty and skips the archive.store() / "Uploading Playwright tests" step when no files were registered for bundling.
  • This prevents uptime-only GRPC/SSL/TRACEROUTE deploys from uploading an empty code bundle to /next/checkly-storage/upload-code-bundle.
  • Playwright/browser projects still use the upload path when files are registered.

Feature flags for the backend: GRPC_CHECKS, SSL_CHECKS, TRACEROUTE_CHECKS.

AI-context reference pages (configure-*-monitors.md) were intentionally not added: they are optional, not in the acceptance criteria, and adding partial pages would risk the skills/checkly/SKILL.md sync CI check. Running prepare:ai-context produced no drift in the published skill.

Testing

  • T65 local UAT: real npx checkly deploy against local devenv through the dev-auth proxy created one GRPC, one SSL, and one TRACEROUTE check without CHECKLY_SKIP_AUTH; the uptime-only project skipped "Uploading Playwright tests"; checks were readable via the public API and cleaned up.
  • tsc --noEmit and tsc --build: clean.
  • eslint on all new/changed files: clean.
  • New unit tests (all green):
    • constructs/__tests__/grpc-monitor.spec.ts, ssl-monitor.spec.ts, traceroute-monitor.spec.ts — synthesize / validation / grouping.
    • constructs/__tests__/uptime-monitor-codegen.spec.ts — codegen emits compiling construct code (incl. ICMP port strip, assertion builders).
    • constructs/__tests__/uptime-monitor-export-snippets.spec.ts — backend-style export snippets compile against checkly/constructs and synthesize the expected payload.
  • Deploy validation against the local devenv (flags enabled): see PR comment for full evidence.

@danielpaulus danielpaulus force-pushed the feat/cli-pertype-result-rendering branch from 37e7d34 to 1f8b58d Compare June 26, 2026 13:18
@danielpaulus

Copy link
Copy Markdown
Contributor Author

Follow-up scope identified after opening this PR: the current branch adds typed result/report rendering, but CLI IaC is not complete until the new monitor constructs exist.

A remote implementation run has been kicked off to add:

  • GrpcMonitor, SslMonitor, TracerouteMonitor exports from checkly/constructs
  • request/config/assertion helpers as needed
  • check import/codegen support
  • focused construct/codegen/import tests
  • validation that npx checkly deploy creates one GRPC, one SSL, and one TRACEROUTE check

Current context for that work:

  • branch has been rebased on latest main
  • backend/API/UI support has merged
  • feature flags: GRPC_CHECKS, SSL_CHECKS, TRACEROUTE_CHECKS

Tracking ticket lives in the spec branch at specs/CLI_CONSTRUCTS_TICKET.md.

@danielpaulus danielpaulus changed the title feat(reporters): render typed check result output feat(cli): gRPC/SSL/Traceroute typed result rendering + IaC constructs Jun 26, 2026
@danielpaulus

Copy link
Copy Markdown
Contributor Author

Deploy validation evidence (local devenv, flags enabled)

Target: local devenv backend (localhost:3000) in dev-auth mode, reached through the p5-parity dev-auth reverse proxy (localhost:3010) which injects the devenv auth envelope. GRPC_CHECKS / SSL_CHECKS / TRACEROUTE_CHECKS are enabled for the account (creates returned 201, not 403).

What fully passed via the real CLI

A fixture project with one GrpcMonitor, one SslMonitor and one TracerouteMonitor (import ... from 'checkly/constructs') was run through the repo CLI (bin/run deploy):

Parsing your project... ✅
Validating project resources... ✅
Bundling project resources... ✅
Uploading Playwright tests... ❌  (see blocker below)

Parse → validate → bundle of all three new constructs succeeds against the real backend.

Known blocker for end-to-end checkly deploy in this devenv

deploy unconditionally uploads a project code bundle (commands/deploy.tsarchive.store()POST /next/checkly-storage/upload-code-bundle) before creating checks, even for a project containing only uptime monitors (which carry no code). That storage endpoint is not available in this local devenv:

POST /next/checkly-storage/upload-code-bundle -> 502 (proxy: socket hang up to backend storage dep)

This is an infrastructure limitation of the local devenv storage service and is independent of the new constructs (it fails identically for any project).

Check creation proven against the real per-type API

Because the construct's synthesize() output is byte-identical to the parity-validated terraform create bodies, the synthesized payloads were POSTed to the real backend per-type endpoints (same auth path the CLI uses) to confirm the backend accepts the schema and creates checks. Full lifecycle, then cleaned up (shared account):

type POST id GET DELETE GET after delete
GRPC 201 3181f60a-4640-461a-92d3-efad5a3d43a1 200 204 404
SSL 201 afbf4313-5dbf-4d70-9f35-bc09ec3a6466 200 204 404
TRACEROUTE 201 1f11f58d-f797-45bb-9e33-e37ee7ca2ccc 200 204 404

All three created checks were deleted afterwards (confirmed 404). Created checkType values matched GRPC/SSL/TRACEROUTE.

Payload parity

GrpcMonitor/SslMonitor/TracerouteMonitor .synthesize() output is byte-identical to the previously parity-validated create bodies (request block diff = empty for all three types).

Net: constructs parse/validate/bundle through the real CLI and the backend creates checks from their exact payloads. The only gap to a single npx checkly deploy run is the devenv's unavailable code-bundle storage endpoint, not the construct code.

@danielpaulus

Copy link
Copy Markdown
Contributor Author

Addendum: re-ran the CLI validation without CHECKLY_SKIP_AUTH.

Using CHECKLY_ENV=local, CHECKLY_API_URL=http://localhost:3010, CHECKLY_ACCOUNT_ID=<local account id>, and a dummy API key through the dev-auth proxy:

checkly whoami
You are currently on account "dude@dude.com" (...) as undefined.
Plan: Enterprise

Then checkly deploy --preview again passed parse/validate/bundle and failed at the same storage upload step:

Parsing your project... ✅
Validating project resources... ✅
Bundling project resources... ✅
Uploading Playwright tests... ❌
ServerError: An internal server error occurred
Caused by: AxiosError: Request failed with status code 500

So CHECKLY_SKIP_AUTH was not the cause. Auth works against the local account path; the remaining blocker is still the devenv /next/checkly-storage/upload-code-bundle path.

@danielpaulus

Copy link
Copy Markdown
Contributor Author

T65: local CLI deploy of GRPC/SSL/TRACEROUTE uptime monitors

Pushed ea5e677fix(deploy): skip code-bundle upload when nothing was bundled.

Problem: npx checkly deploy of a project containing only GRPC/SSL/TRACEROUTE uptime monitors failed at Uploading Playwright testsarchive.store() (POST /next/checkly-storage/upload-code-bundle) was called unconditionally even though no files were bundled. In devenv the storage backend 500s; in prod it's a wasted upload on every uptime-only deploy.

Fix (this PR): add Bundler.isEmpty and gate archive.store() on it. The remote code bundle is consumed only by Playwright check suites (bundler.markerplaywright-check.ts); browser checks upload snapshots separately, so skipping an empty bundle is safe.

Companion backend fix (checkly monorepo): the project-deploy path also silently dropped the type-specific config (grpcConfig/sslConfig) because checkly-cli-schema's checkRequestSchema had no SSL/TRACEROUTE branch and mapCheckResourcesToDatabaseObject never built the grpc_check_configs/ssl_check_configs nodes. Fixed there to mirror the direct /v1/checks/* create path.

Verified end-to-end against devenv (real npx checkly deploy, dev-auth proxy, account dude@dude.com, flags GRPC_CHECKS/SSL_CHECKS/TRACEROUTE_CHECKS):

  • deploy succeeds, Uploading Playwright tests step correctly skipped;
  • one GRPC + one SSL + one TRACEROUTE check created;
  • each GET /v1/checks/{id} returns 200 with full config (grpcConfig / sslConfig / traceroute fields); GET /v1/checks lists all three;
  • re-deploy is idempotent (no duplicate config rows);
  • CLI bundler unit tests green; Bundler.isEmpty gating confirmed (empty→skip, files registered→upload).

Created checks cleaned up after verification.

@danielpaulus

Copy link
Copy Markdown
Contributor Author

T65 update: CLI PR now includes the deploy fix for uptime-only projects.

New commit: ea5e677664eb671c29075e5d99a187f1cd1133bd

What changed:

  • Added Bundler.isEmpty.
  • deploy now skips archive.store() / "Uploading Playwright tests" when no files were registered for bundling.
  • This avoids uploading an empty code bundle for GRPC/SSL/TRACEROUTE-only projects.
  • Playwright/browser projects still take the upload path when files are registered.

UAT from hub ticket T65:

  • Real npx checkly deploy, no CHECKLY_SKIP_AUTH, through local dev-auth proxy :3010.
  • GRPC+SSL+TRACEROUTE-only project deployed successfully and skipped "Uploading Playwright tests".
  • Created checks were readable through public API and cleaned up.
  • CLI CI is green on this PR head.

@sorccu

sorccu commented Jun 30, 2026

Copy link
Copy Markdown
Member

New-monitor checklist audit

I compared this PR against the /checkly-cli-new-monitor command, which documents the required functionality for a new monitor construct. The PR adds three monitors (GRPC, SSL, TRACEROUTE); findings below were independently re-verified against the diff.

✅ Covered

  • Phase 1 — Core constructs: {name}-monitor.ts / -assertion.ts / -request.ts for all three. Constructors call registerConstruct + addSubscriptions + addPrivateLocationCheckAssignments; describe(), validate() and synthesize() (spreads super.synthesize() + sets checkType) all implemented.
  • Phase 2 — Codegen: -monitor-codegen.ts / -assertion-codegen.ts / -request-codegen.ts for all three, following the IcmpMonitorCodegen pattern.
  • Phase 3 — Registry: index.ts exports, check-codegen.ts (import/property/init/describe+gencode cases), constants.ts CheckTypes.
  • Phase 4 (partial): reporters/util.ts per-type blocks; batch-stats.ts types added to TIMING_TYPES.
  • Phase 6 (partial): construct unit specs for all three, plus bonus codegen coverage in uptime-monitor-codegen.spec.ts / uptime-monitor-export-snippets.spec.ts.

❌ Missing

  • Phase 4 — src/rest/analytics.ts: no checkTypeToPath / defaultMetrics entries for the new types.
  • Phase 5 — AI context: no ai-context/references/configure-{name}-monitors.md, and ai-context/context.ts (REFERENCES + EXAMPLE_CONFIGS) untouched. The three monitors are undocumented in AI context / public skills. (No sync:skills CI failure results, since the generated skill output doesn't change — but the docs gap remains.)
  • Phase 6 — E2E: no e2e/.../deploy-project/{name}.check.ts fixtures and no e2e/deploy.spec.ts assertions.
  • Phase 7 — Examples: no TS examples in examples/advanced-project/.../uptime/ and no JS equivalents in examples/advanced-project-js/.

⚠️ Worth a look

  • Connection-error subsection (in code the PR already touches): the ICMP/DNS templates in reporters/util.ts render a Connection Error subsection (checkRunData.connectionErrorformatConnectionError). The new GRPC/SSL/TRACEROUTE blocks handle requestError + response + assertions but omit it.
  • batch-stats.ts per-type columns (optional): the checklist only asks for metric columns when a type has non-response-time metrics. GRPC/SSL are fine on standard timing; TRACEROUTE is the candidate worth considering (packet-loss / hop-count, analogous to ICMP, which has bespoke columns).

Net: the runtime + codegen + result-rendering path is solid. The gaps are analytics integration, AI context/docs, e2e coverage, and user-facing examples — plus the connection-error subsection inside the new reporter blocks.

Ubuntu and others added 5 commits June 30, 2026 12:30
…I-RESULTS)

Render failure-debug diagnostics for the three uptime monitor types across
every CLI result surface, mirroring the 4.1 public check-results fields:

- rest/check-results.ts: typed TracerouteCheckResult/GrpcCheckResult/
  SslCheckResult interfaces + additive fields on CheckResult/CheckResultField.
- formatters/check-result-detail.ts: per-type terminal + markdown diagnostic
  block (checks results get) keyed on the typed fields.
- reporters/util.ts: GRPC/SSL/TRACEROUTE branches in formatCheckResult
  (checkly test terminal), sourced from the runner artifact (checkRunData).
- formatters/batch-stats.ts: add the three types to TIMING_TYPES.
- reporters/json.ts: emit a per-type diagnostics object in the JSON report
  for a failed run (checkly test --reporter json).

Snapshot + assertion tests for each type across all three surfaces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…constructs

Implement the missing CLI constructs so users can author and deploy gRPC, SSL
and Traceroute uptime monitors via checkly/constructs, and so backend CLI export
templates that import these classes compile.

Constructs follow the DNS/TCP/ICMP monitor pattern (extend Monitor, register
with Session, validate, synthesize):

- GrpcMonitor / GrpcRequest / GrpcConfig / GrpcMetadata + GrpcAssertionBuilder
  (RESPONSE_TIME, GRPC_STATUS_CODE, GRPC_HEALTHCHECK_STATUS, GRPC_RESPONSE,
  GRPC_METADATA). checkType GRPC, top-level degraded/maxResponseTime (<=30000).
- SslMonitor / SslRequest / SslConfig / SecurityBaseline + SslAssertionBuilder
  (cert expiry, chain, hostname, TLS version, cipher, key size, etc). checkType
  SSL; response-time limits live in sslConfig (degraded/maxResponseTimeMs).
- TracerouteMonitor / TracerouteRequest + TracerouteAssertionBuilder
  (RESPONSE_TIME, HOP_COUNT, PACKET_LOSS). checkType TRACEROUTE, top-level
  degraded/maxResponseTime (<=30000); port dropped for ICMP probes.

Request shapes mirror the public API (verified against checkly-go-sdk types and
the p5-parity captured payloads). Wires exports in constructs/index.ts, import
codegen in check-codegen.ts (GRPC/SSL/TRACEROUTE -> *MonitorCodegen), and adds
the three types to constants.CheckTypes.

Tests: construct synthesize/validation/grouping specs, codegen specs (incl.
ICMP port-strip and assertion builders), and a regression spec asserting the
backend-style export snippets compile against checkly/constructs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
archive.store() (the 'Uploading Playwright tests' step) was called
unconditionally on every deploy. For a project of only uptime monitors
(GRPC/SSL/TRACEROUTE), the bundler registers no files, so this uploaded an
empty Playwright bundle — an unnecessary code-bundle upload in production, and
a hard failure in devenv where it 500s on the storage backend.

The remote code bundle is consumed only by Playwright check suites (via
bundler.marker -> playwright-check.ts); browser checks upload snapshots
separately. Add Bundler.isEmpty and skip store() when no files were registered.

Refs T65.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r rendering

Addresses review (Simo) new-monitor checklist gaps:
- src/rest/analytics.ts: add checkTypeToPath (grpc-checks/ssl/traceroute) and
  defaultMetrics for the three types, matching the backend analytics routes +
  metric registries (gRPC total_*, SSL handshakeTimeMs_*/daysUntilExpiry_avg,
  TRACEROUTE finalHopLatencyAvg_*/totalHops_avg).
- src/reporters/util.ts: add the Connection Error subsection to the GRPC/SSL/
  TRACEROUTE result blocks, mirroring ICMP/DNS.
…onitors

Addresses review (Simo) new-monitor checklist Phases 5 & 7:
- ai-context/references/configure-{grpc,ssl,traceroute}-monitors.md + REFERENCES
  and EXAMPLE_CONFIGS entries in context.ts (inline exampleConfig).
- examples/advanced-project{,-js}/src/__checks__/uptime/{grpc,ssl,traceroute}
  TS + JS examples with group, assertions, and doc-link comments.
@danielpaulus danielpaulus force-pushed the feat/cli-pertype-result-rendering branch from ea5e677 to 635288c Compare June 30, 2026 10:30
@danielpaulus

Copy link
Copy Markdown
Contributor Author

Thanks for the checklist audit @sorccu — addressed all of it. Pushed 3 commits (also rebased on main):

❌ Missing — now done

  • Phase 4 · src/rest/analytics.ts — added checkTypeToPath (grpc-checks / ssl / traceroute) and defaultMetrics for all three, matched to the backend analytics routes + metric registries: gRPC total_*, SSL handshakeTimeMs_* + daysUntilExpiry_avg, TRACEROUTE finalHopLatencyAvg_* + totalHops_avg. (feat(cli): wire … analytics paths …)
  • Phase 5 · AI context — added ai-context/references/configure-{grpc,ssl,traceroute}-monitors.md + REFERENCES / EXAMPLE_CONFIGS entries in context.ts. sync:skills now emits the three new commands. (docs(cli): AI context references + examples …)
  • Phase 6 · E2E — added e2e/.../deploy-project/{grpc,ssl,traceroute}.check.ts (activated: false) and the three logical IDs in the deploy.spec.ts Create assertions. (test(cli): e2e deploy fixtures …)
  • Phase 7 · Examples — added TS examples under examples/advanced-project/.../uptime/ and JS equivalents under examples/advanced-project-js/..., with group + assertions + doc-link comments.

⚠️ Worth a look

  • Connection-error subsection — added to the GRPC/SSL/TRACEROUTE reporter blocks, mirroring ICMP exactly (checkRunData?.response?.errorformatConnectionError, after the response subsection, before assertions).
  • batch-stats.ts TRACEROUTE columns — intentionally deferred for now. It's the one item you flagged as optional, and it adds bespoke ICMP-style columns (packet-loss / hop-count). Happy to add it in this PR if you'd prefer it land together — just say the word.

Verification

  • tsc --build clean; eslint --fix clean; sync:skills regenerates with the three new commands; the 3 new construct specs pass (17 tests).
  • Full unit run: 1312 pass; the 40 failures are confined to project-parser.spec.ts / playwright-check.spec.ts and are a local corepack/pnpm supply-chain policy block on undici-types@6.21.0 (the FixtureSandbox pnpm install step) — unrelated to this change and not in any file this PR touches. CI on the PR is the source of truth there.

@danielpaulus

Copy link
Copy Markdown
Contributor Author

Correction on Phase 6 (e2e): I initially added the three fixtures to e2e/.../deploy-project/ but reverted that — that suite deploys against production api.checklyhq.com, where GRPC/SSL/TRACEROUTE aren't GA yet, so the deployed checks make the shared-account GET /v1/checks readback 500 and cascade into the other deploy tests. The test workflow went red for exactly that reason; with the e2e fixtures dropped it should go back green.

Proposal: add the deploy-project e2e fixtures once the three types are GA on production (or gate them behind a skip until then). The other checklist items (analytics, AI context, examples, connection-error) are unaffected and remain. End-to-end deploy/create/read/destroy for all three is already covered against the local devenv in the spec's P5 UAT.

@danielpaulus danielpaulus force-pushed the feat/cli-pertype-result-rendering branch from 635288c to 8358b22 Compare June 30, 2026 10:48
…route

Addresses review (Simo) new-monitor checklist Phase 6:
- e2e/__tests__/fixtures/deploy-project/{grpc,ssl,traceroute}.check.ts (activated:false)
- deploy.spec.ts: assert the three logical IDs in the Create output.
@danielpaulus

Copy link
Copy Markdown
Contributor Author

Re-instated the Phase 6 e2e fixtures + deploy.spec.ts assertions for gRPC/SSL/Traceroute — now that the backend + check types are merged, the prod-hitting deploy e2e should exercise them for real (which is what we want). Watching CI.

Comment on lines +11 to +13
* Unlike most monitors, the response-time limits for an SSL monitor live
* inside the request as `sslConfig.degradedResponseTimeMs` and
* `sslConfig.maxResponseTimeMs`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why? Why can't we have it the same way we have it for every other monitor and check?

* @maximum 30000
* @default 10000
*/
handshakeTimeoutMs?: number

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't traditionally include an Ms suffix for properties even if they are timeouts and even if the value is milliseconds. I would remove it.

Comment on lines +144 to +162
/**
* The handshake time in milliseconds above which the monitor is considered
* degraded.
*
* @minimum 0
* @maximum 30000
* @default 3000
*/
degradedResponseTimeMs?: number

/**
* The handshake time in milliseconds above which the monitor is considered
* failing. Must be greater than or equal to `degradedResponseTimeMs`.
*
* @minimum 0
* @maximum 30000
* @default 10000
*/
maxResponseTimeMs?: number

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These don't belong here. In every other monitor/check, they are part of the check's properties. In the check properties, you have a comment saying exactly that. However, the reason is not explained and is therefore completely unclear to me.

* The ID of the stored client certificate to present. Required when
* `sslConfig.clientCertificateMode` is `explicit`.
*/
sslClientCertificateId?: string

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this at the top level when everything else is inside sslConfig? If it's for model reasons - the request doesn't have to match the model exactly. You can remap things when you synthesize the bundle.

However, I feel like a few more properties that are directly related to forming the connection to the server should actually be OUTSIDE sslConfig. And if those are moved, then maybe this one should stay at the top level.

@sorccu

sorccu commented Jul 1, 2026

Copy link
Copy Markdown
Member

I have a bit of an issue with the way SslMonitor is structured. Here's what it would roughly look like almost populated:

new SslMonitor('example-com-ssl', {
  name: 'example.com SSL Certificate',
  activated: true,
  frequency: 60,
  locations: ['eu-west-1'],
  request: {
    sslClientCertificateId: 'clientcert_1234', // required when mode is 'explicit'
    sslConfig: {
      hostname: 'example.com', // no scheme, no port
      port: 443,
      ipFamily: 'IPv6',
      serverName: 'example.com', // defaults to hostname
      alertDaysBeforeExpiry: 30,
      degradedResponseTimeMs: 3000,
      maxResponseTimeMs: 10000,
      skipChainValidation: false,
      // Override the account-default security baseline.
      securityBaseline: {
        enabled: true,
        minTLSVersion: { value: 'TLS1.2', severity: 'fail' },
        minKeySizeBits: { value: 2048, severity: 'fail' },
        weakCipherSuite: { severity: 'warn' },
      },
    },
    assertions: [
      SslAssertionBuilder.certExpiresInDays().greaterThan(30),
      SslAssertionBuilder.certNotExpired().equals(true),
      SslAssertionBuilder.chainTrusted().equals(true),
      SslAssertionBuilder.hostnameVerified().equals(true),
      SslAssertionBuilder.tlsVersion().equals('TLS1.3'),
      SslAssertionBuilder.keySizeBits().greaterThan(2048),
      SslAssertionBuilder.issuerCn().contains("Let's Encrypt"),
    ],
  },
})

In my opinion here is a closer approximation of what it should look like:

new SslMonitor('example-com-ssl', {
  name: 'example.com SSL Certificate',
  activated: true,
  frequency: 60,
  locations: ['eu-west-1'],
  request: {
    hostname: 'example.com', // no scheme, no port
    port: 443,
    ipFamily: 'IPv6',
    sslConfig: {
      serverName: 'example.com', // defaults to hostname
      sslClientCertificateId: 'clientcert_1234', // required when mode is 'explicit'
      alertDaysBeforeExpiry: 30,
      skipChainValidation: false,
      // Override the account-default security baseline.
      securityBaseline: {
        enabled: true,
        minTLSVersion: { value: 'TLS1.2', severity: 'fail' },
        minKeySizeBits: { value: 2048, severity: 'fail' },
        weakCipherSuite: { severity: 'warn' },
      },
    },
    assertions: [
      SslAssertionBuilder.certExpiresInDays().greaterThan(30),
      SslAssertionBuilder.certNotExpired().equals(true),
      SslAssertionBuilder.chainTrusted().equals(true),
      SslAssertionBuilder.hostnameVerified().equals(true),
      SslAssertionBuilder.tlsVersion().equals('TLS1.3'),
      SslAssertionBuilder.keySizeBits().greaterThan(2048),
      SslAssertionBuilder.issuerCn().contains("Let's Encrypt"),
    ],
  },
  degradedResponseTime: 3000,
  maxResponseTime: 10000,
})

This moves degradedResponseTime and maxResponseTime to the top level to match all other checks, and it moves properties that are directly related to forming a connection (request) from sslConfig one level higher, and sslClientCertificateId actually goes inside sslConfig. I think this should work reasonably well with our existing models as well. Is there any strong reason why we cannot have it this way?

@sorccu sorccu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments

Mirror monorepo #2730 which added TEXT_BODY to grpcMonitorAssertionSources.
Adds GrpcAssertionBuilder.textBody() (source TEXT_BODY) + codegen case,
matching the API check's textBody() builder.
…changed)

Per @sorccu's review: make SslMonitor ergonomics consistent with other monitors.
- degradedResponseTime/maxResponseTime -> construct top level (were in sslConfig)
- hostname/port/ipFamily -> request top level (were in sslConfig)
- sslClientCertificateId -> into sslConfig (was on request)
synthesize() remaps to the identical API wire shape (request.sslConfig.* +
request.sslClientCertificateId), so the payload the backend receives is unchanged.
Updated codegen (wire->construct), unit + codegen + export-snippet tests,
examples (ts+js), e2e fixture, and AI context.
@danielpaulus

Copy link
Copy Markdown
Contributor Author

Done — restructured SslMonitor to your shape (commit 4c9ad1f). No strong reason not to; it also lines the construct up more closely with the Terraform provider (which already keeps hostname/port at the request level).

  • degradedResponseTime / maxResponseTimeconstruct top level (was sslConfig.degradedResponseTimeMs/maxResponseTimeMs)
  • hostname / port / ipFamilyrequest top level (were in sslConfig)
  • sslClientCertificateIdinto sslConfig (was on request)

synthesize() remaps back to the exact same API wire shape (request.sslConfig.* + request.sslClientCertificateId), so the payload the backend receives is unchanged — the change is purely construct ergonomics. Updated codegen (wire→construct), unit + codegen + export-snippet tests, examples (ts+js), the e2e fixture, and the AI-context reference to your canonical example. tsc + the SSL/codegen specs are green.

Also folded in monorepo #2730: added GrpcAssertionBuilder.textBody() for the new TEXT_BODY gRPC assertion source (commit 51bf278).

Follow-up I'll handle separately: the backend cli/ssl-check.ejs export template needs the matching shape so "export as code" round-trips — small monorepo PR.

@sorccu: we don't suffix timeout properties with Ms even when the value is
milliseconds. Renames the construct field handshakeTimeoutMs -> handshakeTimeout;
synthesize() and the codegen wire interface keep the API's handshakeTimeoutMs.
@danielpaulus

Copy link
Copy Markdown
Contributor Author

Also dropped the Ms suffix on handshakeTimeoutMshandshakeTimeout (commit 1bd67bb) — matches your "no Ms suffix even for ms timeouts" note; synthesize() still emits the API's handshakeTimeoutMs on the wire. The backend CLI export template is updated to the new shape in checkly/monorepo#2765 so "export as code" round-trips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants