Skip to main content
s2 apply reads a JSON spec file and ensures the declared basins and streams exist with the specified configuration. Resources that already exist are reconfigured to match the spec, and 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

Flags

FlagDescription
-f, --filePath to the JSON spec file (required unless --schema is used)
--dry-runPreview changes without making any mutations
--schemaPrint the JSON Schema for the spec format to stdout