Skip to content

Configuration Reference

All configuration lives under the plugin's config key, in two independently toggleable sinks. Both default to disabled.

Where config lives

Two layers compose into the effective config:

  1. Profile cordis.patch.yml layersdsh plugin add applies the shipped bundle patch, which inserts one inert row; a profile layer then replaces that row with its config (later layers replace a row's whole config, so restate the full block):

    yaml
    # $DSH_HOME/profiles/<your-profile>/cordis.patch.yml
    - replace:
        - id: trajectory-persistence
          name: dsh-trajectory-persistence
          config:
            sinks:
              s3:
                enabled: true
                bucket: my-bucket
  2. $DSH_HOME/settings.yaml — deep-merges on top of the composed config under the trajectory-persistence namespace and applies live, without a restart; see Hot-reload behavior. Restate only the keys you change.

The annotated default below is what every field falls back to:

yaml
config:
  sinks:
    s3:
      enabled: false                    # master switch
      mode: push                        # 'push' (event stream) | 'ship' (tail on-disk artifact)
      bucket: ''                        # required when enabled
      prefix: dsh-trajectories          # key prefix
      region: us-east-1                 # signing region
      endpoint: ~                       # S3-compatible endpoint (OSS, MinIO)
      forcePathStyle: ~                 # path-style addressing (MinIO, OSS)
      credentials: ~                    # { accessKeyId, secretAccessKey }; absent = AWS default chain
      batchSize: 100                    # push: flush trigger, buffered events per session
      maxBufferedEvents: 10000          # push: ring cap; oldest dropped with a warning beyond it
      maxRetries: 3                     # retries after the first attempt
      retryBaseDelayMs: 200             # backoff base (retry n waits base * 2^(n-1) + jitter)
      deadLetterDir: .dsh/trajectory-deadletter  # push: parts whose upload finally failed
      root: ~/.dsh/sessions             # ship: official jsonl backend's session root ($DSH_HOME/sessions)
      pollIntervalMs: 5000              # ship: poll interval for artifact growth
      segmentBytes: 262144              # ship: target segment size (never splits a zstd frame)
      segmentMaxDelayMs: 60000          # ship: ship a short segment after this without growth
      dormantAfterMs: 300000            # ship: dormant after this without change
      writerId: ~                       # ship: stable writer identity override
    otel:
      enabled: false
      url: ''                           # full OTLP traces endpoint, e.g. http://jaeger:4318/v1/traces (mutually exclusive with aws)
      aws: ~                            # { region, url?, service? }; SigV4-signed OTLP to CloudWatch / AgentCore
      headers: ~                        # extra HTTP headers (auth, …)
      serviceName: dsh-trajectory-persistence
      maxExportBatchSize: 512           # BatchSpanProcessor
      scheduledDelayMillis: 5000        # BatchSpanProcessor
      shutdownTimeoutMillis: 3000       # dispose drain allowance

sinks.s3

FieldTypeDefaultDescription
enabledbooleanfalseMaster switch for this sink.
mode'push' | 'ship''push'Delivery mode: push buffers live events and uploads JSONL parts (legacy, default — see S3); ship tails the official jsonl backend's on-disk artifact and uploads zstd frame segments (see Ship & Sync).
bucketstring''Target bucket. Required when enabled.
prefixstringdsh-trajectoriesKey prefix; push parts land at {prefix}/{projectId}/{sessionId}/{seqStart}-{seqEnd}.jsonl, ship segments at {prefix}/{projectDir}/{sessionId}/{offsetStart}-{offsetEnd}.jsonl.zstd.
regionstringus-east-1AWS region (or the endpoint's signing region for S3-compatible stores).
endpointstring?Custom endpoint for S3-compatible object stores (Aliyun OSS, MinIO, …).
forcePathStyleboolean?Path-style addressing — required by MinIO and most OSS-compatible endpoints.
credentialsobject?{ accessKeyId, secretAccessKey } (both required if the object is present). Absent means the AWS default provider chain (env, shared config, IAM role).
batchSizeinteger ≥ 1100Push mode: flush a session's buffer once it holds at least this many events.
maxBufferedEventsinteger ≥ 110000Push mode: upper bound of buffered events per session; oldest events are dropped (with a warning) beyond it.
maxRetriesinteger ≥ 03Retries after the first upload attempt (exponential backoff).
retryBaseDelayMsinteger ≥ 0200Base backoff delay; retry n waits retryBaseDelayMs * 2^(n-1) plus up to 25 % jitter.
deadLetterDirstring.dsh/trajectory-deadletterPush mode: local directory receiving parts whose upload finally failed.
rootstring$DSH_HOME/sessions (or ~/.dsh/sessions)Ship mode: root directory of the official jsonl backend's session artifacts. Required in ship mode.
pollIntervalMsinteger ≥ 15000Ship mode: poll interval for artifact growth, in milliseconds.
segmentBytesinteger ≥ 1262144Ship mode: target segment size in bytes; segments never split a zstd frame.
segmentMaxDelayMsinteger ≥ 060000Ship mode: ship a short segment after this many milliseconds without growth.
dormantAfterMsinteger ≥ 0300000Ship mode: mark a session dormant after this many milliseconds without change.
writerIdstring?Ship mode: stable writer identity override; defaults to the persisted per-machine id.

sinks.otel

FieldTypeDefaultDescription
enabledbooleanfalseMaster switch for this sink.
urlstring''Full OTLP HTTP/protobuf traces endpoint (e.g. http://localhost:4318/v1/traces). Mutually exclusive with aws.
awsobject?AWS delivery: SigV4-signed OTLP to CloudWatch / Bedrock AgentCore Observability. See below.
headersmap<string>?Extra OTLP HTTP headers (auth, …). With aws, merged into the signed request.
serviceNamestringdsh-trajectory-persistenceservice.name resource attribute of exported spans.
maxExportBatchSizeinteger ≥ 1512BatchSpanProcessor's maximum export batch size.
scheduledDelayMillisinteger ≥ 05000BatchSpanProcessor's scheduled delay in milliseconds.
shutdownTimeoutMillisinteger ≥ 03000Maximum time spent awaiting the provider's shutdown drain.

sinks.otel.aws

FieldTypeDefaultDescription
regionstringAWS region of the endpoint (and the signing region). Required when aws is present.
urlstring?https://xray.{region}.amazonaws.com/v1/tracesFull OTLP traces endpoint override — VPC endpoints or partitions with a different domain (e.g. amazonaws.com.cn).
servicestring?xraySigV4 service name; xray for the CloudWatch OTLP endpoint.

Credentials never go in the config: the AWS default provider chain applies (env, shared config, IAM role).

Cross-field validation rules

The schema cannot express these, so validateConfig enforces them — both on the composed config path (a violating config fails plugin load) and as the settings namespace's validate hook (a violating settings.yaml write is refused upfront, before anything persists):

  • sinks.s3.enabled requires sinks.s3.bucket.
  • sinks.s3.batchSize must not exceed sinks.s3.maxBufferedEvents.
  • sinks.s3.root is required when sinks.s3.mode is 'ship' (a non-empty default exists, so this only bites if you explicitly blank it).
  • sinks.otel.enabled requires sinks.otel.url or sinks.otel.aws.
  • sinks.otel.url and sinks.otel.aws are mutually exclusive (aws already implies its endpoint).
  • sinks.otel.aws.region is required when aws delivery is configured.

Hot-reload behavior

When a settings provider is mounted (the standard harness profiles have one), the plugin registers its whole Config schema as the trajectory-persistence settings namespace. The composed plugin config (cordis.patch.yml layers) becomes the base; $DSH_HOME/settings.yaml layers on top of it — deep-merged, so you only restate the keys you change:

yaml
# $DSH_HOME/settings.yaml
trajectory-persistence:
  sinks:
    s3:
      enabled: true
      bucket: my-bucket

Every committed change applies live, without a restart:

  • The plugin compares the newly resolved config per sink and rebuilds exactly the sinks whose effective config changed (sinks.s3.* and sinks.otel.* are independent).
  • The replaced sink first drains — buffered events are uploaded or dead-lettered, open spans are ended and flushed — so no trajectory data is lost on a switch. New session events go to the new sink immediately.
  • A rebuild whose config the sink cannot use keeps the previous sink running and logs a warning.
  • A write that violates the cross-field rules above is refused upfront by the namespace's validate hook, before anything persists.
  • If the settings service itself goes away, the plugin falls back to the composed config.

Profile recipes

AWS S3

yaml
config:
  sinks:
    s3:
      enabled: true
      bucket: prod-dsh-trajectories
      prefix: trajectories
      region: ap-southeast-1
      # credentials omitted: the AWS default provider chain applies
      # (env AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, shared config, IAM role)

Aliyun OSS (S3-compatible endpoint)

yaml
config:
  sinks:
    s3:
      enabled: true
      bucket: dsh-trajectories
      prefix: trajectories
      region: oss-cn-hangzhou
      endpoint: https://oss-cn-hangzhou.aliyuncs.com
      forcePathStyle: true
      credentials:
        accessKeyId: ${OSS_ACCESS_KEY_ID}
        secretAccessKey: ${OSS_ACCESS_KEY_SECRET}

MinIO is the same shape: endpoint: http://minio:9000, forcePathStyle: true.

More backends: Jaeger, OTel Collector, Langfuse · AWS CloudWatch / AgentCore (SigV4).

Released under the MIT License.