cd /news/artificial-intelligence/noctua-a-privacy-first-oura-ring-sdk… · home topics artificial-intelligence article
[ARTICLE · art-106862] src=github.com ↗ pub= topic=artificial-intelligence verified=true sentiment=· neutral

Noctua – A privacy-first Oura Ring SDK with on-device ML, no cloud

Noctua, an open-source Kotlin SDK for the Oura Ring, provides on-device AI wellness insights and next-day readiness forecasts without uploading health data to the cloud. The SDK includes a typed Oura API v2 client, an on-device AI layer with explainable heuristic rules and a neural forecaster bridged to ExecuTorch, and an example app with demo mode. It targets Android and JVM backends, with modules noctua-core, noctua-ai, and example-app, and supports OAuth2 and personal access tokens.

read4 min views1 publishedAug 22, 2026
Noctua – A privacy-first Oura Ring SDK with on-device ML, no cloud
Image: Michielbdejong (auto-discovered)

On-device AI wellness intelligence for Oura Ring — privacy-first Android SDK.

Noctua (the owl genus — nocturnal wisdom) is an open-source Kotlin toolkit that combines a complete, typed client for the Oura API v2 with an on-device AI layer that turns raw biometrics into explainable insights and a next-day readiness forecast — without your health data ever leaving the phone.

Dashboard AI Coach (on-device) Connect

Captured from the example app running in demo mode on a Pixel 7 Pro emulator.

Most wearable companion apps ship your biometric history to a cloud LLM to generate "insights". Noctua takes the opposite stance:

| Cloud AI companions | Noctua | | |---|---|---| | Raw HRV / sleep / temperature data | uploaded to a server | never leaves the device | | Insight logic | opaque | transparent, unit-tested rules + open model | | Works offline | ✗ | ✓ | | Latency | network round-trip | < 5 ms on-device |

graph TD
    A[Oura Cloud API v2] -->|OAuth2 / PAT| B[noctua-core<br/>typed Kotlin client]
    B --> C[WellnessSnapshot<br/>readiness · sleep · activity · HRV]
    C --> D[noctua-ai<br/>FeatureExtractor]
    D --> E1[HeuristicInsightEngine<br/>explainable rules]
    D --> E2[ExecuTorchForecaster<br/>.pte neural model, on-device]
    E1 --> F[NoctuaReport]
    E2 --> F
    E2 -.missing runtime.-> E3[LinearHeuristicForecaster<br/>zero-dependency fallback]
    E3 --> F
    F --> G[example-app<br/>Jetpack Compose]
Module What it is
noctua-core
Pure-Kotlin Oura API v2 client — OAuth2 helpers, auto-refreshing tokens, all usercollection endpoints, pagination, sandbox support. Runs on Android and any JVM backend.
noctua-ai
On-device intelligence: feature extraction (sleep debt, HRV z-score vs personal baseline, readiness trend), explainable heuristic insights, and a neural readiness forecaster bridged to ExecuTorch.
example-app
Material 3 Compose app — score rings, 14-day readiness trend, AI coach feed, OAuth/token connect flow, and a built-in demo mode that needs no Oura account.
model/
PyTorch → ExecuTorch export script for the readiness forecaster.

Personal use: create a Personal Access Token atcloud.ouraring.com/personal-access-tokens(note: Oura has been moving new integrations to OAuth2).Multi-user apps: register an OAuth2 application atcloud.ouraring.com/oauth/applicationswith redirect URInoctua://callback

.

The modules are plain Gradle project dependencies (publish to Maven or use via includeBuild

/ JitPack):

dependencies {
    implementation("com.noctua:noctua-core:0.1.0")
    implementation("com.noctua:noctua-ai:0.1.0")
    // Optional: enable the neural forecaster
    implementation("org.pytorch:executorch-android:1.0.0")
}
val oura = OuraClient.Builder()
    .token("YOUR_TOKEN")
    .build()

