Real‑world examples

These are intentionally practical: how wildcard keys (*:), unions, and constraints map to popular configuration ecosystems.

Docker Compose

Wildcard service names and structured per-service constraints.

Typical YAML
services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    environment:
      - NODE_ENV=production
    volumes:
      - ./app:/app
CSL schema
config DockerCompose {
  services: {
    *: { // Wildcard service names
      image?: string @regex(".+:.+");
      build?: {
        context: string;
        dockerfile?: string;
      };
      ports?: string[] @regex("^\\d+:\\d+$");
      environment?: {
        *: string | number | boolean;
      };
      volumes?: string[];
      networks?: string[];

      constraints {
        conflicts build with image;
        validate exists(ports) ? environment.NODE_ENV != "test" : true;
      };
    };
  };

  volumes?: { *: {}; };
  networks?: {
    *: {
      driver?: "bridge" | "overlay";
      external?: boolean;
    };
  };
}
Features used: wildcard keys (*:), unions, regex annotations, and a constraints block.

GitHub Actions

Validate mutually exclusive fields (uses vs run) inside steps.

Typical workflow YAML
name: CI
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test
CSL schema
config GitHubActions {
  name?: string;

  on: string | string[] | {
    push?: { branches?: string[] };
    pull_request?: { paths?: string[] };
  };

  env?: { *: string | number; };

  jobs: {
    *: { // Job names
      runs-on: "ubuntu-latest" | "macos-latest" | "windows-latest";
      needs?: string[];
      steps: {
        name?: string;
        uses?: string @regex("^actions/");
        run?: string;
        with?: { *: string | number | boolean; };

        constraints {
          validate exists(uses) || exists(run);
          conflicts uses with run;
          validate uses ? with : true;
        };
      }[];
    };
  };
}
The key trick is scoping constraints inside the steps object so they apply per-step, consistently.

AWS CloudFormation

Use any{} for flexible resource properties while keeping the outer structure strict.

Sample template excerpt
Resources:
  MyBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: my-app-bucket
      AccessControl: Private
CSL schema
config CloudFormation {
  Resources: {
    *: { // Resource names
      Type: string @start_with("AWS::");
      Properties: any{};
      DependsOn?: string | string[];
      Metadata?: any{};

      constraints {
        validate Type == "AWS::S3::Bucket" ?
          Properties.BucketName @regex("^[a-z0-9-]+$") :
          true;
      };
    };
  };

  Parameters?: {
    *: {
      Type: "String" | "Number" | "List";
      Default?: string | number;
      AllowedValues?: any[];
    };
  };

  Outputs?: {
    *: {
      Value: string;
      Export?: { Name: string };
    };
  };
}
This pattern keeps CloudFormation’s “huge surface area” manageable while still catching obvious mistakes early.

Have another example?

If you’ve written a schema for a real config format, we’d love to see it.

Contribute on GitHub Read the language spec