Environment Data Sync
Copy one organization's content from production into a lower environment for testing against real data
Develop and staging are normally seeded with synthetic data only. When a tester needs one real
customer's actual content — games, playable versions, variants, and assets — in a lower
environment to reproduce a bug or verify a fix, the copy-organization tool copies exactly one
organization's content across.
This is an internal engineering tool run by the team from a local machine with GCP access — it is not exposed through the dashboard UI or the public API.
What it copies
Content belonging to a single organization: the organization record, games, playable versions, variants, assets (and their folders, collections, tags, and usages), share links, and localizations — plus the matching files in object storage.
What it never copies
Anything user-bearing: accounts, organization memberships and invitations, teams, API tokens, network credentials, integrations, export history, or any logs. User-audit columns on copied rows (who created or last updated a row) are cleared rather than carried across. No other organization's data is ever touched.
Dry run, then apply
Every copy starts as a dry run — the default behavior with no flags needed — which resolves the
organization, decides whether it's a fresh copy or a refresh of an organization already present
in the target, and prints the full plan (row counts per table, object counts and bytes) without
writing anything. Re-running the identical command with --yes (and a --target-env declaring
which lower environment you mean) applies exactly that plan. Nothing about the plan changes
between the dry run and the real run — only whether it writes.
Re-running the same organization later is always safe: rows are upserted by their existing id, so a re-run refreshes content that changed at the source without duplicating anything, and content removed from the source is left alone in the target rather than deleted.
Safety
The tool is built to fail closed rather than silently do the wrong thing: it refuses to run against a source/target pair that looks swapped or production-shaped on either the database or the object storage side, keeps the source connection strictly read-only, and requires an explicit target-environment declaration before it ever writes. One guard is a warning rather than a refusal: some accounts issue a single object-storage credential across production and dev/staging, so an identical source/target access key alone doesn't block a run — it prints a warning and relies on the bucket-name guards instead.
Full runbook
Every flag, the exact commands for pulling credentials, and the complete list of safety
guarantees live in the operator runbook:
docs/operations/env-sync/copy-organization.md in the main repository. That repository is
private, so the path is given rather than linked — open it from your local checkout.