Skip to content

azoila/openmdm

OpenMDM

A modern, embeddable Mobile Device Management SDK for TypeScript

npm version License: MIT TypeScript Node.js PRs Welcome

Documentation | Quick Start | Examples | Discord


OpenMDM is the "better-auth of MDM" - a flexible, framework-agnostic SDK that lets you add device management to any application without deploying a separate MDM server.

Note

OpenMDM is pre-1.0. The core APIs are working and the packages are published to npm, but minor versions may still introduce breaking changes before 1.0. Pin exact versions in production and review the CHANGELOG before upgrading. Contributions and feedback welcome.

Why OpenMDM?

The Problem with Existing Solutions

Headwind MDM and similar open-source MDM solutions are powerful but:

  • Require separate deployment (Java/Tomcat server)
  • Have their own database (can't use yours)
  • Limited integration options (no SDKs)
  • Session-based auth with legacy patterns
  • Hard to embed in existing applications

Commercial MDMs (Intune, VMware, etc.) are:

  • Expensive at scale
  • Cloud-dependent
  • Vendor lock-in
  • Overkill for many use cases

The OpenMDM Solution

// Install OpenMDM into YOUR application
import { createMDM } from '@openmdm/core';
import { drizzleAdapter } from '@openmdm/drizzle-adapter';
import { fcmPushAdapter } from '@openmdm/push-fcm';

const mdm = createMDM({
  database: drizzleAdapter(db), // Your existing database
  push: fcmPushAdapter({
    credentialPath: './firebase-service-account.json',
  }),
  enrollment: {
    deviceSecret: process.env.DEVICE_HMAC_SECRET,
    autoEnroll: true,
  },
});

// Now you have full MDM capabilities
const devices = await mdm.devices.list();
await mdm.devices.sendCommand(deviceId, { type: 'reboot' });

Features

  • Embeddable - Works within your existing application, not as a separate service
  • HTTP Adapter - First-class Hono adapter with the full REST surface (Express, Fastify and Next.js adapters are on the roadmap)
  • Database Agnostic - Bring your own database with Drizzle, Prisma, or raw SQL
  • Push Notifications - FCM, MQTT, and WebSocket support
  • S3 Storage - Presigned URLs for APK uploads (AWS S3, MinIO, DigitalOcean Spaces)
  • Webhooks - HMAC-signed outbound webhooks with retry logic
  • Plugin System - Extend with kiosk, geofence, and custom plugins
  • TypeScript First - Full type safety with IntelliSense support

Packages

Package Description Status
@openmdm/core Core MDM SDK - devices, policies, commands, events Stable
@openmdm/storage-s3 S3 storage adapter for APK uploads Stable
@openmdm/drizzle-adapter Database adapter for Drizzle ORM Stable
@openmdm/hono Hono framework adapter with REST API routes Stable
@openmdm/push-fcm Firebase Cloud Messaging push adapter Stable
@openmdm/push-mqtt MQTT push adapter for private networks Stable
@openmdm/client Device-side SDK for Android agents Stable
@openmdm/plugin-kiosk Kiosk/lockdown mode plugin Stable
@openmdm/plugin-geofence Geofencing and location-based policies Stable
@openmdm/cli Command-line tools for administration Stable

Quick Start

1. Install

npm install @openmdm/core @openmdm/drizzle-adapter @openmdm/push-fcm
# or
pnpm add @openmdm/core @openmdm/drizzle-adapter @openmdm/push-fcm

2. Configure

import { createMDM } from '@openmdm/core';
import { drizzleAdapter } from '@openmdm/drizzle-adapter';
import { fcmPushAdapter } from '@openmdm/push-fcm';
import { kioskPlugin } from '@openmdm/plugin-kiosk';
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';

// Database connection
const db = drizzle(postgres(process.env.DATABASE_URL!));

// Create MDM instance
export const mdm = createMDM({
  database: drizzleAdapter(db),
  push: fcmPushAdapter({
    credentialPath: './firebase-service-account.json',
  }),
  enrollment: {
    deviceSecret: process.env.DEVICE_SECRET!,
    autoEnroll: true,
  },
  auth: {
    deviceTokenSecret: process.env.JWT_SECRET!,
  },
  webhooks: {
    endpoints: [{
      id: 'main',
      url: 'https://your-app.com/webhooks/mdm',
      events: ['*'],
      enabled: true,
    }],
    signingSecret: process.env.WEBHOOK_SECRET,
  },
  serverUrl: 'https://mdm.example.com',
  plugins: [
    kioskPlugin({ defaultExitPassword: 'admin123' }),
  ],
});

3. Use the API

// List enrolled devices
const { devices, total } = await mdm.devices.list({ status: 'enrolled' });

// Get device details
const device = await mdm.devices.get('device-123');

// Send command
await mdm.commands.send({
  deviceId: 'device-123',
  type: 'sync',
});

// Create policy
const policy = await mdm.policies.create({
  name: 'Kiosk Mode',
  settings: {
    kioskMode: true,
    mainApp: 'com.example.app',
    lockStatusBar: true,
    lockNavigationBar: true,
  },
});

// Apply policy to device
await mdm.devices.assignPolicy('device-123', policy.id);

// Subscribe to events
mdm.on('device.enrolled', async (event) => {
  console.log('New device:', event.payload.device);
});

4. Mount the HTTP Adapter

OpenMDM ships with a first-class Hono adapter that exposes the full REST surface (enrollment, devices, policies, applications, groups, commands, events, agent wire-protocol). Hono runs on Node, Bun, Deno, Cloudflare Workers, Vercel, and more — so the same adapter works across runtimes.

import { Hono } from 'hono';
import { honoAdapter } from '@openmdm/hono';

const app = new Hono();
app.route('/mdm', honoAdapter(mdm));

Using Express, Fastify, or Next.js? Native adapters are on the roadmap for 1.0. In the meantime, the @openmdm/core instance exposes all MDM operations as plain functions, so you can wire your own routes against any framework.

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      YOUR APPLICATION                        │
│                                                              │
│    ┌────────────────────────────────────────────────────┐   │
│    │                    OpenMDM SDK                      │   │
│    │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌───────┐ │   │
│    │  │ Devices │  │ Policies│  │Commands │  │ Push  │ │   │
│    │  └─────────┘  └─────────┘  └─────────┘  └───────┘ │   │
│    │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌───────┐ │   │
│    │  │  Apps   │  │ Groups  │  │ Events  │  │Webhooks│ │   │
│    │  └─────────┘  └─────────┘  └─────────┘  └───────┘ │   │
│    └────────────────────────────────────────────────────┘   │
│                              │                               │
│              ┌───────────────┼───────────────┐              │
│              │               │               │              │
│      ┌───────▼───────┐ ┌────▼────┐ ┌───────▼───────┐       │
│      │ Your Database │ │  FCM/   │ │ Android Agent │       │
│      │   (Drizzle)   │ │  MQTT   │ │    (Kotlin)   │       │
│      └───────────────┘ └─────────┘ └───────────────┘       │
└─────────────────────────────────────────────────────────────┘

Android Agent

The Android Agent is maintained in a separate repository for easier customization and forking:

Repository: openmdm/openmdm-android

Features

  • Device enrollment with QR code or token
  • FCM push notification support
  • Automatic heartbeat/check-in scheduling
  • Command processing (sync, lock, wipe, app install, etc.)
  • Device Owner / Device Admin capabilities
  • Silent app installation and permission granting
  • Kiosk mode support
  • Location reporting

Getting Started

# Clone the Android agent
git clone https://github.com/openmdm/openmdm-android

# Build the full agent app
cd openmdm-android
./gradlew :agent:assembleRelease

# Or use the library in your own app
implementation("com.github.openmdm:openmdm-android:library:0.1.0")

See the openmdm-android README for full documentation.

Push Notification Providers

FCM (Firebase Cloud Messaging)

Recommended for devices with Google Play Services.

import { fcmPushAdapter } from '@openmdm/push-fcm';

const push = fcmPushAdapter({
  credentialPath: './firebase-service-account.json',
  dataOnly: true,
});

MQTT

For private networks, air-gapped environments, or devices without Google Play Services.

import { mqttPushAdapter } from '@openmdm/push-mqtt';

const push = mqttPushAdapter({
  brokerUrl: 'mqtt://mqtt.example.com:1883',
  username: 'mdm-server',
  password: 'secret',
  qos: 1,
});

Plugins

Kiosk Mode

Lock devices to a single app or set of apps.

import { kioskPlugin } from '@openmdm/plugin-kiosk';

const mdm = createMDM({
  plugins: [
    kioskPlugin({
      defaultExitPassword: 'admin123',
      allowRemoteExit: true,
      autoRestart: true,
    }),
  ],
});

Geofencing

Location-based policies and alerts.

import { geofencePlugin } from '@openmdm/plugin-geofence';

const mdm = createMDM({
  plugins: [
    geofencePlugin({
      onEnter: async (device, zone) => {
        await mdm.devices.assignPolicy(device.id, 'office-policy');
      },
      onExit: async (device, zone) => {
        await mdm.devices.assignPolicy(device.id, 'default-policy');
      },
    }),
  ],
});

CLI Tools

# Initialize project
npx openmdm init

# Run database migrations
npx openmdm migrate

# List devices
npx openmdm device list
npx openmdm device show <deviceId>

# Manage policies
npx openmdm policy list
npx openmdm policy create
npx openmdm policy apply <policyId> <deviceId>

# Generate enrollment
npx openmdm enroll qr --output enrollment.png
npx openmdm enroll token

# View statistics
npx openmdm stats

Project Structure

openmdm/                          # This repository (SDK monorepo)
├── packages/
│   ├── core/                     # Core MDM SDK
│   ├── storage/s3/               # S3 storage adapter
│   ├── adapters/
│   │   ├── drizzle/              # Drizzle ORM adapter
│   │   └── hono/                 # Hono framework adapter
│   ├── push/
│   │   ├── fcm/                  # FCM push adapter
│   │   └── mqtt/                 # MQTT push adapter
│   ├── plugins/
│   │   ├── kiosk/                # Kiosk mode plugin
│   │   └── geofence/             # Geofencing plugin
│   ├── client/                   # Device-side SDK (TypeScript types)
│   └── cli/                      # CLI tools
├── examples/                     # Example applications
└── docs/                         # Documentation website

openmdm-android/                  # Separate repository
├── agent/                        # Full-featured MDM agent app
├── library/                      # Core MDM library (embeddable)
└── docs/                         # Android-specific docs

Comparison

Feature OpenMDM Headwind MDM Commercial
Embeddable Yes No No
Use your DB Yes No No
Self-hosted Yes Yes Some
Framework agnostic Yes No No
Plugin system Yes Limited Limited
Webhooks Yes Enterprise Yes
S3 Storage Yes No Yes
Kiosk mode Yes Yes Yes
Device Owner Yes Yes Yes
TypeScript Native No No
Price Free Free/Paid $$$

Development

# Install dependencies
pnpm install

# Build all packages
pnpm build

# Run tests
pnpm test

# Type check
pnpm typecheck

# Watch mode
pnpm dev

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Community

Documentation

License

MIT License - see LICENSE for details.

Acknowledgments


Built with TypeScript. Inspired by better-auth.

Documentation | GitHub | Discord

About

OpenMDM is a modern, embeddable MDM SDK that gives you complete control over Android devices. Built for developers who need enterprise-grade device management without the complexity.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages

 
 
 

Contributors