Skip to content

orbital migrate verify

orbital migrate verify is the third step in a migration. It samples source-side artifacts via the same per-source adapter the planner uses and compares each (path, sha256, size) tuple against the matching OrbitalReg-side artifact row. The output is a pass/fail verdict plus a per-decision divergence list the operator can act on.

The check is read-only — verify never mutates artifacts, repositories, or migration_runs. It also never persists the source-side credential server-side: re-supply the token on each verify run so a credential rotation takes effect immediately.

The CLI is a thin layer over POST /api/admin/migrate/verify.

When to run verify

StrategyWhen to run
big-bangAfter every apply pass, to catch drift before consumers notice.
blue-greenBefore flipping consumers, to confirm the green side is byte-equivalent to the source.
lazy-proxyPeriodically — verify reports missing_on_orbital for not-yet-cached artifacts, but sha256_mismatch flags real corruption regardless of warm-up state.

Verify is also the integration test you run after a --resume'd apply, or after re-running orbital migrate apply against a partially-corrupt target.

Decisions

Each comparison resolves to one of:

DecisionMeaning
matchBoth sides have the artifact and sha256 + size agree. The happy path.
no_source_hashThe source listing didn't carry an sha256. Informational; doesn't fail the verdict.
missing_on_orbitalSource has the artifact; OrbitalReg doesn't. Expected during a lazy-proxy warm-up; failure for big-bang / blue-green.
sha256_mismatchBoth sides have the artifact but content hashes differ. The most actionable failure — re-import the affected artifact.
size_mismatchNeither side reported sha256 but sizes diverge. Treated as failure.
orbital_repo_missingSource has artifacts but the OrbitalReg-side repository row doesn't exist. Apply was never run, or the repo was skipped (unsupported format / slug collision).

Verify passes when every comparison resolves to match or no_source_hash. Anything else makes the CLI exit with the server-error exit code so a CI gate can fail the pipeline.

Quick start

bash
# 1. Generate a plan (Phase A)
orbital migrate plan \
  --source jfrog \
  --endpoint https://jfrog.example.com \
  --token "$JFROG_TOKEN" \
  --output-file migration-plan.json

# 2. Apply it (Phase B)
orbital migrate apply --plan migration-plan.json

# 3. Verify the result (Phase C)
orbital migrate verify \
  --plan migration-plan.json \
  --token "$JFROG_TOKEN"

Sample output (pretty-printed, all-pass):

text
Migration verify — PASS — source=jfrog project=jfrog-import
  Repos:        12 examined, 0 skipped
  Sample:       300 examined (per-repo target: 25)
  Strategy:     big-bang
  Decisions:
    match                  300

Next steps:
  - Migration looks healthy. Re-run periodically to catch drift.

Sample output (one repo with byte-level corruption):

text
Migration verify — FAIL — source=jfrog project=jfrog-import
  Repos:        12 examined, 0 skipped
  Sample:       300 examined (per-repo target: 25)
  Strategy:     big-bang
  Decisions:
    match                  297
    sha256_mismatch        3
  Failures:
    [sha256_mismatch] maven-prod/com/acme/foo/1.0.0/foo-1.0.0.jar — source=ab12cd34ef56… orbital=ff00aa11bb22…
    [sha256_mismatch] maven-prod/com/acme/foo/1.0.0/foo-1.0.0.pom — source=… orbital=…
    [sha256_mismatch] maven-prod/com/acme/foo/1.0.0/foo-1.0.0-sources.jar — source=… orbital=…

Next steps:
  - sha256_mismatch: re-import the affected artifacts.
  - missing_on_orbital: re-run `orbital migrate apply` (or wait — lazy-proxy fills on demand).
  - orbital_repo_missing: the apply step did not create this repo (unsupported format / slug collision).

Flags

FlagPurpose
--plan <path>The plan envelope produced by orbital migrate plan --output-file. Required.
--token <bearer>Source-registry credential. Required. Same shape as plan (bearer / keys:… / sa:…).
--endpoint <url>Override the source endpoint captured in the plan (fresh DNS, mid-migration cutover).
--project-slug <slug>OrbitalReg project to verify against (default <source>-import).
--sample <n>Per-run total sample, split evenly across migrated repos. Default per-repo: 25.
--format <fmt>Limit verify to one format (maven, npm, docker, …).
--jsonEmit the JSON verify-result instead of the pretty-printed view.

Plus the same per-source extras plan accepts (--github-org, --gitlab-project-id, --azure-organization, --aws-region, --gcp-project-id, etc.) — supply them when the source needs them.

Sample sizing

Verify caps each repo at 250 artifacts on the server side, regardless of --sample, so a deliberately oversized request can't pin a production source for minutes. The default per-repo (when --sample is zero) is 25 — enough to surface a systemic divergence on a well-populated repo while keeping the total runtime sub-30-seconds against a representative source.

If you set --sample N, the CLI splits N evenly across the migrated repos that pass --format filtering. So --sample 250 --format maven on a plan with 5 maven repos visits 50 artifacts per repo; --sample 600 on 12 mixed-format repos visits 50 per repo across the board.

What verify does not check

  • Synthetic format-native pulls (npm pack, mvn dependency:get, docker pull) — out of scope for this Phase. Format-native pulls test the full publish-side stack (manifest indexing, virtual repo routing, RBAC), which is a meaningfully different surface from artifact byte equivalence; they ship in a follow-up.
  • Permission spot-checks — verify never simulates synthetic-user pulls. RBAC differences between the source and OrbitalReg are intentional in most migrations (the whole point is often consolidating ten ad-hoc Nexus realms into one OrbitalReg project), so an automated diff would mostly flag deliberate changes.
  • Async metadata like build-info envelopes or per-artifact signatures — those land via their own ingestion paths and have their own verification surfaces (/admin/trust#sigstore, /api/v1/builds).

The byte-equivalence check verify ships answers the procurement question ("did the migration land what it claimed?") in one command; the gaps above are tracked separately.

Exit codes

CodeMeaning
0Verify passed (every comparison resolved to match or no_source_hash).
1User error — bad flags, missing --plan, missing --token.
2Auth error — OrbitalReg session rejected (orbital auth login).
3Server error — including pass=false. The divergence list is on stdout above this exit code; a CI gate that fails-on-non-zero will catch a corrupt migration without parsing JSON.

For CI scripting, prefer --json plus a JSON parser over screen-scraping the pretty output:

bash
result=$(orbital migrate verify --plan plan.json --token "$T" --json)
pass=$(echo "$result" | jq -r .pass)
if [ "$pass" != "true" ]; then
  echo "$result" | jq '.rows[] | select(.decision != "match" and .decision != "no_source_hash")'
  exit 1
fi

Roadmap context

orbital migrate verify is Phase C of item 83 in the product roadmap. Phase A (plan) and Phase B (apply) shipped earlier; Phase D (finalize) and Phase E (importer-plugin glue) hang off the same /api/admin/migrate/* mount and orbital migrate subcommand subtree.

Released under the Apache-2.0 License.