Applesauce v4
Applesauce v4.0: SQLite, Wallet Connect, negentropy, etc.
Another month, another release of applesauce! I don’t think ill be able to keep up this pace next month, but thats because I’m almost “done” with applesauce. its reaching a point where it has enough features to build almost every kind of nostr client you can imagine, even a relay ;)
For example in the last month I’ve built a few experimental server side nostr apps and lots more examples.
- nostr-junk-drawer A nostr relay that collects events from your followers and has NIP-50 search.
- nostr-gatekeeper A nostr bunker that runs on your umbrel to manage all your random nsecs
- nostr-code-snippets A CLI and MCP server for reading and publishing NIP-C0 code snippets.
- rekindle A server rendered nostr client that works on a kindle’s built-in web browser. (mostly a failure)
- nostr-secretary A personal nostr notification service that uses ntfy to send notifications to your phone.
Disclaimer: None of these projects are complete or working, they where experiments to see how far I could push applesauce.
EventDatabase and applesauce-sqlite
The most significant addition in v4.0 is the Event Database and applesauce-sqlite package, which provides persistence for the event store and full text search (depending on the implementation).
The package supports multiple database implementations depending on your runtime. bun:sqlite, better-sqlite3, node:sqlite, libsql and custom.
import { EventStore } from "applesauce-core";
import { BetterSqlite3EventDatabase } from "applesauce-sqlite/better-sqlite3";
// Create a SQLite-backed event store
const database = new BetterSqlite3EventDatabase("events.db");
const eventStore = new EventStore(database);
// Use the event store as normal
eventStore.add(someNostrEvent);
For now the only database implementations I’ve added are for SQLite but id love to build one for nostrdb. Just need to make the nodejs bindings for it.
AsyncEventStore and Custom Databases
If the database uses async methods then your app will have to use the new AsyncEventStore class. its the same api as the regular EventStore but all get* and has* methods are async.
import { AsyncEventStore } from "applesauce-core";
import { LibsqlEventDatabase } from "applesauce-sqlite/libsql";
const database = new LibsqlEventDatabase("events.db");
await database.initialize();
const asyncEventStore = new AsyncEventStore(database);
// Use the async event store as normal
await asyncEventStore.add(someNostrEvent);
Negentropy all the things
The applesauce-relay package now supports NIP-77 negentropy sync to sync events with relays and an event store.
import { unixNow } from "applesauce-core/helpers";
import { RelayPool } from "applesauce-relay";
const pool = new RelayPool();
const relays = ["wss://relay.damus.io", "wss://relay.primal.net"];
const pubkey = "fiatjaf";
const since = unixNow() - 60 * 60 * 24; // Last 24 hours
pool.sync(relays, eventStore, { kinds: [1], "#p": [pubkey], since }).subscribe((event) => {
console.log("Received event:", event);
// Add missing events to the event store
eventStore.add(event);
});
Outbox model (kind of)
I’ve built a few things to help apps use the “outbox model” for selecting the optimal relays to use when subscribing to a users timeline.
The includeMailboxes operator can be used to add the NIP-65 mailboxes to the contacts list. then the selectOptimalRelays helper can be used to select the optimal relays for each user based on the whole array of users. it doesn’t take into account if the relays are online or if they take 10+ seconds to respond. your app will need to handle that.
You can look at the relay selection example to see how it can be used. I’m still not confident its the best way to implement the “outbox model” but its probably a good start.
Wallet connect
I had fun building the applesauce-wallet-connect package. it provides full support for NIP-47 Nostr wallet Connect clients and services. Which means you can connect to a wallet or run a wallet service using applesauce. so go build something crazy using it.
I don’t know how it compares to Ably’s existing @getalby/sdk, but at least now you have two options to choose from when building your next app.
Breaking changes
There are some breaking changes, but its mostly just renaming methods and removing deprecated methods. see the release notes for more details.
Other stuff
- Support for NIP-78 app data events.
- The relay pool in
applesauce-relayno longer returns duplicate events from multiple relays by default.