Implement APNS feedback for remote commands#740
Conversation
|
Cool stuff, kudos. Question: how secure are APNS payloads? Do we really want to send a raw, unencrypted APNS key as payload!? Should this not at least be hashed somehow, where only the "paired" apps know how to read the actual value? |
|
Companion PR: The client-side implementation for this feature in the LoopFollow repository can be found here: loopandlearn/LoopFollow#445 |
I'll refactor the LF & Trio part to encrypt the payload using the shared secret. |
|
I've pushed the latest updates to this PR, which now implements end-to-end encryption flow for remote commands. Instead of sending the shared secret for verification, I now use it as a key to encrypt the entire command payload. Here are the technical details of the implementation: Encryption Algorithm: I've implemented this using AES-256-GCM. The GCM (Galois/Counter Mode) is important because it's an authenticated encryption mode, meaning it not only ensures confidentiality but also provides data integrity, protecting against tampering. Key Derivation: The encryption key is derived from the existing shared secret (the GUID). I pass the secret through a SHA-256 hash to produce the consistent 256-bit key required for the AES algorithm. Nonce Handling: For each message sent, a new, cryptographically secure 12-byte nonce (Initialization Vector) is generated. This ensures that even identical commands result in unique ciphertext. Authentication: The shared secret is no longer sent over the network. Authentication is now implicit: if Trio can successfully decrypt the payload, the sender is proven to be authentic. Important: This is a breaking change. Current versions of LoopFollow and Trio dev will not be compatible with these new versions. Both apps must be updated together for the remote control feature to work. |
|
I like this overall. |
|
Sorry, I‘ve gotten side tracked with life. This PR looks good to me. Have you had any in-vitro or -vivo testers (other than yourself + son I guess) go over this, @bjorkert ? Any approvals from them? @bjornoleh ? @dsnallfot ? |
|
I am not aware of anyone else testing this yet. |
|
I'd like to get distinct testing feedback by a member of the Trio team or the "tester core". Can someone please take the time to test this and provide this feedback? @dsnallfot @bjornoleh @MikePlante1 @marionbarker Thank you in advance. |
|
@bjorkert , sorry, I have forgotten the details here. Is it required to have Production Env: True? I couldn't make that happen with an Xcode build. TestFlight should do that, but was hoping to test from Xcode first. |
|
It works for both sandbox and production APNS environments, and Trio and LoopFollow does not have to built with the same method. But both needs to be build using these two companion PR's. Side note, you can build with Mac and Xcode, select Product->Archive and send it to TestFlight that way, then the app will use production environment - but that is not a requirement for this PR. |
|
Ok, I got it to work from the Xcode build, not sure why it failed initially, and I got hung up on the APNS environmement. If production is not a requirement, do we need to display this at all? It seems to have confused me into barking up the wrong tree. |
|
The first test was to a testing instance of the Trio app on my phone (default bundle ID), and a LF-dev bundle ID on the same phone. This works well now. Then I built the same Trio to the looper's phone, and a default LF bundle ID to my phone. I got a single remote bolus to work including th notification, then no more remote bolus, and no remote carbs worked. I don't know what fails here. I don't suppose having several apps installed should be a problem here. Deleting the testing instances made no difference. Do we expect production APNS environment to be more reliable? I will test from TestFlight to compare. |
|
That information is displayed because it’s been very useful for support. People sometimes switch between sandbox and production or run more than one looping app against the same Nightscout, which can lead to inconsistent setups. Seeing the environment and a few APNS details, together with the user’s explanation of what happened, helps us spot mismatches. I can reword it to avoid confusion. For example: |
|
Ok, sounds good. Perhaps a couple of explaining words about how this relates to Xcode vs TestFlight builds would be useful too? |
|
We currently use sandbox without issues. When it did not work, you got no notification responses and no notes/gray dots uploaded to nightscout? |
|
Yes, that's correct. No positive or negative confirmations |
|
| Ok, sounds good. Perhaps a couple of explaining words about how this relates to Xcode vs TestFlight builds would be useful too? This section "Debug/Info" in the bottom of the screen has Device Token, Environment, Team ID and Bundle ID. They are not explained. I think explaining how they all relate is more for the documentation site. |
Then I'm afraid that message was discarded by apple or phone. You could also look at the trio logs, but if there is nothing there, the message was never delivered. |
|
Ok. Do we get negative confirmations at all, or just positive confirmations when bolus is started etc? |
|
If the message gets delivered to Trio, you will get a notification back, also for negative things like trying to bolus that would exceed max IOB, pump not reachable etc. |
|
Ouch, turns out I had not built the correct version of LF for the testing of remote commands towards the looper's phone... Sorry for wasting time. Remote carbs and bolus worked on first attempt after building the correct thing. |
|
So @bjornoleh can you confirm that all the additions listed in the description are working as intended? It seems like you tested "happy flow", which is good to be working. What about the "rest"? |
|
I have only been doing some occasional tests, nothing structured. The remote feature seems to work, I may have had one or two that never arrived to the loopers phone. But mostly it works, sometimes with some delay (tens of seconds), other times it's nearly instant. But I know this PR doesn't adress the performance as such. I did verify that failure messages also work just now, by turning off BT on the loopers phone. The correct, localised error message about the pod not being connected was displayed as a notification on my phone. So far, this looks good, and represents a major improvement to remote commands. I would still love to see the addition of a new |
|
I do wonder about how the remote commands that never arrive at the loopers phone could be treated the best way. Currently I believe there is just silence? Knowing that the remote command might go through after a significant delay, for just how long will the remote command be valid to the loopers Trio app? Is there a timeout that is displayed to the person sending the remote command? If not, perhaps that could be added? It's tiresome to sit and look at the phone when there's a delay, and one can easily forget to check later in NS. A timeout message saying "The remote command was not received/enacted after xx minutes, please try again", or similar would be nice 😊 |
It would be great, but that is a big change in Trio that will not happen in this PR.
Trio will reject a received message that was sent for more than 10 minutes ago. A rejected message will be logged, info sent as a push notification back as well as a note in Nightscout/LoopFollow.
This might be possible in the future if a is-remote flag is introduced, then LoopFollow could pass a reference to the command and follow up on if there is a treatment with that reference for example. It might be worth to mention that the message back from Trio to LoopFollow has content-available set to 0, so it is not at all processed by LoopFollow, only displayed to the user by iOS. These messages gets through faster and are not subject to the device's power and data usage optimizations. |
|
Thanks for the detailed response. And good to know about the ten minute timeout, and that this will also prompt a notification from Trio to LF. I haven't tested that scenario, but might give that's try as well (make the LF phone or the Trio phone go offline, send remote, wait 10+ minutes before going online again). Is the success rate of remote commands logged in LF? I notice the reliability is discussed in Discord, and I have also been struggling with this occasionally. I haven't tested the latest version enough to know if it's better or the same as before. Some logging of success rate could help with objective measurements, if the response messages themselves are reliable enough. If they get lost at the same rate as outgoing messages, this won't work. But what you wrote about "content-available set to 0" might indicate that these are more reliable than outgoing remote commands? |
|
Test:
Result: no notification in the LF phone, no bolus seen in NS. The loopers phone not available for inspection at the moment. |
TestI tested Trio PR 740 and LoopFollow PR 445. WarningThis is a breaking change. Users must update both Trio and LoopFollow with the updated code at the same time. Requires coordination between LoopFollow and Trio, along with some announcements and updates to LoopFollowDocs |
LF is not aware of the success rate. Success rate of APNS messages are available in the apple developer site, at least for production APNS.
Nothing has changed in this version except for the response message back and encryption of the payload. Yes, the notification only messages are faster and are not throttled the same way AFAK. |
|
So since this one has been sitting for 3 weeks now. Are we good with merging this? Does it have to be 0.5.2? |
|
The added feature seems good and an improvement. There are still issues with remote reliability, but that's not part of this PR. As @marionbarker noted, merging this should be done at the same time as the corresponding changes in LF are merged, otherwise the remote control features will be broken. Both apps must be rebuilt with the respective changes. |
TestRepeat Test because of intervening merges to both Trio and LoopFollow. Summary: Trio, with PR 740 merged, and LoopFollow, with PR 445 merged, still provide the updates described in this PR. The updates for Trio, LoopFollow and LoopFollowDocs need to be coordinated - this will happen via direct messaging between developers. See loopandlearn/LoopFollow#445 (comment) for test details. |

