nts library
Authenticated Network Time Security (RFC 8915) for Dart and Flutter.
Exposes a Rust-backed NTS-KE + AEAD-NTP client. Initialization has
two independent layers; both must be in place before ntsQuery or
ntsWarmCookies is called:
- Native platform bootstrap — captures the platform-specific
handles (e.g. the Android
JavaVM+ applicationContextforrustls-platform-verifier) so TLS 1.3 can validate against the system trust store. On Android this happens automatically: the bundledNtsPluginruns fromGeneratedPluginRegistrantbefore Dartmain(), so consumers do nothing. iOS/macOS/Linux/Windows have no JVM-style bootstrap step. Hosts that bypass the standard Flutter activity lifecycle (custom embeddings, isolates spawned ahead of plugin registration) can callcom.nllewellyn.nts.PlatformInit.init(context)from Kotlin directly. - Dart/FRB initialization —
await NtsRustLib.init()once during startup before anynts*entry point. This loads the bundled Rust dylib through the Native Assets pipeline and wires theflutter_rust_bridgev2 dispatch table on the Dart isolate. Mandatory on every platform; the plugin layer cannot perform this step because it runs on the Android platform thread before the Dart isolate exists. Subsequent calls are no-ops.
The hand-written wrapper in src/api/nts.dart is the package's
stable public contract: the underlying Rust-side bindings live in
src/ffi/ and are an internal implementation detail. See
ARCHITECTURE.md's "Public API stability layer" for the rationale.
Classes
- MonotonicClock
- A sleep-aware monotonic time source.
- NtsClient
- Owned NTS client handle.
- NtsDnsPoolStats
- Snapshot of the bounded DNS resolver pool counters.
- NtsRustLib
- Main entrypoint of the Rust API
- NtsServerSpec
- Address of an NTS-KE endpoint.
- NtsSyncedTime
-
Synchronized clock produced by
ntsGetTime/NtsClient.getTime. - NtsTimeSample
- Successful authenticated NTPv4 sample.
- NtsTrustStatus
-
Process-global trust-anchor diagnostic snapshot returned by
ntsTrustStatus(). - NtsWarmCookiesOutcome
-
Successful outcome of
ntsWarmCookies. - PhaseTimings
-
Microsecond-resolution wall-clock breakdown of a successful
ntsQueryorntsWarmCookiescall, surfaced on thephaseTimingsfield of NtsTimeSample / NtsWarmCookiesOutcome.
Enums
- TimeoutPhase
-
Phase of an
ntsQueryorntsWarmCookiescall whose wall-clock budget elapsed. - TrustBackend
- Trust-anchor backend that authenticated a TLS chain, or that a process-global resolution attempt landed on.
- TrustMode
- Caller-selected policy for which trust-anchor backend an NtsClient is willing to run against. Set immutably at client construction and applied to every handshake the client initiates.
Constants
- kDefaultBridgeConcurrencyCap → const int
-
Default per-call ceiling on concurrently dispatched bridge calls,
applied isolate-wide by ntsQuery / ntsWarmCookies /
NtsClient.query / NtsClient.warmCookies (the gate's state is
Dart-side and isolate-local; each isolate gates its own calls over
the shared process-wide
flutter_rust_bridgeworker pool). - kDefaultDnsConcurrencyCap → const int
- Default per-call ceiling on in-flight DNS resolver workers, applied process-wide by ntsQuery / ntsWarmCookies / NtsClient.query / NtsClient.warmCookies.
- kDefaultTimeout → const Duration
- Default per-call wall-clock budget for ntsQuery / ntsWarmCookies / NtsClient.query / NtsClient.warmCookies.
- kDefaultTimeoutMs → const int
- Deprecated alias for kDefaultTimeout, expressed in milliseconds.
Functions
-
ntsDnsPoolStats(
) → NtsDnsPoolStats - Snapshot the bounded DNS resolver pool counters. Synchronous (no future / isolate hop): backed by four atomic-relaxed loads, cheap enough to call from a UI poll loop.
-
ntsGetTime(
{required NtsServerSpec spec, TrustMode trustMode = TrustMode.platformWithFallback, List< int> ? customRoots, DateTime? verificationTime, int? verificationTimeMs}) → Future<NtsSyncedTime> - One-call "give me the correct time" convenience built on ntsWarmCookies + a burst of ntsQuery calls against the process-wide default client.
-
ntsQuery(
{required NtsServerSpec spec, Duration timeout = kDefaultTimeout, int? timeoutMs, int dnsConcurrencyCap = kDefaultDnsConcurrencyCap, int bridgeConcurrencyCap = kDefaultBridgeConcurrencyCap, DateTime? verificationTime, int? verificationTimeMs}) → Future< NtsTimeSample> -
Run a complete authenticated NTPv4 exchange against
spec. -
ntsTrustStatus(
) → NtsTrustStatus - Snapshot the process-global trust-anchor diagnostic state. Synchronous (no future / isolate hop): backed by seven atomic loads, cheap enough to call from a UI poll loop or a pre-flight "can I even validate against the platform store?" check.
-
ntsWarmCookies(
{required NtsServerSpec spec, Duration timeout = kDefaultTimeout, int? timeoutMs, int dnsConcurrencyCap = kDefaultDnsConcurrencyCap, int bridgeConcurrencyCap = kDefaultBridgeConcurrencyCap, DateTime? verificationTime, int? verificationTimeMs}) → Future< NtsWarmCookiesOutcome> -
Force a fresh NTS-KE handshake against
specand return the cookie count along with the per-phase wall-clock breakdown of the handshake. Replaces any cached session for that spec.
Exceptions / Errors
- NtsError
-
Failure surface for
ntsQueryandntsWarmCookies. - NtsErrorAuthentication
- Variant: AEAD seal/open failed (tag mismatch or malformed input).
- NtsErrorInternal
- Variant: bug guard for unreachable internal states.
- NtsErrorInvalidSpec
-
Variant:
spec(or one of the integer arguments accompanying it) was rejected before any I/O happened. Surfaced from two layers: - NtsErrorKeProtocol
- Variant: TLS handshake or NTS-KE record exchange failed.
- NtsErrorNetwork
- Variant: TCP/UDP I/O error or connection failure.
- NtsErrorNoCookies
- Variant: cookie jar empty after a handshake (server delivered none).
- NtsErrorNtpProtocol
- Variant: NTPv4 packet parsing or extension validation failed.
- NtsErrorTimeout
- Variant: wall-clock budget elapsed inside one of the call's pre-NTP or NTP phases.
-
Variant: caller selected
TrustMode.platformOnlyand the platform trust-anchor backend could not be constructed. New in 3.0.0; see NtsError.trustBackendUnavailable.