Carcin Migrator
Trojan-horse WordPress migration plugin for Carcin — REST-only API for lifting a WP site to Cloudflare (R2 + D1 + Workers) without SSH or hosting access.
by Carcin · github.com/carcinai/carcin-wp-migrator · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/carcinai/carcin-wp-migrator/archive/refs/heads/main.zipReadme
carcin-wp-migrator
Trojan-horse WordPress plugin that gives Carcin's migration pipeline complete, read-only-except-when-necessary access to a live WordPress site so it can be lifted onto a tenant's Cloudflare (R2 + D1 + Workers) infrastructure without SSH, cPanel, or filesystem access.
Deployment model
- Client invites
carcin@carcin.aias a WordPress Administrator. - Carcin operator (or a WP admin the client already trusts) installs
carcin-wp-migrator— either upload the built zip via Plugins → Add New → Upload, orwp plugin install <url-to-zip>from a shell. - On activation the plugin:
- Generates a 256-bit bearer token, hashed at rest in
wp_options(carcin_migrator_token_hash). - Registers REST routes under
carcin/v1. - Shows a one-time admin notice with the raw token (also on Tools → Carcin Migrator, self-clearing on first view).
- Generates a 256-bit bearer token, hashed at rest in
- Carcin's tenant Worker (
carcin-tenant-template,/connections/wordpress) calls the plugin's REST API with the token and streams the site into the tenant's provisioned R2 bucket + D1. - After cutover the plugin stays installed as the rollback path until
the client confirms migration success.
DELETE /carcin/v1/selfdeactivates + uninstalls;POST /rollback/armsets a frontend 302 to the new host as a live cutover switch.
REST surface — namespace carcin/v1
All routes are Authorization: Bearer <token>. Rate-limited to 6000
requests / 10s per IP (sized for a real migration burst, not for probing).
Read routes: GET /status, GET /manifest, GET /db/tables,
GET /db/schema?table=X, GET /db/dump?table=X&offset=N&limit=M,
GET /db/checksum?table=X, GET /fs/tree?path=&depth=&hash=,
GET /fs/file?path=.
Write routes (deliberately narrow): POST /fs/push-to-r2,
POST /verify, POST /rollback/arm, POST /rollback/disarm,
POST /token/rotate, DELETE /self.
Full details in SPEC.md.
Development
Local WP + plugin + smoke-test loop:
docker compose -f docker-compose.test.yml up -d
docker compose -f docker-compose.test.yml exec -T wpcli wp core install \
--url=http://localhost:18080 --title="Carcin Test" \
--admin_user=admin --admin_password=admin --admin_email=admin@example.com \
--skip-email --path=/var/www/html
docker compose -f docker-compose.test.yml exec -T wpcli wp plugin activate \
carcin-wp-migrator --path=/var/www/html
TOKEN=$(docker compose -f docker-compose.test.yml exec -T wpcli \
wp option get carcin_migrator_token_plain_once --path=/var/www/html)
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:18080/?rest_route=/carcin/v1/status"
The full tenant-side importer runs against the same fixture — see
carcinai/carcin-tenant-template
test/wp-import.e2e.test.ts (env-gated on CARCIN_WP_URL + CARCIN_WP_TOKEN).
Related repos
- Tenant importer:
carcin-tenant-template/src/wp-import.ts+src/wp-connection.ts - Connector registry:
carcin-core(services/connectors/registry.py, providerwordpress_plugin) - Cloud UI + orchestration:
carcin-cloud(app/(app)/connections/wordpress_plugin/connect/page.tsx,app/api/carcin-wp/{run,status}/route.ts) - Rollout runbook:
carcin-core/docs/runbooks/ship-wordpress-plugin-connector.md - Cross-repo orchestration spec:
carcin-core/specs/platform/wordpress-plugin-migration-orchestration.md
Distribution
Zipped copy served at
https://carcin.app/plugins/carcin-wp-migrator.zip
(staged in carcin-cloud/public/plugins/; shipped by CI on merge to
main).
Not yet on the WordPress.org plugin directory. See the handoff doc for why that matters and what to do next.