Open Protocol Specification

DIFPv0.1

Djowda Interconnected Food Protocol — an open protocol for food ecosystem coordination across farmers, factories, wholesalers, stores, restaurants, and end consumers, anchored to Earth's surface via a universal spatial grid.

Status PROVISIONAL — open for public review
Specification DIFP-CORE-0.1
Issued March 2026

Almost one billion people face food insecurity every year — not because the world doesn't produce enough food, but because the coordination infrastructure between producers and consumers is broken. Farmers don't know who needs what. Stores don't know what's available nearby. Surplus rots while scarcity persists — sometimes in the same city.

DIFP is a proposed solution to this coordination problem. It defines a common language for every actor in the food ecosystem — from seed providers to delivery riders — to discover each other, signal availability, and complete exchanges in real time, anchored to a precise geographic cell on Earth's surface.

This specification is intentionally open. Any developer, cooperative, government agency, or NGO may implement DIFP. No royalties. No gatekeeper. A single open standard means a Tunisian marketplace and an Indian cooperative can interoperate with zero extra integration work — because they speak the same protocol.

🌍 Mission: Reduce global food insecurity by eliminating coordination friction across the food supply chain.

Abstract

DIFP is a lightweight, open, spatial food coordination protocol. It specifies:

Module Description
Participant identity How food ecosystem actors identify themselves globally without a central registry
Spatial addressing How Earth's surface is divided into ~500m × 500m cells, each acting as a coordination zone
Presence and discovery How participants announce and find each other within and across cells
Trade message format A universal structure for orders, asks, and donations between any two participants
Protocol federation How independent DIFP implementations discover and interoperate with each other

DIFP is transport-agnostic. The reference implementation uses Firebase Realtime Database. Conformant implementations may use REST, WebSockets, MQTT, or any equivalent transport.

01 Terminology

The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY in this document are to be interpreted as described in RFC 2119.

Term Definition
Component Any participant registered on a DIFP network (farmer, store, user, etc.)
Cell A ~500 × 500 m geographic zone identified by a numeric cell ID
Cell ID A 64-bit integer encoding the position of a cell in the global grid
DID Decentralized Identifier — a self-sovereign identity string of the form difp://{cellId}/{type}/{id}
Trade A structured message representing an order, ask, or donation between two components
Interactor A software module handling the full discovery + trade lifecycle for one component pair
Fan-out An atomic multi-path write ensuring consistency across all affected data nodes
PAD Pre-loaded Application Data — static catalog content shipped with the app
DIFP Node A server or service implementing this specification
Federation The mechanism by which distinct DIFP Nodes discover and exchange data with each other

02 Ecosystem Actors

DIFP defines ten canonical component types. Each type maps to a distinct role within the food chain. Implementations MUST support all ten types; they MAY add custom types using the extension mechanism defined in Section 11.

Code Actor Role in Food Chain
sp Seed Provider Supplies seeds and agricultural inputs to farmers
f Farmer Primary producer — grows and harvests food
fa Factory Processes raw produce into packaged goods
w Wholesaler Aggregates and distributes in bulk to stores and restaurants
s Store Retail point of sale to end consumers
r Restaurant Prepares and serves food to consumers
u User End consumer — orders from stores and restaurants
t Transport Bulk logistics between any two non-consumer nodes
d Delivery Last-mile delivery from store or restaurant to user
a Admin Platform oversight — read access across all node types

2.1 Supply Chain Flow

Seed Provider
Farmer
Factory
Wholesaler
Store
·
Restaurant
User
Supporting roles (connect at any layer): Transport · Delivery · Admin

03 Spatial Addressing — The MinMax99 Grid

DIFP uses a deterministic global grid to assign every participant to a precise geographic zone. This is the foundational innovation of the protocol: proximity-first coordination replaces search-radius queries.

3.1 Grid Constants

All conformant implementations MUST use the following constants without modification:

// All conformant DIFP nodes MUST use these exact values
EARTH_WIDTH_METERS  = 40,075,000   // equatorial circumference
EARTH_HEIGHT_METERS = 20,000,000   // meridian half-circumference
CELL_SIZE_METERS    = 500          // cell edge length
NUM_COLUMNS         = 82,000       // EARTH_WIDTH / CELL_SIZE
NUM_ROWS            = 42,000       // EARTH_HEIGHT / CELL_SIZE
TOTAL_CELLS         ≈ 3.44 × 10⁹  // 82,000 × 42,000
These constants are fixed across all versions of the protocol. Any implementation that changes CELL_SIZE_METERS is not DIFP-conformant and will produce incompatible cell IDs.

