/** * OpenMDM Core Types * * These types define the core data structures for the MDM system. * Designed to be database-agnostic and framework-agnostic. */ type DeviceStatus = 'pending' | 'enrolled' | 'unenrolled' | 'blocked'; interface Device { id: string; externalId?: string | null; enrollmentId: string; status: DeviceStatus; model?: string | null; manufacturer?: string | null; osVersion?: string | null; serialNumber?: string | null; imei?: string | null; macAddress?: string | null; androidId?: string | null; policyId?: string | null; agentVersion?: string | null; lastHeartbeat?: Date | null; lastSync?: Date | null; /** * Base64-encoded SPKI public key the device registered on first * enrollment. Requests from this device can be verified against * this key via `verifyDeviceRequest` / `verifyEcdsaSignature` from * `@openmdm/core`. `null` on devices that enrolled via the legacy * HMAC path and have never been migrated. */ publicKey?: string | null; /** * How the device originally enrolled. `'hmac'` for the legacy * shared-secret path; `'pinned-key'` for the device-pinned ECDSA * path. `null` on pre-Phase-2b device rows that predate the * column (treated as `'hmac'`). */ enrollmentMethod?: 'hmac' | 'pinned-key' | null; batteryLevel?: number | null; storageUsed?: number | null; storageTotal?: number | null; location?: DeviceLocation | null; installedApps?: InstalledApp[] | null; tags?: Record | null; metadata?: Record | null; createdAt: Date; updatedAt: Date; } interface DeviceLocation { latitude: number; longitude: number; accuracy?: number; timestamp: Date; } interface InstalledApp { packageName: string; version: string; versionCode?: number; installedAt?: Date; } interface CreateDeviceInput { enrollmentId: string; externalId?: string; model?: string; manufacturer?: string; osVersion?: string; serialNumber?: string; imei?: string; macAddress?: string; androidId?: string; policyId?: string; tags?: Record; metadata?: Record; } interface UpdateDeviceInput { externalId?: string | null; status?: DeviceStatus; policyId?: string | null; agentVersion?: string | null; model?: string; manufacturer?: string; osVersion?: string; batteryLevel?: number | null; storageUsed?: number | null; storageTotal?: number | null; lastHeartbeat?: Date; lastSync?: Date; installedApps?: InstalledApp[]; location?: DeviceLocation; tags?: Record; metadata?: Record; /** Phase 2b — pin a new public key on first enroll. */ publicKey?: string | null; /** Phase 2b — record which auth path the device enrolled via. */ enrollmentMethod?: 'hmac' | 'pinned-key' | null; } interface DeviceFilter { status?: DeviceStatus | DeviceStatus[]; policyId?: string; groupId?: string; search?: string; tags?: Record; limit?: number; offset?: number; } interface DeviceListResult { devices: Device[]; total: number; limit: number; offset: number; } interface Policy { id: string; name: string; description?: string | null; isDefault: boolean; settings: PolicySettings; createdAt: Date; updatedAt: Date; } interface PolicySettings { kioskMode?: boolean; mainApp?: string; allowedApps?: string[]; kioskExitPassword?: string; lockStatusBar?: boolean; lockNavigationBar?: boolean; lockSettings?: boolean; lockPowerButton?: boolean; blockInstall?: boolean; blockUninstall?: boolean; bluetooth?: HardwareControl; wifi?: HardwareControl; gps?: HardwareControl; mobileData?: HardwareControl; camera?: HardwareControl; microphone?: HardwareControl; usb?: HardwareControl; nfc?: HardwareControl; systemUpdatePolicy?: SystemUpdatePolicy; updateWindow?: TimeWindow; passwordPolicy?: PasswordPolicy; encryptionRequired?: boolean; factoryResetProtection?: boolean; safeBootDisabled?: boolean; heartbeatInterval?: number; locationReportInterval?: number; locationEnabled?: boolean; wifiConfigs?: WifiConfig[]; vpnConfig?: VpnConfig; applications?: PolicyApplication[]; custom?: Record; } type HardwareControl = 'on' | 'off' | 'user'; type SystemUpdatePolicy = 'auto' | 'windowed' | 'postpone' | 'manual'; interface TimeWindow { start: string; end: string; } interface PasswordPolicy { required: boolean; minLength?: number; complexity?: 'none' | 'numeric' | 'alphanumeric' | 'complex'; maxFailedAttempts?: number; expirationDays?: number; historyLength?: number; } interface WifiConfig { ssid: string; securityType: 'none' | 'wep' | 'wpa' | 'wpa2' | 'wpa3'; password?: string; hidden?: boolean; autoConnect?: boolean; } interface VpnConfig { type: 'pptp' | 'l2tp' | 'ipsec' | 'openvpn' | 'wireguard'; server: string; username?: string; password?: string; certificate?: string; config?: Record; } interface PolicyApplication { packageName: string; action: 'install' | 'update' | 'uninstall'; version?: string; required?: boolean; autoUpdate?: boolean; } interface CreatePolicyInput { name: string; description?: string; isDefault?: boolean; settings: PolicySettings; } interface UpdatePolicyInput { name?: string; description?: string | null; isDefault?: boolean; settings?: PolicySettings; } interface Application { id: string; name: string; packageName: string; version: string; versionCode: number; url: string; hash?: string | null; size?: number | null; minSdkVersion?: number | null; showIcon: boolean; runAfterInstall: boolean; runAtBoot: boolean; isSystem: boolean; isActive: boolean; metadata?: Record | null; createdAt: Date; updatedAt: Date; } interface CreateApplicationInput { name: string; packageName: string; version: string; versionCode: number; url: string; hash?: string; size?: number; minSdkVersion?: number; showIcon?: boolean; runAfterInstall?: boolean; runAtBoot?: boolean; isSystem?: boolean; metadata?: Record; } interface UpdateApplicationInput { name?: string; version?: string; versionCode?: number; url?: string; hash?: string | null; size?: number | null; minSdkVersion?: number | null; showIcon?: boolean; runAfterInstall?: boolean; runAtBoot?: boolean; isActive?: boolean; metadata?: Record | null; } interface DeployTarget { devices?: string[]; policies?: string[]; groups?: string[]; } interface AppVersion { id: string; applicationId: string; packageName: string; version: string; versionCode: number; url: string; hash?: string | null; size?: number | null; releaseNotes?: string | null; isMinimumVersion: boolean; createdAt: Date; } interface AppRollback { id: string; deviceId: string; packageName: string; fromVersion: string; fromVersionCode: number; toVersion: string; toVersionCode: number; reason?: string | null; status: 'pending' | 'in_progress' | 'completed' | 'failed'; error?: string | null; initiatedBy?: string | null; createdAt: Date; completedAt?: Date | null; } interface CreateAppRollbackInput { deviceId: string; packageName: string; toVersionCode: number; reason?: string; initiatedBy?: string; } type CommandType = 'reboot' | 'shutdown' | 'sync' | 'lock' | 'unlock' | 'wipe' | 'factoryReset' | 'installApp' | 'uninstallApp' | 'updateApp' | 'runApp' | 'clearAppData' | 'clearAppCache' | 'shell' | 'setPolicy' | 'grantPermissions' | 'exitKiosk' | 'enterKiosk' | 'setWifi' | 'screenshot' | 'getLocation' | 'setVolume' | 'sendNotification' | 'whitelistBattery' | 'enablePermissiveMode' | 'setTimeZone' | 'enableAdb' | 'rollbackApp' | 'updateAgent' | 'custom'; type CommandStatus = 'pending' | 'sent' | 'acknowledged' | 'completed' | 'failed' | 'cancelled'; interface Command { id: string; deviceId: string; type: CommandType; payload?: Record | null; status: CommandStatus; result?: CommandResult | null; error?: string | null; createdAt: Date; sentAt?: Date | null; acknowledgedAt?: Date | null; completedAt?: Date | null; } interface CommandResult { success: boolean; message?: string; data?: unknown; } interface SendCommandInput { deviceId: string; type: CommandType; payload?: Record; } interface CommandFilter { deviceId?: string; status?: CommandStatus | CommandStatus[]; type?: CommandType | CommandType[]; limit?: number; offset?: number; } type EventType = 'device.enrolled' | 'device.unenrolled' | 'device.blocked' | 'device.heartbeat' | 'device.locationUpdated' | 'device.statusChanged' | 'device.policyChanged' | 'app.installed' | 'app.uninstalled' | 'app.updated' | 'app.crashed' | 'app.started' | 'app.stopped' | 'policy.applied' | 'policy.failed' | 'command.received' | 'command.acknowledged' | 'command.completed' | 'command.failed' | 'security.tamper' | 'security.rootDetected' | 'security.screenLocked' | 'security.screenUnlocked' | 'custom'; interface MDMEvent { id: string; deviceId: string; type: EventType; payload: T; createdAt: Date; } interface EventFilter { deviceId?: string; type?: EventType | EventType[]; startDate?: Date; endDate?: Date; limit?: number; offset?: number; } interface Group { id: string; name: string; description?: string | null; policyId?: string | null; parentId?: string | null; metadata?: Record | null; createdAt: Date; updatedAt: Date; } interface CreateGroupInput { name: string; description?: string; policyId?: string; parentId?: string; metadata?: Record; } interface UpdateGroupInput { name?: string; description?: string | null; policyId?: string | null; parentId?: string | null; metadata?: Record | null; } type EnrollmentMethod = 'qr' | 'nfc' | 'zero-touch' | 'knox' | 'manual' | 'app-only' | 'adb'; interface EnrollmentRequest { macAddress?: string; serialNumber?: string; imei?: string; androidId?: string; model: string; manufacturer: string; osVersion: string; sdkVersion?: number; agentVersion?: string; agentPackage?: string; method: EnrollmentMethod; timestamp: string; /** * Signature over the canonical enrollment message. * * Phase 2a (HMAC path, backwards-compatible): hex-encoded * HMAC-SHA256 of the nine-field pipe-delimited canonical form * (see `concepts/enrollment`). * * Phase 2b (device-pinned-key path, preferred): base64-encoded * DER ECDSA-P256 signature produced by the device's Keystore * private key, over `canonicalEnrollmentMessage(...)` including * the public key and challenge. The server distinguishes the * two paths by whether `publicKey` is present on the request. */ signature: string; /** * Base64-encoded SPKI public key (EC P-256) the device generated * in its Keystore. When present, enrollment follows the Phase 2b * device-pinned-key path and `signature` must verify as an ECDSA * signature against this key. The server pins this key on the * device row on first successful enroll; any future enroll * attempting a different key for the same `enrollmentId` is * rejected with `PublicKeyMismatchError`. * * Omit for the legacy HMAC path. Callers that want to migrate a * fleet gradually can run both paths in parallel. */ publicKey?: string; /** * Opaque challenge issued by `GET /agent/enroll/challenge`. Must * be present whenever `publicKey` is present — the server uses * it to prevent replay of captured enrollment payloads. The * challenge is single-use: the server consumes it on first * successful verify. */ attestationChallenge?: string; policyId?: string; groupId?: string; } interface EnrollmentResponse { deviceId: string; enrollmentId: string; policyId?: string; policy?: Policy; serverUrl: string; pushConfig: PushConfig; token: string; refreshToken?: string; tokenExpiresAt?: Date; } /** * Single-use nonce issued by `GET /agent/enroll/challenge` and * consumed on first successful verify of a device-pinned-key * enrollment. A persisted record; the `consume*` adapter methods * enforce the single-use invariant. */ interface EnrollmentChallenge { challenge: string; expiresAt: Date; consumedAt?: Date | null; createdAt: Date; } /** * Result of calling `verifyDeviceRequest`. Callers pattern-match on * `ok` and, when `false`, on `reason` to decide their response * shape: * * - `not-found` — unknown device id. Return 401. * - `no-pinned-key` — device exists but never migrated off the * legacy HMAC path. Caller should fall back * to their HMAC verifier, or fail if * they've completed their migration. * - `signature-invalid` — signature did not verify against the * pinned key. Return 401. Never re-pin * in response to this. */ type DeviceIdentityVerification = { ok: true; device: Device; } | { ok: false; reason: 'not-found'; } | { ok: false; reason: 'no-pinned-key'; device: Device; } | { ok: false; reason: 'signature-invalid'; device: Device; }; interface PushConfig { provider: 'fcm' | 'mqtt' | 'websocket' | 'polling'; fcmSenderId?: string; mqttUrl?: string; mqttTopic?: string; mqttUsername?: string; mqttPassword?: string; wsUrl?: string; pollingInterval?: number; } interface Heartbeat { deviceId: string; timestamp: Date; batteryLevel: number; isCharging: boolean; batteryHealth?: 'good' | 'overheat' | 'dead' | 'cold' | 'unknown'; storageUsed: number; storageTotal: number; memoryUsed: number; memoryTotal: number; networkType?: 'wifi' | 'cellular' | 'ethernet' | 'none'; networkName?: string; signalStrength?: number; ipAddress?: string; location?: DeviceLocation; installedApps: InstalledApp[]; runningApps?: string[]; isRooted?: boolean; isEncrypted?: boolean; screenLockEnabled?: boolean; agentVersion?: string; policyVersion?: string; lastPolicySync?: Date; } interface PushToken { id: string; deviceId: string; provider: 'fcm' | 'mqtt' | 'websocket'; token: string; isActive: boolean; createdAt: Date; updatedAt: Date; } interface RegisterPushTokenInput { deviceId: string; provider: 'fcm' | 'mqtt' | 'websocket'; token: string; } interface MDMConfig { /** Database adapter for persistence */ database: DatabaseAdapter; /** Authentication/authorization configuration */ auth?: AuthConfig; /** Push notification provider configuration */ push?: PushProviderConfig; /** Device enrollment configuration */ enrollment?: EnrollmentConfig; /** Server URL (used in enrollment responses) */ serverUrl?: string; /** APK/file storage configuration */ storage?: StorageConfig; /** Outbound webhook configuration */ webhooks?: WebhookConfig; /** Plugins to extend functionality */ plugins?: MDMPlugin[]; /** Event handlers */ onDeviceEnrolled?: (device: Device) => Promise; onDeviceUnenrolled?: (device: Device) => Promise; onDeviceBlocked?: (device: Device) => Promise; onHeartbeat?: (device: Device, heartbeat: Heartbeat) => Promise; onCommand?: (command: Command) => Promise; onEvent?: (event: MDMEvent) => Promise; /** Multi-tenancy configuration */ multiTenancy?: { enabled: boolean; defaultTenantId?: string; tenantResolver?: (context: unknown) => Promise; }; /** Authorization (RBAC) configuration */ authorization?: { enabled: boolean; defaultRole?: string; }; /** Audit logging configuration */ audit?: { enabled: boolean; retentionDays?: number; }; /** Scheduling configuration */ scheduling?: { enabled: boolean; timezone?: string; }; /** Plugin storage configuration */ pluginStorage?: { adapter: 'database' | 'memory'; }; /** * Structured logger. Replaces OpenMDM's internal `console.*` calls * so log output lands in the host application's logging pipeline * (pino, winston, bunyan, OTEL collector, etc.) instead of raw * stderr. * * The shape is a strict subset of the pino / winston / bunyan * interface — any of those can be passed directly. If omitted, a * default logger that writes to the console with an `[openmdm]` * prefix is used. To silence OpenMDM entirely, pass a no-op * implementation (see `createSilentLogger()` in the package * exports). */ logger?: Logger; } /** * Minimal structured-logger interface OpenMDM calls internally. * * The shape is deliberately the pino-compatible subset: an optional * context object as the first argument followed by a message string. * pino, winston, bunyan, and most other structured loggers accept * this shape natively. * * Implementations should be side-effect-free on unconfigured levels * (a production logger filtered to `info` should still accept * `.debug()` calls cheaply). */ interface Logger { /** Human-ignorable, high-volume tracing. Off in production by default. */ debug(context: LogContext, message: string): void; debug(message: string): void; /** Normal operational events. Enrollment, policy changes, command delivery. */ info(context: LogContext, message: string): void; info(message: string): void; /** Something is wrong but the server is still running. Retries, fallbacks, degraded modes. */ warn(context: LogContext, message: string): void; warn(message: string): void; /** Something failed and a request/operation did not complete. */ error(context: LogContext, message: string): void; error(message: string): void; /** * Return a new logger with the given fields attached to every * subsequent call. Used by managers and plugins to scope logs to * a specific subsystem without repeating context. */ child(bindings: LogContext): Logger; } /** * Arbitrary structured context attached to a log line. Values must * be JSON-serializable so host loggers can ship them to any backend. */ type LogContext = Record; interface StorageConfig { /** Storage provider (s3, local, custom) */ provider: 's3' | 'local' | 'custom'; /** S3 configuration */ s3?: { bucket: string; region: string; accessKeyId?: string; secretAccessKey?: string; endpoint?: string; presignedUrlExpiry?: number; }; /** Local storage path */ localPath?: string; /** Custom storage adapter */ customAdapter?: { upload: (file: Buffer, key: string) => Promise; getUrl: (key: string) => Promise; delete: (key: string) => Promise; }; } interface WebhookConfig { /** Webhook endpoints to notify */ endpoints?: WebhookEndpoint[]; /** Retry configuration */ retry?: { maxRetries?: number; initialDelay?: number; maxDelay?: number; }; /** Sign webhooks with HMAC secret */ signingSecret?: string; } interface WebhookEndpoint { /** Unique identifier */ id: string; /** Webhook URL */ url: string; /** Events to trigger this webhook */ events: (EventType | '*')[]; /** Custom headers */ headers?: Record; /** Whether endpoint is active */ enabled: boolean; } interface AuthConfig { /** Get current user from request context */ getUser: (context: unknown) => Promise; /** Check if user has admin privileges */ isAdmin?: (user: unknown) => Promise; /** Check if user can access specific device */ canAccessDevice?: (user: unknown, deviceId: string) => Promise; /** Device JWT secret (for device auth tokens) */ deviceTokenSecret?: string; /** Device token expiration in seconds (default: 365 days) */ deviceTokenExpiration?: number; } interface PushProviderConfig { provider: 'fcm' | 'mqtt' | 'websocket' | 'polling'; fcmCredentials?: string | Record; fcmProjectId?: string; mqttUrl?: string; mqttUsername?: string; mqttPassword?: string; mqttTopicPrefix?: string; wsPath?: string; wsPingInterval?: number; pollingInterval?: number; } interface EnrollmentConfig { /** Auto-enroll devices with valid signature */ autoEnroll?: boolean; /** HMAC secret for device signature verification (Phase 2a path) */ deviceSecret: string; /** Allowed enrollment methods */ allowedMethods?: EnrollmentMethod[]; /** Default policy for new devices */ defaultPolicyId?: string; /** Default group for new devices */ defaultGroupId?: string; /** Require manual approval for enrollment */ requireApproval?: boolean; /** Custom enrollment validation */ validate?: (request: EnrollmentRequest) => Promise; /** * Phase 2b device-pinned-key configuration. Optional — when * omitted, enrollment continues to accept the Phase 2a HMAC path * exclusively, matching pre-0.9 behaviour. */ pinnedKey?: PinnedKeyConfig; } /** * Device-pinned-key enrollment options. See * `docs/concepts/enrollment` for the full flow. */ interface PinnedKeyConfig { /** * Require every new enrollment to use the pinned-key path. When * `true`, requests without `publicKey` are rejected. When `false` * (the default), both paths coexist during rollout — the server * pins a public key when one is provided, falls back to HMAC when * it isn't. */ required?: boolean; /** * TTL for enrollment challenges, in seconds. Defaults to 300 * (5 minutes). Challenges are single-use; this only bounds how * long an unused challenge stays valid. */ challengeTtlSeconds?: number; } interface DatabaseAdapter { findDevice(id: string): Promise; findDeviceByEnrollmentId(enrollmentId: string): Promise; listDevices(filter?: DeviceFilter): Promise; createDevice(data: CreateDeviceInput): Promise; updateDevice(id: string, data: UpdateDeviceInput): Promise; deleteDevice(id: string): Promise; countDevices(filter?: DeviceFilter): Promise; findPolicy(id: string): Promise; findDefaultPolicy(): Promise; listPolicies(): Promise; createPolicy(data: CreatePolicyInput): Promise; updatePolicy(id: string, data: UpdatePolicyInput): Promise; deletePolicy(id: string): Promise; findApplication(id: string): Promise; findApplicationByPackage(packageName: string, version?: string): Promise; listApplications(activeOnly?: boolean): Promise; createApplication(data: CreateApplicationInput): Promise; updateApplication(id: string, data: UpdateApplicationInput): Promise; deleteApplication(id: string): Promise; findCommand(id: string): Promise; listCommands(filter?: CommandFilter): Promise; createCommand(data: SendCommandInput): Promise; updateCommand(id: string, data: Partial): Promise; getPendingCommands(deviceId: string): Promise; createEvent(event: Omit): Promise; listEvents(filter?: EventFilter): Promise; findGroup(id: string): Promise; listGroups(): Promise; createGroup(data: CreateGroupInput): Promise; updateGroup(id: string, data: UpdateGroupInput): Promise; deleteGroup(id: string): Promise; listDevicesInGroup(groupId: string): Promise; addDeviceToGroup(deviceId: string, groupId: string): Promise; removeDeviceFromGroup(deviceId: string, groupId: string): Promise; getDeviceGroups(deviceId: string): Promise; findPushToken(deviceId: string, provider: string): Promise; upsertPushToken(data: RegisterPushTokenInput): Promise; deletePushToken(deviceId: string, provider?: string): Promise; listAppVersions?(packageName: string): Promise; createAppVersion?(data: Omit): Promise; setMinimumVersion?(packageName: string, versionCode: number): Promise; getMinimumVersion?(packageName: string): Promise; createRollback?(data: CreateAppRollbackInput): Promise; updateRollback?(id: string, data: Partial): Promise; listRollbacks?(filter?: { deviceId?: string; packageName?: string; }): Promise; getGroupChildren?(parentId: string | null): Promise; getGroupAncestors?(groupId: string): Promise; getGroupDescendants?(groupId: string): Promise; getGroupTree?(rootId?: string): Promise; getGroupEffectivePolicy?(groupId: string): Promise; moveGroup?(groupId: string, newParentId: string | null): Promise; getGroupHierarchyStats?(): Promise; /** * Persist a new single-use enrollment challenge. The adapter * should store it with `consumed_at = null` and enforce a * primary-key constraint on `challenge` so duplicate inserts * fail loudly. */ createEnrollmentChallenge?(challenge: EnrollmentChallenge): Promise; /** * Look up a challenge by its opaque value. Returns `null` if not * found. Does NOT filter on expiry — the core layer checks * freshness so the adapter stays dumb. */ findEnrollmentChallenge?(challenge: string): Promise; /** * Atomically mark a challenge as consumed. Must set * `consumed_at = now()` and return the updated row only when the * challenge was previously unused. Adapters should implement * this as a conditional UPDATE (e.g. Postgres * `UPDATE ... WHERE consumed_at IS NULL RETURNING *`) so two * concurrent consume attempts cannot both succeed. */ consumeEnrollmentChallenge?(challenge: string): Promise; /** * Delete expired, unconsumed challenges. Called periodically by * the core layer; adapters can no-op if they rely on a TTL index * elsewhere. */ pruneExpiredEnrollmentChallenges?(now: Date): Promise; findTenant?(id: string): Promise; findTenantBySlug?(slug: string): Promise; listTenants?(filter?: TenantFilter): Promise; createTenant?(data: CreateTenantInput): Promise; updateTenant?(id: string, data: UpdateTenantInput): Promise; deleteTenant?(id: string): Promise; getTenantStats?(tenantId: string): Promise; findUser?(id: string): Promise; findUserByEmail?(email: string, tenantId?: string): Promise; listUsers?(filter?: UserFilter): Promise; createUser?(data: CreateUserInput): Promise; updateUser?(id: string, data: UpdateUserInput): Promise; deleteUser?(id: string): Promise; findRole?(id: string): Promise; listRoles?(tenantId?: string): Promise; createRole?(data: CreateRoleInput): Promise; updateRole?(id: string, data: UpdateRoleInput): Promise; deleteRole?(id: string): Promise; assignRoleToUser?(userId: string, roleId: string): Promise; removeRoleFromUser?(userId: string, roleId: string): Promise; getUserRoles?(userId: string): Promise; createAuditLog?(data: CreateAuditLogInput): Promise; listAuditLogs?(filter?: AuditLogFilter): Promise; deleteAuditLogs?(filter: { olderThan?: Date; tenantId?: string; }): Promise; findScheduledTask?(id: string): Promise; listScheduledTasks?(filter?: ScheduledTaskFilter): Promise; createScheduledTask?(data: CreateScheduledTaskInput): Promise; updateScheduledTask?(id: string, data: UpdateScheduledTaskInput): Promise; deleteScheduledTask?(id: string): Promise; getUpcomingTasks?(hours: number): Promise; createTaskExecution?(data: { taskId: string; }): Promise; updateTaskExecution?(id: string, data: Partial): Promise; listTaskExecutions?(taskId: string, limit?: number): Promise; enqueueMessage?(data: EnqueueMessageInput): Promise; dequeueMessages?(deviceId: string, limit?: number): Promise; peekMessages?(deviceId: string, limit?: number): Promise; acknowledgeMessage?(messageId: string): Promise; failMessage?(messageId: string, error: string): Promise; retryFailedMessages?(maxAttempts?: number): Promise; purgeExpiredMessages?(): Promise; getQueueStats?(tenantId?: string): Promise; getPluginValue?(pluginName: string, key: string): Promise; setPluginValue?(pluginName: string, key: string, value: unknown): Promise; deletePluginValue?(pluginName: string, key: string): Promise; listPluginKeys?(pluginName: string, prefix?: string): Promise; clearPluginData?(pluginName: string): Promise; getDashboardStats?(tenantId?: string): Promise; getDeviceStatusBreakdown?(tenantId?: string): Promise; getEnrollmentTrend?(days: number, tenantId?: string): Promise; getCommandSuccessRates?(tenantId?: string): Promise; getAppInstallationSummary?(tenantId?: string): Promise; transaction?(fn: () => Promise): Promise; } interface PushAdapter { /** Send push message to a device */ send(deviceId: string, message: PushMessage): Promise; /** Send push message to multiple devices */ sendBatch(deviceIds: string[], message: PushMessage): Promise; /** Register device push token */ registerToken?(deviceId: string, token: string): Promise; /** Unregister device push token */ unregisterToken?(deviceId: string): Promise; /** Subscribe device to topic */ subscribe?(deviceId: string, topic: string): Promise; /** Unsubscribe device from topic */ unsubscribe?(deviceId: string, topic: string): Promise; } interface PushMessage { type: string; payload?: Record; priority?: 'high' | 'normal'; ttl?: number; collapseKey?: string; } interface PushResult { success: boolean; messageId?: string; error?: string; } interface PushBatchResult { successCount: number; failureCount: number; results: Array<{ deviceId: string; result: PushResult; }>; } interface MDMPlugin { /** Unique plugin name */ name: string; /** Plugin version */ version: string; /** Called when MDM is initialized */ onInit?(mdm: MDMInstance): Promise; /** Called when MDM is destroyed */ onDestroy?(): Promise; /** Additional routes to mount */ routes?: PluginRoute[]; /** Middleware to apply to all routes */ middleware?: PluginMiddleware[]; /** Extend enrollment process */ onEnroll?(device: Device, request: EnrollmentRequest): Promise; /** Extend device processing */ onDeviceEnrolled?(device: Device): Promise; onDeviceUnenrolled?(device: Device): Promise; onHeartbeat?(device: Device, heartbeat: Heartbeat): Promise; /** Extend policy processing */ policySchema?: Record; validatePolicy?(settings: PolicySettings): Promise<{ valid: boolean; errors?: string[]; }>; applyPolicy?(device: Device, policy: Policy): Promise; /** Extend command processing */ commandTypes?: CommandType[]; executeCommand?(device: Device, command: Command): Promise; } interface PluginRoute { method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; path: string; handler: (context: unknown) => Promise; auth?: boolean; admin?: boolean; } type PluginMiddleware = (context: unknown, next: () => Promise) => Promise; interface WebhookManager { /** Deliver an event to all matching webhook endpoints */ deliver(event: MDMEvent): Promise; /** Add a webhook endpoint at runtime */ addEndpoint(endpoint: WebhookEndpoint): void; /** Remove a webhook endpoint */ removeEndpoint(endpointId: string): void; /** Update a webhook endpoint */ updateEndpoint(endpointId: string, updates: Partial): void; /** Get all configured endpoints */ getEndpoints(): WebhookEndpoint[]; /** Test a webhook endpoint with a test payload */ testEndpoint(endpointId: string): Promise; } interface WebhookDeliveryResult { endpointId: string; success: boolean; statusCode?: number; error?: string; retryCount: number; deliveredAt?: Date; } interface MDMInstance { /** Device management */ devices: DeviceManager; /** Policy management */ policies: PolicyManager; /** Application management */ apps: ApplicationManager; /** Command management */ commands: CommandManager; /** Group management */ groups: GroupManager; /** Tenant management (if multi-tenancy enabled) */ tenants?: TenantManager; /** Authorization management (RBAC) */ authorization?: AuthorizationManager; /** Audit logging */ audit?: AuditManager; /** Scheduled task management */ schedules?: ScheduleManager; /** Persistent message queue */ messageQueue?: MessageQueueManager; /** Dashboard analytics */ dashboard?: DashboardManager; /** Plugin storage */ pluginStorage?: PluginStorageAdapter; /** Push notification service */ push: PushAdapter; /** Webhook delivery (if configured) */ webhooks?: WebhookManager; /** Database adapter */ db: DatabaseAdapter; /** Structured logger. Already scoped to the `openmdm` namespace. Plugins should call `.child({...})` to scope further. */ logger: Logger; /** Configuration */ config: MDMConfig; /** Subscribe to events */ on(event: T, handler: EventHandler): () => void; /** Emit an event */ emit(event: T, data: EventPayloadMap[T]): Promise; /** Process device enrollment */ enroll(request: EnrollmentRequest): Promise; /** Process device heartbeat */ processHeartbeat(deviceId: string, heartbeat: Heartbeat): Promise; /** Verify device token */ verifyDeviceToken(token: string): Promise<{ deviceId: string; } | null>; /** Get loaded plugins */ getPlugins(): MDMPlugin[]; /** Get plugin by name */ getPlugin(name: string): MDMPlugin | undefined; } interface DeviceManager { get(id: string): Promise; getByEnrollmentId(enrollmentId: string): Promise; list(filter?: DeviceFilter): Promise; create(data: CreateDeviceInput): Promise; update(id: string, data: UpdateDeviceInput): Promise; delete(id: string): Promise; assignPolicy(deviceId: string, policyId: string | null): Promise; addToGroup(deviceId: string, groupId: string): Promise; removeFromGroup(deviceId: string, groupId: string): Promise; getGroups(deviceId: string): Promise; sendCommand(deviceId: string, input: Omit): Promise; sync(deviceId: string): Promise; reboot(deviceId: string): Promise; lock(deviceId: string, message?: string): Promise; wipe(deviceId: string, preserveData?: boolean): Promise; } interface PolicyManager { get(id: string): Promise; getDefault(): Promise; list(): Promise; create(data: CreatePolicyInput): Promise; update(id: string, data: UpdatePolicyInput): Promise; delete(id: string): Promise; setDefault(id: string): Promise; getDevices(policyId: string): Promise; applyToDevice(policyId: string, deviceId: string): Promise; } interface ApplicationManager { get(id: string): Promise; getByPackage(packageName: string, version?: string): Promise; list(activeOnly?: boolean): Promise; register(data: CreateApplicationInput): Promise; update(id: string, data: UpdateApplicationInput): Promise; delete(id: string): Promise; activate(id: string): Promise; deactivate(id: string): Promise; deploy(packageName: string, target: DeployTarget): Promise; installOnDevice(packageName: string, deviceId: string, version?: string): Promise; uninstallFromDevice(packageName: string, deviceId: string): Promise; } interface CommandManager { get(id: string): Promise; list(filter?: CommandFilter): Promise; send(input: SendCommandInput): Promise; cancel(id: string): Promise; acknowledge(id: string): Promise; complete(id: string, result: CommandResult): Promise; fail(id: string, error: string): Promise; getPending(deviceId: string): Promise; } interface GroupManager { get(id: string): Promise; list(): Promise; create(data: CreateGroupInput): Promise; update(id: string, data: UpdateGroupInput): Promise; delete(id: string): Promise; getDevices(groupId: string): Promise; addDevice(groupId: string, deviceId: string): Promise; removeDevice(groupId: string, deviceId: string): Promise; getChildren(groupId: string): Promise; getTree(rootId?: string): Promise; getAncestors(groupId: string): Promise; getDescendants(groupId: string): Promise; move(groupId: string, newParentId: string | null): Promise; getEffectivePolicy(groupId: string): Promise; getHierarchyStats(): Promise; } interface GroupTreeNode extends Group { children: GroupTreeNode[]; depth: number; path: string[]; effectivePolicyId?: string | null; } interface GroupHierarchyStats { totalGroups: number; maxDepth: number; groupsWithDevices: number; groupsWithPolicies: number; } type TenantStatus = 'active' | 'suspended' | 'pending'; interface Tenant { id: string; name: string; slug: string; status: TenantStatus; settings?: TenantSettings | null; metadata?: Record | null; createdAt: Date; updatedAt: Date; } interface TenantSettings { maxDevices?: number; maxUsers?: number; features?: string[]; branding?: { logo?: string; primaryColor?: string; }; } interface CreateTenantInput { name: string; slug: string; settings?: TenantSettings; metadata?: Record; } interface UpdateTenantInput { name?: string; slug?: string; status?: TenantStatus; settings?: TenantSettings; metadata?: Record; } interface TenantFilter { status?: TenantStatus; search?: string; limit?: number; offset?: number; } interface TenantListResult { tenants: Tenant[]; total: number; limit: number; offset: number; } interface TenantStats { deviceCount: number; userCount: number; policyCount: number; appCount: number; } type PermissionAction = 'create' | 'read' | 'update' | 'delete' | 'manage' | '*'; type PermissionResource = 'devices' | 'policies' | 'apps' | 'groups' | 'commands' | 'users' | 'roles' | 'tenants' | 'audit' | '*'; interface Permission { action: PermissionAction; resource: PermissionResource; resourceId?: string; } interface Role { id: string; tenantId?: string | null; name: string; description?: string | null; permissions: Permission[]; isSystem: boolean; createdAt: Date; updatedAt: Date; } interface CreateRoleInput { tenantId?: string; name: string; description?: string; permissions: Permission[]; } interface UpdateRoleInput { name?: string; description?: string; permissions?: Permission[]; } interface User { id: string; tenantId?: string | null; email: string; name?: string | null; status: 'active' | 'inactive' | 'pending'; metadata?: Record | null; lastLoginAt?: Date | null; createdAt: Date; updatedAt: Date; } interface UserWithRoles extends User { roles: Role[]; } interface CreateUserInput { tenantId?: string; email: string; name?: string; status?: 'active' | 'inactive' | 'pending'; metadata?: Record; } interface UpdateUserInput { email?: string; name?: string; status?: 'active' | 'inactive' | 'pending'; metadata?: Record; } interface UserFilter { tenantId?: string; status?: 'active' | 'inactive' | 'pending'; search?: string; limit?: number; offset?: number; } interface UserListResult { users: User[]; total: number; limit: number; offset: number; } type AuditAction = 'create' | 'read' | 'update' | 'delete' | 'login' | 'logout' | 'enroll' | 'unenroll' | 'command' | 'export' | 'import' | 'custom'; interface AuditLog { id: string; tenantId?: string | null; userId?: string | null; action: AuditAction; resource: string; resourceId?: string | null; status: 'success' | 'failure'; error?: string | null; details?: Record | null; ipAddress?: string | null; userAgent?: string | null; createdAt: Date; } interface CreateAuditLogInput { tenantId?: string; userId?: string; action: AuditAction; resource: string; resourceId?: string; status?: 'success' | 'failure'; error?: string; details?: Record; ipAddress?: string; userAgent?: string; } interface AuditConfig { enabled: boolean; retentionDays?: number; skipReadOperations?: boolean; logActions?: AuditAction[]; logResources?: string[]; } interface AuditSummary { totalLogs: number; byAction: Record; byResource: Record; byStatus: { success: number; failure: number; }; topUsers: Array<{ userId: string; count: number; }>; recentFailures: AuditLog[]; } interface AuditLogFilter { tenantId?: string; userId?: string; action?: string; resource?: string; resourceId?: string; startDate?: Date; endDate?: Date; limit?: number; offset?: number; } interface AuditLogListResult { logs: AuditLog[]; total: number; limit: number; offset: number; } type TaskType = 'command' | 'policy_update' | 'app_install' | 'maintenance' | 'custom'; type ScheduledTaskStatus = 'active' | 'paused' | 'completed' | 'failed'; interface MaintenanceWindow { daysOfWeek: number[]; startTime: string; endTime: string; timezone: string; } interface TaskSchedule { type: 'once' | 'recurring' | 'window'; executeAt?: Date; cron?: string; window?: MaintenanceWindow; } interface ScheduledTask { id: string; tenantId?: string | null; name: string; description?: string | null; taskType: TaskType; schedule: TaskSchedule; target?: DeployTarget; payload?: Record | null; status: ScheduledTaskStatus; nextRunAt?: Date | null; lastRunAt?: Date | null; maxRetries: number; retryCount: number; createdAt: Date; updatedAt: Date; } interface CreateScheduledTaskInput { tenantId?: string; name: string; description?: string; taskType: TaskType; schedule: TaskSchedule; target?: DeployTarget; payload?: Record; maxRetries?: number; } interface UpdateScheduledTaskInput { name?: string; description?: string; schedule?: TaskSchedule; target?: DeployTarget; payload?: Record; status?: ScheduledTaskStatus; maxRetries?: number; } interface ScheduledTaskFilter { tenantId?: string; taskType?: TaskType | TaskType[]; status?: ScheduledTaskStatus | ScheduledTaskStatus[]; limit?: number; offset?: number; } interface ScheduledTaskListResult { tasks: ScheduledTask[]; total: number; limit: number; offset: number; } interface TaskExecution { id: string; taskId: string; status: 'running' | 'completed' | 'failed'; startedAt: Date; completedAt?: Date | null; devicesProcessed: number; devicesSucceeded: number; devicesFailed: number; error?: string | null; details?: Record | null; } type QueueMessageStatus = 'pending' | 'processing' | 'delivered' | 'failed' | 'expired'; interface QueuedMessage { id: string; tenantId?: string | null; deviceId: string; messageType: string; payload: Record; priority: 'high' | 'normal' | 'low'; status: QueueMessageStatus; attempts: number; maxAttempts: number; lastAttemptAt?: Date | null; lastError?: string | null; expiresAt?: Date | null; createdAt: Date; updatedAt: Date; } interface EnqueueMessageInput { tenantId?: string; deviceId: string; messageType: string; payload: Record; priority?: 'high' | 'normal' | 'low'; maxAttempts?: number; ttlSeconds?: number; } interface QueueStats { pending: number; processing: number; delivered: number; failed: number; expired: number; byDevice: Record; oldestPending?: Date; } interface DashboardStats { devices: { total: number; enrolled: number; active: number; blocked: number; pending: number; }; policies: { total: number; deployed: number; }; applications: { total: number; deployed: number; }; commands: { pendingCount: number; last24hTotal: number; last24hSuccess: number; last24hFailed: number; }; groups: { total: number; withDevices: number; }; } interface DeviceStatusBreakdown { byStatus: Record; byOs: Record; byManufacturer: Record; byModel: Record; } interface EnrollmentTrendPoint { date: Date; enrolled: number; unenrolled: number; netChange: number; totalDevices: number; } interface CommandSuccessRates { overall: { total: number; completed: number; failed: number; successRate: number; }; byType: Record; last24h: { total: number; completed: number; failed: number; pending: number; }; } interface AppInstallationSummary { total: number; byStatus: Record; recentFailures: Array<{ packageName: string; deviceId: string; error: string; timestamp: Date; }>; topInstalled: Array<{ packageName: string; name: string; installedCount: number; }>; } interface PluginStorageAdapter { get(pluginName: string, key: string): Promise; set(pluginName: string, key: string, value: T): Promise; delete(pluginName: string, key: string): Promise; list(pluginName: string, prefix?: string): Promise; clear(pluginName: string): Promise; } interface PluginStorageEntry { pluginName: string; key: string; value: unknown; createdAt: Date; updatedAt: Date; } interface TenantManager { get(id: string): Promise; getBySlug(slug: string): Promise; list(filter?: TenantFilter): Promise; create(data: CreateTenantInput): Promise; update(id: string, data: UpdateTenantInput): Promise; delete(id: string, cascade?: boolean): Promise; getStats(tenantId: string): Promise; activate(id: string): Promise; deactivate(id: string): Promise; } interface AuthorizationManager { createRole(data: CreateRoleInput): Promise; getRole(id: string): Promise; listRoles(tenantId?: string): Promise; updateRole(id: string, data: UpdateRoleInput): Promise; deleteRole(id: string): Promise; createUser(data: CreateUserInput): Promise; getUser(id: string): Promise; getUserByEmail(email: string, tenantId?: string): Promise; listUsers(filter?: UserFilter): Promise; updateUser(id: string, data: UpdateUserInput): Promise; deleteUser(id: string): Promise; assignRole(userId: string, roleId: string): Promise; removeRole(userId: string, roleId: string): Promise; getUserRoles(userId: string): Promise; can(userId: string, action: PermissionAction, resource: PermissionResource, resourceId?: string): Promise; canAny(userId: string, permissions: Array<{ action: PermissionAction; resource: PermissionResource; }>): Promise; requirePermission(userId: string, action: PermissionAction, resource: PermissionResource, resourceId?: string): Promise; isAdmin(userId: string): Promise; } interface AuditManager { log(entry: CreateAuditLogInput): Promise; list(filter?: AuditLogFilter): Promise; getByResource(resource: string, resourceId: string): Promise; getByUser(userId: string, filter?: AuditLogFilter): Promise; export(filter: AuditLogFilter, format: 'json' | 'csv'): Promise; purge(olderThanDays?: number): Promise; getSummary(tenantId?: string, days?: number): Promise; } interface ScheduleManager { get(id: string): Promise; list(filter?: ScheduledTaskFilter): Promise; create(data: CreateScheduledTaskInput): Promise; update(id: string, data: UpdateScheduledTaskInput): Promise; delete(id: string): Promise; pause(id: string): Promise; resume(id: string): Promise; runNow(id: string): Promise; getUpcoming(hours: number): Promise; getExecutions(taskId: string, limit?: number): Promise; calculateNextRun(schedule: TaskSchedule): Date | null; } interface MessageQueueManager { enqueue(message: EnqueueMessageInput): Promise; enqueueBatch(messages: EnqueueMessageInput[]): Promise; dequeue(deviceId: string, limit?: number): Promise; acknowledge(messageId: string): Promise; fail(messageId: string, error: string): Promise; retryFailed(maxAttempts?: number): Promise; purgeExpired(): Promise; getStats(tenantId?: string): Promise; peek(deviceId: string, limit?: number): Promise; } interface DashboardManager { getStats(tenantId?: string): Promise; getDeviceStatusBreakdown(tenantId?: string): Promise; getEnrollmentTrend(days: number, tenantId?: string): Promise; getCommandSuccessRates(tenantId?: string): Promise; getAppInstallationSummary(tenantId?: string): Promise; } type EventHandler = (event: MDMEvent) => Promise | void; interface EventPayloadMap { 'device.enrolled': { device: Device; }; 'device.unenrolled': { device: Device; reason?: string; }; 'device.blocked': { device: Device; reason: string; }; 'device.heartbeat': { device: Device; heartbeat: Heartbeat; }; 'device.locationUpdated': { device: Device; location: DeviceLocation; }; 'device.statusChanged': { device: Device; oldStatus: DeviceStatus; newStatus: DeviceStatus; }; 'device.policyChanged': { device: Device; oldPolicyId?: string; newPolicyId?: string; }; 'app.installed': { device: Device; app: InstalledApp; }; 'app.uninstalled': { device: Device; packageName: string; }; 'app.updated': { device: Device; app: InstalledApp; oldVersion: string; }; 'app.crashed': { device: Device; packageName: string; error?: string; }; 'app.started': { device: Device; packageName: string; }; 'app.stopped': { device: Device; packageName: string; }; 'policy.applied': { device: Device; policy: Policy; }; 'policy.failed': { device: Device; policy: Policy; error: string; }; 'command.received': { device: Device; command: Command; }; 'command.acknowledged': { device: Device; command: Command; }; 'command.completed': { device: Device; command: Command; result: CommandResult; }; 'command.failed': { device: Device; command: Command; error: string; }; 'security.tamper': { device: Device; type: string; details?: unknown; }; 'security.rootDetected': { device: Device; }; 'security.screenLocked': { device: Device; }; 'security.screenUnlocked': { device: Device; }; custom: Record; } declare class MDMError extends Error { code: string; statusCode: number; details?: unknown | undefined; constructor(message: string, code: string, statusCode?: number, details?: unknown | undefined); } declare class DeviceNotFoundError extends MDMError { constructor(deviceId: string); } declare class PolicyNotFoundError extends MDMError { constructor(policyId: string); } declare class ApplicationNotFoundError extends MDMError { constructor(identifier: string); } declare class CommandNotFoundError extends MDMError { constructor(commandId: string); } declare class TenantNotFoundError extends MDMError { constructor(identifier: string); } declare class RoleNotFoundError extends MDMError { constructor(identifier: string); } declare class GroupNotFoundError extends MDMError { constructor(identifier: string); } declare class UserNotFoundError extends MDMError { constructor(identifier: string); } declare class EnrollmentError extends MDMError { constructor(message: string, details?: unknown); } declare class AuthenticationError extends MDMError { constructor(message?: string); } declare class AuthorizationError extends MDMError { constructor(message?: string); } declare class ValidationError extends MDMError { constructor(message: string, details?: unknown); } export { type AppInstallationSummary, type AppRollback, type AppVersion, type Application, type ApplicationManager, ApplicationNotFoundError, type AuditAction, type AuditConfig, type AuditLog, type AuditLogFilter, type AuditLogListResult, type AuditManager, type AuditSummary, type AuthConfig, AuthenticationError, AuthorizationError, type AuthorizationManager, type Command, type CommandFilter, type CommandManager, CommandNotFoundError, type CommandResult, type CommandStatus, type CommandSuccessRates, type CommandType, type CreateAppRollbackInput, type CreateApplicationInput, type CreateAuditLogInput, type CreateDeviceInput, type CreateGroupInput, type CreatePolicyInput, type CreateRoleInput, type CreateScheduledTaskInput, type CreateTenantInput, type CreateUserInput, type DashboardManager, type DashboardStats, type DatabaseAdapter, type DeployTarget, type Device, type DeviceFilter, type DeviceIdentityVerification, type DeviceListResult, type DeviceLocation, type DeviceManager, DeviceNotFoundError, type DeviceStatus, type DeviceStatusBreakdown, type EnqueueMessageInput, type EnrollmentChallenge, type EnrollmentConfig, EnrollmentError, type EnrollmentMethod, type EnrollmentRequest, type EnrollmentResponse, type EnrollmentTrendPoint, type EventFilter, type EventHandler, type EventPayloadMap, type EventType, type Group, type GroupHierarchyStats, type GroupManager, GroupNotFoundError, type GroupTreeNode, type HardwareControl, type Heartbeat, type InstalledApp, type LogContext, type Logger, type MDMConfig, MDMError, type MDMEvent, type MDMInstance, type MDMPlugin, type MaintenanceWindow, type MessageQueueManager, type PasswordPolicy, type Permission, type PermissionAction, type PermissionResource, type PinnedKeyConfig, type PluginMiddleware, type PluginRoute, type PluginStorageAdapter, type PluginStorageEntry, type Policy, type PolicyApplication, type PolicyManager, PolicyNotFoundError, type PolicySettings, type PushAdapter, type PushBatchResult, type PushConfig, type PushMessage, type PushProviderConfig, type PushResult, type PushToken, type QueueMessageStatus, type QueueStats, type QueuedMessage, type RegisterPushTokenInput, type Role, RoleNotFoundError, type ScheduleManager, type ScheduledTask, type ScheduledTaskFilter, type ScheduledTaskListResult, type ScheduledTaskStatus, type SendCommandInput, type StorageConfig, type SystemUpdatePolicy, type TaskExecution, type TaskSchedule, type TaskType, type Tenant, type TenantFilter, type TenantListResult, type TenantManager, TenantNotFoundError, type TenantSettings, type TenantStats, type TenantStatus, type TimeWindow, type UpdateApplicationInput, type UpdateDeviceInput, type UpdateGroupInput, type UpdatePolicyInput, type UpdateRoleInput, type UpdateScheduledTaskInput, type UpdateTenantInput, type UpdateUserInput, type User, type UserFilter, type UserListResult, UserNotFoundError, type UserWithRoles, ValidationError, type VpnConfig, type WebhookConfig, type WebhookDeliveryResult, type WebhookEndpoint, type WebhookManager, type WifiConfig };