Documentation
¶
Overview ¶
Package synth generates realistic, coherent, referentially-consistent records from plain Go structs. It is a pure data provider: it never touches the network, a database, or DDL. A struct goes in; records come out — in memory, to a file, or streamed.
Index ¶
- Constants
- func Fill[T any](p *T, opts ...Option) error
- func Generate(p Preset, n int, opts ...Option) ([]map[string]any, error)
- func Make[T any](n int, opts ...Option) []T
- func MakeParallel[T any](n, workers int, opts ...Option) ([]T, error)
- func Orders(n int, opts ...Option) ([]map[string]any, error)
- func ParseInstant(s string) (time.Time, error)
- func Payments(n int, opts ...Option) ([]map[string]any, error)
- func PresetSpec(p Preset) (string, bool)
- func Register(name string, fn func(r R) any)
- func RegisterSet(name string, values ...string)
- func Transactions(n int, opts ...Option) ([]map[string]any, error)
- func TryMake[T any](n int, opts ...Option) ([]T, error)
- func Users(n int, opts ...Option) ([]map[string]any, error)
- func Warnings[T any]() []schema.Warning
- func WriteCDC[T any](path string, n int, cfg CDCConfig) error
- func WriteCSV[T any](path string, records []T) error
- func WriteJSONL[T any](path string, records []T) error
- func WriteSQL[T any](path, table string, records []T) error
- type APISpec
- type CDCConfig
- type CDCEvent
- type CDCStream
- type CascadeConfig
- type CascadeStream
- type Config
- type DDLTable
- type Env
- type Generator
- func (g *Generator) Amount(min, max int) float64
- func (g *Generator) Card() string
- func (g *Generator) City() string
- func (g *Generator) Company() string
- func (g *Generator) Country() string
- func (g *Generator) Currency() string
- func (g *Generator) Email() string
- func (g *Generator) FirstName() string
- func (g *Generator) IBAN() string
- func (g *Generator) IPv4() string
- func (g *Generator) Name() string
- func (g *Generator) Phone() string
- func (g *Generator) Postcode() string
- func (g *Generator) Region() string
- func (g *Generator) URL() string
- func (g *Generator) Username() string
- type MaskReport
- type MaskRule
- type Masker
- type Option
- func Offset(n int) Option
- func Ref[P any](parents []P, fkField string, opts ...RefOption) Option
- func RefValues(fkField string, values []any) Option
- func Unmasked() Option
- func Weighted(field string, choices map[string]float64) Option
- func WithChaos(p float64) Option
- func WithLocale(name string) Option
- func WithSeed(seed uint64) Option
- type Preset
- type Profiled
- func (p *Profiled) Columns() []string
- func (p *Profiled) Constraints() []constraint.Constraint
- func (p *Profiled) Generate(n int, opts ...Option) ([]map[string]any, error)
- func (p *Profiled) SampleRows() int
- func (p *Profiled) Stats() map[string]*profile.ColumnStats
- func (p *Profiled) YAML(name string, count int) ([]byte, error)
- type ProtoMessage
- type R
- type RateConfig
- type RateStream
- type RefOption
- type SchemaFile
- type SnapshotConfig
- type Streamer
- type Timeline
- type YAMLSpec
- func (y *YAMLSpec) CDC(cfg CDCConfig) (*CDCStream, error)
- func (y *YAMLSpec) Cascade(child *YAMLSpec, cfg CascadeConfig) (*CascadeStream, error)
- func (y *YAMLSpec) Columns() []string
- func (y *YAMLSpec) Constraints() []constraint.Constraint
- func (y *YAMLSpec) Count() int
- func (y *YAMLSpec) Generate(opts ...Option) ([]map[string]any, error)
- func (y *YAMLSpec) GenerateN(n int, opts ...Option) ([]map[string]any, error)
- func (y *YAMLSpec) Name() string
- func (y *YAMLSpec) Schema() *schema.Schema
- func (y *YAMLSpec) SetCount(n int)
- func (y *YAMLSpec) Snapshot(cfg SnapshotConfig) (*Timeline, error)
Constants ¶
const ( MaskKeep = mask.Keep MaskFake = mask.Fake MaskRedact = mask.Redact MaskDrop = mask.Drop MaskDP = mask.DP )
Masking strategies.
Variables ¶
This section is empty.
Functions ¶
func Generate ¶
Generate produces n records from a built-in schema.
rows, _ := synth.Generate(synth.PresetTransaction, 100)
Card numbers and national identifiers come back masked. Pass Unmasked() when a test genuinely needs the raw value — for example to check that a validator accepts it.
func Make ¶
Make generates n records of type T. It panics on configuration errors (unknown tag, dependency cycle) — use TryMake in production code.
func MakeParallel ¶
MakeParallel generates n records across `workers` goroutines. Each worker forks its own rng from a per-record deterministic seed, so output is independent of worker count — no shared-rand mutex, and still reproducible. workers <= 0 uses GOMAXPROCS.
func ParseInstant ¶
ParseInstant reads a date or timestamp the way the CLI accepts it.
func PresetSpec ¶
PresetSpec returns a preset's YAML, so it can be read, edited and committed rather than treated as a black box.
func Register ¶
Register adds a custom field type. After this, a field can select it by tag (`synth:"cinema"`) or by a matching field name, and its value is produced by fn. fn must be deterministic given R for reproducible output.
synth.Register("cinema", func(r synth.R) any {
return r.Pick([]string{"Inception", "Interstellar", "Tenet"})
})
func RegisterSet ¶
RegisterSet is the common case: a custom type that picks uniformly from a fixed set of values (e.g. movie titles for a "cinema" type).
synth.RegisterSet("cinema", "Inception", "Interstellar", "Tenet", "Dune")
func WriteCSV ¶
WriteCSV writes records to a CSV file. Column order and header come from the struct's field order.
func WriteJSONL ¶
WriteJSONL writes one JSON object per line.
Types ¶
type APISpec ¶
type APISpec struct {
// contains filtered or unexported fields
}
APISpec wraps a parsed OpenAPI spec for payload generation.
func OpenAPIBytes ¶
OpenAPIBytes parses an OpenAPI 3 spec from bytes.
func (*APISpec) Payload ¶
Payload generates one valid request-body payload (as a field→value map) for the given method and path.
func (*APISpec) PayloadJSON ¶
PayloadJSON generates one payload and marshals it to indented JSON.
type CDCStream ¶
CDCStream produces insert/update/delete events over a schema.
func CDC ¶
CDC builds a deterministic change-event stream for type T. The history is coherent: a row is inserted before it is updated, updates carry the true `before` image, and deleted rows are never touched again.
s, _ := synth.CDC[User](synth.CDCConfig{Table: "users", UpdateRate: 0.3, DeleteRate: 0.1})
s.WriteJSONL(os.Stdout, 1000)
type CascadeConfig ¶
type CascadeConfig = cdc.CascadeConfig
CascadeConfig controls a two-table change stream with cascade deletes.
type CascadeStream ¶
type CascadeStream = cdc.CascadeStream
CascadeStream produces an interleaved change stream over a parent and a child table, where deleting a parent deletes its children too.
type DDLTable ¶
type DDLTable struct {
// contains filtered or unexported fields
}
DDLTable is a table parsed from SQL DDL, ready to generate rows.
func LoadDDL ¶
LoadDDL parses CREATE TABLE statements from a .sql file. Synth reads the DDL as text — it never connects to a database.
type Env ¶ added in v1.7.0
type Env interface {
R
// From returns the value of the field named by this field's from=, or nil
// when it has none. It is how a provider stays coherent with a sibling:
// a device name reads the model code it must match.
From() any
// Sibling returns an already-generated field of the same record by name,
// or nil when that field does not exist or has not been generated yet.
// Field order follows the from=/match=/derive= dependency graph, so a
// provider that needs a sibling should be declared with from=.
Sibling(name string) any
// LocaleName is the record's locale, e.g. "uz_UZ".
LocaleName() string
// CountryCode is the record locale's international dialling prefix with
// its leading '+', e.g. "+998".
CountryCode() string
// PhonePrefix is the operator or area digits of the place this record was
// given, e.g. "90" for Tashkent or "213" for Los Angeles. It is what keeps
// a generated number in the same city as the record's address.
PhonePrefix() string
}
Env is the wider surface a custom provider may reach for when randomness alone is not enough: the record's locale, and the fields already generated for it. The R handed to a provider always satisfies it, so a provider that needs more than R asserts for it:
synth.Register("device_brand", func(r synth.R) any {
env, ok := r.(synth.Env)
...
})
It is a separate interface rather than more methods on R so that adding to it later does not break providers written against R.
type Generator ¶
type Generator struct {
// contains filtered or unexported fields
}
Generator is a stateful, per-instance value source. Each Generator owns its own RNG, so different goroutines using different Generators never contend — unlike a package-level faker guarded by a global mutex. Not safe for concurrent use by ONE Generator; give each goroutine its own.
type Masker ¶
Masker anonymizes a real data export, replacing personal data with synthetic values of the same format. See the mask package for the guarantees.
type Option ¶
type Option func(*config)
Option configures a generation call.
func Offset ¶
Offset starts row generation at record index n instead of 0.
Each row is seeded from its index, so the output is a deterministic function of the index. Offsetting the index is what lets a second run extend a first one: Offset(1000) produces rows 1000..1000+n, which differ from the first run's rows 0..999 yet stay reproducible. This is the mechanism behind the CLI's --append.
func Ref ¶
Ref links a foreign-key field on the child to a parent slice, so every child row points at a real parent. Pass OneToMany to control cardinality.
func RefValues ¶
RefValues links a foreign-key field to values the caller already holds, rather than to a parent slice generated in the same process. This is the cross-run case: the parent was written to a file in an earlier run, its key column read back, and passed here so the child points at rows that already exist on disk.
users, _ := synth.Users(10000) // run 1, written out
keys := readColumn("users.csv", "id") // read back later
orders, _ := spec.GenerateN(500000, synth.RefValues("user_id", keys))
A nil or empty values slice is a no-op: with no parent keys there is nothing to point at, and the field generates as it otherwise would.
func Unmasked ¶
func Unmasked() Option
Unmasked strips the mask= setting from every field, returning raw values.
The name is deliberate: at the call site it reads as a decision, not a default. Output from this option must not be pasted anywhere a real value would be unwelcome.
func Weighted ¶
Weighted turns a field into a weighted enum in code (an alternative to the `synth:"enum,choices=...,weights=..."` tag). Weights need not sum to 1.
synth.Weighted("Status", map[string]float64{"settled":0.94,"pending":0.05,"failed":0.01})
func WithChaos ¶
WithChaos makes a fraction p (0..1) of string/numeric fields carry an edge-case value — empty strings, emoji, RTL text, SQL/HTML fragments, pathologically long input, boundary numerics. Use it to test the paths the happy path never reaches. Referential-key fields are never corrupted.
func WithLocale ¶
WithLocale selects a locale ("uz_UZ", "en_US", ...).
type Profiled ¶
type Profiled struct {
// contains filtered or unexported fields
}
Profiled is a schema learned from a real-data sample (see the profile package). Generating from it produces synthetic rows whose shape — types, ranges and category frequencies — matches the sample, without ever copying the original data.
func Profile ¶
Profile learns a schema from a CSV or JSONL export of real data. Synth reads the file only; it never connects to a database.
func ProfileBytes ¶
ProfileBytes is Profile for data already in memory, with the format named rather than read from a file extension. Callers that must not touch the filesystem — the MCP server is one — use this.
It takes bytes rather than an io.Reader because profiling and constraint mining each need their own pass over the data: profiling streams and keeps only statistics, while an invariant is a relationship between whole rows. A single reader cannot be consumed twice, and buffering it here would hide the memory cost from the caller who chose to hold the data in the first place.
format is "csv" (the default) or "jsonl"/"ndjson".
func (*Profiled) Constraints ¶
func (p *Profiled) Constraints() []constraint.Constraint
Constraints returns the cross-column invariants mined from the sample.
func (*Profiled) SampleRows ¶
SampleRows returns how many rows were profiled.
func (*Profiled) Stats ¶
func (p *Profiled) Stats() map[string]*profile.ColumnStats
Stats exposes the observed per-column statistics (distinct counts, null counts, numeric ranges) so you can inspect what was learned.
type ProtoMessage ¶
type ProtoMessage struct {
// contains filtered or unexported fields
}
ProtoMessage is a message parsed from a .proto file, ready to generate rows.
func LoadProto ¶
func LoadProto(path string) ([]*ProtoMessage, error)
LoadProto parses .proto source from a file. Synth reads it as text — no protoc, no code generation, no schema registry.
func ProtoBytes ¶
func ProtoBytes(src []byte) ([]*ProtoMessage, error)
ProtoBytes parses .proto source from memory.
func (*ProtoMessage) Columns ¶
func (p *ProtoMessage) Columns() []string
Columns returns field names in declaration order.
type R ¶
type R interface {
// Intn returns a value in [0,n).
Intn(n int) int
// IntRange returns a value in [min,max].
IntRange(min, max int) int
// Float64 returns a value in [0,1).
Float64() float64
// Pick returns a random element of s (empty string if s is empty).
Pick(s []string) string
// Digits returns n random decimal digits.
Digits(n int) string
}
R is the minimal randomness surface handed to a custom provider. It keeps user code decoupled from Synth internals while staying deterministic (it is the record's own seeded stream).
type RateConfig ¶
type RateConfig struct {
// PerSecond is the target event rate. Values <= 0 mean "as fast as
// possible" (no pacing).
PerSecond float64
// Burst is how many events are emitted per tick. Larger bursts mean fewer,
// coarser wakeups; 0 defaults to 1.
Burst int
// Jitter (0..1) randomizes each interval by up to ±Jitter, so arrivals look
// like real traffic instead of a metronome.
Jitter float64
// Total caps how many events are emitted. 0 means run until the context is
// cancelled.
Total int
}
RateConfig paces a generated stream so it arrives over wall-clock time, the way a real event source would. Use it to drive streaming tests, load tests and consumer back-pressure experiments without a broker.
type RateStream ¶
type RateStream[T any] struct { // contains filtered or unexported fields }
RateStream emits records at a wall-clock rate.
func Rate ¶
func Rate[T any](cfg RateConfig, opts ...Option) *RateStream[T]
Rate prepares a paced stream of T. Nothing is generated until Run is called.
synth.Rate[Event](synth.RateConfig{PerSecond: 500, Total: 10_000}).
Run(ctx, func(e Event) error { return producer.Send(e) })
type SchemaFile ¶
type SchemaFile struct {
// contains filtered or unexported fields
}
SchemaFile is a record definition parsed from a JSON Schema or Avro schema.
func AvroBytes ¶
func AvroBytes(data []byte) (*SchemaFile, error)
AvroBytes parses an Avro record schema.
func JSONSchemaBytes ¶
func JSONSchemaBytes(data []byte) (*SchemaFile, error)
JSONSchemaBytes parses a JSON Schema document.
func LoadSchema ¶
func LoadSchema(path string) (*SchemaFile, error)
LoadSchema parses a JSON Schema or Avro schema file (detected by content).
func (*SchemaFile) Columns ¶
func (s *SchemaFile) Columns() []string
Columns returns field names in declaration order.
func (*SchemaFile) Name ¶
func (s *SchemaFile) Name() string
Name returns the record name (JSON Schema title / Avro record name).
type SnapshotConfig ¶
SnapshotConfig describes a table's life across time.
type Streamer ¶
type Streamer[T any] struct { // contains filtered or unexported fields }
Streamer generates n records lazily and writes them straight to a sink, never holding more than one record in memory — for 100M-row runs.
func (*Streamer[T]) Each ¶
Each calls fn for every generated record, one at a time (constant memory).
type Timeline ¶
Timeline answers what a table looked like at any instant, and what changed between two of them.
func Snapshot ¶
func Snapshot[T any](cfg SnapshotConfig) (*Timeline, error)
Snapshot builds a timeline for type T. Ask it for the table as of any instant, or for the change events between two — applying the events to the earlier snapshot reproduces the later one exactly.
tl, _ := synth.Snapshot[Order](synth.SnapshotConfig{Rows: 10_000, Churn: 2})
jan := tl.At(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC))
events := tl.Between(jan1, jul1)
type YAMLSpec ¶
type YAMLSpec struct {
// contains filtered or unexported fields
}
YAMLSpec is a parsed declarative data definition (see the yamlfe package).
func Spec ¶
Spec turns a preset into an editable spec, for when the built-in shape is close but not exact.
func (*YAMLSpec) CDC ¶
CDCFromSpec builds a change-event stream from a YAML spec rather than a Go type, so the CLI can generate a history without compiled structs.
func (*YAMLSpec) Cascade ¶
func (y *YAMLSpec) Cascade(child *YAMLSpec, cfg CascadeConfig) (*CascadeStream, error)
Cascade builds a two-table change stream from this spec (the parent) and a child spec, where deleting a parent cascades to its children. The schemas are copied so the streams do not mutate the parsed specs.
func (*YAMLSpec) Columns ¶
Columns returns the field names in declaration order (for CSV/SQL headers).
func (*YAMLSpec) Constraints ¶
func (y *YAMLSpec) Constraints() []constraint.Constraint
Constraints returns the spec's cross-column invariants.
func (*YAMLSpec) Generate ¶
Generate produces the spec's records as field→value maps. Options override the spec's seed/locale when provided.
func (*YAMLSpec) GenerateN ¶
GenerateN generates exactly n records, overriding the spec's own count.
func (*YAMLSpec) Schema ¶
Schema returns the parsed schema. Packages that build on the fields rather than on generated rows — snapshot and constraint mining — need it.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cdc emits a deterministic stream of change events — insert, update, delete — in Debezium's envelope shape.
|
Package cdc emits a deterministic stream of change events — insert, update, delete — in Debezium's envelope shape. |
|
cmd
|
|
|
synth
module
|
|
|
Package constraint mines cross-column invariants from a real sample and enforces them during generation.
|
Package constraint mines cross-column invariants from a real sample and enforces them during generation. |
|
Package ddlfe is a frontend that parses SQL CREATE TABLE statements (from a .sql file or migration) into Synth schemas.
|
Package ddlfe is a frontend that parses SQL CREATE TABLE statements (from a .sql file or migration) into Synth schemas. |
|
Package diff compares the shape of two datasets — their columns, types, numeric ranges, null rates and category sets — rather than their rows.
|
Package diff compares the shape of two datasets — their columns, types, numeric ranges, null rates and category sets — rather than their rows. |
|
Package dist provides statistical distributions so generated data has the shape production data does — skew, hot keys, long tails — instead of the uniform noise most fakers produce.
|
Package dist provides statistical distributions so generated data has the shape production data does — skew, hot keys, long tails — instead of the uniform noise most fakers produce. |
|
examples
|
|
|
basic
command
Command basic demonstrates Synth as a pure data provider: structs in, coherent records out — to memory, a file, and a stream.
|
Command basic demonstrates Synth as a pure data provider: structs in, coherent records out — to memory, a file, and a stream. |
|
images
command
Command images demonstrates the drawn-image kinds: an avatar that belongs to the person in the same row, a thumbnail that belongs to the product, and a company mark that belongs to the company.
|
Command images demonstrates the drawn-image kinds: an avatar that belongs to the person in the same row, a thumbnail that belongs to the product, and a company mark that belongs to the company. |
|
localize
command
Command localize shows two things at once: a complex struct with a nested sub-struct, and locale coherence — the same schema rendered per locale, where name, phone, address and card all agree with the chosen region.
|
Command localize shows two things at once: a complex struct with a nested sub-struct, and locale coherence — the same schema rendered per locale, where name, phone, address and card all agree with the chosen region. |
|
Package gen is the engine: schema.Schema + rng → records.
|
Package gen is the engine: schema.Schema + rng → records. |
|
Package imagegen renders small, deterministic images from a name and a seed.
|
Package imagegen renders small, deterministic images from a name and a seed. |
|
Package infer turns an untagged field (its name + Go type) into a schema.Kind.
|
Package infer turns an untagged field (its name + Go type) into a schema.Kind. |
|
internal
|
|
|
rng
Package rng provides a fast, per-instance random source.
|
Package rng provides a fast, per-instance random source. |
|
webspec
Package webspec holds the pieces the workbench needs on both sides of its two backends: the local HTTP server and the WebAssembly build that runs the same page with no server at all.
|
Package webspec holds the pieces the workbench needs on both sides of its two backends: the local HTTP server and the WebAssembly build that runs the same page with no server at all. |
|
Package locale holds locale-coherent datasets.
|
Package locale holds locale-coherent datasets. |
|
Package mask anonymizes a real data export: it replaces personal data with synthetic values while preserving the FORMAT and the referential structure of the original, so the result still exercises the same code paths.
|
Package mask anonymizes a real data export: it replaces personal data with synthetic values while preserving the FORMAT and the referential structure of the original, so the result still exercises the same code paths. |
|
Package openapi is a frontend that turns an OpenAPI 3 spec into Synth schemas, so you can generate valid request payloads for an endpoint without hand-writing a struct.
|
Package openapi is a frontend that turns an OpenAPI 3 spec into Synth schemas, so you can generate valid request payloads for an endpoint without hand-writing a struct. |
|
Package pgcopy writes generated rows in the two formats Postgres COPY accepts, which is the fast way to get bulk data into a table: an INSERT statement per row is the slowest path the server offers, and at the volumes Synth targets the difference is hours.
|
Package pgcopy writes generated rows in the two formats Postgres COPY accepts, which is the fast way to get bulk data into a table: an INSERT statement per row is the slowest path the server offers, and at the volumes Synth targets the difference is hours. |
|
Package profile learns a schema from a SAMPLE FILE of real data (a CSV or JSONL export) and produces a Synth schema that reproduces its shape: column types, null rates, numeric ranges, and — for low-cardinality columns — the observed value set with its real frequencies.
|
Package profile learns a schema from a SAMPLE FILE of real data (a CSV or JSONL export) and produces a Synth schema that reproduces its shape: column types, null rates, numeric ranges, and — for low-cardinality columns — the observed value set with its real frequencies. |
|
Package protofe parses .proto files (proto2/proto3) into Synth schemas.
|
Package protofe parses .proto files (proto2/proto3) into Synth schemas. |
|
Package providers holds atomic value generators, one per schema.Kind.
|
Package providers holds atomic value generators, one per schema.Kind. |
|
Package reflectfe is the struct frontend: it turns a Go type + `synth:` tags into a schema.Schema.
|
Package reflectfe is the struct frontend: it turns a Go type + `synth:` tags into a schema.Schema. |
|
Package schema defines the intermediate representation (IR) that Synth's engine consumes.
|
Package schema defines the intermediate representation (IR) that Synth's engine consumes. |
|
Package schemafe parses JSON Schema and Avro schema documents into Synth schemas.
|
Package schemafe parses JSON Schema and Avro schema documents into Synth schemas. |
|
Package snapshot makes time an explicit axis of Synth's determinism.
|
Package snapshot makes time an explicit axis of Synth's determinism. |
|
Package ui serves a local browser workbench for designing a schema and seeing the data it produces.
|
Package ui serves a local browser workbench for designing a schema and seeing the data it produces. |
|
Package verify audits an existing dataset.
|
Package verify audits an existing dataset. |
|
Command synth-wasm runs the workbench entirely in the browser.
|
Command synth-wasm runs the workbench entirely in the browser. |
|
Package yamlfe is a structless frontend: it builds a Synth schema from a YAML document, so data can be described declaratively (and driven from the CLI) without writing Go types.
|
Package yamlfe is a structless frontend: it builds a Synth schema from a YAML document, so data can be described declaratively (and driven from the CLI) without writing Go types. |