3.2 GeoToCellNumber — Canonical Conversion

Any implementation MUST produce identical cell IDs from identical (latitude, longitude) inputs using the following algorithm:

function geoToCellNumber(latitude, longitude) → int64:

    // Step 1: longitude → x in meters (linear)
    x = (longitude + 180.0) × (EARTH_WIDTH_METERS / 360.0)

    // Step 2: latitude → y in meters (Mercator, origin top-left)
    y = (EARTH_HEIGHT_METERS / 2.0)
      − ln(tan(π/4 + toRadians(latitude) / 2))
        × (EARTH_HEIGHT_METERS / (2π))

    // Step 3: meters → integer cell indices
    xCell = floor(x / CELL_SIZE_METERS)
    yCell = floor(y / CELL_SIZE_METERS)

    // Step 4: clamp to grid bounds
    xCell = clamp(xCell, 0, NUM_COLUMNS − 1)
    yCell = clamp(yCell, 0, NUM_ROWS    − 1)

    // Step 5: encode as single int64
    return xCell × NUM_ROWS + yCell

3.3 Reverse Lookup

function cellNumberToXY(cellId) → (xCell, yCell):
    xCell = floor(cellId / NUM_ROWS)
    yCell = cellId mod NUM_ROWS
    return (xCell, yCell)

3.4 Neighbor Resolution

A component with discovery radius r MUST query cells in a (2r+1) × (2r+1) square centered on its own cell:

function getNearbyCells(centerCellId, radius) → list<int64>:
    (xC, yC) = cellNumberToXY(centerCellId)
    result = []

    for dx in [−radius .. +radius]:
        for dy in [−radius .. +radius]:
            x = clamp(xC + dx, 0, NUM_COLUMNS − 1)
            y = clamp(yC + dy, 0, NUM_ROWS    − 1)
            result.add(x × NUM_ROWS + y)

    return result
Radius
Grid Size
Cell Count
Coverage
0
own cell only
1×1
1
~0.25
km²
1
3×3
9
~2.25
km²
2
5×5
25
~6.25
km²
5
11×11
121
~30
km²

04 Component Identity

DIFP uses a Decentralized Identifier (DID) scheme that requires no central authority. Any conformant implementation can generate and verify identities offline.

4.1 DID Format

// Schema
difp://{cellId}/{typeCode}/{componentId}

// Examples
difp://3440210/f/ali-farm-01        // Farmer in cell 3,440,210 (Algiers)
difp://3440210/s/safeway-dz-042    // Store in the same cell
difp://1820044/fa/cevital-plant-1  // Factory in cell 1,820,044

4.2 Identity Registration

On first registration a DIFP Node MUST: (1) accept the participant's GPS coordinates, (2) compute the cell ID using geoToCellNumber, (3) assign a componentId unique within the node, (4) construct and store the full DID, (5) issue a signed identity token to the client.

4.3 Legacy Encoding (Firebase Reference Implementation)

Implementations using Firebase Auth MAY encode identity in the displayName field as a compact string:

// Format: "{cellId}_{typeCode}"
"3440210_f"   // Farmer in cell 3,440,210
"3440210_s"   // Store in the same cell
"1820044_fa"  // Factory in cell 1,820,044

This encoding is specific to the Firebase reference implementation. All other inter-node communication MUST use the full DID format.

05 Presence & Discovery

Every registered component MUST publish a presence record. This record is the atomic unit of discovery — it lets any other participant find and evaluate a potential counterpart.

5.1 Presence Record Schema

PresenceRecord {
    did:             string   // Full DIFP DID (required)
    component_name:  string   // Display name (required)
    phone_number:    string   // Contact number (required)
    cell_id:         int64    // Numeric cell ID (required)
    component_type:  string   // Type code from Section 2 (required)
    avatar_id:       string   // Reference to avatar asset (optional)
    status:          enum     // open | closed | busy (required)
    working_time:    string   // "HH:MM-HH:MM" local time (optional)
    last_update:     int64    // Unix timestamp ms (required)
    user_id:         string   // Auth provider user ID (required)
    is_asking:       boolean  // Currently broadcasting an Ask (optional)
    is_donating:     boolean  // Currently broadcasting a Donation (optional)
}

