Every release, every fix, every improvement — in one place. See what's coming next on the roadmap →
The Windows agent now correctly splits migration into two phases separated by a reboot: Phase 1 clears caches and leaves the old domain/tenant, then reboots. Phase 2 runs automatically on the next service start and completes the Entra ID join and Intune enrollment. Device state is auto-detected from dsregcmd /status rather than inferred from payload flags.
Two-phase migration with reboot in between — Phase 1 (clear caches → leave domain → leave Azure AD/Intune → save Phase 2 config to registry → reboot in 30s). Phase 2 runs on the next MigratorAgent service start after reboot, waits for network, and completes Entra join + Intune enrollment.
Phase 2 enrollment supports three mechanisms in priority order: (1) Provisioning Package (.ppkg) from Intune → Devices → Enrollment → Bulk Enrollment Tokens — handles AAD join and MDM enrollment in one silent step; (2) enrollment URL via deviceenroller.exe post-reboot; (3) auto-enrollment registry keys as fallback.
DeviceState helper — parses dsregcmd /status output to detect DomainJoined, AzureAdJoined, and MdmEnrolled state. All phase commands now read actual device state before taking action rather than blindly running leave/join commands.
FullMigrationCommand — correct order enforced: cache clear first (while credentials still valid), then domain leave, then Azure AD/Intune leave, then Phase 2 registry save, then reboot. Previously Intune ran first and cache clear happened last.
DomainCommand — now auto-detects whether the machine is domain-joined from dsregcmd /status and leaves unconditionally if joined. Previously domain leave only ran if a destDomain was provided in the command payload.
IntuneCommand — checks IsAzureAdJoined and IsMdmEnrolled before running dsregcmd /leave. Uses deviceenroller.exe /c /e /servername for enrollment (correct inbox Windows tool post-reboot). Falls back to the auto-enrollment scheduled task if deviceenroller.exe fails.
AgentService — checks for pending Phase 2 registry state on every startup and completes enrollment before entering the normal check-in loop. Waits up to 5 minutes for network connectivity (retrying every 15s) before attempting Entra/Intune enrollment.
Fixed multiple issues in the Windows agent migration sequence: wrong execution order, no state detection before leaving tenant, and incorrect MDM enrollment API usage.
Cache clearing now happens before leaving old tenant credentials — previously Office/Teams/OneDrive reset ran last, after dsregcmd /leave had already invalidated the user's token. Apps now shut down cleanly while credentials are still valid.
Domain disjoin is now unconditional when the machine is domain-joined — previously DomainCommand only ran if destDomain was included in the command payload, meaning domain-joined machines were never automatically disjoined.
IntuneCommand no longer blindly runs dsregcmd /leave — it first reads enrollment status and only leaves if the device is actually Azure AD joined or MDM enrolled.
MDM enrollment via mdmregistration.dll removed — that API does not work that way. Replaced with deviceenroller.exe /c /e which is the correct Windows inbox tool for service-context silent MDM enrollment.
The agent download is now served directly from /downloads/MigratorAgent.exe on the web app rather than from GitHub releases. GitHub Actions builds the exe on a Windows runner first, injects it into the web Docker image, and Next.js serves it as a static file.
MigratorAgent.exe served from /downloads/MigratorAgent.exe — customers download the agent directly from themigrator.app, not from GitHub releases. URL shown in Settings > Devices installation instructions.
Release workflow restructured — build-agent (Windows runner) runs first and uploads the exe as a workflow artifact. build-and-push (Ubuntu runner) depends on build-agent, downloads the artifact into apps/web/public/downloads/ before building the web Docker image.
Fixed duplicate args declaration compile error in Program.cs — in .NET 8 top-level programs the args parameter is implicit and cannot be redeclared. Removed the explicit string[] args = Environment.GetCommandLineArgs() line.
Bumped System.Text.Json from 8.0.0 to 8.0.5 to resolve two high-severity vulnerability advisories flagged by the NuGet restore.
New Devices section in Settings lets IT admins register Windows devices, monitor their status, and send migration commands. A C# Windows service agent installs on target devices, checks in every 3 minutes, and executes commands sent from the dashboard.
Settings > Devices tab — shows all registered devices with status (Online / Offline / Migrating), last seen time, OS version, domain, IP, and agent version. Includes a Send command modal with payload JSON editor and a Remove button per device.
MigratorAgent.exe — .NET 8 self-contained Windows service. Install with: MigratorAgent.exe /install /org <orgId> /api <apiUrl>. Registers the device with the API, installs itself as a Windows service (sc create), and begins checking in automatically every 3 minutes.
Device authentication — each device generates a 32-byte random secret at registration. All subsequent check-ins include deviceId + deviceSecret. The API validates the secret via bcrypt comparison (secret never stored in plaintext).
Agent configuration persisted in HKLM\SOFTWARE\TheMigrator\Agent registry key — survives service restarts and reboots. Stores orgId, apiBaseUrl, deviceId, and deviceSecret.
Six command types supported: FULL_MIGRATION (complete domain + Intune + credential reset sequence), INTUNE_MIGRATION (leave and rejoin Intune/Entra only), DOMAIN_MIGRATION (leave/join Windows domain), OFFICE_RESET, TEAMS_RESET, ONEDRIVE_RESET (clear credential caches).
REST API — POST /v1/devices/register (no auth — device authenticates via orgId), POST /v1/devices/checkin, POST /v1/devices/result, GET /v1/devices (JWT auth), DELETE /v1/devices/:id, POST/GET /v1/devices/:id/commands, PATCH /v1/devices/:id/commands/:id/cancel.
Prisma — Device and DeviceCommand models with DeviceStatus (ONLINE / OFFLINE / MIGRATING), DeviceCommandType, and DeviceCommandStatus (PENDING / RUNNING / DONE / ERROR / CANCELLED) enums. Migration 20260514011740_add_devices applied.
Offline detection computed dynamically — a device is shown as OFFLINE if lastSeenAt is more than 10 minutes ago. No background job required; status is evaluated on each list query.
Phased rollouts are now first-class: the wizard supports automatic waves and custom batches with item-level assignment and CSV import. A new POST /v1/jobs/batch API endpoint enables batch creation programmatically. Maintenance mode lets admins pause all migrations and notify users instantly. Advanced filtering added to all four migration list pages.
Batch scheduling in the migration wizard — enable on the Schedule & options step to split items into multiple jobs that start at different times. Two modes: Automatic waves (equal-sized groups at a fixed interval) and Custom batches (manually defined groups with individual start times).
Item-level assignment in Custom batch mode — each batch card shows a searchable checkbox list of all available mailboxes, sites, or teams. Items already assigned to another batch are greyed out and labelled with that batch's name. 'Add unassigned' shortcut instantly fills a batch with every item not yet allocated.
CSV import per batch — click the CSV button on any batch card to paste a list of email addresses, display names, site names, or site URLs. TheMigrator parses the input, matches items case-insensitively, and reports unmatched entries before committing the selection.
POST /v1/jobs/batch API endpoint — creates multiple migration jobs in a single request. Each batch item specifies its own scheduledAt, mailboxes, mailboxMappings, or siteMappings while sharing the source tenant, destination tenant, type, and scope. Returns an ordered array of job IDs.
Maintenance mode — admins can toggle maintenance on/off from the Admin → System page, with an optional scheduled window (start/end datetime and message). A red 'Maintenance' banner appears at the top of every page (dashboard and public/marketing) with no caching delay. An amber 'Scheduled maintenance' banner appears before the window begins.
Advanced filtering on migration list pages — each of the four migration pages (Email, SharePoint, OneDrive, Teams) now has a filter bar with tenant domain search, date range pickers, and a type-specific item filter: mailbox email for Email/OneDrive, site URL or name for SharePoint, team name for Teams. Filter state shows in the footer alongside the filtered/total job count.
Batch launch success screen — when multiple jobs are created via batch scheduling, the SuccessStep shows a per-batch job list with individual links to each job's live progress page, plus a 'View all jobs' button to the migrations dashboard.
Review step Schedule card — now shows batch configuration summary (mode, wave size or batch count, gap between waves or first batch start time) when batch scheduling is enabled, instead of the single-job schedule fields.
Docker image pruning — the Unraid update script (scripts/update.sh) now runs docker image prune -f after pulling and restarting containers, preventing orphaned images from accumulating on disk.
GitHub Actions GHCR login resilience — the release workflow now retries the GHCR login up to 3 times with a 15-second gap, handling transient Docker Hub / GHCR network timeouts that were occasionally breaking builds.
Docs — batch scheduling, CSV import, and maintenance mode documented under /docs (Migrations section). API reference updated with full POST /v1/jobs/batch endpoint specification including parameter table, response schema, and a multi-wave cURL example.
Teams migration completely redesigned. Teams Channels is now team-centric — pick which Teams workspaces to migrate and map them to destination teams. Teams Chat is a separate workload with its own user mapping and Chat.Read.All permission.
Teams Channels workload now uses a team picker — select specific Teams workspaces from the source tenant, map them to destination teams, and migrate channels, message history, channel files, and member rosters per selected team. Previously only user-level mapping was shown for Teams migrations.
Teams Chat is now a separate workload toggle — controls migration of private 1:1 and group chat history between users. Has its own user mapping table and requires the Chat.Read.All application permission on the source tenant's App Registration.
Team discovery — TheMigrator now discovers all Microsoft Teams workspaces in the source tenant via the Microsoft Graph groups API (groups filtered by resourceProvisioningOptions eq 'Team') and stores them as DiscoveredTeam records including name, description, and member count. Triggered automatically during tenant discovery when the TEAMS workload is enabled.
GET /v1/tenants/:id/teams endpoint — returns all discovered Teams workspaces for a tenant alongside the latest discovery job metadata.
Wizard mapping step for Teams now shows two distinct sections: a team mapping table (Teams Channels enabled) for picking and matching source teams to destination teams by name, and a user mapping table (Teams Chat enabled) for mapping per-user chat history. Both sections can be active simultaneously.
Review step scope summary updated — Teams Channels and Teams Chat shown as separate line items with independent enabled/disabled indicators and accurate token cost calculation per workload.
Admin panel gains a full logs viewer with filters. SharePoint wizard gets editable auto-matched site rows and CSV bulk upload. Discovery reliability fixed across SharePoint and OneDrive.
Admin logs page — two tabs: Migration logs (filterable by level, tenant domain, job ID, and date range) and API logs (filterable by HTTP method, path, and errors-only toggle). Paginated, color-coded, and auto-refreshing.
API request logging — every API request is logged to the database with method, path, status code, duration, and user/org context. Powers the new API logs tab in the admin panel.
Editable site mappings in the SharePoint wizard — auto-matched site rows can now be clicked to reveal the destination dropdown and override the match, matching the UX of the email mailbox mapping step.
CSV upload for SharePoint site mappings — 'Upload CSV' and '↓ CSV template' buttons added to the site mapping step. Format: source_site,destination_site (matched by name or URL). Works alongside the existing CSV upload for email/OneDrive/Teams user mapping.
Dry-run jobs incorrectly showing status 'Done' — jobs completed via dry run now correctly display as 'Dry Run' in the dashboard, all-jobs table, and per-mailbox progress. Only real migrations show 'Done'.
SharePoint site discovery stuck at 'Discovering…' — the $expand=drive($select=quota) parameter added to the sites?search=* Graph API call caused a 400 error that silently broke all site discovery. Fixed by separating site listing (no expand) from per-site storage fetch (individual drive calls). Site count now always persists even if storage fetch fails.
Mailbox sizeInBytes returning zero — $select=sizeInBytes on the mailFolder Graph API endpoint returns HTTP 400 (it is not an OData schema property). Fixed by dropping $select entirely and reading the field from the raw JSON response, which includes it without requiring $select.
Light mode email templates — reset password and verification emails now render correctly in light mode. Previous version had hardcoded dark hex values.
Password reset token TTL not enforced on page load — the /reset-password page now validates the token expiry on load and redirects to /forgot-password if the token has expired, matching the server-side enforcement.
Admin panel theme — light and dark mode CSS variable coverage extended across overview, customers, jobs, and system pages. Amber status variables added to the log badge system.
Storage and licensing validation before every migration launch, full product documentation, REST API reference, migration guides hub, and a smarter wizard throughout.
Storage & licensing checks — the wizard review step now runs pre-flight validation before launch: flags Exchange Online Plan 1 (>50 GB) and Plan 2 (>100 GB) mailbox overflows for Email, compares total source data against destination SharePoint quota for SharePoint, and checks each user's source drive against their destination OneDrive quota. Affected items are shown in an expandable table sorted by size.
Product documentation at /docs — single-page reference covering introduction, quick start, core concepts, connecting tenants, migrations (scope, dry run, scheduling), monitoring, security, and billing.
REST API reference at /api-reference — full endpoint docs for all v1 tenants and jobs endpoints, with request/response schemas, parameter tables, cURL examples, authentication guide, rate limits, and error code reference.
Migration guides hub at /guides — eight step-by-step technical guides across six categories (Planning, Email, SharePoint, OneDrive, Teams, Cutover & DNS) with difficulty badges, duration estimates, prerequisites, and expandable substep lists.
Reports page — summary metrics, jobs-by-type and jobs-by-status bar charts, 6-month activity chart, and completed jobs table with CSV export.
Dedicated migration list pages for Email, SharePoint, OneDrive, and Teams — each with status filter chips, type-specific columns, and data transfer totals.
Migration wizard source and destination steps now show workload-specific stats: mailboxes for Email, sites and storage for SharePoint, drives and storage for OneDrive, and users for Teams.
Workload not enabled warning in wizard — amber banner with required permissions listed and a direct link to Tenant settings when a tenant is missing the required workload.
New migration CTAs on type-specific pages now pre-select the migration type and skip the type selection step.
Wizard mapping step is now fully type-aware — Email, OneDrive, and Teams show user mapping; SharePoint shows site mapping.
Enterprise plan billing page now shows ∞ for token balance and free tokens, hides the usage progress bar, and removes the token rates section.
Items column in migration tables capped correctly at total items — prevents inflated counts from legacy job data.
Full admin panel shipped with light/dark theme support, tenant management, and real-time job monitoring.
Admin panel — customers, all jobs, queue status, worker health, system metrics, and revenue overview in a single SPA.
Light and dark theme support across both the customer portal and admin panel, with system preference detection and a persistent toggle.
Pause all jobs, delete tenants, delete users, and reset passwords directly from the admin panel.
Job detail page — live log viewer with SSE streaming, per-mailbox progress table, timeline, error list, and log export.
Admin overview and system pages fully rewritten to use CSS variables — no more hardcoded dark hex values breaking light mode.
Sidebar Reports link wired up. Tenants page updated with workload toggles and app registration status.
Theme toggle icon replaced with SVG — unicode characters were near-invisible on certain system fonts.
TypeScript strict-mode errors in admin customers and jobs pages resolved with typed inline fallback objects.
Core migration workers shipped for all four workloads. Real-time progress via SSE. Dry run mode.
Email migration processor — mailboxes, calendars, contacts, and distribution lists migrated via Microsoft Graph API application permissions.
SharePoint migration processor — site collections, document libraries, lists, pages, and permission inheritance.
OneDrive migration processor — personal drives with file metadata and sharing links preserved.
Teams migration processor — channels, message history, channel files, and member rosters.
BullMQ job queue with Redis — 8 concurrent workers per node, 3-attempt exponential backoff, stalled-job detection.
Server-Sent Events (SSE) endpoint for live job log streaming — no polling required.
Dry run mode — validates mappings and estimates item counts without moving any data.
Per-mailbox progress tracked in database — individual status, item counts, start and completion times.
OAuth tokens encrypted at rest with AES-256-GCM before being written to the database.
M365 tenant connect flow, 6-step migration wizard, and the core customer dashboard.
M365 tenant OAuth connect flow — admin consent URL, callback handler, and encrypted token storage.
Bring your own Azure App Registration — connect with a custom Client ID and Secret for full permission control.
6-step migration wizard — type selection, source tenant, destination tenant, item mapping, schedule & options, review and launch.
User auto-mapping — source and destination mailboxes matched by UPN prefix, with manual override and skip support.
Site auto-matching for SharePoint — source sites matched to destination by name, with dropdown selection for unmatched sites.
Customer dashboard — active job cards, metric summary, recent activity feed, and all-jobs table.
Scheduled migrations — choose to start immediately or at a specific date and time.
Billing with Stripe — token bundles, one-time purchases, Stripe customer portal, and invoice history.
Initial platform scaffolding — monorepo, auth, database schema, and infrastructure.
Turborepo monorepo with pnpm workspaces — web, api, admin, worker, and shared packages.
Next.js 14 App Router customer portal with Tailwind CSS and shadcn/ui components.
NestJS API with Prisma ORM and PostgreSQL — full domain model for orgs, tenants, jobs, logs, and mailbox progress.
NextAuth authentication with organisation support, JWT access tokens, and email/password sign-in.
Docker Compose stack — web, api, admin, worker, PostgreSQL, and Redis fully containerised.
GitHub Actions CI/CD — lint, type-check, build, push Docker images to GHCR, and deploy via update script.
Design system implemented — Syne + DM Sans + JetBrains Mono, CSS variable palette, light and dark themes.
We build what customers need. If something's missing, tell us — most requests make it into the next release.