Local Optimization with Overflow Protocol
Draft Specification - Request for Comments
Version: 0.2.0 Status: Draft (DPP-Compatible Lab Baseline) Updated: March 2026 License: CC BY-SA 4.0
- Introduction
- Terminology
- Protocol Overview
- MaterialDNA Specification
- LoopCoin Specification
- LoopSignal Specification
- LoopCost Calculation
- API Endpoints
- Federation Protocol
- Security Considerations
- Implementation Guidance
- Examples
- Future Considerations
- References
The Local Optimization with Overflow Protocol (LOOP) enables federated material exchange between autonomous municipal nodes while preserving local economic sovereignty. This specification defines the core protocol for inter-city communication, material identification, economic settlement, and democratic governance mechanisms that together create a planetary-scale circular economy infrastructure.
LOOP allows cities to:
- Track materials using universal identifiers (MaterialDNA)
- Track products using DPP-compatible identifiers (ProductDNA)
- Issue local currencies with defined properties (LoopCoin)
- Express community trade preferences (LoopSignal)
- Calculate optimal material routing (LoopCost)
- Settle transactions across municipal boundaries
This document specifies LOOP version 0.2.0, focusing on core functionality required for basic interoperability, DPP-compatible extensions, and lab-only demos.
Current material recovery systems operate in isolation, leading to suboptimal resource allocation and missed opportunities for circular economy implementation. Cities lack infrastructure to coordinate material flows while maintaining local control over their economies.
LOOP addresses these challenges by providing:
- Federated architecture that preserves local autonomy
- Economic incentives for circular behaviors
- Democratic mechanisms for community preferences
- Transparent routing algorithms
- Open standards for interoperability
LOOP aims to:
- Enable any city to participate in global material exchange
- Preserve local economic sovereignty
- Optimize material flows through market mechanisms
- Incorporate democratic community preferences
- Create transparent, auditable transactions
- Support multiple implementation approaches
This specification covers:
- Protocol message formats
- Required API endpoints
- Calculation methodologies
- Settlement procedures
- Security requirements
This specification does NOT cover:
- Local implementation details
- User interface requirements
- Specific database schemas
- Local governance procedures
- Business logic beyond protocol requirements
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
Node: An autonomous LOOP implementation, typically operated by a municipality
MaterialDNA: Globally unique identifier for any material item or batch
ProductDNA: Globally unique identifier for a product or product batch, referencing constituent MaterialDNA entries
LoopCoin (LC): Local digital currency with expiry properties
LoopSignal: Community-expressed preference percentage for material categories
LoopCost: Total cost including base price, export/import penalties, and distance
Federation: Network of interconnected LOOP nodes
Settlement: Process of confirming material transfer and payment
Penalty: Additional cost applied when materials cross city boundaries
Overflow: Routing mechanism when local demand is insufficient
The following expanded definitions constitute the canonical reference for LOOP's six core concepts. All other documents (glossary, website, guides) derive their definitions from this subsection.
LOOP (Local Optimization with Overflow Protocol). LOOP is an open, federated protocol standard for tracking material and product flows between autonomous municipal nodes. It enables cities to register materials and products, publish surplus and demand signals, compute transfer costs, and settle cross-boundary transactions — all while preserving local data sovereignty and economic autonomy. The protocol is built on four foundational primitives (MaterialDNA, ProductDNA, LoopCoin, LoopSignal) and one derived metric (LoopCost), each defined below.
MaterialDNA. MaterialDNA is a globally unique digital identity assigned to a physical material or material batch within the LOOP protocol. Each MaterialDNA record captures the material's composition, category, origin, quantity, quality metrics, and chain of custody, providing a complete provenance trail from source through every subsequent transfer. MaterialDNA serves as the primary identifier in the protocol's material lifecycle: registration, offer, match, and transfer. It is distinct from ProductDNA, which operates at the product layer; a single product may reference multiple MaterialDNA entries as its constituent materials.
ProductDNA.
ProductDNA is a globally unique digital identity for a finished or semi-finished product (or product batch) within the LOOP protocol. It is aligned with the EU Digital Product Passport framework (ESPR Art. 9–10) and captures product category, name, condition, manufacturer, model, manufacture year, functional status, and lifecycle stage. A ProductDNA entry MAY reference zero or more MaterialDNA identifiers via a composition link (material_ids), enabling full traceability from product to constituent materials. ProductDNA is the product layer in LOOP's two-tier identity hierarchy; MaterialDNA is the composition layer. The same lifecycle flow (Offer, Match, Transfer) applies to both MaterialDNA and ProductDNA entities.
LoopCoin (LC). LoopCoin is a node-issued local digital currency used to settle material and product transfers between nodes in the federation. Each node defines its own LoopCoin configuration, including a currency code, backing currency, exchange rate, local-use bonus, expiry period, decay rate, maximum issuance, and reserve ratio. LoopCoin's built-in expiry and decay rules incentivise local circulation and prevent indefinite hoarding. When transfers cross node boundaries, LoopCoin settlement occurs through inter-node clearing, converting between each node's local currency at the declared exchange rate. LoopCoin is the unit of account for all LoopCost calculations.
LoopSignal. A LoopSignal is a community-expressed preference signal for material categories, published by a node. Each signal is a percentage (between 0.0 and 1.0) that represents the community's desire to retain or attract materials in a given category — higher values indicate stronger preference. LoopSignals are set through democratic voting (LoopVote) and are subject to constraints: changes MUST NOT exceed 0.10 per voting period, and signals MUST apply equally to imports and exports. LoopSignals serve as inputs to the LoopCost calculation (§7), where they determine the export and import penalty components. A LoopSignal is the preference input; LoopCost is the computed outcome.
LoopCost.
LoopCost is the total routing cost for any material or product transfer between nodes, computed according to the formula: LoopCost = BasePrice + ExportPenalty + ImportPenalty + DistanceCost. The BasePrice is the offered price in LoopCoins. The ExportPenalty equals BasePrice × OriginLoopSignal (applied when a material or product leaves its origin node). The ImportPenalty equals BasePrice × DestinationLoopSignal (applied when a material or product enters the destination node). The DistanceCost equals Distance_km × 0.02 LC/km. For local transactions (same node), only the BasePrice applies, making intra-node exchanges structurally cheaper than cross-boundary ones. This cost asymmetry keeps circular value circulating close to its source while still permitting inter-node trade when economically justified. The full calculation methodology and settlement distribution are specified in §7.
LOOP follows a federated architecture where each city operates an autonomous node. Nodes communicate using HTTPS and JSON-LD messages.
flowchart LR
Munich[Munich Node]
Berlin[Berlin Node]
Vienna[Vienna Node]
Munich <-->|HTTPS| Berlin
Munich --- Vienna
Berlin --- Vienna
Each LOOP node MUST implement:
- Material Registry: Tracks MaterialDNA within node jurisdiction
- Product Registry: Tracks ProductDNA (DPP-compatible product identifiers) within node jurisdiction
- Currency Engine: Manages LoopCoin issuance and transfers
- Signal Governor: Handles democratic voting on preferences
- Routing Calculator: Computes optimal destinations using LoopCost
- Settlement Handler: Confirms and records transactions
Standard material exchange flow:
- Registration: Material registered with unique MaterialDNA
- Announcement: Availability broadcast to federated nodes
- Discovery: Interested nodes discover available materials
- Calculation: Each node calculates their LoopCost
- Offer: Nodes submit offers to material owner
- Selection: Optimal match determined by lowest/highest LoopCost
- Settlement: Transaction executed and confirmed
- Confirmation: Final state broadcast to network
All protocol messages MUST use:
- Transport: HTTPS (TLS 1.3 or higher)
- Content-Type:
application/ld+json - Encoding: UTF-8
- Timestamps: ISO 8601 format in UTC
This subsection defines the smallest interoperable flow required for lab-only demonstrations. It is intentionally minimal, scoped to controlled environments, and does not represent a public pilot.
- MaterialDNA is registered by a node.
- Offer is published for that material.
- Match is accepted between two nodes.
- Transfer is completed and recorded.
All payloads MUST include @context, @type, and schema_version.
Emitters targeting the v0.2.0 baseline SHOULD set schema_version to 0.2.0.
Receivers SHOULD accept additive minor/patch versions when unknown fields can be preserved or ignored without data loss.
Payloads using schema_version: "0.1.1" remain valid against v0.2.0 schemas (backward compatible).
- MaterialDNA
Schema:
https://local-loop-io.github.io/projects/loop-protocol/schemas/v0.2.0/material-dna.schema.json - ProductDNA
Schema:
https://local-loop-io.github.io/projects/loop-protocol/schemas/v0.2.0/product-dna.schema.json - Offer
Schema:
https://local-loop-io.github.io/projects/loop-protocol/schemas/v0.2.0/offer.schema.json - Match
Schema:
https://local-loop-io.github.io/projects/loop-protocol/schemas/v0.2.0/match.schema.json - Transfer
Schema:
https://local-loop-io.github.io/projects/loop-protocol/schemas/v0.2.0/transfer.schema.json
Note: v0.1.1 schema URLs remain available for backward compatibility. See RFC-0003 for the versioning policy.
To remain GDPR-aligned for lab demos, payloads MUST NOT include personal data. Node identifiers, city names, and organization identifiers are permitted; emails, phone numbers, and names are not permitted in these entities.
To improve forward compatibility with emerging regulatory data requirements, implementations MAY attach additive extension blocks such as passport, classification, and traceability.
These extensions:
- MUST remain optional in the v0.1.1 baseline
- MUST NOT weaken the data-minimization rule above
- SHOULD use the canonical JSON-LD context or explicit namespaced terms
- SHOULD preserve unknown fields when relaying payloads between nodes
MaterialDNA identifiers MUST follow this pattern:
MAT-{COUNTRY}-{CITY}-{YEAR}-{CATEGORY}-{UNIQUE}Where:
MAT: Fixed prefix identifying a materialCOUNTRY: ISO 3166-1 alpha-2 codeCITY: Three-letter city code (locally defined)YEAR: Four-digit year of registrationCATEGORY: Material category codeUNIQUE: Alphanumeric unique identifier (min 6 characters)
Example:
MAT-DE-MUC-2025-PLASTIC-B847F3{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialDNA",
"schema_version": "0.1.1",
"id": "MAT-DE-MUC-2025-PLASTIC-B847F3",
"category": "plastic-pet",
"quantity": {
"value": 1000,
"unit": "kg"
},
"quality": 0.95,
"origin_city": "Munich",
"current_city": "Munich",
"location": {
"lat": 48.1351,
"lon": 11.5820,
"address": "Recycling Center Munich"
},
"available_from": "2025-05-27T10:00:00Z",
"expires": "2025-06-03T10:00:00Z",
"certifications": ["food-grade", "iso-14001"],
"images": ["https://example.com/material-photo.jpg"],
"metadata": {
"source": "consumer-collection",
"batch_number": "2025-W22-01",
"notes": "Clean, sorted PET bottles"
}
}schema_version: Schema version (v0.1.1)id: Unique MaterialDNA identifiercategory: Standardized category codequantity: Amount and unitorigin_city: Registering citycurrent_city: Current custodian cityavailable_from: When material becomes available
Base categories (extensible by communities):
plastics/
plastic-pet # Polyethylene terephthalate
plastic-hdpe # High-density polyethylene
plastic-pvc # Polyvinyl chloride
plastic-ldpe # Low-density polyethylene
plastic-pp # Polypropylene
plastic-ps # Polystyrene
plastic-mixed # Mixed/unsorted plastics
metals/
metal-steel # Steel and iron
metal-aluminum # Aluminum
metal-copper # Copper
metal-mixed # Mixed metals
organics/
organic-food # Food waste
organic-garden # Garden waste
organic-wood # Wood waste
glass/
glass-clear # Clear glass
glass-brown # Brown glass
glass-green # Green glass
glass-mixed # Mixed glass
paper/
paper-clean # Clean paper
paper-newsprint # Newspapers
cardboard # Cardboard
paper-mixed # Mixed paper
textiles/
textile-cotton # Cotton
textile-wool # Wool
textile-synthetic # Synthetic fabrics
textile-mixed # Mixed textiles
electronics/
ewaste-computers # Computers and peripherals
ewaste-phones # Mobile phones
ewaste-batteries # Batteries
ewaste-mixed # Mixed electronics
ProductDNA represents the product layer in LOOP's two-tier hierarchy: materials compose products. While MaterialDNA tracks raw or processed materials (e.g., plastic-hdpe, metal-copper), ProductDNA tracks finished or semi-finished products (e.g., office desks, laptops) that contain those materials.
This separation aligns with:
- EU ESPR (Art. 9-10): Digital Product Passports are defined at the product level
- UNTP ProductPassport: Materials are nested inside product passports
- GS1/EPCIS: Trade items (products) are the primary tracking unit
MaterialDNA (composition layer) ProductDNA (product layer, DPP-facing)
├── category: plastic-hdpe ├── product_category: electronics-laptop
├── quantity: 2kg ├── material_ids: [MAT-DE-MUC-2025-PLASTIC-..., ...]
├── origin_city: Munich ├── condition: good
└── passport (DPP fields) ├── passport (DPP fields, ESPR-aligned)
└── lifecycle_stage: in-use
A ProductDNA entry MAY reference zero or more MaterialDNA entries via material_ids. This composition link enables traceability from product to constituent materials.
ProductDNA identifiers MUST follow this pattern:
PRD-{COUNTRY}-{CITY}-{YEAR}-{CATEGORY}-{UNIQUE}Where:
PRD: Fixed prefix identifying a productCOUNTRY: ISO 3166-1 alpha-2 codeCITY: Three-letter city codeYEAR: Four-digit year of registrationCATEGORY: Product category keywordUNIQUE: Alphanumeric unique identifier (min 6 characters)
Example:
PRD-DE-MUC-2025-DESK-F4A7B2{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.2.0.jsonld",
"@type": "ProductDNA",
"schema_version": "0.2.0",
"id": "PRD-DE-MUC-2025-DESK-F4A7B2",
"product_category": "furniture-office",
"name": "Standing Desk — Ergotron WorkFit",
"condition": "good",
"quantity": { "value": 12, "unit": "piece" },
"origin_city": "Munich",
"current_city": "Munich",
"available_from": "2026-03-15T08:00:00Z",
"manufacturer": "Ergotron",
"model": "WorkFit-S",
"manufacture_year": 2021,
"functional_status": "fully-functional",
"lifecycle_stage": "end-of-first-use",
"material_ids": [
"MAT-DE-MUC-2025-METAL-4EB84C",
"MAT-DE-MUC-2025-PLASTIC-96FE78"
]
}schema_version: Schema version (0.2.0)id: Unique ProductDNA identifier (PRD- prefix)product_category: Standardized product categoryname: Product name (2-200 characters)condition: Physical condition (new, like-new, good, fair, poor, for-parts)quantity: Amount and unitorigin_city: Registering citycurrent_city: Current custodian cityavailable_from: When product becomes available
furniture/
furniture-office # Office furniture
furniture-residential # Residential furniture
furniture-industrial # Industrial furniture
building/
building-structural # Structural building components
building-fixture # Fixtures and fittings
building-hvac # HVAC equipment
building-electrical # Electrical installations
electronics/
electronics-computing # Computers and peripherals
electronics-mobile # Mobile devices
electronics-appliance # Household appliances
electronics-components # Electronic components
textiles/
textile-garment # Garments and clothing
textile-industrial # Industrial textiles
packaging/
packaging-reusable # Reusable packaging
vehicles/
vehicle-parts # Vehicle parts and components
equipment/
equipment-industrial # Industrial equipment
equipment-medical # Medical equipment
The Offer, Match, and Transfer schemas accept either material_id or product_id. At least one MUST be present. This allows the same lifecycle flow (Offer → Match → Transfer) to work for both materials and products.
{
"@type": "Offer",
"product_id": "PRD-DE-MUC-2025-DESK-F4A7B2",
"from_city": "Munich",
"to_city": "Berlin",
...
}Each node's LoopCoin configuration:
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "LoopCoinConfig",
"issuer": "munich.loop",
"currency_code": "LC-MUC",
"backed_by": "EUR",
"exchange_rate": 1.0,
"local_bonus": 0.05,
"expiry_months": 6,
"decay_rate": 0.05,
"max_issuance": 1000000,
"current_supply": 450000,
"reserve_ratio": 1.0
}{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "LoopCoinTransfer",
"id": "550e8400-e29b-41d4-a716-446655440000",
"from": "user:maria@munich.loop",
"to": "business:brewery@munich.loop",
"amount": 50,
"currency": "LC-MUC",
"material_ref": "MAT-DE-MUC-2025-FOOD-B847F3",
"timestamp": "2025-05-27T14:30:00Z",
"memo": "Payment for spent grain",
"signature": "..."
}When LoopCoins cross node boundaries:
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "InterNodeSettlement",
"from_node": "munich.loop",
"to_node": "berlin.loop",
"transfers": [
{
"from": "business:brewery@munich.loop",
"to": "business:biogas@berlin.loop",
"amount": 65,
"from_currency": "LC-MUC",
"to_currency": "LC-BER",
"exchange_rate": 1.0,
"fees": {
"export_penalty": 26,
"import_penalty": 0,
"distance_cost": 12
}
}
],
"settlement_method": "clearing",
"settlement_period": "daily"
}Community preferences for material categories:
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "LoopSignalConfig",
"node": "munich.loop",
"signals": {
"plastic-pet": 0.30,
"plastic-hdpe": 0.25,
"metal-aluminum": 0.15,
"organic-food": 0.40,
"glass-clear": 0.10,
"paper-clean": 0.05,
"ewaste-phones": 0.35,
"default": 0.05
},
"valid_from": "2025-06-01T00:00:00Z",
"valid_until": "2025-06-30T23:59:59Z",
"approved_by": {
"vote_id": "2025-05-vote",
"turnout": 0.35,
"approval": 0.68
}
}- Values MUST be between 0.0 and 1.0 (0% to 100%)
- Changes MUST NOT exceed 0.10 (10%) per voting period
- Nodes MUST publish signals publicly
- Signals MUST apply equally to imports and exports
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "LoopVote",
"node": "munich.loop",
"vote_id": "2025-05-plastic-increase",
"proposals": [
{
"category": "plastic-pet",
"current_value": 0.20,
"proposed_value": 0.30,
"rationale": "Increase local plastic recycling"
}
],
"voting_period": {
"start": "2025-05-01T00:00:00Z",
"end": "2025-05-07T23:59:59Z"
},
"results": {
"total_eligible": 150000,
"total_voted": 52500,
"votes_for": 35700,
"votes_against": 16800,
"status": "passed"
}
}The LoopCost for any material transaction:
LoopCost = BasePrice + ExportPenalty + ImportPenalty + DistanceCost
Where:
BasePrice: Offered price in LoopCoinsExportPenalty: BasePrice × OriginSignal (if leaving origin)ImportPenalty: BasePrice × DestinationSignal (if entering destination)DistanceCost: Distance_km × 0.02 LC/km
- Local transactions (same node): Only BasePrice applies
- Export penalty: Applied to seller's community fund
- Import penalty: Applied to buyer's community fund
- Distance cost: Split between nodes or transport provider
function calculateLoopCost(offer) {
const { basePrice, origin, destination, materialType, distance } = offer;
let loopCost = basePrice;
let exportPenalty = 0;
let importPenalty = 0;
let distanceCost = 0;
// Export penalty if material leaves origin
if (origin.node !== destination.node) {
const exportSignal = origin.getSignal(materialType);
exportPenalty = basePrice * exportSignal;
loopCost += exportPenalty;
}
// Import penalty if material enters destination
if (destination.node !== origin.node) {
const importSignal = destination.getSignal(materialType);
importPenalty = basePrice * importSignal;
loopCost += importPenalty;
}
// Distance cost (simplified)
if (distance > 0) {
distanceCost = distance * 0.02;
loopCost += distanceCost;
}
return {
total: loopCost,
breakdown: {
base: basePrice,
export: exportPenalty,
import: importPenalty,
distance: distanceCost
}
};
}For cross-node transactions:
Total Payment = LoopCost
├── Seller receives: BasePrice - ExportPenalty
├── Origin community fund: ExportPenalty
├── Destination community fund: ImportPenalty
└── Transport/Network: DistanceCost
All LOOP nodes MUST implement these RESTful endpoints.
The repository openapi.json is the reference contract for the protocol endpoints in this section.
Lab-only extensions documented below are intentionally excluded from that artifact.
POST /api/v1/material
POST /api/v1/material
Content-Type: application/ld+json
Authorization: Bearer {token}
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialDNA",
"id": "MAT-DE-MUC-2025-PLASTIC-B847F3",
"category": "plastic-pet",
"quantity": {"value": 1000, "unit": "kg"},
...
}
Response: 201 Created
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialDNA",
"id": "MAT-DE-MUC-2025-PLASTIC-B847F3",
"status": "registered",
...
}GET /api/v1/material/{id}
GET /api/v1/material/MAT-DE-MUC-2025-PLASTIC-B847F3
Response: 200 OK
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialDNA",
"id": "MAT-DE-MUC-2025-PLASTIC-B847F3",
...
}POST /api/v1/material/search
POST /api/v1/material/search
Content-Type: application/ld+json
{
"category": "plastic-*",
"radius_km": 100,
"min_quantity": 500,
"max_loop_cost": 150
}
Response: 200 OK
{
"results": [...],
"total": 15,
"next": "/api/v1/material/search?page=2"
}ProductDNA is part of the v0.2.0 baseline. Nodes implementing the baseline MUST expose ProductDNA using the same JSON-LD and versioning rules as MaterialDNA.
POST /api/v1/product
POST /api/v1/product
Content-Type: application/ld+json
Authorization: Bearer {token}
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.2.0.jsonld",
"@type": "ProductDNA",
"schema_version": "0.2.0",
"id": "DE-MUC-2026-FURNITURE-CHAIR-001",
"product_category": "office-furniture",
"condition": "used-good",
...
}
Response: 201 Created
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.2.0.jsonld",
"@type": "ProductDNA",
"schema_version": "0.2.0",
"id": "DE-MUC-2026-FURNITURE-CHAIR-001",
"status": "registered",
...
}GET /api/v1/product/{id}
GET /api/v1/product/DE-MUC-2026-FURNITURE-CHAIR-001
Response: 200 OK
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.2.0.jsonld",
"@type": "ProductDNA",
"schema_version": "0.2.0",
"id": "DE-MUC-2026-FURNITURE-CHAIR-001",
...
}The following endpoint is optional and used by the localLOOP lab demo only. It is not required for protocol compliance.
POST /api/v1/material-status
POST /api/v1/material-status
Content-Type: application/ld+json
X-API-Key: {api-key}
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialStatusUpdate",
"schema_version": "0.1.1",
"id": "3c9a6a0b-8c1a-4d3f-9c2c-3c1c2f9d5c2a",
"material_id": "MAT-DE-MUC-2025-PLASTIC-B847F3",
"status": "reserved",
"updated_at": "2025-06-03T09:15:00Z",
"reason": "Reserved by city exchange",
"notes": "Holding until pickup is confirmed",
"source_node": "lab-hub.loop",
"metadata": { "ticket": "LAB-42" }
}
Response: 201 Created
{
"id": "3c9a6a0b-8c1a-4d3f-9c2c-3c1c2f9d5c2a",
"created_at": "2025-06-03T09:15:05Z"
}GET /api/v1/node/info
GET /api/v1/node/info
Response: 200 OK
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "NodeInfo",
"id": "munich.loop",
"name": "Munich LOOP Node",
"version": "0.1.0",
"location": {"lat": 48.1351, "lon": 11.5820},
"capabilities": ["material-registry", "loopcoin", "loopsignal"],
"endpoint": "https://munich.loop/api/v1",
"statistics": {
"materials_active": 1247,
"transactions_daily": 89,
"loopcoin_supply": 450000
}
}GET /api/v1/signals
GET /api/v1/signals
Response: 200 OK
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "LoopSignalConfig",
"node": "munich.loop",
"signals": {
"plastic-pet": 0.30,
...
},
"valid_from": "2025-06-01T00:00:00Z",
"valid_until": "2025-06-30T23:59:59Z"
}POST /api/v1/transaction
POST /api/v1/transaction
Content-Type: application/ld+json
Authorization: Bearer {token}
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialTransaction",
"id": "TXN-2025-05-27-001",
"material": "MAT-DE-MUC-2025-PLASTIC-B847F3",
"seller": "munich.loop",
"buyer": "berlin.loop",
"offer": {
"base_price": 120,
"loop_cost": 156
},
"timestamp": "2025-05-27T16:00:00Z"
}
Response: 201 Created
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "TransactionStatus",
"transaction_id": "TXN-2025-05-27-001",
"status": "pending",
"updated_at": "2025-05-27T16:00:00Z",
"settlement_url": "/api/v1/transaction/TXN-2025-05-27-001"
}For node-to-node communication:
POST /api/v1/federate/announce
POST /api/v1/federate/announce
X-Node-Signature: {signature}
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialAnnouncement",
"material": "MAT-DE-MUC-2025-PLASTIC-B847F3",
"origin": "munich.loop",
"available": true
}POST /api/v1/federate/offer
POST /api/v1/federate/offer
X-Node-Signature: {signature}
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialOffer",
"material": "MAT-DE-MUC-2025-PLASTIC-B847F3",
"from": "berlin.loop",
"base_price": 120,
"loop_cost": 156,
"valid_until": "2025-05-27T18:00:00Z"
}Standard error format:
{
"error": {
"code": "MATERIAL_NOT_FOUND",
"message": "Material with ID MAT-DE-MUC-2025-PLASTIC-B847F3 not found",
"details": {
"searched_id": "MAT-DE-MUC-2025-PLASTIC-B847F3",
"timestamp": "2025-05-27T15:30:00Z"
}
}
}Error codes:
INVALID_REQUEST: Malformed requestUNAUTHORIZED: Missing or invalid authenticationFORBIDDEN: Insufficient permissionsNOT_FOUND: Resource not foundCONFLICT: Resource conflictINTERNAL_ERROR: Server error
Nodes maintain a registry of peers:
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "NodeRegistry",
"version": "2025-05-27",
"nodes": [
{
"id": "munich.loop",
"endpoint": "https://munich.loop/api/v1",
"public_key": "-----BEGIN PUBLIC KEY-----...",
"capabilities": ["interop-v0.1.1", "loopcoin", "loopsignal"],
"location": {"lat": 48.1351, "lon": 11.5820},
"status": "active",
"last_seen": "2025-05-27T15:00:00Z"
}
]
}Node-to-node requests MUST include:
X-Node-ID: Requesting node identifierX-Node-Signature: Request signatureX-Timestamp: Request timestamp (±5 minutes tolerance)
Material announcements follow controlled flooding:
- Node announces to direct peers
- Peers forward to their peers (TTL=3)
- Duplicate detection prevents loops
- Geographic radius limits propagation
Recommended peering strategy:
- 3-5 nearby nodes (< 200km)
- 2-3 regional nodes (200-1000km)
- 1-2 distant nodes (> 1000km)
- All connections MUST use TLS 1.3 or higher
- Nodes MUST validate certificates
- Perfect Forward Secrecy REQUIRED
- Users authenticate via OAuth 2.0 / OIDC
- Nodes authenticate via mutual TLS or signed requests
- API tokens MUST expire (recommended: 1 hour)
- Personal data MUST NOT be included in MaterialDNA
- Transaction details are public, user identities are not
- Nodes MUST comply with local privacy laws (GDPR, etc.)
Recommended limits:
- User requests: 100/minute
- Node requests: 1000/minute
- Search queries: 10/minute
Nodes MUST maintain immutable logs of:
- Material registrations
- Transaction settlements
- Signal changes
- Node interactions
Required components:
- HTTPS server with valid certificate
- JSON-LD parser and validator
- Database for material registry
- Basic routing calculator
- Federation client
- Languages: JavaScript/Node.js, Python, Go, Rust
- Databases: PostgreSQL, MongoDB, SQLite (small nodes)
- Message Queue: Redis, RabbitMQ (optional)
- Caching: Redis, Memcached (recommended)
- Material registry: Index by category, location, availability
- Federation: Implement connection pooling
- Search: Consider Elasticsearch for large nodes
- Archival: Move expired materials to cold storage
Test suite MUST cover:
- Protocol compliance
- LoopCost calculations
- Federation connectivity
- Error handling
- Performance benchmarks
Step 1: Munich registers spent grain
POST munich.loop/api/v1/material
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialDNA",
"id": "MAT-DE-MUC-2025-FOOD-B847F3",
"category": "organic-food",
"quantity": {"value": 500, "unit": "kg"},
"quality": 0.90,
"location": {
"lat": 48.1372,
"lon": 11.5755,
"address": "Augustiner Brewery"
},
"available_from": "2025-05-27T08:00:00Z",
"expires": "2025-05-30T08:00:00Z"
}
Response:
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialDNA",
"id": "MAT-DE-MUC-2025-FOOD-B847F3",
"status": "registered"
}Step 2: Munich announces to network
POST vienna.loop/api/v1/federate/announce
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialAnnouncement",
"material": "MAT-DE-MUC-2025-FOOD-B847F3",
"origin": "munich.loop",
"category": "organic-food",
"quantity": 500,
"available": true
}Step 3: Vienna calculates LoopCost
Base offer: 60 LC
Munich export signal (40%): +24 LC
Vienna import signal (20%): +12 LC
Distance (400km × 0.02): +8 LC
Total LoopCost: 104 LC
Step 4: Vienna submits offer
POST munich.loop/api/v1/federate/offer
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "MaterialOffer",
"material": "MAT-DE-MUC-2025-FOOD-B847F3",
"from": "vienna.loop",
"base_price": 60,
"loop_cost": 104,
"valid_until": "2025-05-27T18:00:00Z"
}Step 5: Munich selects local buyer
{
"offers": [
{"from": "munich-farm", "loop_cost": 50},
{"from": "vienna.loop", "loop_cost": 104},
{"from": "berlin.loop", "loop_cost": 103}
],
"selected": "munich-farm",
"reason": "lowest_loop_cost"
}{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "SignalProposal",
"node": "munich.loop",
"changes": [
{
"category": "plastic-pet",
"current": 0.20,
"proposed": 0.30,
"reason": "Increase local plastic recycling"
}
],
"voting_opens": "2025-06-01T00:00:00Z",
"voting_closes": "2025-06-07T23:59:59Z"
}
{
"@context": "https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld",
"@type": "LoopVote",
"node": "munich.loop",
"vote_id": "2025-06-plastic-increase",
"proposals": [
{
"category": "plastic-pet",
"current_value": 0.20,
"proposed_value": 0.30,
"rationale": "Increase local plastic recycling"
}
],
"voting_period": {
"start": "2025-06-01T00:00:00Z",
"end": "2025-06-07T23:59:59Z"
},
"results": {
"total_eligible": 150000,
"total_voted": 52500,
"votes_for": 35700,
"votes_against": 16800,
"status": "passed"
}
}v0.2.0 delivered the following (see Appendix B for full changelog):
- ProductDNA schema — product-level DPP entity referencing MaterialDNA composition
- Offer/Match/Transfer schemas accept
product_idas alternative tomaterial_id - Comprehensive DPP extension fields (ESPR, UNTP, Battery Passport, PPWR, NKWS aligned)
- Schema version consolidation (all schemas unified under v0.2.0)
- Complete JSON-LD context with all type and term mappings
- Conformity claims model (UNTP-aligned)
- EPCIS event references and W3C VC pointers in traceability blocks
- Schema versioning policy (RFC-0003)
- Smart contract integration for automated settlement (see RFC-0004)
- Multi-material bundles for efficient trading
- Reputation system for reliable traders
- Advanced routing with machine learning
- Mobile node implementations
- Zero-knowledge proofs for private transactions
- Decentralized identity for users
- Carbon credit integration
- IoT sensor integration
- Cross-chain interoperability
Goal: Submit to standards body (W3C, IETF) after v1.0
-
Carlsson, R., Nevzorova, T. (2025). "Internet of Materials – A concept for circular material traceability". Cleaner Engineering and Technology, 25, 100911. https://doi.org/10.1016/j.clet.2025.100911
-
W3C. (2018). "ActivityPub". W3C Recommendation. https://www.w3.org/TR/activitypub/
-
W3C. (2020). "JSON-LD 1.1". W3C Recommendation. https://www.w3.org/TR/json-ld11/
-
IETF. (2014). "RFC 7231: Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content". https://tools.ietf.org/html/rfc7231
-
IETF. (1997). "RFC 2119: Key words for use in RFCs to Indicate Requirement Levels". https://www.ietf.org/rfc/rfc2119.txt
-
Bernstein, D. J. (2006). "Curve25519: new Diffie-Hellman speed records". Public Key Cryptography - PKC 2006. pp. 207–228.
-
European Parliament and Council. (2024). "Regulation (EU) 2024/1781 on ecodesign requirements for sustainable products (ESPR)". https://eur-lex.europa.eu/eli/reg/2024/1781/oj/eng
-
UN/CEFACT. (2024). "United Nations Transparency Protocol (UNTP) — Digital Product Passport". https://uncefact.github.io/spec-untp/
-
German Federal Government. (2024). "National Circular Economy Strategy (Nationale Kreislaufwirtschaftsstrategie — NKWS)". https://www.bundesumweltministerium.de/en/topics/circular-economy/circular-economy-strategy
The canonical JSON-LD context for v0.2.0 is published at:
https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.2.0.jsonld
The v0.1.1 context remains available at:
https://local-loop-io.github.io/projects/loop-protocol/contexts/loop-v0.1.1.jsonld
Implementations MUST treat the published context files as the source of truth. This appendix is intentionally brief to avoid drift between the specification text and the published context files.
Key properties of the v0.2.0 context:
- It is a superset of the v0.1.1 context (all v0.1.1 mappings are preserved)
- It adds type mappings for all 16+ entity types across all 10 schemas
- It adds term mappings for all DPP extension fields (passport, classification, traceability, conformity claims)
- It adds term mappings for v1-origin schema fields (settlement, voting, signals, transactions)
- It sets
@vocabto the LOOP terms namespace so additive fields expand predictably - It keeps date-time fields typed for interoperable processing
- Unified all 10 schemas under v0.2.0 (eliminated v0.1.1/v1 split)
- Added comprehensive DPP extension fields (ESPR, UNTP, Battery Passport, PPWR, NKWS aligned)
- Added conformity claims model
- Added EPCIS event references and W3C VC pointers
- Completed JSON-LD context with all type and term mappings
- Fixed factual errors (Green Claims Directive withdrawal, BMUV URL)
- Clarified dual license (MIT for code, CC BY-SA 4.0 for prose)
- Established schema versioning policy (RFC-0003)
- Backend API paths aligned to
/api/v1/ - Added ProductDNA schema (product-level DPP entity, ESPR Art. 9-10 aligned)
- Offer/Match/Transfer schemas now accept
product_idas alternative tomaterial_id(anyOf) - Added product category enum (17 categories across furniture, building, electronics, textile, packaging, vehicle, equipment)
- Added ProductDNA term mappings to JSON-LD context
- Minimal interop flow definition (MaterialDNA → Offer → Match → Transfer)
- JSON schemas for Offer, Match, Transfer, MaterialStatusUpdate, NodeHandshake
- Lab demo baseline with data minimization rules
- Initial specification release
- Core protocol definition
- Basic federation support
- Reference calculations
End of Specification
This specification is released under Creative Commons BY-SA 4.0. Code and schemas are released under the MIT license. Contributions welcome at https://github.com/local-loop-io/loop-protocol