Skip to content

Lace(lang)

Star Fork Repository

Lace is a domain-specific language (DSL) for defining HTTP monitoring probes. It is not an executable program — it is a specification language that describes what to probe and what to assert. A .lace file is run by an executor, an independent program that interprets the script, makes the HTTP calls, evaluates assertions, and returns a structured JSON result.

Lace defines the language. Executors implement it. Anyone can build an executor in any programming language — the conformance test suite ensures they all behave identically. The canonical reference implementation is the Python executor, which conforms to spec version 0.9.0. See Executors for how this works.

Your first probe

get("https://www.google.com/")
    .expect(status: [200, 301, 302])

Run it and you get:

{
  "outcome": "success",
  "elapsedMs": 639,
  "calls": [
    {
      "index": 0,
      "outcome": "success",
      "response": {
        "status": 200,
        "statusText": "OK",
        "responseTimeMs": 637,
        "dnsMs": 8,
        "connectMs": 31,
        "tlsMs": 50,
        "ttfbMs": 516,
        "transferMs": 100,
        "sizeBytes": 79243
      },
      "assertions": [
        {
          "method": "expect",
          "scope": "status",
          "op": "eq",
          "outcome": "passed",
          "actual": 200,
          "expected": [200, 301, 302]
        }
      ]
    }
  ]
}
{
  "outcome": "success",
  "startedAt": "1970-01-01T00:00:00.000Z",
  "endedAt": "1970-01-01T00:00:00.000Z",
  "elapsedMs": 639,
  "runVars": {},
  "calls": [
    {
      "index": 0,
      "outcome": "success",
      "startedAt": "1970-01-01T00:00:00.000Z",
      "endedAt": "1970-01-01T00:00:00.000Z",
      "request": {
        "url": "https://www.google.com/",
        "method": "get",
        "headers": {
          "User-Agent": "lace-probe/0.1.0 (lacelang-python)"
        },
        "bodyPath": null
      },
      "response": {
        "status": 200,
        "statusText": "OK",
        "headers": {
          "date": "Thu, 01 Jan 1970 00:00:00 GMT",
          "expires": "-1",
          "cache-control": "private, max-age=0",
          "content-type": "text/html; charset=ISO-8859-1",
          "server": "gws",
          "x-frame-options": "SAMEORIGIN",
          "alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
          "vary": "Accept-Encoding",
          "transfer-encoding": "chunked"
        },
        "bodyPath": "bodies/call_0_response.html",
        "responseTimeMs": 637,
        "dnsMs": 8,
        "connectMs": 31,
        "tlsMs": 50,
        "ttfbMs": 516,
        "transferMs": 100,
        "sizeBytes": 79243,
        "dns": {
          "resolvedIps": ["203.0.113.1", "203.0.113.2"],
          "resolvedIp": "203.0.113.1"
        },
        "tls": {
          "protocol": "TLSv1.3",
          "cipher": "TLS_AES_256_GCM_SHA384",
          "alpn": "http/1.1",
          "certificate": {
            "subject": { "cn": "www.google.com" },
            "subjectAltNames": ["DNS:www.google.com"],
            "issuer": { "cn": "WR2" },
            "notBefore": "1970-01-01T00:00:00.000Z",
            "notAfter": "1970-01-01T00:00:00.000Z"
          }
        }
      },
      "redirects": [],
      "assertions": [
        {
          "method": "expect",
          "scope": "status",
          "op": "eq",
          "outcome": "passed",
          "actual": 200,
          "expected": [200, 301, 302],
          "options": null
        }
      ],
      "config": {},
      "warnings": [],
      "error": null
    }
  ],
  "actions": {}
}

Every call includes a full timing breakdown (DNS, connect, TLS, TTFB, transfer), and every assertion records the actual and expected values -- not just pass/fail.

Features

  • Hard and soft assertions -- .expect() stops execution on failure; .check() records the failure and continues. Both evaluate all scopes before cascading.
  • Timing decomposition -- assert on DNS, connect, TLS handshake, time to first byte, and transfer time individually.
  • Request chaining -- capture values with .store() and use them in subsequent requests. Run-scope variables ($$var) flow forward; write-back variables ($var) are emitted in the result for the backend to persist.
  • Previous results -- access the last run's data via prev for change detection, repeat suppression, and rolling baselines.
  • Extensions -- declarative .laceext files add schema fields, result actions, and hook-based rules without modifying the core executor. Ships with laceNotifications and laceBaseline.
  • Backend-agnostic -- the executor is side-effect-free. The result is a standardized JSON structure any platform can consume.
  • Portable -- the same .lace file runs identically on every conformant executor (Python, JavaScript, Kotlin).

Documentation

  • Getting Started


    Install the executor, run your first script, learn the CLI.

  • Language


    HTTP calls, assertions, variables, chaining, failure semantics.

  • Extensions


    The .laceext format, hook points, built-in extensions.

  • Reference


    Grammar, result schema, error codes, scope reference.