bg-coolify-migrate 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. bg_coolify_migrate-1.0.0/.gitignore +83 -0
  2. bg_coolify_migrate-1.0.0/CHANGELOG.md +3 -0
  3. bg_coolify_migrate-1.0.0/LICENSE +21 -0
  4. bg_coolify_migrate-1.0.0/PKG-INFO +157 -0
  5. bg_coolify_migrate-1.0.0/README.MD +101 -0
  6. bg_coolify_migrate-1.0.0/docs/README.template.MD +100 -0
  7. bg_coolify_migrate-1.0.0/docs/SECURITY.template.MD +68 -0
  8. bg_coolify_migrate-1.0.0/docs/architecture.md +149 -0
  9. bg_coolify_migrate-1.0.0/docs/cli.md +70 -0
  10. bg_coolify_migrate-1.0.0/docs/configuration.md +88 -0
  11. bg_coolify_migrate-1.0.0/docs/index.md +47 -0
  12. bg_coolify_migrate-1.0.0/docs/installation.md +73 -0
  13. bg_coolify_migrate-1.0.0/docs/safety.md +147 -0
  14. bg_coolify_migrate-1.0.0/docs/server-migration.md +170 -0
  15. bg_coolify_migrate-1.0.0/docs/troubleshooting.md +182 -0
  16. bg_coolify_migrate-1.0.0/docs/why.md +124 -0
  17. bg_coolify_migrate-1.0.0/pyproject.toml +173 -0
  18. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/__init__.py +86 -0
  19. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/api/__init__.py +1 -0
  20. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/api/client.py +491 -0
  21. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/api/fields.py +382 -0
  22. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/api/resources.py +466 -0
  23. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/cli.py +689 -0
  24. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/discovery/__init__.py +1 -0
  25. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/discovery/docker.py +282 -0
  26. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/discovery/quiesce.py +305 -0
  27. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/dns/__init__.py +1 -0
  28. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/dns/extract.py +253 -0
  29. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/dns/gate.py +229 -0
  30. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/dns/resolve.py +160 -0
  31. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/domain/__init__.py +10 -0
  32. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/domain/compose.py +281 -0
  33. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/domain/drift.py +479 -0
  34. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/domain/images.py +219 -0
  35. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/domain/kinds.py +260 -0
  36. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/domain/manifest.py +290 -0
  37. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/domain/naming.py +318 -0
  38. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/domain/plan.py +255 -0
  39. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/domain/statemachine.py +256 -0
  40. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/engine/__init__.py +1 -0
  41. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/engine/compensations.py +178 -0
  42. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/engine/context.py +141 -0
  43. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/engine/executor.py +276 -0
  44. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/engine/keys.py +157 -0
  45. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/engine/planner.py +628 -0
  46. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/engine/runner.py +308 -0
  47. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/engine/steps.py +741 -0
  48. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/errors.py +168 -0
  49. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/journal/__init__.py +1 -0
  50. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/journal/store.py +302 -0
  51. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/observability/__init__.py +1 -0
  52. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/observability/logging_setup.py +173 -0
  53. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/py.typed +0 -0
  54. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/server/__init__.py +1 -0
  55. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/server/appkey.py +234 -0
  56. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/server/fencing.py +141 -0
  57. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/server/inventory.py +205 -0
  58. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/server/runner.py +140 -0
  59. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/server/statemachine.py +94 -0
  60. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/server/steps.py +362 -0
  61. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/settings/__init__.py +1 -0
  62. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/settings/base.py +188 -0
  63. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/transfer/__init__.py +1 -0
  64. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/transfer/partition.py +153 -0
  65. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/transfer/rsync.py +279 -0
  66. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/transfer/ssh.py +313 -0
  67. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/transfer/verify.py +269 -0
  68. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/ui/__init__.py +1 -0
  69. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/ui/console.py +87 -0
  70. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/ui/dashboard.py +189 -0
  71. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/ui/report.py +224 -0
  72. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/ui/run_report.py +150 -0
  73. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/ui/server_report.py +91 -0
  74. bg_coolify_migrate-1.0.0/src/bg_coolify_migrate/ui/wizard.py +215 -0
  75. bg_coolify_migrate-1.0.0/tests/__init__.py +8 -0
  76. bg_coolify_migrate-1.0.0/tests/conftest.py +105 -0
  77. bg_coolify_migrate-1.0.0/tests/e2e/README.md +125 -0
  78. bg_coolify_migrate-1.0.0/tests/e2e/__init__.py +1 -0
  79. bg_coolify_migrate-1.0.0/tests/e2e/bootstrap.py +268 -0
  80. bg_coolify_migrate-1.0.0/tests/e2e/conftest.py +184 -0
  81. bg_coolify_migrate-1.0.0/tests/e2e/docker-compose.yml +200 -0
  82. bg_coolify_migrate-1.0.0/tests/e2e/prepare.py +174 -0
  83. bg_coolify_migrate-1.0.0/tests/e2e/runner.Dockerfile +53 -0
  84. bg_coolify_migrate-1.0.0/tests/e2e/test_all_engines.py +314 -0
  85. bg_coolify_migrate-1.0.0/tests/e2e/test_compose_service.py +198 -0
  86. bg_coolify_migrate-1.0.0/tests/e2e/test_drift_gate.py +116 -0
  87. bg_coolify_migrate-1.0.0/tests/e2e/test_label_contract.py +134 -0
  88. bg_coolify_migrate-1.0.0/tests/e2e/test_multi_resource.py +169 -0
  89. bg_coolify_migrate-1.0.0/tests/e2e/test_real_migration.py +227 -0
  90. bg_coolify_migrate-1.0.0/tests/e2e/test_rollback.py +173 -0
  91. bg_coolify_migrate-1.0.0/tests/integration/__init__.py +6 -0
  92. bg_coolify_migrate-1.0.0/tests/integration/docker-compose.yml +66 -0
  93. bg_coolify_migrate-1.0.0/tests/integration/prepare.py +63 -0
  94. bg_coolify_migrate-1.0.0/tests/integration/test_real_transfer.py +379 -0
  95. bg_coolify_migrate-1.0.0/tests/test_api_client.py +268 -0
  96. bg_coolify_migrate-1.0.0/tests/test_api_fields.py +337 -0
  97. bg_coolify_migrate-1.0.0/tests/test_api_resources.py +477 -0
  98. bg_coolify_migrate-1.0.0/tests/test_cli.py +219 -0
  99. bg_coolify_migrate-1.0.0/tests/test_discovery.py +400 -0
  100. bg_coolify_migrate-1.0.0/tests/test_dns.py +329 -0
  101. bg_coolify_migrate-1.0.0/tests/test_domain_compose.py +457 -0
  102. bg_coolify_migrate-1.0.0/tests/test_domain_drift.py +343 -0
  103. bg_coolify_migrate-1.0.0/tests/test_domain_images.py +182 -0
  104. bg_coolify_migrate-1.0.0/tests/test_domain_kinds.py +222 -0
  105. bg_coolify_migrate-1.0.0/tests/test_domain_manifest.py +248 -0
  106. bg_coolify_migrate-1.0.0/tests/test_domain_naming.py +245 -0
  107. bg_coolify_migrate-1.0.0/tests/test_domain_plan.py +282 -0
  108. bg_coolify_migrate-1.0.0/tests/test_domain_statemachine.py +197 -0
  109. bg_coolify_migrate-1.0.0/tests/test_engine.py +374 -0
  110. bg_coolify_migrate-1.0.0/tests/test_engine_context.py +85 -0
  111. bg_coolify_migrate-1.0.0/tests/test_engine_planner.py +141 -0
  112. bg_coolify_migrate-1.0.0/tests/test_engine_steps.py +638 -0
  113. bg_coolify_migrate-1.0.0/tests/test_engine_wiring.py +259 -0
  114. bg_coolify_migrate-1.0.0/tests/test_journal.py +197 -0
  115. bg_coolify_migrate-1.0.0/tests/test_observability.py +175 -0
  116. bg_coolify_migrate-1.0.0/tests/test_packaging.py +123 -0
  117. bg_coolify_migrate-1.0.0/tests/test_server_migration.py +423 -0
  118. bg_coolify_migrate-1.0.0/tests/test_transfer.py +332 -0
  119. bg_coolify_migrate-1.0.0/tests/test_ui.py +254 -0
  120. bg_coolify_migrate-1.0.0/tests/test_ui_run.py +195 -0
  121. bg_coolify_migrate-1.0.0/tests/test_ui_wizard.py +206 -0
  122. bg_coolify_migrate-1.0.0/tests/test_verify_collect.py +161 -0
