diff --git a/mobile/lib/domain/jobs/job.dart b/mobile/lib/domain/jobs/job.dart
new file mode 100644
index 0000000000..90775bd995
--- /dev/null
+++ b/mobile/lib/domain/jobs/job.dart
@@ -0,0 +1,223 @@
+import 'dart:async';
+import 'dart:isolate';
+import 'dart:ui';
+
+import 'package:flutter/services.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/jobs/job_context.dart';
+import 'package:immich_mobile/domain/jobs/job_messages.dart';
+import 'package:immich_mobile/domain/jobs/job_status.dart';
+import 'package:immich_mobile/domain/jobs/job_types.dart';
+import 'package:immich_mobile/domain/services/log.service.dart';
+import 'package:immich_mobile/entities/store.entity.dart';
+import 'package:immich_mobile/extensions/completer_extensions.dart';
+import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
+import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
+import 'package:immich_mobile/providers/db.provider.dart';
+import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
+import 'package:immich_mobile/utils/bootstrap.dart';
+import 'package:immich_mobile/utils/debug_print.dart';
+import 'package:isar/isar.dart';
+import 'package:logging/logging.dart';
+
+part 'job_entrypoint.dart';
+
+final _jobLogger = Logger('Job');
+
+abstract class Job {
+ Isolate? _isolate;
+ SendPort? _jobPort;
+ ReceivePort? _receivePort;
+ final Map>> _jobProgressControllers = {};
+ bool get isRunning => _isolate != null;
+
+ // ═══════════════════════════════════════════════════════════
+ // ABSTRACT METHODS / FIELDS - Concrete implementations override these
+ // ═══════════════════════════════════════════════════════════
+
+ JobType get type;
+
+ Duration get idleInterval;
+ Future onIdle();
+
+ Duration get shutdownTimeout => const Duration(seconds: 5);
+
+ // ═══════════════════════════════════════════════════════════
+ // CONCRETE METHODS
+ // ═══════════════════════════════════════════════════════════
+
+ Timer? _idleTimer;
+
+ void _resetIdleTimer() {
+ _idleTimer?.cancel();
+ _idleTimer = Timer.periodic(idleInterval, (_) async {
+ await onIdle();
+ });
+ }
+
+ void _cancelIdleTimer() {
+ _idleTimer?.cancel();
+ _idleTimer = null;
+ }
+
+ Future start() async {
+ if (_isolate != null) return;
+
+ final rootToken = RootIsolateToken.instance;
+ if (rootToken == null) {
+ throw StateError('RootIsolateToken is not available. Ensure that the job is started from the main isolate.');
+ }
+
+ _receivePort = ReceivePort();
+ _receivePort!.listen(_handleMessage);
+
+ _isolate = await Isolate.spawn(_isolateEntryPoint, (
+ type: type,
+ token: rootToken,
+ mainPort: _receivePort!.sendPort,
+ ), debugName: type.id);
+
+ _resetIdleTimer();
+ }
+
+ String trigger(Input input) {
+ if (_jobPort == null) {
+ throw StateError('Job not started. Call start() before triggering jobs.');
+ }
+
+ final jobId = _generateJobId();
+ _jobPort!.send(JobTriggerRequest(jobId: jobId, input: input));
+ _jobProgressControllers[jobId] = StreamController>.broadcast();
+ _resetIdleTimer();
+ return jobId;
+ }
+
+ Future