A real-time world map where anyone, anywhere on earth can list food, ask for it, or donate it — and be seen instantly, globally — with no account, no backend, and no central server.
This post covers:
- What we built and why
- The architecture behind it (Nostr + DIFP geo-grid)
- A Python script to query any cell on the network right now
- What's coming next (three-way trades + Trade on Map)
The problem we're trying to solve
The global food system is enormously centralized. Platforms extract rent from every transaction between producers and consumers. Farmers in a village can't be discovered by someone 10km away without paying a marketplace. During a disaster, there's no open channel to broadcast "I have food here" or "we need food here" without going through a gatekeeper.
We wanted to build the infrastructure layer that makes that possible. Open, permissionless, unstoppable.
The result is DIFP — the Djowda Interconnected Food Protocol. The Food Freedom Map is the first public UI on top of it.
What the map does right now
Open djowda.com/food-freedom-map, click anywhere on earth, and you can:
- 📦 List — add a food product with a price
- 🙋 Ask — broadcast that you need something
- 🤝 Donate — offer something for free
- 🗺️ Discover — browse nearby stores, farms, restaurants — see their catalog, ask list, and donations in real time
- 🔍 Search — jump to any location by coordinates or Cell ID
Everything happens without an account. Your identity is a secp256k1 keypair auto-generated in your browser and stored in localStorage. Your pin is a signed Nostr event broadcast to wss://relay.damus.io.
The architecture
Layer 1 — The geo-grid (MinMax99)
The earth is divided into a flat 500m × 500m grid: 82,000 columns × 42,000 rows = ~3.4 billion cells. Any lat/lng maps to exactly one cell ID:
import math
EARTH_W = 40_075_000 # metres
EARTH_H = 20_000_000
CELL_SIZE = 500
NUM_ROWS = 42_000
NUM_COLS = 82_000
def geo_to_cell(lat, lng):
x = (lng + 180) * (EARTH_W / 360)
y = EARTH_H / 2 - math.log(
math.tan(math.pi / 4 + (lat * math.pi) / 360)
) * (EARTH_H / (2 * math.pi))
x_cell = max(0, min(int(x / CELL_SIZE), NUM_COLS - 1))
y_cell = max(0, min(int(y / CELL_SIZE), NUM_ROWS - 1))
return x_cell * NUM_ROWS + y_cell
# Example: Algiers
cell = geo_to_cell(36.737, 3.086)
print(cell) # → 1711935606
Cells are grouped into lobbies (41×41 cells ≈ 20km² each). Subscribing to a lobby ID means you get all components within that area — no bounding-box query needed, no spatial index on a server.
LOBBY_SIZE = 41
NUM_LOBBY_ROWS = 1025 # ceil(42000 / 41)
def cell_to_lobby(cell_id):
x_cell = cell_id // NUM_ROWS
y_cell = cell_id % NUM_ROWS
lx = x_cell // LOBBY_SIZE
ly = y_cell // LOBBY_SIZE
return lx * NUM_LOBBY_ROWS + ly
Layer 2 — Nostr (NIP-01 + NIP-33)
Every component presence is a kind:30420 replaceable event (NIP-33). One pubkey = one component. Updating your presence replaces the old event on the relay — no accumulation of stale data.
The event tags that matter:
[
["d", "main"],
["t", "difp"],
["t", "s"],
["cell", "1711935606"],
["lobby", "40780"],
["t", "lobby-40780"],
["status","1"]
]
The #t: ["lobby-40780"] tag is what makes geo-discovery possible. Any client can subscribe to a lobby ID with a standard Nostr REQ — no custom relay extensions, no spatial query language. It's just tag filtering.
Layer 3 — Catalog encoding
Product listings, ask lists, and donations are separate kind:30421 events with a compact payload:
Listing: 1:29900;6:14900;21:9900 (productId:priceCents pairs)
Ask: 3;7;44 (productIds)
Donation: 2;15;88 (productIds)
This keeps event sizes tiny and fits thousands of products in a single Nostr event.
Query the network right now — Python script
You don't need the map UI at all. Here's a minimal Python script to query any cell and print what's there:
import json
import websocket
# ── DIFP grid constants ─────────────────────────────────────
NUM_ROWS = 42_000
LOBBY_SIZE = 41
NUM_LOBBY_ROWS = 1_025
def cell_to_lobby(cell_id):
x_cell = cell_id // NUM_ROWS
y_cell = cell_id % NUM_ROWS
lx = x_cell // LOBBY_SIZE
ly = y_cell // LOBBY_SIZE
return lx * NUM_LOBBY_ROWS + ly
# ── Input ───────────────────────────────────────────────────
cell_id = int(input("Enter DIFP Cell ID: ").strip())
lobby_id = cell_to_lobby(cell_id)
print(f"\nCell ID : {cell_id}")
print(f"Lobby ID : {lobby_id}")
print("Connecting to relay...\n")
# ── Connect & subscribe ─────────────────────────────────────
ws = websocket.create_connection("wss://relay.damus.io")
req = ["REQ", "difp-cell-query", {
"kinds": [30420],
"#t": [f"lobby-{lobby_id}"],
"limit": 500
}]
ws.send(json.dumps(req))
# ── Read results ────────────────────────────────────────────
found = 0
while True:
msg = json.loads(ws.recv())
if msg[0] == "EVENT":
event = msg[2]
event_cell = next(
(tag[1] for tag in event.get("tags", []) if tag[0] == "cell"),
None
)
# Filter to exact cell match
if event_cell != str(cell_id):
continue
found += 1
content = {}
try:
content = json.loads(event["content"])
except Exception:
pass
print("=" * 60)
print(f"COMPONENT #{found}")
print("=" * 60)
print(f"PubKey : {event['pubkey']}")
print(f"Name : {content.get('n', 'Unknown')}")
print(f"Type : {content.get('cT', '?')}")
print(f"Avatar : {content.get('aI', '?')}")
print(f"Cell : {event_cell}")
listings = [t[1] for t in event.get("tags", []) if t[0] == "listing"]
catalogs = [t[1] for t in event.get("tags", []) if t[0] == "catalog"]
print(f"Listings : {listings}")
print(f"Catalogs : {catalogs}")
print()
elif msg[0] == "EOSE":
break
ws.close()
print(f"Done. Components found in cell {cell_id}: {found}")
Install the dependency:
pip install websocket-client
Run it:
python query_cell.py
# Enter DIFP Cell ID: 1711935606
You'll see every registered component at that 500m cell — their name, type, avatar, and any catalog/listing data — pulled directly from the Nostr relay, no API key, no rate limit, no account.
Try it with Cell ID 1711935606 (Algiers) or drop your own pin on the map first, grab your Cell ID from the confirmation toast, then query it here.
Why this is interesting for AI
One of the things we're actively experimenting with: querying the DIFP network from an AI agent.
The geo-cell model maps cleanly to natural language queries:
-
"Find me a farm within 5km of these coordinates" → convert to cell → find nearby lobbies → subscribe → filter by type
f - "What food donations are available near me right now?" → same pipeline, filter by donation events
The protocol is just Nostr tag filtering. Any model that can make a WebSocket connection and parse JSON can become a food discovery agent on the open network. No API contract. No terms of service. No SDK required.
We're planning to publish a reference agent implementation in the next sprint.
What's coming next
Three-way trades
The current version supports three independent broadcast types per component: listing (sell), ask (need), and donation (give). The next update will connect these across components — enabling matched trades:
- A farmer broadcasts surplus tomatoes as a listing
- A restaurant broadcasts tomatoes as an ask
- The protocol surfaces the match
- Both parties transact directly, peer-to-peer
No escrow platform. No commission. Just two signed events and a handshake.
Trade on Map
We're also prototyping a new concept: Trade on Map. Instead of navigating to a store profile and browsing a catalog, you see the trades themselves as map objects — color-coded by type (listing / ask / donation), filterable by product category, visible in real time as they're published to the relay.
Think of it as a live order book, but geographic. You can see at a glance where surpluses and shortfalls are clustering, and what's moving.
This is still early-stage. We're designing the UX now. If you have thoughts, we'd love to hear them.
Try it / get involved
The map is live: djowda.com/food-freedom-map
Drop your pin. Try the Python script. Share your Cell ID in the comments — we'll tell you what's in your neighborhood on the network.
We're aiming for 1,000,000 pins as a signal of demand for open food infrastructure. Not a vanity metric — a message to the food system that people are ready for something better.
The npm library if you want to build on the protocol:
npm install @djowda/difp
→ npmjs.com/package/@djowda/difp
The protocol is open. The relay is public. Build something. 🌾
Part of the Djowda open food infrastructure project.
Protocol spec: DIFP v0.4-alpha | Built on Nostr NIP-01 + NIP-33
Top comments (0)