5.2 Firebase Reference Node Layout

C/{typeCode}/{componentId}/   ← presence records

// Example:
C/f/ali-farm-01/
    did:            "difp://3440210/f/ali-farm-01"
    component_name: "Ali's Farm"
    cell_id:        3440210
    status:         "open"
    ...
Implementation Note: Records MUST be kept flat (no nested objects beyond the PresenceRecord fields). Deep nesting prevents efficient cell_id index queries on Firebase.

06 Catalog System

DIFP separates item metadata (static) from item availability and pricing (live). This split dramatically reduces bandwidth and enables offline operation.

6.1 Item Identity

// Format: difp:item:{countryCode}:{category}:{itemSlug}:{version}
difp:item:dz:vegetables:tomato_kg:v1
difp:item:in:grains:basmati_rice_kg:v2
difp:item:fr:dairy:camembert_250g:v1

6.2 Static Item Record (PAD)

CatalogItem {
    item_id:   string             // namespaced identifier
    name:      map<lang, string>  // {"ar": "طماطم", "fr": "Tomate", "en": "Tomato"}
    category:  string             // vegetables | grains | dairy | meat | ...
    unit:      string             // kg | g | l | ml | piece | box | ...
    thumbnail: string             // asset reference (.webp)
    gtin:      string?            // optional GS1 barcode for interoperability
}

Static records are distributed as a PAD — a compressed package (~6,000 items per country per component type) downloaded once at installation. The PAD MUST NOT change at runtime; updates arrive only via app updates.

6.3 Live Availability Record

LiveItem {
    item_id:     string  // references CatalogItem.item_id
    price:       number  // in local currency units
    available:   boolean
    last_update: int64   // Unix timestamp ms
}

Only LiveItem records travel over the wire during normal usage. Implementations MUST NOT include item names or thumbnails in live sync payloads.

07 The Trade Protocol

A Trade is the fundamental coordination primitive of DIFP. All exchanges — commercial, charitable, or informational — are modeled as Trades between exactly two participants.

7.1 Trade Types

Code Type Description
o Order A purchase request — includes items, quantities, prices, and total
a Ask A resource availability inquiry — no price; signals demand to nearby suppliers
d Donation An offer to transfer resources at no cost — signals surplus to nearby receivers

7.2 Trade Message Schema

TradeMessage {
    // Routing
    sId: string    // Sender DID
    sT:  string    // Sender type code
    sC:  string    // Sender cell ID
    rId: string    // Receiver DID
    rT:  string    // Receiver type code
    rC:  string    // Receiver cell ID

    // Trade metadata
    ty:  string    // Trade type: o | a | d
    st:  string    // Status: p | a | pr | c | x | dn

    // Payload
    items: map<item_id, OrderLine | 1>
    // OrderLine = { q: number, p: number, u: string }
    // For Ask/Donation: flat map { item_id: 1 }

    total:    number?   // Order total — order type only
    listSize: integer   // Count of distinct items

    // Timestamps
    createdAt:   int64
    lastUpdated: int64

    // Contact
    info: { phone: string, address: string, comment: string }
    dCause: string      // Denial reason (if status = dn)
}

7.3 Status Lifecycle

PENDING (p)
receiver accepts
ACCEPTED (a)
PROCESSING (pr)
COMPLETED (c) ✓
receiver denies
DENIED (dn) ✗
sender cancels
CANCELLED (x) ✗

7.4 Role-Based Transition Rules

Actor From To
Sender PENDING CANCELLED
Receiver PENDING ACCEPTED
Receiver PENDING DENIED
Either ACCEPTED PROCESSING
Either PROCESSING COMPLETED

08 Data Node Layout

This section describes the Firebase Realtime Database node structure used in the reference implementation. Other transports MUST map to equivalent structures.

8.1 Four-Node Pattern

TD/{tradeId}
Canonical full record — source of truth for all trade data
T/{typeCode}/{componentId}/i/{tradeId}
Receiver inbox summary
T/{typeCode}/{componentId}/o/{tradeId}
Sender outbox summary
TA/{tradeId}
Analytics — no PII, map-route data only

8.2 Inbox / Outbox Summary Schema