// Coroutine-first; pagination is handled for you.
val readiness = oura.dailyReadiness(startDate = "2026-08-01", endDate = "2026-08-21")
val sleep     = oura.dailySleep(startDate = "2026-08-01", endDate = "2026-08-21")
val periods   = oura.sleep(startDate = "2026-08-01", endDate = "2026-08-21")

OAuth2 (client-side flow) in two lines:

val url = OuraOAuth.authorizationUrl(clientId, redirectUri = "myapp://callback")
// open `url` in a Custom Tab, then in your deep-link handler:
val token = OuraOAuth.parseClientSideRedirect(intent.dataString!!).accessToken

For long-lived apps, OAuthTokenProvider

refreshes expiring tokens automatically via Oura's refresh_token

grant.

val ai = NoctuaAI()
val report = ai.analyze(WellnessSnapshot(
    readiness = readiness,
    sleep = sleep,
    activity = oura.dailyActivity("2026-08-01", "2026-08-21"),
    sleepPeriods = periods,
))

println(report.forecastedReadiness)   // e.g. 74 — tomorrow's predicted score
report.insights.forEach { println("• ${it.title} (${it.confidence}%)") }
// • Sleep debt accumulating (88%)
// • HRV below your baseline (80%)
cd model
pip install torch executorch
python export_readiness_forecaster.py   # → readiness_forecaster.pte

Ship the .pte

with your app and swap the forecaster:

val ai = NoctuaAI(forecaster = ExecuTorchForecaster(pteFile.absolutePath))

If the ExecuTorch runtime or model file is absent, Noctua silently falls back to the bundled linear model — the app never breaks.

| Endpoint | OuraClient method | Scope | |---|---|---| /v2/usercollection/personal_info | personalInfo() | personal | daily_sleep / daily_readiness / daily_activity | dailySleep() · dailyReadiness() · dailyActivity() | daily | daily_spo2 · daily_stress · daily_resilience | dailySpo2() · dailyStress() · dailyResilience() | spo2 / daily | daily_cardiovascular_age · vO2_max | dailyCardiovascularAge() · vo2Max() | heart_health | sleep (detailed periods) · sleep_time | sleep() · sleepTime() | daily | heartrate (time series) | heartrate(start, end) ISO-8601 datetimes | heartrate | workout · session · tag / enhanced_tag | workouts() · sessions() · tags() · enhancedTags() | workout / session / tag | rest_mode_period · ring_configuration | restModePeriods() · ringConfigurations() | daily / ring_configuration | Sandbox (/v2/sandbox/... ) | Builder().sandbox(true) | none |

Errors map to typed OuraException

subtypes: Unauthorized

, RateLimited

(Oura allows ~5000 req / 5 min), Http

, Network

, Serialization

.

git clone https://github.com/RanjithRagavan/Noctua.git
cd Noctua
./gradlew :example-app:installDebug

The app boots into demo mode with a deterministic 21-day dataset, so you can evaluate the full UX — score rings, trend chart, forecast card, AI coach — before connecting a real ring. The screenshots above show exactly what demo mode renders.

  • On-device LLM sleep coach (ExecuTorch Llama runner, fully local chat)
  • Personal fine-tuning loop: retrain the forecaster nightly on-device
  • Health Connect write-back (share derived insights with Android Health)
  • Webhook subscription helpers ( /v2/webhook/subscription

) - Compose Multiplatform + iOS (KMP) port of noctua-ai

Issues and PRs welcome. The heuristics in HeuristicInsightEngine

are deliberately readable — improving them with better evidence is a great first contribution. Run ./gradlew test

before submitting.

Apache 2.0 — use it in personal or commercial apps.

Noctua is an independent open-source project and is not affiliated with, endorsed by, or sponsored by Ōura Health Oy.

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @noctua 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/noctua-a-privacy-fir…] indexed:0 read:4min 2026-08-22 ·