nts 1.0.4
nts: ^1.0.4 copied to clipboard
Authenticated NTP via Network Time Security (RFC 8915) for Dart and Flutter using the Native Assets pipeline.
example/example.md
nts — minimal usage snippet #
The smallest end-to-end use of package:nts: one authenticated NTPv4
exchange against time.cloudflare.com over RFC 8915 NTS-KE, with an
exhaustive switch on every NtsError variant.
This file is the canonical Example tab content on pub.dev. The same
source ships in the package as example/main.dart, which
is the runnable Flutter target; the .md form is here so pub.dev's
selection algorithm prefers it over the larger GUI showcase at
example/lib/main.dart.
// Minimal `package:nts` usage example — one authenticated NTPv4
// exchange against `time.cloudflare.com` over RFC 8915.
//
// Run from a Flutter target (`flutter run -t example/main.dart`)
// so the Native Assets pipeline bundles the Rust dylib. Plain
// `dart run` does not invoke build hooks; see `example/bin/nts_cli.dart`
// for the explicit `ExternalLibrary.open` loader pattern needed there.
// ignore_for_file: avoid_print
import 'package:nts/nts.dart';
Future<void> main() async {
// Bridge bootstrap. Resolves the bundled `libnts_rust.{dylib|so|dll}`
// through the stable Native Assets API and wires the FRB dispatch
// table. Must be awaited exactly once before any nts* entry point;
// subsequent calls are no-ops.
await RustLib.init();
// RFC 8915 NTS-KE endpoint. Port 4460 is the IANA-assigned default;
// any host listed at <https://github.com/jauderho/nts-servers> works.
const spec = NtsServerSpec(host: 'time.cloudflare.com', port: 4460);
try {
// First call performs the full TLS 1.3 NTS-KE handshake, then the
// AEAD-protected NTPv4 query. Subsequent calls against the same
// `spec` reuse the cached keys and spend a stored cookie, so
// steady-state cost is one UDP round-trip. The 5-second timeout
// applies independently to the KE leg and the UDP recv leg.
final sample = await ntsQuery(spec: spec, timeoutMs: 5000);
final utc = DateTime.fromMicrosecondsSinceEpoch(
sample.utcUnixMicros.toInt(),
isUtc: true,
);
final rttMs = sample.roundTripMicros.toInt() / 1000.0;
print('utc = ${utc.toIso8601String()}');
print('rtt = ${rttMs.toStringAsFixed(2)} ms');
print('stratum = ${sample.serverStratum}');
print('aead-id = ${sample.aeadId}');
print('cookies = ${sample.freshCookies}');
} on NtsError catch (err) {
// `NtsError` is a `freezed` sealed class — exhaustive switch
// expressions catch new variants at compile time if the package
// ever grows them.
final detail = switch (err) {
NtsError_InvalidSpec(:final field0) => 'invalid spec: $field0',
NtsError_Network(:final field0) => 'network: $field0',
NtsError_KeProtocol(:final field0) => 'NTS-KE: $field0',
NtsError_NtpProtocol(:final field0) => 'NTP: $field0',
NtsError_Authentication(:final field0) => 'AEAD auth: $field0',
NtsError_Timeout() => 'timeout',
NtsError_NoCookies() => 'no cookies returned',
NtsError_Internal(:final field0) => 'internal: $field0',
};
print('nts query failed: $detail');
}
}
Want the GUI? #
The full Flutter showcase lives at
example/lib/main.dart and is invoked with an
explicit target:
# from the example directory
flutter run -t lib/main.dart
# from the repo root
flutter run -t example/lib/main.dart
See example/README.md for the three entry points
(snippet, GUI, CLI) and the bridge-mode toggles.