DEV Community

Cover image for DIFP is Now a Library. Plug Any App Into the Open Food Network in 3 Lines of Code.
Djowda for Djowda

Posted on

DIFP is Now a Library. Plug Any App Into the Open Food Network in 3 Lines of Code.

The protocol works. The POC proved it. Now we're making it dead simple for any developer — in any language — to join the network.


Two weeks ago we published a proof-of-concept: two Android apps syncing a 6,000-product food catalog over Nostr in real time, geo-filtered to a 500m cell, zero backend, zero commission.

Today we're announcing the next step.

@djowda/difp is live on npm. Alpha. Open source. Ready to use.


What just shipped

The first DIFP SDK — a lightweight TypeScript/JavaScript library that wraps the entire protocol into a handful of intuitive calls.

npm install @djowda/difp
Enter fullscreen mode Exit fullscreen mode

Three lines to join the open food network:

import { createDifpClient } from '@djowda/difp';

const client = await createDifpClient();

await client.registerPresence({
  name: 'Marché El Baraka',
  lat:  36.737,
  lng:  3.086,
});
Enter fullscreen mode Exit fullscreen mode

That's it. Your component is now discoverable by anyone in your geo-cell, on the open Nostr network, with no central authority, no API key, no monthly bill.

📦 npm: npmjs.com/package/@djowda/difp

🔗 Source: github.com/Djowda/difp-npm

🌐 Reference web app: github.com/Djowda/difp-reference-webApp


What the SDK does

The library exposes 9 core operations — the same API that will ship across every language binding:

Presence

// Register on the network (or let the library generate random test data)
const result = await client.registerPresence({
  name: 'Green Valley Store',
  lat:  36.737,
  lng:  3.086,
  type: 's',         // store
});
console.log(result.cellId);   // 1711935606
console.log(result.lobbyId);  // 875346

// Update your presence
await client.updatePresence({ status: false }); // mark as closed

// Or just test with zero config
await client.registerPresence('test');
// → generates a realistic random component and publishes it
Enter fullscreen mode Exit fullscreen mode

Catalog management

// List a product (id=14, price=150.00 DA, available)
await client.updateItem('l', 14n, 15000n, true);

// Mark a product as something you need
await client.updateItem('a', 7n);

// Mark a product you're donating
await client.updateItem('d', 22n);
Enter fullscreen mode Exit fullscreen mode

Each call re-broadcasts the full catalog payload over Nostr as a NIP-33 addressable event — always latest state, no history needed, 6,000+ products per event.

Discovery

// Find components near a cell, 5 lobby levels out (default)
const nearby = await client.getNearby(1711935606n, 5);

console.log(nearby.components);
// [{ id: '79be667...', n: 'Ferme Bouzidi', cT: 'f', cI: 1711935580n, ... }]
Enter fullscreen mode Exit fullscreen mode

The library computes nearby lobby IDs from the center cell, queries Nostr with #t tag filters, and returns parsed Component objects. No backend involved.

Catalog browsing

// Browse another component's full catalog
const catalog  = await client.listCatalog('79be667ef9dc...');
const asks     = await client.listAsk('79be667ef9dc...');
const donations= await client.listDonation('79be667ef9dc...');

console.log(catalog.entries[0]);
// { productId: 14n, price: 15000n, available: true }
Enter fullscreen mode Exit fullscreen mode

Status

const s = client.status();
// {
//   pubkey:       '79be667...',
//   cellId:       1711935606n,
//   lobbyId:      875346n,
//   connected:    true,
//   catalogSize:  312,
//   askCount:     7,
//   donationCount: 3
// }
Enter fullscreen mode Exit fullscreen mode

Geo utilities (also exported standalone)

import { geoToCell, cellToLobby } from '@djowda/difp';

const cellId  = geoToCell(36.737, 3.086);   // 1711935606n
const lobbyId = cellToLobby(cellId);         // 875346n
Enter fullscreen mode Exit fullscreen mode

