Seed More Than Your Database
Describe your application state once, in reusable HCL components. Orchard builds it with the tools you already have, from databases to APIs to scripts, and tears it down when you’re done. One definition for your demos, tests, and local development.
scenarios/checkout.hcl
component "merchant" "store" {
source = "../components/merchant.hcl"
for_each = { eu = "Acme Europe", us = "Acme America" }
inputs = { name = each.value, region = each.key }
}
action "http_request" "subscribe" {
method = "POST"
url = "/subscriptions"
body = jsonencode({
merchant_id = component.merchant.store["eu"].id
})
}
action "exec_run" "reindex" {
command = "./scripts/reindex.sh"
args = ["${component.merchant.store["eu"].id}"]
}Terminal
orchard plan scenarios/checkout.hcl
orchard run scenarios/checkout.hclBuilt for
Demo data
A database that tells a believable story: customers with histories, orders with realistic timing, payments with a mix of outcomes.
Seeding a demoTest datasets
Shared, versioned fixtures for unit and integration tests. Each test gets a predictable slice of state from a scenario.
Integration testingLocal environments
Clone the repo, run one command, and get a working environment with enough data to develop against.
Local developmentAutomation
Multi-step changes across complex systems: database writes, API calls, and scripts in one plan, run in dependency order.
ProvidersHow It Fits Together
component
Reusable units of state
A component is like a function: typed inputs, outputs, and the actions that create its state. It also says how to undo them.
Build a library of components (a merchant, a subscription, a user with SSO) and every scenario can reuse them.
component "merchant" {
variable "name" { type = string }
variable "region" {
type = string
default = "us"
}
action "postgres_query" "create" {
query = <<-SQL
INSERT INTO merchants (name, region)
VALUES ('${var.name}', '${var.region}')
RETURNING id, name
SQL
lifecycle {
teardown {
query = "DELETE FROM merchants WHERE id = ${self.id}"
}
}
}
output "id" { value = action.postgres_query.create.id }
output "name" { value = action.postgres_query.create.name }
}scenario
One scenario per application state
A scenario names the providers it talks to, the components it uses, and what it hands back.
It’s more than database fixtures. Mix in API calls and scripts, reference one step’s output in another, and Orchard works out the order. for_each and count turn one block into many.
scenario "checkout" {
required_providers {
postgres = { source = "builtin/postgres" }
http = { source = "builtin/http" }
exec = { source = "builtin/exec" }
}
variable "dsn" { default = env("DATABASE_URL") }
variable "api_url" { default = env("API_URL") }
provider "postgres" { dsn = var.dsn }
provider "http" { base_url = var.api_url }
component "merchant" "store" {
source = "../components/merchant.hcl"
for_each = { eu = "Acme Europe", us = "Acme America" }
inputs = { name = each.value, region = each.key }
}
action "http_request" "subscribe" {
method = "POST"
url = "/subscriptions"
body = jsonencode({
merchant_id = component.merchant.store["eu"].id
})
}
action "exec_run" "reindex" {
command = "./scripts/reindex.sh"
args = ["${component.merchant.store["eu"].id}"]
}
}orchard plan
See it before it happens
Plan type-checks every reference, resolves the dependency graph, and prints what would run, without touching any external system.
Run it in CI to catch a broken scenario before anyone needs it.
$ orchard plan scenarios/checkout.hcl
▸ checkout
4 actions, 2 components — via exec, http, postgres
component "merchant" "store"["eu"]
source ../components/merchant.hcl
inputs name, region
↳ action "postgres_query" "create" via postgres
component "merchant" "store"["us"]
source ../components/merchant.hcl
inputs name, region
↳ action "postgres_query" "create" via postgres
action "exec_run" "reindex" via exec
also after component.merchant.store["eu"]
action "http_request" "subscribe" via http
also after component.merchant.store["eu"]
✓ Plan is validorchard run · orchard teardown
Build it, then clear it away
Run builds the state and writes a record of everything it did. Hand that record to teardown and Orchard undoes each step in reverse dependency order, so foreign keys unwind cleanly.
$ orchard run scenarios/checkout.hcl
record: orchard_checkout_run_20261023T103045Z_a3f29e5b.json
$ orchard teardown scenarios/checkout.hcl --latest
tearing down run 20261023T103045Z_a3f29e5b (orchard_checkout_run_20261023T103045Z_a3f29e5b.json)Components
Reusable units of state with typed inputs and outputs, instead of copied seed scripts.
Automatic wiring
Reference one step's output in another and Orchard builds the dependency graph.
Plan before run
Type-check references and preview the graph without side effects, locally or in CI.
Teardown
Every run writes a record. Teardown replays it in reverse and keeps going if one step fails.
Repetition
for_each and count turn one block into many: a merchant per region, three failed payments.
Pluggable providers
Postgres, MySQL, SQLite, Redis, S3, gRPC, HTTP, and exec built in. Any binary that speaks the JSON-over-stdio protocol can be a provider.
Install
One binary, no daemon or server. It reads HCL files and talks to the systems you point it at.
brew install pthm/tap/orchard