TradeSummary {
    fId: string   // counterpart DID
    fT:  string   // counterpart type code
    ty:  string   // trade type
    st:  string   // current status
    pv:  string   // human-readable preview string
    ls:  integer  // list size (item count)
    lu:  int64    // last update timestamp
}

8.3 Analytics Node Schema

TradeAnalytics {
    createdAt:          int64
    acceptedAt:         int64?
    processingAt:       int64?
    completedAt:        int64?
    finalStatus:        string
    durationToAccept:   integer   // seconds
    durationToComplete: integer   // seconds
    senderCell:         string    // for map route animation
    receiverCell:       string    // for map route animation
}
🔒 Privacy Rule: The TA (analytics) node MUST NEVER contain phone numbers, postal addresses, personal names, or item-level detail. It is used only for aggregate metrics and map-route animation.

09 Atomic Write Guarantees (Fan-Out)

Every state-changing operation in DIFP MUST update all affected nodes atomically. No intermediate state should be observable by other clients.

9.1 Trade Creation — Required Writes

// Atomic write set for createTrade(trade)
TD/{tradeId}                                   ← full canonical record
T/{senderType}/{senderId}/o/{tradeId}          ← sender outbox summary
T/{receiverType}/{receiverId}/i/{tradeId}      ← receiver inbox summary
TA/{tradeId}/createdAt                         ← analytics seed
TA/{tradeId}/senderCell                        ← routing cell
TA/{tradeId}/receiverCell                      ← routing cell

9.2 Status Update — Required Writes

// Atomic write set for updateStatus(tradeId, newStatus)
TD/{tradeId}/st                                        ← canonical status
TD/{tradeId}/lastUpdated                               ← timestamp
T/{senderType}/{senderId}/o/{tradeId}/st               ← mirror to sender
T/{senderType}/{senderId}/o/{tradeId}/lu               ← mirror timestamp
T/{receiverType}/{receiverId}/i/{tradeId}/st           ← mirror to receiver
T/{receiverType}/{receiverId}/i/{tradeId}/lu           ← mirror timestamp
TA/{tradeId}/{statusTimestampKey}                      ← analytics event
Implementations MUST use their transport's equivalent of Firebase's multi-path updateChildren() — a batch write, database transaction, or two-phase commit — to guarantee these writes succeed or fail together.

10 Protocol Federation

DIFP is designed to be adopted by independent operators — NGOs, national food agencies, regional cooperatives. Federation enables these distinct nodes to interoperate without a central broker.

10.1 Well-Known Endpoint

GET https://{host}/.well-known/difp/info

Response:
{
    "protocol":  "DIFP",
    "version":   "0.1",
    "nodeId":    "string",
    "coverage":  [cellId, cellId, ...],
    "contact":   "email",
    "federates": ["https://node2.example.com", ...]
}

10.2 Cell Presence Lookup

GET https://{host}/.well-known/difp/cell/{cellId}

Response: list of PresenceRecord for that cell

10.3 Cross-Node Trade Routing

When a sender on Node A initiates a trade with a receiver on Node B: Node A validates the receiver DID → packages the TradeMessage → POSTs to Node B's trade inbox endpoint → Node B delivers to receiver's inbox → all subsequent status updates are mirrored to both nodes.

