Monster Cloud orchestrates the complete migration lifecycle — discovery, planning, execution, data transfer, and DNS cutover — across all six major cloud providers and on-premises infrastructure.
Monster Cloud On-Prem orchestrates bare-metal and VM migrations entirely within your private infrastructure — discovery, planning, Ansible execution, data transfer, and traffic cutover — with zero cloud dependencies.
Prepare environment variables, generate a signing key, start the stack, verify readiness. The default mode is preview — no cloud operations will execute until you explicitly set PROVIDER_EXECUTION_MODE=apply.
# 1. Create your local env file
cp .env.example .env
perl -i -pe 's/JWT_SECRET=.*/JWT_SECRET='"$(openssl rand -hex 32)"'/' .env
# 2. Generate a stable Ed25519 signing key and paste it into .env
go run ./cmd/api --gen-key
# → ED25519_PRIVATE_KEY=<base64>
# 3. Start the API stack
docker compose up -d db redis api
# 4. Verify readiness
curl http://localhost:8080/readyz
# → {"status":"ready"}
PROVIDER_EXECUTION_MODE=apply, every provider action returns the exact CLI command it would run — nothing touches your cloud. Inspect, validate, then flip the switch.
DATABASE_URL
JWT_SECRET
ED25519_PRIVATE_KEY
docker compose up -d--profile temporal upGET /healthz
— livenessGET /readyz
— readiness + DBGET /metrics
— expvar countersGET /debug/pprof/
— Go profiles
On-prem mode requires Ansible ≥ 2.14 and SSH access to your target hosts. No cloud credentials needed. The same preview-first safety default applies — set PROVIDER_EXECUTION_MODE=apply only when ready.
# 1. Generate a stable Ed25519 signing key
./cloudmigrator-api --gen-key
# → ED25519_PRIVATE_KEY=<base64> — add to .env
# 2. Set required on-prem bootstrap variables
export PROVIDER=onprem
export CLOUDMIGRATOR_LICENSE_KEY=<issued-license-key>
export ADMIN_EMAIL=admin@example.com
export ADMIN_PASSWORD='change-me-long-random-password'
export DATABASE_URL=sqlite:///var/lib/cloudmigrator/cloudmigrator.db
# 3. Install without Docker or start the container stack
sudo ./install-onprem.sh
# or: docker compose up -d db redis api
# 4. Verify readiness
curl http://localhost:8080/readyz
# → {"status":"ready","provider":"onprem"}
ansible-playbook invocations. Monster Cloud generates the playbooks at runtime — you only need a working SSH inventory and sudo access on target hosts.
DATABASE_URL
JWT_SECRET
ED25519_PRIVATE_KEY
ANSIBLE_INVENTORY
~/.ssh/id_ed25519.docker compose up -d--profile temporal upPOST /auth/register creates a user and tenant in one call. It returns a signed JWT immediately — no email verification step.plan=free. Subsequent registrations get plan=pending_payment. Demo credentials (demo@monstercloud.io / Demo1234!) are seeded automatically by migration 010.
tenant_id, roles, and exp (8-hour TTL). The UI calls /auth/refresh automatically when ≤30 minutes remain. On any 401, the session is cleared and the user is redirected to signin.html. JWT_SECRET is mandatory — the server will not start without it.
Every migration is modelled as a chain of four objects. Understanding these unlocks the entire API.
tenantId.PlannedActions (create_network, provision_database, etc.), risk scores, approval gates, and a dry-run simulation result.ready → running → completed), action approvals, command evidence, and rollback artifacts. Immutable once completed.LedgerEntry. The chain is tamper-evident; each entry hashes the previous, forming an audit log.Each step is a discrete API call. Run them sequentially, or let the pipeline endpoint orchestrate them end-to-end.
ansible -m setup and parses the returned facts.executionAllowed: true/false before you spend a single API call on execution.passed/warnings/errors. A 422 response means errors must be fixed before proceeding.translationConfidence < 0.75, IAM actions, and database provisioning are gated pending per-action approval. Approve them individually or in bulk before execution begins.UPDATE ... WHERE status NOT IN ('running','completed','failed'). If another pod already claimed it, the request returns 400 immediately — no double-execution is possible in multi-pod environments. For each action, an idempotency check (describe-before-create) runs against the target provider. If the resource already exists, the step is marked already_exists and skipped — safe to re-run after partial failures. In apply mode, the real CLI command executes and the result is signed into the evidence ledger.rollbackFn — the reverse command — so you can restore in one call.AWS, GCP, Azure, OVHcloud, Kubernetes, and OnPrem implement the same nine action types. Oracle is handled as a specialised assessment connector for database portability. NetApp ONTAP is handled as a specialised storage connector — SVMs, volumes, LUNs, export policies, and SnapMirror relationships — across six deployment variants (on-prem, Cloud Volumes ONTAP, FSx for ONTAP, Azure NetApp Files, Google Cloud NetApp Volumes, and StorageGRID). The six generic-action executors dispatch to the native CLI — no provider SDKs in the binary; NetApp is provisioned over the ONTAP REST API instead.
The on-prem executor maps every action type to an auto-generated Ansible playbook. All SSH connections use your inventory's configured key — no secrets stored in the API process.
The engine is chosen automatically based on the resource kind and target provider. Credentials are injected at runtime from your secrets backend — never written to logs.
cmd.Env — never written to evidence, logs, or the database.
# Step 1: review the commands before anything runs
curl -X POST http://localhost:8080/v1/migrations/data-plan \
-H "Authorization: Bearer $JWT" \
-d '{"executionId":"exec-abc123"}'
# Step 2: inspect the generated commands in the response
# e.g. "pg_dump -h $SOURCE_HOST ... | pg_restore -h $TARGET_HOST ..."
# Step 3: set credentials and execute
export SOURCE_HOST=db.old.internal TARGET_HOST=db.new.internal
export CM_SECRETS_BACKEND=aws-secretsmanager
curl -X POST http://localhost:8080/v1/migrations/data-execute \
-H "Authorization: Bearer $JWT" \
-d '{"executionId":"exec-abc123"}'
Every cutover operation generates a rollbackFn — the exact command to restore the previous state — stored in the database for instant recovery.
route53 change-resource-record-setsgcloud dns record-sets updateaz network dns record-set updatekubectl patch ingress
targetWeight: 10 for 10% canary, increment incrementally.elbv2 modify-listenerkubectl patch virtualservice
Layer 1 is built-in and always enforced. Layer 2 is operator-defined JSON loaded from POLICY_RULES_FILE at startup.
{
"allowedTargetProviders": ["gcp", "aws"],
"allowedRegions": ["us-central1", "eu-west-1"],
"maxResourceCount": 500,
"maxAutonomyLevel": 3,
"blockPublicResources": true,
"blockIamWithoutReview": true,
"requireComplianceTags": ["cost-center", "owner"],
"maxBlastRadius": "medium",
"customBlockReasons": {
"has_databases": "Open a JIRA ticket for DBA sign-off first"
}
}
publicAccess: true.
always, has_databases, has_kubernetes, cross_region.
Every layer has independent controls. Compromising any one layer does not bypass the others.
tenantId, actorId, and role claims. The exp claim is mandatory and enforced — tokens without an exp claim, or with an expired one, are rejected with 401. The UI calls /auth/refresh automatically when the token has ≤30 minutes remaining.viewer, operator, approver, admin. Tenant isolation enforced at every store method — row-level separation.ED25519_PRIVATE_KEY to the same value on every pod; generate with --gen-key.cmd.Env — never written to logs, evidence, or the database. Four backends: AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets, env vars.application/json — never text/plain. X-Content-Type-Options, X-Frame-Options security headers. CORS gated by CORS_ORIGINS.INCR/EXPIRE per-tenant distributed rate limiting. In-process per-tenant fallback when Redis is unavailable — rate limiting is never disabled, but in multi-pod deployments the effective rate per tenant multiplies by the number of pods (each pod has its own bucket). For accurate enforcement across replicas, set REDIS_URL. Tune with RATE_LIMIT_RPS and RATE_LIMIT_BURST.The worker binary uses only the Temporal REST API. No gRPC, no SDK — it ships as a 5 MB static binary with zero new dependencies.
POST /v1/migrations/execute returns 202 Accepted immediately. The execution runs in a background goroutine with context.Background() — client disconnects never cancel an in-flight migration. Poll GET /v1/executions/{id} for status. Concurrent calls to execute the same ID are blocked by an atomic Postgres claim.export TEMPORAL_HOST=https://<ns>.<account>.tmprl.cloud
export TEMPORAL_API_KEY=<temporal-cloud-api-key>
export CLOUDMIGRATOR_API_URL=http://cloudmigrator-api:8080
export CLOUDMIGRATOR_SERVICE_TOKEN=<from-api-startup-log>
export CLOUDMIGRATOR_TENANT_ID=tenant-acme
./cloudmigrator-worker
# → cloudmigrator temporal worker host=https://... ns=default queue=cloudmigrator-executions
CLOUDMIGRATOR_SERVICE_TOKEN is not set, and printed to the startup log. Copy it to a Kubernetes Secret or secrets manager so the worker can authenticate across restarts.
X-Tenant-ID / X-Actor / X-Roles headers (used by the worker to act on behalf of a tenant) must also supply a matching X-Service-Token header. The API validates it with a constant-time HMAC comparison against CLOUDMIGRATOR_SERVICE_TOKEN. Requests missing or supplying an incorrect token are rejected with 401 — this prevents any external caller from spoofing tenant identity via headers.
v3 expands the product from a migration API into a full control plane. These sections cover the systems that were missing from the earlier manual.
platform_api.go: drift detection, cost intelligence, compliance checks, observability, runbooks, network fabric, collaboration, and tenant operations. Count these 34 platform routes in addition to the migration API, SaaS/billing operations, growth endpoints, and public trust-anchor endpoints.RATE_LIMIT_RPS and RATE_LIMIT_BURST for production traffic./v1/oracle/assess, Dockerfile.oracle, and a build-tagged godror connector. Use it to scan Oracle schemas, detect Oracle-specific SQL constructs, estimate licence exposure, and recommend Aurora PostgreSQL, AlloyDB, Azure PostgreSQL, Redshift, BigQuery, or Synapse targets.exp claim, and a SQLite driver shim that silently dropped all but the first statement in a migration. Every fix is pinned to an executable regression test, and scripts/check_tree_drift.sh now fails CI if the cloud and on-prem auth/tenancy code diverge again.CLOUDMIGRATOR_LICENSE_KEY. Put the issued key in the systemd environment file, Docker secret, or Kubernetes secret before first boot.DATABASE_URL=sqlite:///var/lib/cloudmigrator/cloudmigrator.db for small teams; move to Postgres for HA or heavy concurrency.ADMIN_EMAIL and ADMIN_PASSWORD on first boot. The bootstrap creates the initial administrator and should be removed or rotated after setup.install-onprem.sh installs the binary, creates the service user, writes the environment file, applies migrations, and registers a systemd service for no-Docker deployments.X-Tenant-ID/X-Actor/X-Roles headers with no service-token check, accepted JWTs with no exp claim, and never wired up token revocation. All three are now fail-closed and covered by regression tests.# No-Docker / systemd path
sudo ./install-onprem.sh
sudo systemctl enable --now cloudmigrator-api
sudo systemctl status cloudmigrator-api
# Real-time execution stream
curl -N -H "Authorization: Bearer $TOKEN" http://localhost:8080/v1/executions/stream
Archive-synced version: route list and environment variables have been checked against cmd/api/main.go, internal/api/http.go, platform_api.go, and .env.example.
dsn=stub for demo data or build with -tags oracle for live discovery