@@ -0,0 +1,83 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # Distribution / packaging
8
+ build/
9
+ dist/
10
+ *.egg-info/
11
+ .eggs/
12
+
13
+ # Virtual environments
14
+ .venv/
15
+ venv/
16
+ env/
17
+
18
+ # Testing / coverage
19
+ .pytest_cache/
20
+ .coverage
21
+ .coverage.*
22
+ coverage.xml
23
+ htmlcov/
24
+ .hypothesis/
25
+ .benchmarks/
26
+
27
+ # Type checking / linting
28
+ .mypy_cache/
29
+ .ruff_cache/
30
+
31
+ # Docs
32
+ /site
33
+
34
+ # IDE
35
+ .vscode/
36
+ .claude/
37
+ .idea/
38
+
39
+ # Secrets & local config — NEVER commit these.
40
+ # A Coolify root token can read every secret of every project in the team.
41
+ .env
42
+ .env.*
43
+ !.env.example
44
+ !.env.template
45
+ .pypirc
46
+
47
+ # Runtime state. Migration journals contain resource UUIDs and host addresses;
48
+ # they belong in the user state dir (platformdirs), never in the repo. This entry
49
+ # only catches accidental --state-dir=. runs.
50
+ /state/
51
+ *.journal.jsonl
52
+
53
+ # Integration rig: throwaway keys, generated on demand by
54
+ # tests/integration/prepare.py. They authorise nothing but two local containers,
55
+ # but a key in git is a key in git.
56
+ tests/integration/keys/
57
+
58
+ # ── e2e rig artefacts ───────────────────────────────────────────────────────
59
+ # Minted per run and every one of them is a credential. The rig is reproducible
60
+ # from prepare.py + bootstrap.py, so there is nothing here worth keeping.
61
+ tests/e2e/keys/
62
+ tests/e2e/rig.json
63
+ tests/e2e/coolify.env
64
+ tests/e2e/.probe.json
65
+
66
+ # Lockfile — deliberately absent, not merely unwritten
67
+ # ---------------------------------------------------
68
+ # Nothing here would read it. All three CI workflows install with
69
+ # `pip install -e ".[dev]"`, consumers get a PyPI wheel and resolve the ranges in
70
+ # pyproject.toml themselves, and even our own setup instructions use `uv pip
71
+ # install`, which is the pip-compatible interface and ignores a lock (only
72
+ # `uv sync`/`uv run` consume one).
73
+ #
74
+ # A lock nothing consumes is worse than none: it claims a reproducibility that
75
+ # does not exist, and it is a second source of truth beside pyproject.toml that
76
+ # drifts in silence. Dependabot already maintains the pip ecosystem weekly from
77
+ # pyproject; a lock would need its own update path or quietly fossilise.
78
+ #
79
+ # To reverse this, the lock is the small part — the work is consuming it: move
80
+ # the workflows to `uv sync --locked`, add setup-uv, and arrange for the lock to
81
+ # be updated. Committing a lock while CI still runs pip is the worst of both:
82
+ # all of the cost, none of the benefit.
83
+ uv.lock
@@ -0,0 +1,3 @@
1
+ # CHANGELOG
2
+
3
+ <!-- This file is managed by python-semantic-release. Do not hand-edit below this line. -->
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 BAUER GROUP
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,157 @@
1
+ Metadata-Version: 2.4
2
+ Name: bg-coolify-migrate
3
+ Version: 1.0.0
4
+ Summary: BAUER GROUP Coolify migration toolkit — failsafe, resumable project and instance migration with byte-exact volume mirroring
5
+ Project-URL: Homepage, https://github.com/bauer-group/IP-CoolifyMigration
6
+ Project-URL: Source, https://github.com/bauer-group/IP-CoolifyMigration
7
+ Project-URL: Issues, https://github.com/bauer-group/IP-CoolifyMigration/issues
8
+ Project-URL: Changelog, https://github.com/bauer-group/IP-CoolifyMigration/blob/main/CHANGELOG.md
9
+ Author-email: "BAUER GROUP (Karl Bauer)" <karl.bauer@bauer-group.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: coolify,devops,docker,migration,paas,rsync,self-hosted
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: System :: Archiving :: Mirroring
24
+ Classifier: Topic :: System :: Systems Administration
25
+ Classifier: Topic :: Utilities
26
+ Requires-Python: >=3.12
27
+ Requires-Dist: asyncssh<3.0.0,>=2.18.0
28
+ Requires-Dist: dnspython<3.0.0,>=2.7.0
29
+ Requires-Dist: httpx<1.0.0,>=0.28.1
30
+ Requires-Dist: platformdirs<5.0.0,>=4.3.0
31
+ Requires-Dist: pydantic-settings<3.0.0,>=2.14.1
32
+ Requires-Dist: pydantic<3.0.0,>=2.10.0
33
+ Requires-Dist: pyyaml<7.0.0,>=6.0.0
34
+ Requires-Dist: questionary<3.0.0,>=2.0.0
35
+ Requires-Dist: rich<16.0.0,>=13.7.0
36
+ Requires-Dist: structlog<26.0.0,>=24.4.0
37
+ Requires-Dist: typer<1.0.0,>=0.15.0
38
+ Provides-Extra: dev
39
+ Requires-Dist: build>=1.0.0; extra == 'dev'
40
+ Requires-Dist: hypothesis<7.0.0,>=6.100.0; extra == 'dev'
41
+ Requires-Dist: mypy>=1.13.0; extra == 'dev'
42
+ Requires-Dist: pre-commit>=3.5.0; extra == 'dev'
43
+ Requires-Dist: pytest-asyncio<2.0.0,>=0.24.0; extra == 'dev'
44
+ Requires-Dist: pytest-cov<8.0.0,>=6.0.0; extra == 'dev'
45
+ Requires-Dist: pytest<10.0.0,>=8.3.0; extra == 'dev'
46
+ Requires-Dist: python-semantic-release>=9.0.0; extra == 'dev'
47
+ Requires-Dist: respx<1.0.0,>=0.22.0; extra == 'dev'
48
+ Requires-Dist: ruff>=0.7.0; extra == 'dev'
49
+ Requires-Dist: twine>=5.0.0; extra == 'dev'
50
+ Requires-Dist: types-pyyaml>=6.0.0; extra == 'dev'
51
+ Provides-Extra: docs
52
+ Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
53
+ Requires-Dist: mkdocs>=1.6.0; extra == 'docs'
54
+ Requires-Dist: mkdocstrings[python]>=0.26.0; extra == 'docs'
55
+ Description-Content-Type: text/markdown
56
+
57
+ <!-- AUTO-GENERATED FILE. DO NOT EDIT. Edit docs/README.template.MD instead. Generated 2026-07-16. -->
58
+ # BAUER GROUP - Coolify Migration Toolkit
59
+
60
+ [![PyPI](https://img.shields.io/pypi/v/bg-coolify-migrate.svg)](https://pypi.org/project/bg-coolify-migrate/)
61
+ [![Python](https://img.shields.io/pypi/pyversions/bg-coolify-migrate.svg)](https://pypi.org/project/bg-coolify-migrate/)
62
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
63
+ [![Tests](https://github.com/bauer-group/IP-CoolifyMigration/actions/workflows/tests.yml/badge.svg)](https://github.com/bauer-group/IP-CoolifyMigration/actions/workflows/tests.yml)
64
+
65
+ > **Today, Tomorrow, Together** | Building Better Software Together
66
+
67
+ Moves a Coolify **project — with its data —** between servers, and relocates a
68
+ whole Coolify instance to a new host. Failsafe, resumable, and rollback-capable.
69
+
70
+ Coolify can clone a resource to another server but deliberately will not move the
71
+ data. `VolumeCloneJob` and `CloneMe`'s `cloneVolumeData` flag exist upstream, but
72
+ PR #4777 shipped them **disabled**. The maintainer's stated blockers — permission
73
+ damage, job-queue spam at 50+ resources, no progress tracking, large-volume
74
+ failures — are all consequences of running inside Coolify's Laravel queue. An
75
+ external orchestrator has none of those constraints. That is what this is.
76
+
77
+ ## Repository Information
78
+
79
+ - **Version:** 0.1.0
80
+ - **Repository:** bauer-group/IP-CoolifyMigration
81
+ - **Branch:** main
82
+ - **Architecture:** Pure-logic cores with thin IO shells; saga engine with a
83
+ crash-safe journal
84
+
85
+ ## Features
86
+
87
+ - **Application-unaware mirroring** — a cleanly stopped stack makes a volume just
88
+ bytes. No per-engine logic, no supported-database allowlist. Postgres, MySQL,
89
+ MariaDB, MongoDB, Redis, KeyDB, Dragonfly, ClickHouse and anything else you run
90
+ are all handled the same way, because none of them are special once stopped.
91
+ - **Byte-exact** — `rsync -aHAXS --numeric-ids`, parallelised, with SHA-256 and
92
+ metadata verification on both sides. Never `chown`.
93
+ - **Failsafe** — every step has a compensating action, journalled to disk. The
94
+ source is never destroyed until you explicitly say so, so rollback is always
95
+ available. `resume` reconciles against reality, not against the journal.
96
+ - **DNS gate** — extracts every Traefik/Caddy hostname and refuses to start the
97
+ target while DNS still resolves to the old server, with an actionable cutover
98
+ checklist.
99
+ - **Honest about drift** — the target is built exactly as the source is
100
+ configured, but a tag is a pointer and a branch moves. We detect a floating
101
+ `latest` that could cross a database major, or a HEAD that has moved, and put
102
+ the concrete question to you rather than deciding for you.
103
+ - **Server migration** — relocate the whole Coolify instance, with the `APP_KEY`
104
+ treated as a first-class, asserted artifact rather than a lucky side effect.
105
+ - **Proven, not assumed** — an integration rig of two real sshd containers
106
+ asserts that uid 999, hardlinks, xattrs, sparse files and symlinks survive an
107
+ actual rsync, and that a wrong `chown` is caught by verification.
108
+
109
+ ## Architecture
110
+
111
+ | Layer | Role |
112
+ | ----- | ---- |
113
+ | `domain/` | Pure logic: classification, compose analysis, volume pairing, drift, state machine |
114
+ | `api/` | Coolify REST client with per-endpoint request whitelists |
115
+ | `discovery/` | Docker + API reconciliation; the label-based quiesce gate |
116
+ | `transfer/` | asyncssh, rsync planning, checksum verification |
117
+ | `journal/` | Append-only crash-safe state |
118
+ | `dns/` | FQDN extraction, authoritative resolution, cutover gate |
119
+ | `ui/` | Rich dashboard, wizard, and a plain non-TTY fallback |
120
+
121
+ > **Requires the instance API switched on (Settings > API — off by default) and a
122
+ > token with `root` or `read:sensitive`.** Without the switch every call is
123
+ > `403 "API is disabled."`, which looks like a token problem but is not. Without
124
+ > the scope, Coolify's `ApiSensitiveData` middleware silently omits `value`,
125
+ > `real_value` and `docker_compose_raw` from responses — no error, no redaction
126
+ > marker. The tool checks both at startup and fails closed.
127
+
128
+ ## Quick start
129
+
130
+ ```bash
131
+ pip install bg-coolify-migrate
132
+
133
+ export COOLIFY_URL="https://coolify.example.com"
134
+ export COOLIFY_TOKEN="..." # root or read:sensitive
135
+
136
+ coolify-migrate doctor # verify token scope + server reachability
137
+ coolify-migrate plan my-project --to target-server # dry run: no changes
138
+ coolify-migrate run my-project --to target-server
139
+ ```
140
+
141
+ Interrupted? `coolify-migrate resume <id>`. Regret it? `coolify-migrate rollback <id>`.
142
+
143
+ ## Server migration
144
+
145
+ ```bash
146
+ coolify-migrate server plan --to new-host.example.com
147
+ coolify-migrate server run --to new-host.example.com
148
+ ```
149
+
150
+ ## Documentation
151
+
152
+ See [`docs/`](docs/): installation, configuration, cli, architecture, safety,
153
+ server-migration, troubleshooting.
154
+
155
+ ## License
156
+
157
+ MIT © BAUER GROUP
@@ -0,0 +1,101 @@
1
+ <!-- AUTO-GENERATED FILE. DO NOT EDIT. Edit docs/README.template.MD instead. Generated 2026-07-16. -->
2
+ # BAUER GROUP - Coolify Migration Toolkit
3
+
4
+ [![PyPI](https://img.shields.io/pypi/v/bg-coolify-migrate.svg)](https://pypi.org/project/bg-coolify-migrate/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/bg-coolify-migrate.svg)](https://pypi.org/project/bg-coolify-migrate/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+ [![Tests](https://github.com/bauer-group/IP-CoolifyMigration/actions/workflows/tests.yml/badge.svg)](https://github.com/bauer-group/IP-CoolifyMigration/actions/workflows/tests.yml)
8
+
9
+ > **Today, Tomorrow, Together** | Building Better Software Together
10
+
11
+ Moves a Coolify **project — with its data —** between servers, and relocates a
12
+ whole Coolify instance to a new host. Failsafe, resumable, and rollback-capable.
13
+
14
+ Coolify can clone a resource to another server but deliberately will not move the
15
+ data. `VolumeCloneJob` and `CloneMe`'s `cloneVolumeData` flag exist upstream, but
16
+ PR #4777 shipped them **disabled**. The maintainer's stated blockers — permission
17
+ damage, job-queue spam at 50+ resources, no progress tracking, large-volume
18
+ failures — are all consequences of running inside Coolify's Laravel queue. An
19
+ external orchestrator has none of those constraints. That is what this is.
20
+
21
+ ## Repository Information
22
+
23
+ - **Version:** 0.1.0
24
+ - **Repository:** bauer-group/IP-CoolifyMigration
25
+ - **Branch:** main
26
+ - **Architecture:** Pure-logic cores with thin IO shells; saga engine with a
27
+ crash-safe journal
28
+
29
+ ## Features
30
+
31
+ - **Application-unaware mirroring** — a cleanly stopped stack makes a volume just
32
+ bytes. No per-engine logic, no supported-database allowlist. Postgres, MySQL,
33
+ MariaDB, MongoDB, Redis, KeyDB, Dragonfly, ClickHouse and anything else you run
34
+ are all handled the same way, because none of them are special once stopped.
35
+ - **Byte-exact** — `rsync -aHAXS --numeric-ids`, parallelised, with SHA-256 and
36
+ metadata verification on both sides. Never `chown`.
37
+ - **Failsafe** — every step has a compensating action, journalled to disk. The
38
+ source is never destroyed until you explicitly say so, so rollback is always
39
+ available. `resume` reconciles against reality, not against the journal.
40
+ - **DNS gate** — extracts every Traefik/Caddy hostname and refuses to start the
41
+ target while DNS still resolves to the old server, with an actionable cutover
42
+ checklist.
43
+ - **Honest about drift** — the target is built exactly as the source is
44
+ configured, but a tag is a pointer and a branch moves. We detect a floating
45
+ `latest` that could cross a database major, or a HEAD that has moved, and put
46
+ the concrete question to you rather than deciding for you.
47
+ - **Server migration** — relocate the whole Coolify instance, with the `APP_KEY`
48
+ treated as a first-class, asserted artifact rather than a lucky side effect.
49
+ - **Proven, not assumed** — an integration rig of two real sshd containers
50
+ asserts that uid 999, hardlinks, xattrs, sparse files and symlinks survive an
51
+ actual rsync, and that a wrong `chown` is caught by verification.
52
+
53
+ ## Architecture
54
+
55
+ | Layer | Role |
56
+ | ----- | ---- |
57
+ | `domain/` | Pure logic: classification, compose analysis, volume pairing, drift, state machine |
58
+ | `api/` | Coolify REST client with per-endpoint request whitelists |
59
+ | `discovery/` | Docker + API reconciliation; the label-based quiesce gate |
60
+ | `transfer/` | asyncssh, rsync planning, checksum verification |
61
+ | `journal/` | Append-only crash-safe state |
62
+ | `dns/` | FQDN extraction, authoritative resolution, cutover gate |
63
+ | `ui/` | Rich dashboard, wizard, and a plain non-TTY fallback |
64
+
65
+ > **Requires the instance API switched on (Settings > API — off by default) and a
66
+ > token with `root` or `read:sensitive`.** Without the switch every call is
67
+ > `403 "API is disabled."`, which looks like a token problem but is not. Without
68
+ > the scope, Coolify's `ApiSensitiveData` middleware silently omits `value`,
69
+ > `real_value` and `docker_compose_raw` from responses — no error, no redaction
70
+ > marker. The tool checks both at startup and fails closed.
71
+
72
+ ## Quick start
73
+
74
+ ```bash
75
+ pip install bg-coolify-migrate
76
+
77
+ export COOLIFY_URL="https://coolify.example.com"
78
+ export COOLIFY_TOKEN="..." # root or read:sensitive
79
+
80
+ coolify-migrate doctor # verify token scope + server reachability
81
+ coolify-migrate plan my-project --to target-server # dry run: no changes
82
+ coolify-migrate run my-project --to target-server
83
+ ```
84
+
85
+ Interrupted? `coolify-migrate resume <id>`. Regret it? `coolify-migrate rollback <id>`.
86
+
87
+ ## Server migration
88
+
89
+ ```bash
90
+ coolify-migrate server plan --to new-host.example.com
91
+ coolify-migrate server run --to new-host.example.com
92
+ ```
93
+
94
+ ## Documentation
95
+
96
+ See [`docs/`](docs/): installation, configuration, cli, architecture, safety,
97
+ server-migration, troubleshooting.
98
+
99
+ ## License
100
+
101
+ MIT © BAUER GROUP
@@ -0,0 +1,100 @@
1
+ # BAUER GROUP - Coolify Migration Toolkit
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/bg-coolify-migrate.svg)](https://pypi.org/project/bg-coolify-migrate/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/bg-coolify-migrate.svg)](https://pypi.org/project/bg-coolify-migrate/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+ [![Tests]({{REPO_URL}}/actions/workflows/tests.yml/badge.svg)]({{REPO_URL}}/actions/workflows/tests.yml)
7
+
8
+ > **Today, Tomorrow, Together** | Building Better Software Together
9
+
10
+ Moves a Coolify **project — with its data —** between servers, and relocates a
11
+ whole Coolify instance to a new host. Failsafe, resumable, and rollback-capable.
12
+
13
+ Coolify can clone a resource to another server but deliberately will not move the
14
+ data. `VolumeCloneJob` and `CloneMe`'s `cloneVolumeData` flag exist upstream, but
15
+ PR #4777 shipped them **disabled**. The maintainer's stated blockers — permission
16
+ damage, job-queue spam at 50+ resources, no progress tracking, large-volume
17
+ failures — are all consequences of running inside Coolify's Laravel queue. An
18
+ external orchestrator has none of those constraints. That is what this is.
19
+
20
+ ## Repository Information
21
+
22
+ - **Version:** {{VERSION}}
23
+ - **Repository:** {{REPO_FULL_NAME}}
24
+ - **Branch:** {{CURRENT_BRANCH}}
25
+ - **Architecture:** Pure-logic cores with thin IO shells; saga engine with a
26
+ crash-safe journal
27
+
28
+ ## Features
29
+
30
+ - **Application-unaware mirroring** — a cleanly stopped stack makes a volume just
31
+ bytes. No per-engine logic, no supported-database allowlist. Postgres, MySQL,
32
+ MariaDB, MongoDB, Redis, KeyDB, Dragonfly, ClickHouse and anything else you run
33
+ are all handled the same way, because none of them are special once stopped.
34
+ - **Byte-exact** — `rsync -aHAXS --numeric-ids`, parallelised, with SHA-256 and
35
+ metadata verification on both sides. Never `chown`.
36
+ - **Failsafe** — every step has a compensating action, journalled to disk. The
37
+ source is never destroyed until you explicitly say so, so rollback is always
38
+ available. `resume` reconciles against reality, not against the journal.
39
+ - **DNS gate** — extracts every Traefik/Caddy hostname and refuses to start the
40
+ target while DNS still resolves to the old server, with an actionable cutover
41
+ checklist.
42
+ - **Honest about drift** — the target is built exactly as the source is
43
+ configured, but a tag is a pointer and a branch moves. We detect a floating
44
+ `latest` that could cross a database major, or a HEAD that has moved, and put
45
+ the concrete question to you rather than deciding for you.
46
+ - **Server migration** — relocate the whole Coolify instance, with the `APP_KEY`
47
+ treated as a first-class, asserted artifact rather than a lucky side effect.
48
+ - **Proven, not assumed** — an integration rig of two real sshd containers
49
+ asserts that uid 999, hardlinks, xattrs, sparse files and symlinks survive an
50
+ actual rsync, and that a wrong `chown` is caught by verification.
51
+
52
+ ## Architecture
53
+
54
+ | Layer | Role |
55
+ | ----- | ---- |
56
+ | `domain/` | Pure logic: classification, compose analysis, volume pairing, drift, state machine |
57
+ | `api/` | Coolify REST client with per-endpoint request whitelists |
58
+ | `discovery/` | Docker + API reconciliation; the label-based quiesce gate |
59
+ | `transfer/` | asyncssh, rsync planning, checksum verification |
60
+ | `journal/` | Append-only crash-safe state |
61
+ | `dns/` | FQDN extraction, authoritative resolution, cutover gate |
62
+ | `ui/` | Rich dashboard, wizard, and a plain non-TTY fallback |
63
+
64
+ > **Requires the instance API switched on (Settings > API — off by default) and a
65
+ > token with `root` or `read:sensitive`.** Without the switch every call is
66
+ > `403 "API is disabled."`, which looks like a token problem but is not. Without
67
+ > the scope, Coolify's `ApiSensitiveData` middleware silently omits `value`,
68
+ > `real_value` and `docker_compose_raw` from responses — no error, no redaction
69
+ > marker. The tool checks both at startup and fails closed.
70
+
71
+ ## Quick start
72
+
73
+ ```bash
74
+ pip install bg-coolify-migrate
75
+
76
+ export COOLIFY_URL="https://coolify.example.com"
77
+ export COOLIFY_TOKEN="..." # root or read:sensitive
78
+
79
+ coolify-migrate doctor # verify token scope + server reachability
80
+ coolify-migrate plan my-project --to target-server # dry run: no changes
81
+ coolify-migrate run my-project --to target-server
82
+ ```
83
+
84
+ Interrupted? `coolify-migrate resume <id>`. Regret it? `coolify-migrate rollback <id>`.
85
+
86
+ ## Server migration
87
+
88
+ ```bash
89
+ coolify-migrate server plan --to new-host.example.com
90
+ coolify-migrate server run --to new-host.example.com
91
+ ```
92
+
93
+ ## Documentation
94
+
95
+ See [`docs/`](docs/): installation, configuration, cli, architecture, safety,
96
+ server-migration, troubleshooting.
97
+
98
+ ## License
99
+
100
+ MIT © BAUER GROUP
@@ -0,0 +1,68 @@
1
+ # Security Policy
2
+
3
+ > **{{COMPANY_NAME}}** | {{REPO_FULL_NAME}}
4
+
5
+ ## Supported Versions
6
+
7
+ | Version | Supported |
8
+ | ------- | ------------------ |
9
+ {{SUPPORTED_VERSIONS_TABLE}}
10
+
11
+ ## Reporting a Vulnerability
12
+
13
+ Report security issues privately to **security@bauer-group.com** or via
14
+ [GitHub Security Advisories]({{REPO_URL}}/security/advisories/new).
15
+
16
+ Please do not open a public issue for a vulnerability. We aim to acknowledge
17
+ within 2 business days and to ship a fix or mitigation within 30 days.
18
+
19
+ ## Threat model
20
+
21
+ This tool is operated by an administrator against infrastructure they already
22
+ control. It is not a multi-tenant service and has no privilege boundary of its
23
+ own. That said, it handles material worth protecting:
24
+
25
+ - **A Coolify API token with `root` / `read:sensitive`** can read every secret of
26
+ every project in the team. The tool requires such a token by design (Coolify
27
+ silently omits secret values otherwise), which makes the token the single most
28
+ sensitive input.
29
+ - **SSH access to both servers**, effectively as root.
30
+ - **`APP_KEY`** during instance migration, which decrypts Coolify's entire
31
+ credential store.
32
+
33
+ ## Security invariants
34
+
35
+ These are enforced in code and must never be weakened. See `AGENTS.md`.
36
+
37
+ 1. **Never `chown`.** Ownership is preserved numerically (`--numeric-ids`).
38
+ Coolify's own clone hardcodes `chown -R 1000:1000` and that is precisely what
39
+ corrupts postgres/mysql/redis (uid 999) and clickhouse (uid 101) volumes.
40
+ 2. **Never route volume data through the operator's workstation filesystem.**
41
+ 3. **Never disable host-key checking.** No `StrictHostKeyChecking=no`.
42
+ 4. **Never write SQL to `coolify-db`.** The REST API is the only write path.
43
+ 5. **Never log secrets** — environment variable values, private keys, `APP_KEY`.
44
+ Redaction is a structlog processor and is applied additively.
45
+ 6. **Never start a target whose FQDN still resolves to the source.**
46
+ 7. **Never delete the source before verification passes.**
47
+ 8. **Never swallow a stop failure**, and never trust a stop endpoint — poll the
48
+ Docker daemon.
49
+ 9. **Never string-replace a UUID to derive a volume name.** Pair by `mount_path`.
50
+
51
+ ## Credential handling
52
+
53
+ - Secrets are read from the environment or a `.env` file; never hardcoded, never
54
+ committed, never written to the journal.
55
+ - Ephemeral SSH keys minted for a direct transfer are revoked in a guaranteed
56
+ cleanup step, and the revocation is journalled so a crashed run still cleans up
57
+ on the next invocation.
58
+ - The journal stores resource UUIDs and host addresses. It never stores secret
59
+ values.
60
+
61
+ ## Scanning
62
+
63
+ Secret scanning (gitleaks + GitGuardian) runs on every push via the
64
+ `security-management` reusable workflow. Dependencies are monitored by Dependabot.
65
+
66
+ ---
67
+
68
+ _Last updated: {{LAST_UPDATED}}_