sdgen generates deterministic synthetic datasets from versioned, file-backed scenarios and loads them into DB-backed targets (PostgreSQL, Elasticsearch). It ships with a CLI, an HTTP API, and a small web UI.
- Stored as YAML files (typically managed in Git).
- Exposed read-only via CLI/UI/API (list/show/validate).
- Scenario files define entities, columns, generators, and dependencies.
- Stored in the sdgen PostgreSQL metadata DB.
- Support create/update/delete and test connection.
- DSNs are stored raw internally, but are redacted in API responses and the UI.
- Runs are created via API/CLI and tracked in the sdgen PostgreSQL metadata DB.
- Run-time row counts are controlled by the run request, not by editing scenarios:
scale(float), optional per-entityentity_scales, optionalentity_counts- optional
include_entities/exclude_entities - Resolution order:
- scenario defaults
- apply scale
- apply per-entity scale
- apply explicit per-entity counts
/api/v1/runs/planreturns execution order + resolved counts + warnings without executing.
go mod download
go build -o bin/sdgen ./cmd/sdgen
go build -o bin/sdgen-api ./cmd/sdgen-api./bin/sdgen-apiWeb UI:
- Home / Run builder: http://localhost:8080
- Targets management: http://localhost:8080/targets
Environment variables:
SDGEN_SCENARIOS_DIR— Scenarios directory (default:./scenarios)SDGEN_DB— Runs/targets metadata database DSN (required)SDGEN_LOG_LEVEL— Log level (default:info)SDGEN_BIND— API bind address (default:127.0.0.1:8080)SDGEN_BATCH_SIZE— Insert batch size (default:1000)
.env is loaded automatically from the current working directory if present.
Use .env.example as the template for your local .env.
List:
./bin/sdgen scenario listShow:
./bin/sdgen scenario show exampleValidate:
./bin/sdgen scenario validate exampleAdd (postgres):
./bin/sdgen target add --name dev-pg --kind postgres --schema public --dsn "postgres://user:pass@localhost:5432/db?sslmode=disable"Add (postgres, DSN helper flags):
./bin/sdgen target add --name dev-pg --kind postgres --host localhost --port 5432 --user user --password pass --database appdb --sslmode disableAdd (elasticsearch):
./bin/sdgen target add --name dev-es --kind elasticsearch --dsn http://localhost:9200Update:
./bin/sdgen target update <target-id> --name dev-pg --kind postgres --schema public --dsn "postgres://user:pass@localhost:5432/db?sslmode=disable"List (DSNs redacted):
./bin/sdgen target listShow (DSN redacted):
./bin/sdgen target show <target-id>Remove:
./bin/sdgen target rm <target-id>Test connection (structured output):
./bin/sdgen target test <target-id>Start:
./bin/sdgen run start --scenario iot_demo_small --target-id <target-id> --mode createStart with scale:
./bin/sdgen run start --scenario iot_demo_small --target-id <target-id> --mode create --scale 0.25Start with explicit per-entity overrides:
./bin/sdgen run start --scenario iot_demo_small --target-id <target-id> --mode create \
--entity-count users=1000 \
--entity-count events=50000Start with per-entity scale and include/exclude:
./bin/sdgen run start --scenario finance --target-id <target-id> --mode create \
--scale 0.5 \
--entity-scale payments=2.0 \
--include-entity customers \
--include-entity accounts \
--include-entity payments \
--exclude-entity fraud_alertsRun against a different database on the same physical target:
./bin/sdgen run start --scenario finance --target-id <target-id> --target-db tenant_a --mode createPlan only (no execution):
./bin/sdgen run start --scenario iot_demo_small --target-id <target-id> --mode create --planInline target (not stored):
./bin/sdgen run start \
--scenario iot_demo_small \
--target-kind postgres \
--target-schema public \
--target "postgres://user:pass@localhost:5432/db?sslmode=disable" \
--mode truncateList runs:
./bin/sdgen run list --limit 20Show run:
./bin/sdgen run show <run-id>GET /api/v1/scenarios— list scenariosGET /api/v1/scenarios/{id}— get scenario
GET /api/v1/targets— list targets (DSN redacted)POST /api/v1/targets— create targetGET /api/v1/targets/{id}— get target (DSN redacted)PUT /api/v1/targets/{id}— update targetDELETE /api/v1/targets/{id}— delete targetPOST /api/v1/targets/{id}/test— test connection
Target test response shape
{
"ok": true,
"latency_ms": 12,
"server_version": "15.6",
"capabilities": {
"can_create": true,
"can_truncate": true,
"can_insert": true
},
"error": ""
}POST /api/v1/runs— create run (executes)POST /api/v1/runs/plan— plan only (no execution)GET /api/v1/runs— list runsGET /api/v1/runs/{id}— get runGET /api/v1/runs/{id}/logs— get run logs (most recent first;?limit=N)
Run detail responses include progress fields:
progress_rows_generatedprogress_rows_totalprogress_entities_doneprogress_entities_totalprogress_current_entity
Built-in generators include:
const— constant valueuuid4— UUID v4uniform_int— uniform integer distributionuniform_float— uniform float distributionnormal— normal distributionchoice— random choice (optional weights)faker_name— random person namefaker_city— random city namefaker_device_name— random device nametime_series— time series with start/step/jitterfk— foreign key reference
id: example
name: Example Scenario
version: 1.0.0
seed: 12345
entities:
- name: users
target_table: users
rows: 100
columns:
- name: id
type: uuid
generator:
type: uuid4
- name: name
type: string
generator:
type: faker_name
- name: age
type: int
generator:
type: uniform_int
params:
min: 18
max: 80- Table, column, and schema identifiers are validated to reject unsafe strings (prevents SQL injection via config).
- DSNs are redacted in API responses and the web UI, but stored raw internally (for now).
MIT