A modern, high-performance diabetes management platform built with .NET 10. Nocturne is a complete rewrite of the Nightscout API with full feature parity, providing native C# implementations of all endpoints with optimized performance and modern cloud-native architecture.
Nocturne is a comprehensive diabetes data platform that provides:
- Complete Nightscout API Implementation - All Nightscout endpoints natively implemented in C# with full compatibility
- Data Connectors - Native integration with major diabetes platforms (Dexcom, Glooko, LibreLinkUp, MiniMed CareLink, MyFitnessPal, Nightscout)
- Real-time Updates - WebSocket/SignalR support for live glucose readings and alerts
- Advanced Analytics - Comprehensive glucose statistics, time-in-range calculations, and reports
- Cloud-Native - Built on Aspire for seamless local development and cloud deployment
Nocturne/
├── src/
│ ├── API/ # REST API (Nightscout-compatible)
│ ├── Aspire/ # Aspire orchestration
│ ├── Connectors/ # Data source integrations (Dexcom, Libre, etc.)
│ ├── Core/ # Domain models, interfaces, and constants
│ ├── Desktop/ # Desktop application
│ ├── Infrastructure/ # EF Core data access, caching, security
│ ├── Portal/ # Marketing website
│ ├── Services/ # Background services
│ ├── Tools/ # CLI tools and MCP server
│ ├── Web/ # pnpm monorepo (SvelteKit frontend, bot, bridge)
│ └── Widgets/ # Embeddable widgets
└── tests/ # Comprehensive test suite
- Full Nightscout API Parity - All v1, v2, and v3 endpoints
- High Performance - Optimized queries with PostgreSQL
- Authentication - JWT-based auth with API_SECRET support
- Real-time - SignalR hubs for live data streaming
- Data Connectors - Dexcom Share, Glooko, LibreLinkUp, MiniMed CareLink, MyFitnessPal, Nightscout, and MyLife
- PostgreSQL - Modern relational database with EF Core migrations
- Observability - OpenTelemetry (OTLP) export for metrics, traces, and logs
- Containerized - Docker support for all services
- .NET 10.0 SDK or later
- Docker Desktop
- Node.js 24+
- pnpm 9+
Aspire orchestrates all services with a single command:
aspire startAspire will automatically:
- Start PostgreSQL in a container
- Run database migrations
- Start the Nocturne API and SvelteKit frontend
- Launch any configured data connectors
- Set up service discovery, health checks, and a YARP gateway
Once running, open the Aspire dashboard link from the console output to see all services. Access the app at https://localhost:1612.
By default, Aspire serves the app at https://localhost:1612. This works for single-tenant development but WebAuthn passkeys fail on tenant subdomains because browsers reject localhost as a passkey Relying Party ID for subdomain origins.
To test multitenancy with passkeys locally:
1. Install mkcert
# Windows
winget install FiloSottile.mkcert
# macOS
brew install mkcert
# Linux — use your distro's package manager2. Set the custom domain
cd src/Aspire/Nocturne.Aspire.Host
dotnet user-secrets set "LocalDev:Domain" "nocturne.test"3. Add hosts file entries
Add lines to your hosts file (C:\Windows\System32\drivers\etc\hosts on Windows, /etc/hosts on macOS/Linux):
127.0.0.1 nocturne.test
127.0.0.1 demo.nocturne.test
127.0.0.1 riley.nocturne.test
Add one line per tenant slug you want to use. Hosts files don't support wildcards.
4. Start Aspire
aspire startAspire will automatically generate a wildcard TLS certificate for *.nocturne.test, install the mkcert CA into your system trust store, and configure the YARP gateway to use it. Access the app at https://nocturne.test.
The easiest way to deploy Nocturne is with the production Docker Compose bundle. Each GitHub Release includes ready-to-use artifacts, or you can generate them locally.
Download docker-compose.yaml and .env.example from the latest release.
# 1. Copy the env template and fill in your passwords and domain
cp .env.example .env
# 2. Start Nocturne
docker compose up -dThe production compose includes Watchtower for automatic container updates (checks daily), and omits the Aspire dashboard and Scalar API explorer. Watchtower will automatically pull new images as they are published — no manual updates needed.
A fresh install has no tenants, so the API answers every request with 503 {"error":"setup_required"} until the first owner secures the instance. This is expected — it's the signal for the web UI to show the setup wizard.
In a browser (normal path). Open your Nocturne site (the apex https://<BASE_DOMAIN>/). The setup wizard walks you through creating the first tenant and registering a passkey (or linking an OIDC provider). When it completes you're shown a set of recovery codes — save them. That's the whole setup.
Headless / automated setup (no browser). Trusted automation can stand up a tenant — create it, configure connectors, change settings, push data — before any human registers a passkey, using the INSTANCE_KEY from your .env. The instance key is the highest-trust service credential (cross-tenant platform admin), so treat it like a root password. Requests authenticate with two headers: X-Instance-Key carrying the SHA-256 hex of INSTANCE_KEY, and an X-Instance-Service marker naming the caller.
# The X-Instance-Key header is the SHA-256 hex of your raw INSTANCE_KEY, not the key itself.
KEY_HASH=$(printf %s "$INSTANCE_KEY" | sha256sum | cut -d' ' -f1)
# 1. Create the first tenant. This endpoint is anonymous and tenantless — call it on the apex.
curl -fsS -X POST "https://$BASE_DOMAIN/api/v4/setup/tenant" \
-H 'Content-Type: application/json' \
-d '{"slug":"alice","displayName":"Alice"}'
# 2. Configure the tenant as trusted automation. Target the tenant's subdomain and
# present the instance key + service marker. Normal tenant traffic still gets 503
# until a human completes setup, but instance-key calls are allowed through.
curl -fsS -X PUT "https://alice.$BASE_DOMAIN/api/v4/connectors/config/Dexcom/secrets" \
-H "X-Instance-Key: $KEY_HASH" \
-H 'X-Instance-Service: nocturne-setup-agent' \
-H 'Content-Type: application/json' \
-d '{ "username": "...", "password": "..." }'When the human is ready, they open the site and register the first passkey through the normal wizard — recovery codes and all — exactly as on a brand-new instance. Their pre-configured connectors and data are already there. (A bare X-Instance-Key without the X-Instance-Service marker is ignored, so an instance key accidentally forwarded onto a browser request can't bypass setup.)
Redirected to /setup even though a tenant is already configured. Two usual causes:
BASE_DOMAINis unset. The API resolves tenants as{slug}.{BASE_DOMAIN}, and the WebSocket bridge requires it. If it's empty, host→tenant resolution fails and the dashboard bounces to/setup(and the bridge logsBASE_DOMAIN is required). Set it in.envto the domain you serve Nocturne on, for both the API and web containers, then recreate them.- Single-tenant install served at the base domain. If your one tenant lives at the apex (
https://<BASE_DOMAIN>/, no subdomain), update to the latest image — older builds reportedsetup_requiredfor/api/v4/statuson the apex and bounced configured single-tenant installs to/setup.
If you have the .NET 10 SDK and Aspire CLI installed, you can generate the production bundle from source:
dotnet run scripts/publish-release.cs # outputs to ./release-output
dotnet run scripts/publish-release.cs ./deploy # or specify a directoryNocturne uses three separate PostgreSQL roles for defense in depth. All three have NOBYPASSRLS so they obey Row Level Security policies, even when the database has no superuser connected.
| Role | Purpose | Privileges |
|---|---|---|
nocturne_migrator |
Runs EF Core migrations (schema DDL). Owns the database and public schema. |
CREATE, ALTER, DROP on tables. Cannot bypass RLS. |
nocturne_app |
Runtime connection pool for the .NET API. Owns nothing. | SELECT, INSERT, UPDATE, DELETE on migrator-created tables. Cannot bypass RLS. |
nocturne_web |
SvelteKit bot framework (chat state storage). Owns only its own chat_state_* tables. |
CREATE on public schema (for its own tables only). No access to Nocturne tenant tables. Cannot bypass RLS. |
The bootstrap user (POSTGRES_USER) is only used for initial container setup. After container-init/00-init.sh runs, all application traffic flows through the three roles above. Passwords are set via environment variables in .env.
For bring-your-own PostgreSQL (not using the bundled container), run docs/postgres/bootstrap-roles.sql once as a superuser. See the comments in that file for details.
# Run all tests
dotnet test
# Run unit tests only
dotnet test --filter "Category!=Integration&Category!=Performance"
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"# Create a new migration
cd src/Infrastructure/Nocturne.Infrastructure.Data
dotnet ef migrations add YourMigrationName
# Apply migrations
dotnet ef database updateAPI documentation is available via Scalar at https://localhost:1612/scalar when running locally.
Nocturne aims to match Nightscout's API 1:1, so any Nightscout API endpoint should be usable. Nocturne-only endpoints are scoped to v4.
Both the API and web containers are instrumented with OpenTelemetry and export metrics, traces, and logs over OTLP push when OTEL_EXPORTER_OTLP_ENDPOINT is set. With no endpoint configured, telemetry is collected in-process and discarded with negligible overhead, so there is nothing to turn off on a default install. There is no Prometheus /metrics scrape endpoint — to use Prometheus/Grafana, run an OpenTelemetry Collector with an otlp receiver and a prometheus exporter.
- Docker Compose: add the
OTEL_*variables to thenocturne-apiandnocturne-webservices. - Kubernetes (Helm): set
observability.otlp.enabled: trueandobservability.otlp.endpoint(lights up both containers). See the Helm chart README.
Full setup guide: Observability docs.
Nocturne is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). Commercial licensing is available for organizations that need to use Nocturne without AGPL obligations — contact the maintainers for details.
Nocturne is a community project and is not affiliated with or endorsed by the Nightscout Project, Abbott, Dexcom, Medtronic, Glooko, or MyFitnessPal.
Important: This software is provided as-is for personal use. Always verify glucose readings with approved medical devices. Never make treatment decisions based solely on data from this application.
Nocturne is a labor of love built by volunteers. If you find it useful, please consider supporting the project:
- ⭐ Star the repository on GitHub
- Donate to the Nightscout Foundation
- Support the maintainers on GitHub Sponsors!
- Built on the shoulders of the Nightscout Project
- Powered by .NET 10 and Aspire