Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

NS Bridge

iOS app that runs a local HTTP server on 127.0.0.1:17580 and acts as a Nightscout-compatible bridge between a CGM uploader and a Nightscout reader (e.g. iAPS) running on the same device.

What it does

  • Accepts incoming HTTP traffic on http://127.0.0.1:17580 (loopback only).
  • Forwards everything to a configured Nightscout server (SettingsManager.nightscoutURL) and returns the upstream response to the client.
  • Intercepts a few endpoints and serves them locally so an uploader can authenticate and read its own data even when upstream is unreachable or has a mismatched API secret:
    • GET /api/v1/verifyauth — always returns authorized.
    • GET /api/v1/status and /api/v1/status.json — returns a synthetic fully-authorized status response.
    • GET /api/v1/entries and /api/v1/entries.json — served from the local store, with find[dateString][$gte|$lte] and count query parameters honored.
  • Persists POST /api/v1/entries payloads to local storage (UserDefaults) so the GET endpoint above can replay them. Each entry is keyed by _id; if missing, a stable id is synthesized from type + date + device.
  • Queues outbound forwards for retry while offline (NWPathMonitor-driven) and replays them when the network returns. 4xx upstream responses are treated as terminal and dropped from the queue; 5xx and transport errors stay queued.

Retention

Local CGM entries are kept for 1 hour (sliding window). See EntryStore.retentionWindow in NSBridge/EntryStore.swift.

The retry queue keeps pending requests for 7 days — see RequestQueue.maxQueueAge.

Background operation

To keep the HTTP listener responsive while the app is backgrounded, NS Bridge:

  • Plays a silent audio loop (AVAudioSession .playback + .mixWithOthers) — see NSBridge/BackgroundAudioPlayer.swift. This requires UIBackgroundModes = ["audio", ...] in the Info.plist.
  • Requests BGTaskScheduler app refresh time as a fallback (com.nsbridge.app.refresh).

The audio is muted (mainMixerNode.outputVolume = 0) and configured with .mixWithOthers, so it does not interrupt other audio.

⚠️ The silent-audio keepalive is sufficient for personal/sideload use. App Store review may flag it for distribution.

Settings

Tap the gear icon to set the upstream Nightscout URL (default: https://your.nightscout.cloud). Changing it restarts the listener.

Authentication / API secret

NS Bridge does not store a Nightscout API secret of its own. The token is supplied by the client (e.g. the CGM uploader) on each request and is forwarded as-is to the upstream Nightscout host.

  • The client sends its API secret in the standard Nightscout api-secret header (the SHA-1 hex of the plaintext secret) — or as a ?token=… query parameter for token-based auth.
  • For locally-served endpoints (/api/v1/verifyauth, /api/v1/status[.json], GET /api/v1/entries[.json]), NS Bridge always responds as authorized regardless of the header value, so the uploader can keep working even if its secret does not match upstream.
  • For everything else (including POST /api/v1/entries), the request is forwarded to SettingsManager.nightscoutURL with the client's api-secret / token preserved. Only Host, Connection, and Content-Length are stripped — see forwardRequest in NSBridge/WebServer.swift.
  • This means the client must be configured with the secret/token that the upstream host accepts. NS Bridge is a transparent pass-through for auth.

Example — point an uploader at http://127.0.0.1:17580 and configure its API secret as you would for the real Nightscout. The bridge will hand the same header to upstream:

curl -X POST http://127.0.0.1:17580/api/v1/entries \
  -H "Content-Type: application/json" \
  -H "api-secret: $(echo -n 'your-plaintext-secret' | shasum | awk '{print $1}')" \
  -d '[{"device":"test","sgv":110,"date":'"$(($(date +%s)*1000))"',"dateString":"'"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"'","direction":"Flat","type":"sgv"}]'

Tested CGMs

CGM Status Notes
Instara ✅ Works Uploads directly via the Nightscout API, so the bridge can forward and cache transparently.
Syai ❌ Unsupported Data is not transmitted directly to Nightscout — it goes to Syai Cloud first, which then pushes to the Nightscout host out-of-band. The bridge never sees the traffic and cannot intercept or cache it.

Networking config

NSAppTransportSecurity allows arbitrary loads so the proxy can forward to plain http:// Nightscout servers. See NSBridge/Info.plist.

The listener is bound to 127.0.0.1 via NWParameters.requiredLocalEndpoint, so it is not reachable from other devices on the same Wi-Fi.

API summary

Method Path Behavior
GET /api/v1/verifyauth Local — always authorized
GET /api/v1/status / status.json Local — synthetic authorized status
GET /api/v1/entries[.json] Local — served from device storage
POST /api/v1/entries[.json] Stored locally and forwarded to upstream
any anything else Forwarded to upstream Nightscout server

Forwarded requests strip Host, Connection, and Content-Length headers (URLSession sets these correctly itself).

Requirements

  • iOS 17+
  • Xcode 15+

Running

  1. Open NSBridge.xcodeproj.
  2. Select a target (simulator or device).
  3. Build & Run.

Quick test

# Health-style probe (intercepted)
curl http://127.0.0.1:17580/api/v1/status.json

# Submit a CGM reading
curl -X POST http://127.0.0.1:17580/api/v1/entries \
  -H "Content-Type: application/json" \
  -H "api-secret: $(echo -n 'mysecret' | shasum | awk '{print $1}')" \
  -d '[{"device":"test","sgv":110,"date":'"$(($(date +%s)*1000))"',"dateString":"'"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"'","direction":"Flat","type":"sgv"}]'

# Read it back (within retention window)
curl 'http://127.0.0.1:17580/api/v1/entries?count=10'

About

iOS loopback HTTP server (127.0.0.1:17580) that forwards a CGM uploader's traffic to a Nightscout server, intercepts auth/status locally so an on-device reader (e.g. iAPS) keeps working offline, and queues outbound writes until the network returns

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages