damus icon indicating copy to clipboard operation
damus copied to clipboard

ios app level notifications

Open githubbbie opened this issue 2 years ago • 4 comments

Need to know when updates and/mentions ready for review

githubbbie avatar Dec 20 '22 06:12 githubbbie

In app notifications seem to work as of Feb 13. Should this ticket be considered closed?

alltheseas avatar Feb 13 '23 16:02 alltheseas

App level would be Push Notifications? I cannot get it to work. Even tho the App does ask for notifications permissions. I believe what actually is needed to get push notifications working is the Nostr relay side needs to generate the push somehow. Not sure how that can be done.

IMG_0357

nobu-maeda avatar Feb 15 '23 05:02 nobu-maeda

I created issue #257 in nostr-protocol/nips. This is what I suspect is needed to make Push Notifications possible. Please add/critique, etc as needed to help make this a reality!

nobu-maeda avatar Feb 15 '23 06:02 nobu-maeda

App level would be Push Notifications?

I think you are right. In my earlier comment I misread the title of the ticket (in app notifications vs. iOS notifications).

alltheseas avatar Feb 15 '23 07:02 alltheseas

@githubbbie @nobu-maeda we now have local notifications in Damus. Does this close this issue for you? 🙏

alltheseas avatar Apr 14 '23 21:04 alltheseas

Hi @alltheseas. Thanks for the headsup. Do you have a reference to what's implemented?

I am not sure what App Level vs Local notification exactly refers when this issue was initially created by @githubbbie. I am more thinking about Push Notifications up to the iOS level when say, a DM is received. If this is not the right issue for that I can create a new one. @githubbbie to clarify tho

nobu-maeda avatar Apr 14 '23 23:04 nobu-maeda

Hi @alltheseas. Thanks for the headsup. Do you have a reference to what's implemented?

I am not sure what App Level vs Local notification exactly refers when this issue was initially created by @githubbbie. I am more thinking about Push Notifications up to the iOS level when say, a DM is received. If this is not the right issue for that I can create a new one. @githubbbie to clarify tho

When user receives them local vs push notifications appear the same.

Local notifications appear when you have the app open, or minimized recently. See your settings to check local notifications configuration - e.g. for DMs, likes, zaps etc..

We don't have push notifications yet.

alltheseas avatar Apr 15 '23 00:04 alltheseas

@alltheseas this should be in our roadmap, high priority

jb55 avatar Jul 31 '23 09:07 jb55

@alltheseas this should be in our roadmap, high priority

Push notifications are now high priority

alltheseas avatar Aug 01 '23 19:08 alltheseas

image

Signal

alltheseas avatar Oct 19 '23 01:10 alltheseas

image

image

alltheseas avatar Oct 19 '23 01:10 alltheseas

Example methodology from Will. Script running in strfry - when see event, create notification. Build on damus relay for real time processing. E.g. go script.

cc @danieldaquino

alltheseas avatar Oct 31 '23 00:10 alltheseas

Sprint 7 commitment @danieldaquino second highest priority after https://github.com/damus-io/damus/issues/1668

alltheseas avatar Oct 31 '23 00:10 alltheseas

At nostrasia Rabble discusses push notifications solution for his safety & moderation focused client https://youtu.be/gOIZMMvHSZw?t=19753

alltheseas avatar Nov 01 '23 14:11 alltheseas

Nice! Thanks for sharing @alltheseas, I will definitely take a look once I start working on this ticket

danieldaquino avatar Nov 01 '23 17:11 danieldaquino

On Wed, Nov 01, 2023 at 10:56:09AM -0700, Daniel D’Aquino wrote:

Nice! Thanks for sharing @alltheseas, I will definitely take a look once I start working on this ticket

This is a different approach, which requires pulling down notes from other relays. I'm not against using it but it would start failing for DMs once we require AUTH for pulling DMs.

jb55 avatar Nov 02 '23 00:11 jb55

Currently working on this ticket!

danieldaquino avatar Nov 03 '23 20:11 danieldaquino

-received a reply -received a mention -received a DM -received a zap -received a reaction (e.g. 🤙) -someone re-posted your note -someone quoted your note -someone followed you -someone added you to a user list

alltheseas avatar Nov 03 '23 21:11 alltheseas

