Skip to main content
Define your S2 infrastructure in a JSON spec file and apply it declaratively using the CLI. Resources that already exist are reconfigured to match the spec — only the fields present in the spec are updated.
s2 apply -f spec.json

Spec file format

The spec is a JSON file with a top-level basins array. Each basin entry has a name, an optional config, and an optional streams array.
{
  "$schema": "https://raw.githubusercontent.com/s2-streamstore/s2/main/cli/schema.json",
  "basins": [
    {
      "name": "my-basin",
      "config": {
        "create_stream_on_append": true,
        "default_stream_config": {
          "storage_class": "express",
          "retention_policy": "7days"
        }
      },
      "streams": [
        {
          "name": "events",
          "config": {
            "storage_class": "standard",
            "retention_policy": "infinite"
          }
        }
      ]
    }
  ]
}

IDE setup

Add the $schema key to your spec file to point to the published schema hosted on GitHub to enable inline validation and autocomplete in your editor:
{
  "$schema": "https://raw.githubusercontent.com/s2-streamstore/s2/main/cli/schema.json",
  "basins": []
}
Or generate a local copy and reference it by path:
s2 apply --schema > schema.json
{
  "$schema": "./schema.json",
  "basins": []
}
VS Code — configure schema associations in .vscode/settings.json so $schema is not needed in every file:
{
  "json.schemas": [
    {
      "fileMatch": ["*.s2spec.json", "s2-spec.json"],
      "url": "https://raw.githubusercontent.com/s2-streamstore/s2/main/cli/schema.json"
    }
  ]
}
You can use any glob pattern in fileMatch to match your spec files by naming convention. IntelliJ / JetBrains IDEs — add a schema mapping in the IDE settings:
  1. Open Settings (Ctrl+Alt+S / ⌘,) → Languages & FrameworksSchemas and DTDsJSON Schema Mappings
  2. Click + to add a new mapping
  3. Give the mapping a name (e.g. S2 Spec)
  4. Set the schema to https://raw.githubusercontent.com/s2-streamstore/s2/main/cli/schema.json (or a local path)
  5. Set the Schema version to JSON Schema version 7
  6. Add file patterns to associate (e.g. *.s2spec.json)

Basin config fields

FieldTypeDescription
default_stream_configobjectDefault stream configuration applied to newly created streams
create_stream_on_appendbooleanAuto-create a stream on first append using the default config
create_stream_on_readbooleanAuto-create a stream on first read using the default config

Stream config fields

FieldTypeDescription
storage_class"express" | "standard"Storage class for recent writes
retention_policy"infinite" | humantime stringHow long records are retained (e.g. "7days", "1week")
timestampingobjectTimestamping behavior (see below)
delete_on_emptyobjectDelete the stream automatically when empty

Timestamping

{
  "mode": "client-prefer",
  "uncapped": false
}
FieldValuesDescription
mode"client-prefer" | "client-require" | "arrival"How record timestamps are resolved
uncappedbooleanAllow client timestamps to exceed the server arrival time

Delete-on-empty

{
  "min_age": "1day"
}
Set min_age to a humantime duration (e.g. "1day", "2h 30m") to enable automatic deletion of empty streams once they reach that age. The default of "0s" disables delete-on-empty.

Dry run

Preview what would change without making any mutations:
s2 apply -f spec.json --dry-run
Output uses the following legend:
SymbolMeaning
+Will be created
~Will be reconfigured (shows field-level diffs)
=Already matches spec, no change
Example output:
+ basin new-basin
    storage_class: express
  + stream new-basin/events
    retention_policy: 7days
= basin existing-basin
  ~ stream existing-basin/logs
      retention_policy: 7days → 30days
For the full list of CLI flags, see s2 apply.