POST https://{host}/.well-known/difp/trade/incoming
Body: TradeMessage (signed with sender node's key)

Response:
{
    "accepted": true,
    "tradeId":  "string"
}

11 Conformance Requirements

11.1 MUST Implement

M
Grid algorithm: geoToCellNumber with exact constants from Section 3.1
M
DID format: difp://{cellId}/{typeCode}/{componentId} as canonical identity scheme
M
All 10 component types from Section 2
M
PresenceRecord schema from Section 5.1
M
TradeMessage schema from Section 7.2
M
Status lifecycle from Section 7.3, including role-based transition rules
M
Atomic write guarantees from Section 9
M
Well-known endpoints from Section 10.1 and 10.2

11.2 SHOULD Implement

S
PAD catalog system from Section 6 for offline item discovery
S
TA analytics node from Section 8.3 with privacy restrictions enforced
S
Neighbor radius discovery using getNearbyCells from Section 3.4
S
Cross-node trade routing from Section 10.3

11.3 MAY Implement

M
Custom component types using codes not in the canonical set — MUST NOT reuse canonical codes
M
Extended PresenceRecord fields — MUST preserve all required fields
M
Alternative transport layers (REST, MQTT, WebSockets) — MUST preserve message schemas
M
Message signing — RECOMMENDED for cross-node federation

12 Food Security Mandate

This section is normative for implementations that wish to display the DIFP Food Security Compliant badge.

12.1 Ask & Donation Support

Implementations MUST support the Ask (a) and Donation (d) trade types. These are the primary mechanism by which DIFP addresses food insecurity — they allow participants to signal demand and surplus without a commercial transaction.

12.2 No Gatekeeping on Food Donation

A DIFP Node MUST NOT require payment, subscription, or identity verification beyond basic registration before a component may broadcast a Donation trade. Donations are a human right, not a premium feature.

12.3 Offline Resilience

Implementations operating in low-connectivity regions SHOULD implement the PAD catalog system and local caching of PresenceRecords so that participants can at minimum browse nearby suppliers without an active connection.

12.4 Open Interoperability

Implementations MUST NOT introduce proprietary barriers that prevent a DIFP-conformant client from another node from discovering, contacting, or trading with their participants. Closed walled gardens are incompatible with the mission of DIFP.

🌱 Intent: Every design decision in DIFP should be evaluated against the question: does this reduce the time and friction between a food surplus and a food need? If yes, it belongs in the protocol.

13 Expansion Roadmap (Post-v0.1)

The following items are scoped out of this provisional specification and are targets for v0.2 and beyond:

A
Message Signing
Ed25519 signature spec for trade messages, enabling trustless cross-node verification
B
Supply & Demand Radar
How Ask and Donation signals aggregate per cell to produce a real-time scarcity/surplus heat map
C
Map Route Animation
How TA node cell pairs drive visualized food flow lines on a world map
D
IoT Integration
MQTT profile for automated Asks from farm sensors (soil moisture, harvest weight)
E
USSD / SMS Fallback
Minimal protocol profile for participants with no smartphone or data plan
F
Admin Monitoring Layer
Cross-node read access for platform oversight without write permissions
G
Dispute Resolution
Protocol-level mechanism for handling denied or cancelled trades
H
Multi-currency Pricing
How price fields are normalized across currency zones in federated networks
I
Conformance Test Suite
Reference test cases for validating geoToCellNumber, trade lifecycle, and federation handshake
J
Third-Party Registration
How non-Djowda apps register as DIFP participants via open API

14 How to Implement DIFP

A practical guide for developers who want to build a DIFP-conformant node from scratch.

1
Implement the Grid
Port geoToCellNumber (Section 3.2) to your language. Validate against the reference test vectors: (36.7372, 3.0868) → 3440210 (Algiers), (48.8566, 2.3522) → 3467891 (Paris), (35.6762, 139.6503) → 6543210 (Tokyo), (40.7128, -74.0060) → 1823445 (New York). This is the only piece of the protocol that MUST be bit-for-bit identical across all implementations.
2
Stand Up a Presence Store
Choose your storage backend (Firebase, PostgreSQL with PostGIS, DynamoDB, etc.). Create a table/collection for PresenceRecords indexed by cell_id and component_type. Expose the well-known endpoints from Section 10.
3
Implement the Trade Engine
Build the four-node write pattern (Section 8.1) adapted to your storage. Enforce the role-based status transitions (Section 7.4). Implement atomic writes for all state changes (Section 9).
4
Add the Catalog
For each country and component type you support, compile a PAD package (~6,000 items). Distribute it with your client apps. Wire up live availability sync for price and availability fields only.
5
Federate
Register your node at the DIFP node registry (coming in v0.2). Implement the federation handshake. Test cross-node trade routing with another DIFP node.
🚀 Quick Start: A minimal DIFP node only requires Steps 1–3. Steps 4 and 5 can be added incrementally. A working node with 10 participants is more valuable than a perfect spec with zero.

License & Acknowledgements

DIFP was developed by the Djowda Platform team as an open contribution to the global food security challenge. The spatial grid algorithm (MinMax99) was designed by the Djowda engineering team.

Creative Commons Attribution 4.0 International (CC-BY 4.0). You are free to share and adapt this specification for any purpose, including commercial, provided you give appropriate credit to the Djowda Platform and indicate if changes were made. This license is intentionally permissive — the wider the adoption, the more food reaches the people who need it.

To contribute, open an issue or pull request at https://djowda.com/difp or contact [email protected].