How it works under the hood

Everything in the library maps to two Nostr event kinds:

Kind 30420 — Component (NIP-33 replaceable)

Your presence on the network. Contains name, type, cell ID, lobby ID, working hours, and flags for ask/donation broadcasting. Replaced silently every time you update.

Kind 30421 — Catalog (NIP-33 replaceable)

Your product data. One event per catalog type (-l, -a, -d). Content is raw DIFP compact encoding:

14:15000;1:4000;12:120000;7:8250
Enter fullscreen mode Exit fullscreen mode

ID colon price-in-cents, semicolon separated. Presence means available. Absence means not. No field names. ~7,000 products per 64 KB Nostr event.

Discovery uses lobby-based #t tag filtering:

{
  "kinds": [30420],
  "#t":    ["lobby-875346", "lobby-875347", "lobby-875472"],
  "limit": 200
}
Enter fullscreen mode Exit fullscreen mode

One query. Up to 200 components across a ~20 km radius. Entirely decentralized.


The PAD system — and why the next milestone matters

The library ships with a built-in demo PAD (Preloaded Asset Distribution): a CSV of product metadata that travels with the app, not over the wire. Product names, images, units, categories — bundled at install. Only price and availability travel over Nostr.

The alpha PAD has 30 demo products. The full PAD spec reserves indexes 1–1000 for strictly necessary raw foods — universal staples that exist in every food system on earth, indexed consistently across all implementations.

Indexes 1001+ are open for claim.

If you are a food company, a grocery brand, or you have products in production that belong on the global food network — you can claim a permanent index in the DIFP PAD. Indexes are limited. The global network shares one namespace.

We'll publish the claim process shortly. If you're interested, reach out now.


Multi-language SDK roadmap

The npm library is the first. The same spec — same API names, same encoding, same Nostr wire protocol — is being implemented across:

Language Registry Status
JS/TS npm ✅ Live
Java/Android Maven Central 🔨 In progress
Python PyPI 🔨 In progress
Rust crates.io 📋 Planned
Go GitHub Modules 📋 Planned
C# NuGet 📋 Planned
C/C++ GitHub + Conan 📋 Planned

Every library will expose the same 9 methods with the same semantics. A component published from a Python script is discoverable by a Rust app, by the Android prototype, by the reference web app. One protocol, any language.


Try the test mode — zero config required

You don't need a real location or product data to experience the protocol. Every method accepts 'test' as an argument:

const client = await createDifpClient();

// Generate a random realistic component and publish it
const result = await client.registerPresence('test');
console.log(result.component.n);  // e.g. "Ferme Bouzidi"
console.log(result.cellId);       // e.g. 2034871922n

// Update with a new random component
await client.updatePresence('test');

// Check nearby (uses the auto-generated cell)
const s      = client.status();
const nearby = await client.getNearby(s.cellId, 3);
Enter fullscreen mode Exit fullscreen mode

The library handles keypair generation, storage, and relay connection automatically. First run: new identity. Subsequent runs: same identity loaded from ~/.difp/identity.json (Node.js) or localStorage (browser).


What we're building toward

DIFP is not trying to be a lower-fee food delivery platform. It's trying to make the fee structurally impossible.

The relay is open infrastructure. The events are signed by the publisher's key, not by us. There is no Djowda server sitting in the middle routing discovery. The protocol runs on Nostr's relay network — run by the community, for the community.

The next milestones are:

  • Full PAD v1 — 1,000 standardized raw food indexes, globally consistent
  • Open PAD claim — brands and producers can register their products permanently in the shared namespace
  • Android prototype public release — the reference implementation made available to test with real devices
  • Multi-relay support — publishing to multiple relays for resilience

We're building open food infrastructure, one protocol at a time.


Links


Questions? Open an issue on the repo, or find us on Nostr.

Top comments (0)