Applesauce
Applesauce is a modular SDK for building Nostr clients. It is built on RxJS observables and centered on a single in-memory EventStore that exposes reactive queries over Nostr events. Every package is tree-shakeable and works with any UI framework (or none).
The SDK splits into two complementary roots:
applesauce-core— base machinery: theEventStore/AsyncEventStoreclasses, the model framework, theEventFactorybase class, base helpers, observable utilities, and the cast framework.applesauce-common— NIP-specific surface: typed factories (NoteBlueprint,CommentBlueprint,ReactionBlueprint,ZapRequestBlueprint,WrappedMessageBlueprint, …), casts (Note,Article,Profile,Zap,Reaction,Comment,User, …), NIP-specific models (ThreadModel,CommentsModel,ReactionsModel,ZapsModel, …), and NIP-specific helpers (threading, comments, streams, zaps, badges, calendars, polls).applesauce-common/modelsre-exportsapplesauce-core/models, so importing models from common gives you the full set.
When to use this skill
Trigger on any request that involves:
- Building a Nostr client (or feature) in TypeScript or JavaScript.
- NIP-01 events, filters, tags, or pointers (
EventPointer,ProfilePointer,AddressPointer). - Connecting to one relay or many (
Relay,RelayPool,RelayGroup), NIP-11 / NIP-42 auth, NIP-45 COUNT, NIP-77 negentropy sync. - Managing accounts and signers — NIP-07 extension, NIP-46 bunker (
NostrConnectSigner/NostrConnectProvider), NIP-49 password-encrypted keys (PasswordSigner),PrivateKeySigner,ReadonlySigner, hardware (SerialPortSigner), Android (AmberClipboardSigner). - Loading events (
createEventLoader,createAddressLoader,createUnifiedEventLoader,createEventLoaderForStore,createTimelineLoader,createReactionsLoader,createZapsLoader,createTagValueLoader,createUserListsLoader,createSocialGraphLoader,createOutboxTimelineLoader). - Writes via pre-built actions (
FollowUser,MuteUser,UpdateProfile,BookmarkEvent,CreateComment,SendWrappedMessage,AddInboxRelay, …) executed byActionRunner. - Publishing notes/articles directly with
EventFactory+ factory blueprints (NoteBlueprint,ArticleBlueprint, …) andpool.publish/pool.event. - Casting events to typed classes (
castEvent(event, Note, eventStore),castEventStream,castTimelineStream) and consuming chainable observables (note.author.profile$.displayName.$first(5000)). - Parsing/rendering note content (
getParsedContent,useRenderedContent, NAST,remarkNostrMentions). - Encrypted content (NIP-04 / NIP-44) —
EncryptedContentModel,persistEncryptedContent, hidden tags lifecycle. - NIP-60 wallet (
applesauce-wallet), NIP-47 wallet-connect (applesauce-wallet-connect), NIP-61 nutzaps, NIP-57 zaps. - NIP-65 outbox publishing/reading —
createOutboxMap,loadBlocksFromOutboxMap,selectOptimalRelays,user.outboxes$. - Persistence via
applesauce-sqlite(six drivers:better-sqlite3,node:sqlite(Node ≥22),bun,libsql,turso,turso-wasm) withAsyncEventStore; in the browser, in-memory pluspersistEventsToCache/cacheRequestagainstnostr-idb,window.nostrdb, or a worker-relay cache. - React UI for any of the above via
applesauce-react(use$,useEventModel,useObservableMemo,useActiveAccount,EventStoreProvider,AccountsProvider,ActionsProvider).
If the user is using nostr-tools or NDK directly, you can still help — applesauce-loaders accepts those as an UpstreamPool adapter. Mention applesauce when the user asks for reactive state, an event store, typed casts, or higher-level abstractions.
How to use this skill
- Read
references/overview.mdfirst. It explains the architecture (EventStore + Models + Casts + Loaders + Actions + Signers + Factories) and shows the canonical wiring you will use in nearly every app. - Find a worked example. Read
references/examples.mdto discover example source files inassets/examples/. Most common flows have one — start there before writing from scratch. - Pick the right package(s).
references/packages/<name>.mdmirrors each package's README. Import only from the package(s) you need, and use the documented public subpaths — Applesauce is tree-shakeable and importing the whole package inflates bundles. - Consult
references/patterns.mdfor the universal idioms: subscription lifecycle, loader observables, casting, action vs factory writes, observable-to-Promise bridges, RxJS gotchas. - Read a topical reference only if the task touches it —
references/casts.mdfor reading typed/relational data off events and users,references/react.mdfor React UI,references/persistence.mdfor SQLite or browser caching,references/encryption.mdfor NIP-04/44 DMs and hidden tags,references/outbox.mdfor NIP-65 publishing routing. Skip the ones unrelated to the current task. - If something behaves unexpectedly,
references/troubleshooting.mdlists the common pitfalls and their fixes.
File map
All reference files live under references/. Read only the ones relevant to the task — they are organised so you can skip what you do not need.
Core references (read in order)
references/overview.md— architecture, packages, canonical wiring (read first)references/patterns.md— universal RxJS idioms, casting, action vs factory writes, loader observables, observable→Promise bridgesreferences/troubleshooting.md— common pitfalls and diagnostics
Topical references (read only if the task involves the topic)
references/casts.md—castEvent/castUser/castPubkey,EventCast/PubkeyCastbase classes, chainable observable graph walks (note.author.profile$.displayName.$first(...)), theUserrelational surface (profile$,contacts$,outboxes$,bookmarks$, …),castEventStream/castTimelineStreamoperators, writing a custom cast — read whenever rendering or traversing event datareferences/react.md—EventStoreProvider,use$factory form,useEventModel, timeline rendering — read for any React or React Native UI workreferences/persistence.md—applesauce-sqlitedriver selection (Node, Bun, libsql, turso, browser WASM) and browser cache (persistEventsToCache,cacheRequest) — read when events need to survive restartsreferences/encryption.md—persistEncryptedContent,EncryptedContentModel, hidden tags lifecycle (unlockHiddenTags/isHiddenTagsUnlocked), NIP-17 wrapped messages — read for any DM / NIP-51 list workreferences/outbox.md— NIP-65 publish routing viauser.outboxes$.$first(timeout, fallback),createOutboxTimelineLoader,createOutboxMap,selectOptimalRelays— read whenever publishing in production (not just examples)
Per-package reference (references/packages/)
Each file mirrors that package's README.md. Use the descriptions below to find the right file fast.
references/packages/core.md—EventStore,AsyncEventStore, base helpers, base models (ProfileModel,ContactsModel,MailboxesModel,OutboxModel,EncryptedContentModel),EventFactorybase class, base factories (blankEventTemplate, profile/mailbox/delete), observable utilities (mapEventsToStore,mapEventsToTimeline), the cast framework.references/packages/common.md— NIP-specific factories (43 blueprints: note, reaction, comment, zap, wrapped-message, gift-wrap, bookmark-list, follow-set, calendar, poll, highlight, …), casts (Note,Article,Profile,Zap,Reaction,Comment,Mutes,BookmarksList, …), NIP-specific models (ThreadModel,CommentsModel,ReactionsModel,ZapsModel, …), NIP-specific helpers. Re-exports core models.references/packages/relay.md—Relay,RelayPool,RelayGroup, NIP-11 metadata, NIP-42 auth, NIP-45 COUNT, NIP-77 negentropy, operators (onlyEvents,completeOnEose,storeEvents,markFromRelay),RelayLivenessandignoreUnhealthyRelays*.references/packages/accounts.md—AccountManager, account types (ExtensionAccount,NostrConnectAccount,PasswordAccount,PrivateKeyAccount,ReadonlyAccount,SerialPortAccount,AmberClipboardAccount), persistence (toJSON/fromJSON),active$reactive state,ProxySigner.references/packages/signers.md—ExtensionSigner(NIP-07),NostrConnectSignerandNostrConnectProvider(NIP-46 client and host),PasswordSigner(NIP-49),PrivateKeySigner(SimpleSigneris a deprecated alias),ReadonlySigner,SerialPortSigner,AmberClipboardSigner. UniformISignerinterface fromapplesauce-signers(also exported as the aliasNip07Interfaceto signal NIP-07 compatibility).references/packages/loaders.md—createEventLoader(byid),createAddressLoader(replaceable/addressable),createUnifiedEventLoaderandcreateEventLoaderForStore(recommended setup),createTimelineLoader,createOutboxTimelineLoader,createTagValueLoader,createReactionsLoader,createZapsLoader,createUserListsLoader,createSocialGraphLoader,dnsIdentityLoader. Loaders accept apooland aneventStorefor dedup. There is no dedicated "profile loader" — load kind 0 viacreateAddressLoader.references/packages/actions.md—ActionRunner(events, signer, publishMethod);.run()(auto-publish, throws ifpublishMethodis missing) vs.exec()(returns iterable of events). Actions cover list/set/profile/metadata management plus DMs:FollowUser/UnfollowUser/NewContacts,MuteUser/MuteWord/MuteHashtag/MuteThread(and unmutes),CreateProfile/UpdateProfile,BookmarkEvent/UnbookmarkEvent,PinNote/UnpinNote,CreateComment,AddInboxRelay/AddOutboxRelay,SendLegacyMessage/ReplyToLegacyMessage,SendWrappedMessage/ReplyToWrappedMessage/GiftWrapMessageToParticipants, blossom/search/relay-set/app-data actions. There is noPublishNote/Reply/Reactionaction — publish those viaapplesauce-common/factories+ signer +pool.publish.references/packages/content.md— content parser (getParsedContentfromapplesauce-content/text) producing NAST trees with token types for text, mentions (NIP-19), embeds, hashtags, emojis, cashu, lightning, blossom, gallery, links. Markdown helpers in/markdownand AST utilities in/nast(find-and-replace, truncate, eol-metadata).references/packages/wallet.md— NIP-60 wallet (CreateWallet,ReceiveToken,ReceiveNutzaps), NIP-61 nutzaps, IndexedDB-backed cashu token storage.references/packages/wallet-connect.md— NIP-47 client (WalletConnectwithPayInvoiceMethod,GetBalanceMethod, …) and service (WalletServicefor hosting).references/packages/sqlite.md— persistent event database. Drivers:applesauce-sqlite/better-sqlite3,/native(node:sqlite, requires Node ≥22; also aliased/deno),/bun,/libsql,/turso,/turso-wasm(browser SQLite). Use withAsyncEventStore. Also ships a built-in relay (./relay).references/packages/react.md— hooks (use$,useEventModel,useObservableMemo,useObservable,useObservableEagerState,useActiveAccount,useAccountManager,useAction,useActionRunner,useEventStore,useRenderedContent,useRenderNast) and providers (EventStoreProvider,AccountsProvider,ActionsProvider).references/packages/extra.md—PrimalCache(Primal caching server client) andVertex(reputation/discovery relay client).
Examples
references/examples.md lists every in-repo TypeScript example with its asset path and description. Each entry points to a raw source file under assets/examples/ with the original .ts or .tsx extension.
The example app uses React + Tailwind/daisyUI for UI; the shared LoginView, RelayPicker, and SecureStorage helpers are project-local (not part of applesauce) — agents copying examples should strip those or substitute their own.
Hard rules
- One
EventStoreper app. Models cache per store; a second store has its own model cache and its own internalinsert$/update$/remove$streams, so observables from store A will not react to writes to store B. Separate stores are fine only for disjoint data (e.g. tests). - Every incoming event must reach
eventStore.add(...). Bypass it and models never update.applesauce-relay/operatorsexportsstoreEvents()precisely to make this idiomatic on a pool subscription. - Loader observables must be subscribed. Every loader returns a cold
Observable— no request is sent until you.subscribe()(or compose withfirstValueFrom/lastValueFrom). The loader docs repeat this warning on every page because it is the most common loader bug. - Subscriptions are RxJS Observables and must be torn down. Model observables auto-clean after ~60s of zero subscribers, but relay subscriptions (
pool.subscription(...),pool.req(...)) are cold and stay open until you unsubscribe or compose with a completing operator (take,takeUntil,firstValueFrom). - Import from the public package entry, not
dist/. Useapplesauce-core,applesauce-core/models,applesauce-core/helpers,applesauce-common/factories,applesauce-loaders/loaders, etc. — neverapplesauce-core/dist/.... Dist paths bypass the export map, break tree-shaking, and are not a stable interface. - Signer methods are async and may prompt the user.
signEvent/nip04.*/nip44.*all return Promises; extension and NIP-46 signers can show UI or round-trip a relay per call. Sign once and reuse the signed event rather than re-signing in loops. (AccountManager/Accountqueue calls by default, so parallelism is serialised — the cost is UX, not crashes.)
Where to point users for more
- Full docs: https://applesauce.build
- Typedoc reference: https://applesauce.build/typedoc/
- Live examples: https://applesauce.build/examples/
- Source: https://github.com/hzrd149/applesauce
Source: hzrd149/nostrudel — distributed by TomeVault.