{"id":49302540,"url":"https://github.com/attaradev/ditto","last_synced_at":"2026-05-01T10:00:37.758Z","repository":{"id":348985318,"uuid":"1200621437","full_name":"attaradev/ditto","owner":"attaradev","description":"Real production data, PII scrubbed, isolated for every run — ephemeral Postgres and MySQL copies, no shared state.","archived":false,"fork":false,"pushed_at":"2026-04-23T00:26:49.000Z","size":311,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-26T07:37:26.986Z","etag":null,"topics":["ci","database-testing","developer-tools","devtools","docker","erd","github-actions","homebrew","local-development","migration-testing","mysql","postgres","python","testing"],"latest_commit_sha":null,"homepage":"https://github.com/attaradev/ditto","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/attaradev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-03T16:22:33.000Z","updated_at":"2026-04-22T12:42:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/attaradev/ditto","commit_stats":null,"previous_names":["attaradev/ditto"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/attaradev/ditto","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attaradev%2Fditto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attaradev%2Fditto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attaradev%2Fditto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attaradev%2Fditto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/attaradev","download_url":"https://codeload.github.com/attaradev/ditto/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attaradev%2Fditto/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32492594,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ci","database-testing","developer-tools","devtools","docker","erd","github-actions","homebrew","local-development","migration-testing","mysql","postgres","python","testing"],"created_at":"2026-04-26T07:37:24.220Z","updated_at":"2026-05-01T10:00:37.748Z","avatar_url":"https://github.com/attaradev.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/logo.svg\" alt=\"ditto\" width=\"320\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003editto\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/attaradev/ditto/actions/workflows/ci.yml\"\u003e\n    \u003cimg src=\"https://github.com/attaradev/ditto/actions/workflows/ci.yml/badge.svg?branch=main\" alt=\"CI\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/attaradev/ditto/releases/latest\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/v/release/attaradev/ditto\" alt=\"Release\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://pkg.go.dev/github.com/attaradev/ditto\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/go-1.26%2B-00ADD8?logo=go\u0026logoColor=white\" alt=\"Go 1.26+\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://goreportcard.com/report/github.com/attaradev/ditto\"\u003e\n    \u003cimg src=\"https://goreportcard.com/badge/github.com/attaradev/ditto\" alt=\"Go Report Card\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/license/attaradev/ditto\" alt=\"License: MIT\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n## Ephemeral database copies with real schema, real data shape, and no shared state\n\nditto refreshes a cached Postgres or MySQL dump with `ditto reseed`, then restores disposable\ndatabase copies from that dump. It is built for teams that want production-faithful database\nbehavior in CI, migration dry-runs, and local development without sharing a mutable staging\ndatabase.\n\n- One clean copy per run, job, or developer\n- Optional PII scrubbing baked into the dump during `ditto reseed`\n- Works as a local CLI or as a shared host via `ditto host`\n\n## Is ditto for you?\n\nditto is a good fit when:\n\n- you already have a Postgres or MySQL source database that the Docker runtime can reach\n- your tests or migrations need real constraints, triggers, and data shape\n- shared staging state is making CI or local development unreliable\n- you want to keep real production data out of developer laptops and CI logs\n\nditto is not a good fit when:\n\n- you only need synthetic fixtures or an in-memory test database\n- you cannot expose a network-reachable source host to the machine running ditto\n- you want a hosted service instead of operating a local or shared ditto host\n\n## How it works\n\n1. `ditto reseed` writes a compressed dump from the configured source database.\n2. `ditto copy create` or `ditto copy run` restores that dump into an ephemeral database container.\n3. `ditto host` keeps the dump fresh, refills warm copies, expires old copies, and serves the shared-host API.\n\nSee [Architecture](docs/explanation/architecture.md) for the full lifecycle and trade-offs.\n\n## Choose your mode\n\n| Mode | Use it when | Main commands |\n| --- | --- | --- |\n| Local host | The same machine can run Docker and owns the dump file | `reseed`, `copy create`, `copy run` |\n| Shared host | CI runners or developers should request copies from a central host without local Docker | `host`, `copy create --server=...`, `copy run --server=...` |\n\n## Install\n\n### Homebrew (macOS and Linux)\n\n```bash\nbrew tap attaradev/ditto\nbrew install --cask ditto\n```\n\n### Pre-built binaries\n\nDownload the archive for your platform from\n[GitHub Releases](https://github.com/attaradev/ditto/releases), extract it, and move the\n`ditto` binary onto your `PATH`.\n\n### Linux packages\n\n`.deb`, `.rpm`, and `.apk` packages are published on\n[GitHub Releases](https://github.com/attaradev/ditto/releases).\n\n### Go install\n\nRequires Go 1.26+.\n\n```bash\ngo install github.com/attaradev/ditto/cmd/ditto@latest\n```\n\n### Build from source\n\n```bash\ngit clone https://github.com/attaradev/ditto\ncd ditto\ngo build -o ./ditto ./cmd/ditto\n```\n\n### SDKs\n\n| Language | Install |\n| --- | --- |\n| Python 3.11+ | `pip install ditto-sdk` |\n| Node.js 18+ | `npm install @attaradev/ditto-sdk` |\n| Go | `import \"github.com/attaradev/ditto/pkg/ditto\"` |\n\n## Quickstart\n\nPrerequisites:\n\n- a Docker-compatible runtime on the same host as ditto\n- a Postgres or MySQL source host reachable from that runtime\n- a read-only dump user on that source database\n\nIf your source host is `localhost`, `127.0.0.1`, or `::1`, this quickstart will fail. Dump helpers\nrun inside containers, so the source must be reachable from the Docker runtime by hostname or\nnetwork address.\n\nThe fastest path uses only environment variables — no config file required:\n\n```bash\nexport DITTO_SOURCE_URL='postgres://ditto_dump:secret@db.example.com:5432/myapp'\nditto doctor                                           # verify Docker, config, and connectivity\nditto reseed                                           # write the first dump\nditto copy run -- env | grep '^DATABASE_URL='         # prove it works\n```\n\nWhat you should see:\n\n- `ditto doctor` prints a green checklist; fix any red items before continuing\n- `ditto reseed` completes and writes a dump to `~/.ditto/latest.gz`\n- `ditto copy run` prints a `DATABASE_URL=...` line, proving the copy was created and injected\n- the copy is destroyed automatically when the command exits\n\nTo generate a starter `ditto.yaml` instead of using environment variables:\n\n```bash\nditto init        # writes ditto.yaml pre-populated from DITTO_SOURCE_URL\nditto doctor      # re-verify with the file-based config\n```\n\nUseful next commands:\n\n```bash\nditto status\nditto erd --output schema.mmd\nditto copy run -- go test ./...\n```\n\nIf your laptop or CI runner should not run Docker, point the CLI at a shared host instead:\n\n```bash\nexport DITTO_SERVER='http://ditto.internal:8080'\nexport DITTO_TOKEN=\"$DITTO_STATIC_TOKEN\"    # or an OIDC bearer token\nditto doctor --server \"$DITTO_SERVER\"\nditto copy run -- go test ./...\n```\n\nSee [Run Your First Copy](docs/tutorials/run-your-first-copy.md),\n[Demonstrate Obfuscation End to End](docs/tutorials/demonstrate-obfuscation-end-to-end.md), and\n[Configuration Reference](docs/reference/configuration.md) for the full setup.\n\n## Common workflows\n\n| Task | Command |\n| --- | --- |\n| Diagnose setup problems | `ditto doctor` |\n| Generate a starter config | `ditto init` |\n| Run one command against a throwaway copy | `ditto copy run -- go test ./...` |\n| Dry-run a migration | `ditto copy run -- alembic upgrade head` |\n| Start a shell session with a persistent copy | `eval \"$(ditto env export)\"` |\n| Generate an ERD from a copy | `ditto erd --output schema.mmd` |\n| Hold a copy across CI steps | `ditto copy create --format=json` and later `ditto copy delete \u003cid\u003e` |\n| Run a shared host for remote runners | `ditto host` |\n| Refresh a configured staging target | `ditto target refresh staging --confirm staging` |\n\n## Documentation\n\n| Section | Start here |\n| --- | --- |\n| Tutorials | [Run your first copy](docs/tutorials/run-your-first-copy.md), [Demonstrate obfuscation end to end](docs/tutorials/demonstrate-obfuscation-end-to-end.md) |\n| How-to guides | [Local development](docs/how-to/use-ditto-for-local-development.md), [CI](docs/how-to/use-ditto-in-ci.md), [Operate a host](docs/how-to/operate-a-ditto-host.md), [Troubleshooting](docs/how-to/troubleshoot.md) |\n| Reference | [Configuration](docs/reference/configuration.md), [CLI](docs/reference/cli.md) |\n| Explanation | [Architecture](docs/explanation/architecture.md) |\n\nThe docs landing page is at [docs/README.md](docs/README.md).\n\n## Trust and project health\n\n- CI, build, integration, and markdown checks run on every push and pull request\n- Security reports are handled privately; see [SECURITY.md](SECURITY.md)\n- Release history is tracked in [CHANGELOG.md](CHANGELOG.md)\n- Community expectations are in [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)\n- Contributor workflow is documented in [CONTRIBUTING.md](CONTRIBUTING.md)\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fattaradev%2Fditto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fattaradev%2Fditto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fattaradev%2Fditto/lists"}