Description
This pull request lays the groundwork for an upcoming feature in LoopFollow (LF) that will display real-time notifications for the results of remote commands. This change enables Trio to send a push notification back to the originating LF app, immediately informing the user whether their command (e.g., bolus, override) was successfully processed or if an error occurred. This greatly improves the user experience compared to waiting for Nightscout being updated.
Technical Walk-through
The core of this implementation allows Trio to send an Apple Push Notification Service (APNS) message back to the LF instance that initiated a remote command.
Extended Remote Command Payload: The
PushMessagemodel has been extended to include a newreturnNotificationobject. This object contains all the information Trio needs to authenticate with APNS and send a push message back to the correct device.Support for Multiple Developer Teams: Since different LF builds could be created with their own developer accounts, each LF instance must provide its APNS credentials (
teamId,keyId,apnsKey,bundleId, etc.) in thereturnNotificationpayload.JWT Caching: A new
APNSJWTManagerhandles the creation and signing of JSON Web Tokens (JWT) required for APNS authentication. To comply with Apple's requirement of not generating tokens too frequently, and to improve efficiency, generated JWTs are cached in Trio for 55 minutes. The cache is keyed byteamIdandkeyId, allowing Trio to manage tokens for multiple LF instances from different developers simultaneously.Enhanced Success and Error Handling: The
logSuccessandlogErrorhelper functions have been extended. They now check for the presence of thereturnNotificationobject in the original command, ensuring backward compability. If it exists, they use the newRemoteNotificationResponseManagerto dispatch a push notification with the result of the command. This ensures feedback is sent for both successful and failed operations.