I wrote some code already (still not functional yet), but building is underway:

  • To send push notifications I am making a strfry plugin using Deno and this policy framework: https://gitlab.com/soapbox-pub/strfry-policies/-/tree/develop?ref_type=heads
    • The strfry plugin will send requests to APNs who will deliver the notifications
  • I am using sqlite to keep track of who got push notifications and on which Apple devices
  • I will create a Deno server endpoint that receives device token IDs from the Damus app (Cryptographically signed and associated with the pubkey) and saves that info into the sqlite database for use with push notifications

danieldaquino avatar Nov 04 '23 03:11 danieldaquino

@jb55, in terms of sharing the code while it is a draft, do have any preferences in terms of whether to make it public or keep it private for now?

danieldaquino avatar Nov 04 '23 03:11 danieldaquino

On Fri, Nov 03, 2023 at 08:57:08PM -0700, Daniel D’Aquino wrote:

@jb55, in terms of sharing the code while it is a draft, do have any preferences in terms of whether to make it public or keep it private for now?

I can setup a repo on damus-io, what should it be called? strfry-push-notify ?

jb55 avatar Nov 07 '23 02:11 jb55

what should it be called? strfry-push-notify?

I like it! Sounds good!

danieldaquino avatar Nov 07 '23 02:11 danieldaquino

