monotime 0.1.0 copy "monotime: ^0.1.0" to clipboard
monotime: ^0.1.0 copied to clipboard

Tamper-resistant network time for Flutter. Anchors NTP + HTTPS-authenticated consensus to the hardware monotonic clock. No Rust, no NTS-KE complexity — just reliable, manipulation-proof time for mobile apps.

monotime #

Tamper-resistant network time for Flutter.

Anchors multi-source NTP + HTTPS consensus to the device's hardware monotonic clock.
Immune to system-clock manipulation. No Rust, no native build complexity.


How it works #

Layer What it does
NTP sources Queries multiple NTP servers concurrently via UDP (RFC 5905)
HTTPS sources Parses Date headers from TLS-verified HTTPS endpoints
Marzullo consensus Resolves a provably correct interval across all sources, enforcing source-diversity
Monotonic anchoring Anchors the consensus UTC to the hardware uptime counter (SystemClock.elapsedRealtime / ProcessInfo.systemUptime)
Tamper detection Listens for ACTION_TIME_CHANGED (Android) and NSSystemClockDidChange (iOS)

The key formula:

now = anchoredNetworkUtcMs + (currentHardwareUptimeMs − anchorUptimeMs)

This makes now immune to the user changing the system clock. The uptime counter only advances forward and resets on reboot.


Quick start #

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await MonoTime.initialize();
  runApp(const MyApp());
}
// Get tamper-resistant time (throws if engine not ready)
final now = MonoTime.now();

// Safe fallback — never throws, null only on cold start with no persisted anchor
final estimate = MonoTime.nowEstimated();
if (estimate != null) {
  print('${estimate.estimatedTime} (confidence: ${estimate.confidence.toStringAsFixed(2)})');
}

// Listen for tamper events
MonoTime.onIntegrityLost.listen((event) {
  switch (event.reason) {
    case TamperReason.systemClockJumped:
      // User changed the system clock — re-evaluate trust
      break;
    case TamperReason.deviceRebooted:
      // Uptime counter reset — anchor invalidated automatically
      break;
    case TamperReason.timezoneChanged:
      // UTC unaffected, but local-time representation changed
      break;
    default:
      break;
  }
});

Configuration #

await MonoTime.initialize(
  config: const MonoTimeConfig(
    ntpServers: ['time.cloudflare.com', 'time.google.com', 'pool.ntp.org'],
    httpsSources: [
      'https://www.cloudflare.com',
      'https://www.google.com',
      'https://www.apple.com',
    ],
    refreshInterval: Duration(hours: 6),
    minimumQuorum: 2,       // min sources in consensus
    minGroupCount: 2,        // min distinct hostnames
    persistState: true,      // survive cold starts via SharedPreferences
  ),
);

Observability #

// Register a SyncObserver for telemetry
MonoTime.registerObserver(MySyncObserver());

class MySyncObserver implements SyncObserver {
  @override
  void onSyncStarted() => print('Sync started');

  @override
  void onConsensusReached(ConsensusResult result) {
    print('Consensus: ${result.utc} ±${result.uncertaintyMs}ms '
          '(${result.confidence.name}, ${result.participantCount} sources)');
  }

  @override
  void onMetricsReported(SyncMetrics metrics) {
    print('Sync took ${metrics.duration.inMilliseconds}ms');
  }

  // ... other callbacks
}

Testing #

// Inject a fixed time for deterministic widget tests
MonoTime.setTestOverride(MonoTimeTestOverride(now: DateTime(2025, 1, 1, 0, 0, 0)));

expect(MonoTime.now(), DateTime.utc(2025, 1, 1));

MonoTime.setTestOverride(null); // restore

Architecture #

MonoTime              ← public static façade
└── MonoTimeImpl      ← lifecycle (bootstrap, warm-start, refresh, retry)
    ├── SyncEngine    ← concurrent multi-source orchestrator
    │   ├── NtpSource   (UDP, ntp package)
    │   └── HttpsSource (HTTPS Date header, http package)
    ├── MarzulloEngine  ← closed-interval consensus algorithm
    ├── IntegrityMonitor← clock tamper + reboot detection
    ├── SyncClock       ← sub-µs projection without platform calls
    └── AnchorStore     ← SharedPreferences persistence

Platform support #

Platform Uptime source Tamper detection Native Network Time (getNetworkTimeMs)
Android SystemClock.elapsedRealtime() ACTION_TIME_CHANGED + ACTION_TIMEZONE_CHANGED Returns null (falls back to Dart multi-source consensus)
iOS ProcessInfo.systemUptime NSSystemClockDidChange + NSSystemTimeZoneDidChange Always returns null (not supported by public APIs)

Roadmap: Google Play Services TrustedTimeClient Integration #

To further enhance tamper-resistance on GMS-equipped Android devices, a future release can integrate Google's native TrustedTimeClient.

Fallback Architecture for Non-GMS Devices #

Since many devices (e.g. Huawei, custom ROMs, or Chinese domestic market phones) do not ship with Google Play Services, the integration will follow a strict fallback architecture:

  1. GMS Availability Check: The Android plugin will dynamically check if Google Play Services are available and up to date on the host device.
  2. Native TrustedTime Query: If available, the native channel will call TrustedTimeClient.getInstant() to obtain Google's cryptographically-anchored network time.
  3. Consensus Fallback: If GMS check fails or the Play Services API throws an error:
    • The plugin returns null for native network time.
    • The Dart engine gracefully falls back to the pure-Dart NTP (UDP) + HTTPS Date header consensus protocol.

This ensures high-precision native time where available, while keeping the app 100% functional and tamper-resistant on all devices globally.


License #

MIT

0
likes
150
points
10
downloads

Documentation

API reference

Publisher

verified publisheranuragbharati.com.np

Weekly Downloads

Tamper-resistant network time for Flutter. Anchors NTP + HTTPS-authenticated consensus to the hardware monotonic clock. No Rust, no NTS-KE complexity — just reliable, manipulation-proof time for mobile apps.

Homepage
Repository (GitHub)
View/report issues

Topics

#time #ntp #security #anti-fraud #tamper-proof

License

MIT (license)

Dependencies

flutter, http, meta, ntp, shared_preferences

More

Packages that depend on monotime

Packages that implement monotime