Updates on this project (What is done and what is still pending): [X] Make a plugin that connects to strfry and processes events [X] Make mechanism to keep track of which pubkeys received notifications for which events [X] Make server endpoint to keep track of device tokens [X] Implement basic mechanism for sending push notification device tokens to the server [X] Implement mechanism for sending basic notifications to the (mock) APNS server [X] Implement basic mock APNS server (For easier testing without authentication) [ ] Implement authentication on the process of sending device tokens to the server (I am thinking to simply sign the device token using the user's keypair) [ ] Generate better notification messages [ ] Notify authors of events to which the new event is a reply of (And other more complex references where the pubkey is not directly referenced in the new event) [ ] Implement authentication with APNS server on push notifications (Not yet clear from Apple Docs how to obtain auth details) [ ] Set it up on a staging server and test (Once ready) [ ] Deploy to production server (Once ready)

I was able to start Damus connected to a local relay (setup with push notifications plugin), post a new event, and get correct push notification payloads to be printed to the console of a mock APNS server.

danieldaquino avatar Nov 07 '23 06:11 danieldaquino

@jb55, created the new strfry-push-notify repo so that we have a place where we can share the code!

https://github.com/damus-io/strfry-push-notify/

I will continue working on it, testing, and pushing code directly (for now), as now I have all the test environment working smoothly

danieldaquino avatar Nov 08 '23 19:11 danieldaquino

Updates on this project (What is done and what is still pending): [X] Make a plugin that connects to strfry and processes events [X] Make mechanism to keep track of which pubkeys received notifications for which events [X] Make server endpoint to keep track of device tokens [X] Implement basic mechanism for sending push notification device tokens to the server [X] Implement mechanism for sending basic notifications to the (mock) APNS server [X] Implement basic mock APNS server (For easier testing without authentication) [/] Implement authentication on the process of sending device tokens to the server (I am thinking to simply sign the device token using the user's keypair) [/] Generate better notification messages [/] Notify authors of events to which the new event is a reply of (And other more complex references where the pubkey is not directly referenced in the new event) [ ] Implement authentication with APNS server on push notifications (Not yet clear from Apple Docs how to obtain auth details) [ ] Set it up on a staging server and test (Once ready) [ ] Deploy to production server (Once ready)

(/ means in progress)

I am currently focusing on improving the data in the notifications (notify on thread replies, getting profile names from relay, etc), as well as improving the structure of the project to something more object-oriented.

I tried making the device token authentication/authorization mechanism work earlier today, but I decided to leave this part for later, as for some reason I cannot get the signatures to verify correctly on the Deno side. I think this part will be essential once we have auth on DMs, but it is probably not immediately urgent. (More on this on the next comment)

danieldaquino avatar Nov 09 '23 05:11 danieldaquino

@jb55, for the authentication/authorization mechanism, I am trying to make an ECDSA signature on the device token + a timestamp (using the secp256k1 curve)

I made a draft implementation here:

  • Relay-side: https://github.com/danieldaquino/strfry-push-notify/commit/feecf33d40d75d9948617b2d479d997f129ca6d7
  • Client-side: https://github.com/danieldaquino/damus/commit/64acb318fdbd3be5ad0c381212398b8af52c8361 (Sorry the code for this is not very clean yet, I will clean it up before sending the official patches)

I already checked that the encoding and decoding of bytes is correct, they are both using ECDSA algorithm on the secp256k1 curve, the SHA256 hash matches on both sides, and I checked that I can successfully verify the signature on the client-side. However, for some reason the signature verification always fails on the server-side. I think there is some kind of cryptographic parameter that is different 🤔

Do you have any thoughts on what I might be doing wrong?

danieldaquino avatar Nov 09 '23 05:11 danieldaquino

Updates on this: I am working on these 3 items:

  • Implement authentication on the process of sending device tokens to the server
  • Generate better notification messages
  • Notify authors of events to which the new event is a reply of (And other more complex references where the pubkey is not directly referenced in the new event)

Here are some of the things discussed in today's standup:

  • For authorization/authentication: Will suggested we can use Nostr libraries to sign a Nostr event instead of using raw cryptography libraries (e.g. we can use https://github.com/jb55/nostr-js)
  • For the formatting of notification content: Formatting notification messages is a non-trivial process consisting of multiple steps including: Localization of text, replacement of pubkeys with profile names, decryption of DMs. Some of these could be done on the server or the client. On the meeting we came to the conclusion that it is preferable to do most or all of this on the client side.
  • The notification DB: We are using sqlite at the moment, which is the simplest way for now. However, we need to watch out for:
    • Performance: How well it will perform under heavier loads
    • Access by multiple processes: Likely works out of the box, but we need to watch out that there will be no corruptions or crashes arising from two processes trying to access the same DB at the same time.
    • Alternatives: If one of the above becomes a real issue under heavy loads, we should consider moving to PostgreSQL, Redis, or another DB that is made for handling heavier loads.
  • The notification DB schema: Will suggested we have rows for each pubkey/event_id notification status (Instead of having pubkeys and their statuses in JSON). This will improve performance by removing the JSON parsing step.
  • Separate notification relay: We discussed that it might be a good idea to run this push notification as a separate relay that does not store any information, to allow the user to get push notifications in a more privacy-preserving way (e.g. if they do not want their notes stored on the Damus relay)
    • For the implementation of this privacy-preserving relay, we can just use strfry with this notification-sender policy, and have it blanket reject all incoming notes. A simple but effective and elegant hack.

danieldaquino avatar Nov 11 '23 02:11 danieldaquino

@jb55, I spent some time today researching into how the notification service extension works and relates to the main app.

I remember you asked me yesterday whether the apps share the same file container. I tested listing the documents folder from the extension and it seems to use a completely separate file container by default, unfortunately.

Since we want to do as much as possible on the client-side, being able to share data between the app and the extension will essential for us to be able to:

  • Unfurl pubkeys into the user's display name
  • Suppress notifications from profiles whom the user has muted

The good news

The good news is that I found a way for us to share data between the main app and the extension! I essentially created an "app group" which involves both the Damus app and the extension, and that caused the creation a shared file container between them. I wrote a small proof-of-concept here where I was able to see data.mdb from the extension.

Not saying that we should necessarily move Ndb data to the shared container 😅 but at least we know we can share data between them, which will be essential.

The not-so-good news

The not-so-good news is that I also found that apparently iOS only gives a maximum of about 24MB of RAM memory to the extension, so even if we could get NostrDB or other existing code to compile and run under the extension, we have to be careful not to "OOM" our extension. Source

Different ways to move forward

I believe decryption of DMs is not much a concern, but since profile display names, user mute lists, etc are only available in NostrDB (AFAIK), it is non-trivial to access that information. There are at least two different approaches to it:

  1. Get NostrDB fully or partially working on the extension so that I can access profile and other relevant data. For that I will need to migrate data.mdb and lock.mdb to the new shared container.
  2. Create some new cache files on the shared container, where we write only the data that the extension needs (e.g. pubkeys and display names, the mute list, etc), and we use those files in the extension to do notification processing.

I am leaning on checking whether NostrDB generally uses less than 20MB of RAM, and if yes, trying approach (1) first (to take maximum advantage of existing code/logic and avoid duplication), and if that fails or becomes a giant rabbit-hole, go for (2).

@jb55, do you have any preference on which approach to try first (Or any other thoughts on this)?

danieldaquino avatar Nov 11 '23 23:11 danieldaquino

Hey Daniel!

responses inline.

On Sat, Nov 11, 2023 at 03:17:21PM -0800, Daniel D’Aquino wrote:

@jb55, I spent some time today researching into how the notification service extension works and relates to the main app.

I remember you asked me yesterday whether the apps share the same file container. I tested listing the documents folder from the extension and it seems to use a completely separate file container by default, unfortunately.

Since we want to do as much as possible on the client-side, being able to share data between the app and the extension will essential for us to be able to:

  • Unfurl pubkeys into the user's display name
  • Suppress notifications from profiles whom the user has muted

The good news

The good news is that I found a way for us to share data between the main app and the extension! I essentially created an "app group" which involves both the Damus app and the extension, and that caused the creation a shared file container between them. I wrote a small proof-of-concept here where I was able to see data.mdb from the extension.

Not saying that we should necessarily move Ndb data to the shared container 😅 but at least we know we can share data between them, which will be essential.

Definitely will be essential if we want profile names in push notifications. I wonder if this could be a phase-2 PR though, since it's getting pretty hairy. I would be ok with a simpler initial PR that doesn't have rendered profile names.

The not-so-good news

The not-so-good news is that I also found that apparently iOS only gives a maximum of about 24MB of RAM memory to the extension, so even if we could get NostrDB or other existing code to compile and run under the extension, we have to be careful not to "OOM" our extension.

We rely on the extended virtual memory entitlement for nostrdb to work:

https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_kernel_extended-virtual-addressing

How much actual ram is used depends on how many active pages the extension loads into memory when accessing the database. It should probably be much less than 20MB. nostrdb doesn't do any allocations when reading from the database.

Also, if these are separate processes, I'm still concerned about concurrent access and if lmdb handles that properly. I'm not sure it does since we've had crashed when multiple open handles are created to the same database in the same process. It may be fine if there isn't any concurrent access which might be the case with the extension?

Here's what chatGPT has to say:

Q: "Can you concurrently access the same lmdb database from different processes ?"

A: "Yes, you can concurrently access the same LMDB (Lightning Memory-Mapped Database) database from different processes. LMDB is designed to support concurrent read access by multiple processes, making it a good choice for scenarios where multiple processes need to access a shared database.

LMDB uses a combination of memory-mapped files and locking mechanisms to ensure data consistency and allow for concurrent access. Each process that wants to access the database must open the LMDB environment, and LMDB handles the synchronization and locking internally to ensure that multiple processes can read from the database simultaneously without corrupting the data.

However, it's important to note that while LMDB supports concurrent read access, it is not designed for concurrent write access from multiple processes. If you have multiple processes that need to write to the database, you should carefully coordinate and synchronize their write operations to avoid data corruption or conflicts. LMDB's design makes it highly efficient for read-heavy workloads but requires caution when dealing with concurrent writes.

In summary, LMDB is suitable for concurrent read access from different processes, but you should be mindful of concurrent write operations and implement proper synchronization mechanisms if needed."

Different ways to move forward

I believe decryption of DMs is not much a concern, but since profile display names, user mute lists, etc are only available in NostrDB (AFAIK), it is non-trivial to access that information. There are at least two different approaches to it:

  1. Get NostrDB fully or partially working on the extension so that I can access profile and other relevant data. For that I will need to migrate data.mdb and lock.mdb to the new shared container.

What about user settings and keychain? Ideally the extension would have access to the private key as well for decrypting DMs.

  1. Create some new cache files on the shared container, where we write only the data that the extension needs (e.g. pubkeys and display names, the mute list, etc), and we use those files in the extension to do notification processing.

I am leaning on checking whether NostrDB generally uses less than 20MB of RAM, and if yes, trying approach (1) first (to take maximum advantage of existing code/logic and avoid duplication), and if that fails or becomes a giant rabbit-hole, go for (2).

@jb55, do you have any preference on which approach to try first (Or any other thoughts on this)?

jb55 avatar Nov 12 '23 02:11 jb55

@jb55 to @danieldaquino How might we forward damus notes to notifications relay?

Str-fry has sync mechanism in background. Does it use negative entropy (neg entropy)?

Str-fry forwards notes, or replicate notes to other str-fry instances.

New relay does not store notes.

alltheseas avatar Nov 13 '23 17:11 alltheseas