BITE: Bidirectional Interchange Transport Envelope

Imported from agstack/pancake · docs/BITE.md
Edits belong in that repository, not in agstack/docs. Licensed under EUPL-1.2, not this site's CC BY-SA 4.0.

Version: 1.0
Status: Proof of Concept
Purpose: Universal format for agricultural data interoperability


Table of Contents

  1. Overview
  2. The Problem: Agricultural Data Fragmentation
  3. Design Philosophy
  4. The IP Packet Metaphor
  5. Three-Section Architecture
  6. Why JSON?
  7. Polyglot Data Strategy
  8. Design Rationale
  9. Security & Immutability
  10. Schema Evolution
  11. Comparison with Alternatives
  12. Implementation Guidelines
  13. Future Considerations

Overview

BITE (Bidirectional Interchange Transport Envelope) is a universal data format designed to solve the agricultural data interoperability crisis. Just as IP packets enable different networks to communicate, BITE enables different agricultural systems to exchange data seamlessly.

Core Purpose

Enable any agricultural data—from any source, in any format—to be packaged, transmitted, validated, and consumed by any system without prior coordination.

Key Characteristics

  • Universal: Works for all agricultural data types
  • Self-describing: Contains its own metadata
  • Immutable: Cryptographically protected against tampering
  • Bidirectional: Human-readable and machine-processable
  • Extensible: Supports future data types without breaking existing systems

The Problem: Agricultural Data Fragmentation

Current State of Agricultural Data

Modern farming relies on dozens of systems:

  • Equipment manufacturers: John Deere, Case IH, AGCO
  • Precision ag platforms: Climate FieldView, Granular, FarmLogs
  • Satellite providers: Planet, Maxar, Sentinel Hub
  • Weather services: DTN, Weather Underground, NOAA
  • Soil labs: AgSource, Waters Agricultural, A&L Labs
  • IoT sensors: CropX, Semios, Arable
  • Marketplaces: Bushel, Indigo Ag, FarmersEdge

The Interoperability Crisis

Each vendor uses proprietary formats:

  • Problem 1: Farmers can’t move data between systems
  • Problem 2: Researchers can’t aggregate data for AI/ML
  • Problem 3: New vendors must build integrations with every existing system
  • Problem 4: Schema changes break existing integrations
  • Problem 5: No provenance tracking - data origins are lost

Economic Impact

  • Vendor lock-in: Farmers trapped in ecosystems
  • Innovation slowdown: 12-18 months to integrate new tools
  • Data silos: 60-80% of farm data never analyzed
  • Redundant work: Same data re-entered 3-5 times
  • Lost value: $50-100B/year in unrealized data value (estimate)

Why Previous Attempts Failed

1. GeoJSON: Designed for mapping, not agricultural workflows

  • No temporal metadata
  • No provenance tracking
  • No integrity verification
  • Too map-centric (not all ag data has geometry)

2. SensorThings API (OGC): Too complex for ag vendors

  • Requires deep understanding of OGC standards
  • Heavy API-first design (what about offline/edge?)
  • Not optimized for farm-scale time-series

3. AgGateway/ADAPT: Industry-controlled, slow evolution

  • Governed by large vendors (conflict of interest)
  • XML-based (2000s technology)
  • Complex schema (steep learning curve)
  • Limited adoption outside North America

4. Vendor APIs: Fragmented by definition

  • 100+ vendors = 100+ APIs
  • Authentication chaos
  • Rate limits vary
  • No unified query language

Design Philosophy

1. Simplicity First

Principle: Anyone with basic JSON knowledge should understand BITE in 10 minutes.

Why: Agricultural developers are not data scientists. Many come from embedded systems, hardware engineering, or agronomy backgrounds. Complex formats (XML, Protobuf, Avro) create adoption barriers.

Trade-off: We sacrifice some efficiency (JSON is verbose) for massive gains in accessibility.

2. Interoperability Over Optimization

Principle: Data portability > performance.

Why: The agricultural ecosystem is fragmented. A format optimized for one vendor’s use case will fail to achieve universal adoption. BITE prioritizes “works everywhere” over “works fastest.”

Trade-off: BITE files are larger than binary formats, but compression (gzip, zstd) mitigates this.

3. Immutability by Design

Principle: Data should never change; create new records instead.

Why: Agriculture is regulated (FDA, EPA, organic certifications). Auditable, tamper-proof records are legally required. Event sourcing patterns also enable time-travel debugging.

Trade-off: Storage costs increase (~2x vs. mutable records), but storage is cheap and getting cheaper.

4. Human-Readable, Machine-First

Principle: Humans should be able to read BITEs, but machines are the primary consumer.

Why: Debugging is critical. When a pesticide application goes wrong, a farmer needs to see the raw data. JSON enables cat file.bite readability while still being parseable.

Trade-off: Not as compact as binary, but readability aids adoption and debugging.

5. Open Standard, No Gatekeepers

Principle: Anyone can implement BITE without permission, licensing, or fees.

Why: Agricultural vendors (especially in developing countries) cannot afford expensive standards. Open-source, royalty-free design democratizes access.

Trade-off: Less control over evolution, but community governance (like Linux, Python) has proven successful.


The IP Packet Metaphor

Why This Analogy Matters

Most agricultural developers understand networking. IP packets solved a similar problem in the 1970s: how do different networks (Ethernet, Token Ring, ARPANET) communicate?

Answer: A universal envelope format with:

  • Header: Routing information (source, destination, protocol)
  • Payload: The actual data (any format)
  • Checksum: Integrity verification

BITE applies the same pattern to agricultural data.

BITE as “Agricultural IP”

IP Packet ComponentBITE EquivalentPurpose
Source AddressHeader.sourceWho/what created this data?
Destination(implicit: consumer)Who should process this?
ProtocolHeader.typeHow to interpret the payload?
TimestampHeader.timestampWhen was this data captured?
PayloadBodyThe actual agricultural data
ChecksumFooter.hashIs the data intact?
TTL(future)How long is this data valid?

Key Differences from IP

1. Location-Aware: Agricultural data is inherently geospatial

  • IP doesn’t care where a packet originates physically
  • BITE requires geoid for spatial context

2. Immutable: IP packets are ephemeral; BITEs are permanent

  • IP packets are discarded after delivery
  • BITEs are stored, versioned, and audited

3. Self-Describing: IP needs external protocol definitions

  • IP relies on TCP/UDP/ICMP standards
  • BITE contains its own schema in Header.type

Why This Metaphor Works

  • Familiar: Developers already understand layered protocols
  • Proven: IP enabled the internet; BITE can enable ag-data interoperability
  • Scalable: IP handles trillions of packets/day; BITE can handle farm data at scale
  • Extensible: IP supports 256 protocols; BITE supports unlimited data types

Three-Section Architecture

Why Three Sections?

Design Rationale: Separation of concerns.

  1. Header: Metadata (unchanging facts about the data)
  2. Body: Content (the actual agricultural observations/measurements)
  3. Footer: Integrity (cryptographic proof and relationships)

This mirrors proven patterns:

  • Email: Headers, Body, Attachments
  • HTTP: Headers, Body, Trailers
  • Databases: Schema, Data, Indexes

Section 1: Header

Purpose: Answer “What, Where, When, Who?”

{
  "id": "01HQXYZ...",           
  "geoid": "63f764...",         
  "timestamp": "2024-11-01T10:30:00Z",
  "type": "observation",        
  "source": {
    "agent": "field-agent-maria",
    "device": "mobile-app-v2.1"
  }
}

Design Decisions

id (ULID, not UUID)

  • Why ULID: Time-ordered, 128-bit unique identifiers
  • Alternative rejected (UUID v4): Random UUIDs don’t sort chronologically
  • Benefit: Database indexes perform better with sorted IDs
  • Trade-off: Reveals creation time (acceptable for ag data)

geoid (AgStack GeoID)

  • Why: Standardized geospatial identifier using S2 geometry
  • Alternative rejected (lat/lon): Coordinates change with field boundaries
  • Alternative rejected (WKT): Too verbose for headers
  • Benefit: Stable identifier even if boundaries shift
  • Trade-off: Requires GeoID infrastructure (Asset Registry)

timestamp (UTC ISO 8601)

  • Why: Universal time standard
  • Alternative rejected (Unix epoch): Not human-readable
  • Alternative rejected (local time): Timezone chaos
  • Benefit: Unambiguous, sortable, parseable
  • Trade-off: Larger than epoch (acceptable)

type (String, not enum)

  • Why: Extensible without spec changes
  • Alternative rejected (numeric codes): Requires central registry
  • Alternative rejected (URIs): Too verbose
  • Benefit: New types can be created by anyone
  • Trade-off: Typos possible (mitigated by validation libraries)

source (Optional object)

  • Why: Provenance matters for auditing
  • Alternative rejected (required field): Some data sources are anonymous
  • Benefit: Traceable to human/device/system
  • Trade-off: Privacy concerns (address with pseudonymization)

Section 2: Body

Purpose: The actual agricultural data (flexible, type-specific).

{
  "observation_type": "disease",
  "crop": "coffee",
  "disease": "coffee_rust",
  "severity": "moderate",
  "affected_plants": 45,
  "notes": "Orange pustules on leaf undersides"
}

Design Decisions

Flexible JSON Schema

  • Why: Agriculture is too diverse for rigid schemas
  • Alternative rejected (fixed schema): Impossible to anticipate all use cases
  • Alternative rejected (key-value pairs): Loses type information
  • Benefit: Vendors can add custom fields without breaking consumers
  • Trade-off: More storage, but enables innovation

No Nesting Limits

  • Why: Complex data (imagery, lab results) needs structure
  • Alternative rejected (flat key-value): Forces artificial flattening
  • Benefit: Natural data representation
  • Trade-off: Query complexity (mitigated by JSONB in Postgres)

Type Coercion Rules

  • Numbers: Always store as JSON numbers (not strings)
  • Dates: ISO 8601 strings (for consistency with header)
  • Booleans: true/false (not "yes"/"no")
  • Units: Include in field names or values (e.g., nitrogen_ppm)

Why not separate schema files?

  • Rejected: Separate .schema.json files (like JSON Schema)
  • Reason: Adds complexity, requires file management
  • Solution: Type-specific documentation (e.g., “observation BITE spec”)

Purpose: Integrity, relationships, metadata.

{
  "hash": "a3f5b2...",
  "schema_version": "1.0",
  "tags": ["disease", "urgent"],
  "references": ["01HQABC..."]
}

Design Decisions

hash (SHA-256)

  • Why: Industry standard for integrity verification
  • Alternative rejected (MD5): Cryptographically broken
  • Alternative rejected (SHA-512): Overkill for this use case
  • Computation: SHA256(canonicalized_header + canonicalized_body)
  • Benefit: Tamper detection, content addressing
  • Trade-off: Computational cost (~1ms per BITE)

Canonicalization (RFC 8785)

  • Why: JSON key order affects hashes
  • Solution: Sort keys alphabetically before hashing
  • Alternative rejected (preserve order): Fragile, parser-dependent
  • Benefit: Same data = same hash, always

schema_version (Semantic versioning)

  • Why: Enables backward compatibility checks
  • Format: "major.minor.patch" (e.g., "1.0.0")
  • Rule: Major version breaks compatibility
  • Benefit: Consumers can validate compatibility

tags (Array of strings)

  • Why: Enables search/filtering without parsing body
  • Alternative rejected (single category): Agriculture is multi-dimensional
  • Usage: ["disease", "urgent", "coffee", "organic"]
  • Benefit: Rapid querying (indexed in database)

references (Array of BITE IDs)

  • Why: Agricultural workflows are sequences (observation → recommendation → application)
  • Example: Soil test BITE → Fertilizer recommendation BITE → Application BITE
  • Benefit: Graph traversal, impact analysis
  • Trade-off: Requires BITE storage/retrieval system

Why Not Four or Five Sections?

Considered: Signature section (for cryptographic signing)

  • Decision: Future enhancement (BITE v2.0)
  • Reason: Adds complexity, not all use cases need it
  • Mitigation: Hash provides integrity; signatures for authentication

Considered: Attachments section (for images, files)

  • Decision: Use Body with base64 or URIs
  • Reason: Keeps three-section simplicity
  • Trade-off: Large images bloat BITEs (use external storage + URI)

Why JSON?

The JSON Decision

Context: Choosing a serialization format is the most critical design decision.

Options Considered:

  1. JSON (JavaScript Object Notation)
  2. XML (eXtensible Markup Language)
  3. Protocol Buffers (Google’s binary format)
  4. MessagePack (binary JSON alternative)
  5. CBOR (Concise Binary Object Representation)
  6. CSV (Comma-Separated Values)

Why JSON Won

1. Universal Parser Support

JSON: Parsers exist in every language

  • Python: json (built-in)
  • JavaScript: Native support
  • Java: Jackson, Gson
  • C/C++: RapidJSON, nlohmann/json
  • Go: encoding/json
  • Rust: serde_json
  • Even COBOL and Fortran have JSON libraries

Comparison:

  • Protobuf: Requires code generation (barrier to entry)
  • XML: More complex parsing, namespace management
  • CSV: No nested structures, poor type support

2. Human Readability

Scenario: A pesticide application fails. The farmer calls support.

With JSON:

$ cat application_error.bite | jq .
{
  "Header": {
    "type": "pesticide_application",
    "timestamp": "2024-11-01T10:30:00Z"
  },
  "Body": {
    "product": "Copper Oxychloride",
    "dosage_per_hectare": 2.5,
    "weather": "dry"
  }
}

Support can immediately see the data.

With Protobuf: Binary blob, requires decoder tool.

With XML:

<bite>
  <header>
    <type>pesticide_application</type>
    ...
  </header>
</bite>

Verbose, harder to scan visually.

3. Web-Native

Reality: Most modern ag-tech uses web APIs (REST, GraphQL).

JSON: Native format for HTTP APIs

  • Content-Type: application/json
  • Zero conversion needed

Protobuf: Requires transcoding (adds latency, complexity)

XML: Falling out of favor (SOAP → REST transition)

4. Ecosystem Maturity

Tools that work with JSON out-of-the-box:

  • Command line: jq, yq
  • Databases: MongoDB, PostgreSQL (JSONB), MySQL (JSON type)
  • Message queues: Kafka, RabbitMQ, Pulsar
  • Cloud storage: S3, GCS (query JSON without download)
  • Analytics: Spark, Pandas, R (native JSON readers)
  • AI/ML: LLMs consume/generate JSON easily

Protobuf/CBOR: Requires custom tooling at every stage.

5. Schema Flexibility

JSON: Add new fields without breaking old consumers

{
  "crop": "coffee",
  "variety": "arabica"  // New field, old parsers ignore it
}

Protobuf: Requires schema versioning, field numbers, backwards compatibility rules.

XML: Namespace management, schema evolution headaches.

Trade-offs We Accepted

1. Size (Larger Files)

JSON:

{"temperature": 23.5, "humidity": 67.2}

= 45 bytes

Protobuf (equivalent): ~10 bytes (4.5x smaller)

Our rationale:

  • Compression: gzip reduces JSON to ~Protobuf size
  • Storage costs: $0.023/GB/month (S3) → negligible
  • Network bandwidth: Agricultural data is not high-volume (MB/day, not TB/sec)
  • Benefit: Accessibility > efficiency

2. Parsing Speed (Slower)

Benchmark (parsing 1000 records):

  • JSON: ~50ms
  • Protobuf: ~10ms (5x faster)

Our rationale:

  • Not the bottleneck: Network latency (100-500ms) dominates
  • Modern CPUs: Fast enough for farm-scale data
  • Benefit: Universal compatibility > marginal speed gain

3. Type Safety (Weaker)

JSON: Everything is string, number, boolean, array, object, null

  • No native support for: Date, Binary, Decimal (precision issues)

Protobuf: Strict typing (int32, int64, float, double, bytes, etc.)

Our mitigation:

  • Conventions: ISO 8601 for dates, strings for decimals (if precision critical)
  • Validation libraries: JSON Schema for type checking
  • Benefit: Flexibility > strict typing (agriculture is messy)

4. No Binary Data (Inefficient)

JSON: Binary data must be base64 encoded (33% overhead)

Protobuf/CBOR: Native binary support

Our solution:

  • Small binaries: Base64 in BITE (e.g., thumbnails)
  • Large binaries: External storage (S3) + URI in BITE
{
  "image_uri": "s3://farm-data/field-123/photo.jpg",
  "image_hash": "sha256:abc123..."
}

Why Not JSON Alternatives?

JSON5 (JSON with comments, trailing commas)

  • Rejected: Not standardized, poor library support
  • Reason: Adds features that break strict JSON parsers

YAML (YAML Ain’t Markup Language)

  • Rejected: Indentation-sensitive, multiple ways to express same thing
  • Reason: Error-prone, harder to generate programmatically

TOML (Tom’s Obvious Minimal Language)

  • Rejected: Config files, not data interchange
  • Reason: Poor nesting support

Polyglot Data Strategy

What is “Polyglot Data”?

Definition: Multiple data types coexisting in a single system without forced schema normalization.

Agricultural Context: A farm produces:

  • Observations: Field scouts report disease (text, images)
  • Sensor data: Soil moisture, temperature (time-series numbers)
  • Satellite imagery: NDVI, LAI (raster arrays)
  • Lab results: Soil nutrients (structured tables)
  • Equipment logs: Tractor GPS, fuel consumption (GPS tracks)
  • Financial data: Inputs, yields, revenue (spreadsheets)

Traditional approach: Force everything into relational tables

  • Soil moisture → sensor_readings table
  • Disease observations → observations table
  • Satellite imagery → External files (not in DB)
  • Lab results → lab_results table

Problem: Queries spanning multiple types require complex JOINs, UNIONs, or external processing.

BITE’s Polyglot Approach

Principle: Every data type uses the same BITE envelope, but different Body structures.

Example 1: Observation BITE

{
  "Header": {
    "type": "observation",
    "geoid": "field-abc",
    "timestamp": "2024-11-01T10:00:00Z"
  },
  "Body": {
    "observation_type": "disease",
    "crop": "coffee",
    "disease": "coffee_rust"
  }
}

Example 2: Soil Moisture BITE (same geoid, 30 minutes later)

{
  "Header": {
    "type": "sensor_reading",
    "geoid": "field-abc",
    "timestamp": "2024-11-01T10:30:00Z"
  },
  "Body": {
    "sensor_type": "soil_moisture",
    "value": 23.5,
    "unit": "percent",
    "depth_cm": 15
  }
}

Example 3: Satellite Image BITE (same geoid, same day)

{
  "Header": {
    "type": "imagery_sirup",
    "geoid": "field-abc",
    "timestamp": "2024-11-01T12:00:00Z"
  },
  "Body": {
    "vendor": "terrapipe.io",
    "ndvi_stats": {
      "mean": 0.65,
      "min": 0.45,
      "max": 0.85
    }
  }
}

Why Polyglot Matters for Agriculture

1. Natural Data Representation

Reality: Agricultural data doesn’t fit neat schemas.

Example: Coffee rust observation

  • Field scout notes: Free text
  • GPS location: Point geometry
  • Photos: Binary (JPEG)
  • Affected area: Polygon geometry
  • Severity: Categorical (low/medium/high)
  • Treatment history: References to other BITEs

Relational DB approach: Normalize into multiple tables

  • observations table (id, geoid, timestamp)
  • observation_text table (observation_id, note)
  • observation_photos table (observation_id, photo_url)
  • observation_geometry table (observation_id, geometry)

BITE approach: One BITE with nested structure

{
  "Body": {
    "notes": "Orange pustules visible...",
    "location": {"type": "Point", "coordinates": [...]},
    "photos": ["s3://..."],
    "affected_area": {"type": "Polygon", "coordinates": [...]},
    "severity": "moderate",
    "previous_treatments": ["01HQABC..."]
  }
}

Benefit: One query retrieves everything.

2. Schema Evolution Without Migration

Scenario: Lab adds new soil test (micronutrients).

Traditional DB:

  1. Alter lab_results table (add columns)
  2. Update application code
  3. Migrate existing records (backfill NULLs)
  4. Update documentation
  5. Retrain users

BITE approach:

  1. New BITE includes new fields:
{
  "Body": {
    "nitrogen_ppm": 45,
    "phosphorus_ppm": 12,
    "zinc_ppm": 2.3,  // New field
    "boron_ppm": 0.8   // New field
  }
}
  1. Old parsers ignore new fields
  2. New parsers use new fields

Benefit: Zero downtime, no coordination.

3. Cross-Type Queries

Query: “Show all data (any type) for field-abc in the last 30 days”

Traditional DB:

SELECT * FROM observations WHERE geoid = 'field-abc' AND timestamp > '...'
UNION ALL
SELECT * FROM sensor_readings WHERE geoid = 'field-abc' AND timestamp > '...'
UNION ALL
SELECT * FROM lab_results WHERE geoid = 'field-abc' AND timestamp > '...'
-- Repeat for every table

BITE approach (assuming BITEs stored in PANCAKE):

SELECT * FROM bites 
WHERE geoid = 'field-abc' 
AND timestamp > '2024-10-01'
ORDER BY timestamp DESC;

Benefit: Single query, all data types.

4. AI/ML Readiness

Use case: Train a disease prediction model.

Traditional DB: Export and join multiple tables

  1. Export observations → CSV
  2. Export sensor data → CSV
  3. Export weather data → CSV
  4. Join on (geoid, timestamp) → Nightmare
  5. Handle missing data, schema mismatches

BITE approach: All data already in JSON

  1. Filter BITEs by type and date range
  2. Extract features from BITE Bodies
  3. Feed to model

Benefit: Unified format simplifies data pipelines.

Trade-Offs of Polyglot Design

1. Storage Overhead

Issue: Storing full JSON for every record is less space-efficient than normalized tables.

Example: 1000 soil moisture readings

  • Normalized: 1000 rows × 4 columns = ~32KB
  • BITE: 1000 BITEs × ~500 bytes each = ~500KB (15x larger)

Mitigation:

  • Compression: gzip reduces JSON to ~30% of original size
  • Batch storage: Store arrays of BITEs (deduplicates common fields)
  • Archival: Cold storage (S3 Glacier) costs $0.004/GB/month

Conclusion: Storage is cheap; interoperability is priceless.

2. Query Complexity

Issue: JSONB queries are slower than indexed columns.

Example: Find all high-severity observations

  • Normalized: SELECT * FROM observations WHERE severity = 'high' (instant, indexed)
  • BITE: SELECT * FROM bites WHERE body->>'severity' = 'high' (slower, JSONB scan)

Mitigation:

  • GIN indexes: PostgreSQL indexes JSONB (fast enough)
  • Extracted columns: Store common fields (geoid, timestamp, type) separately
  • Caching: Recent queries cached in Redis

Conclusion: Acceptable trade-off for flexibility.

3. Type Safety

Issue: JSONB doesn’t enforce schemas; typos go unnoticed.

Example:

{"severty": "high"}  // Typo! Should be "severity"

Mitigation:

  • Validation libraries: JSON Schema at write time
  • BITE types: Published specs for common types
  • Linters: Pre-commit hooks check BITEs

Conclusion: Validation catches 99% of issues.


Design Rationale

Decision 1: ULID vs UUID

Options:

  1. UUID v4 (Random, 128-bit)
  2. UUID v7 (Time-ordered, 128-bit)
  3. ULID (Time-ordered, lexicographically sortable)
  4. Snowflake ID (Twitter’s 64-bit)

Decision: ULID

Rationale:

  • Time-ordering: Database indexes perform better (B-trees love sorted keys)
  • Lexicographic sorting: String comparison = chronological order
  • Uniqueness: 128 bits = 2^128 possible IDs (no collisions)
  • Readability: Base32 encoding (easier than UUID hex)

Example:

  • UUID: f47ac10b-58cc-4372-a567-0e02b2c3d479
  • ULID: 01HQXYZ9876ABCDEFGHJKLMNPQR

Trade-off: Reveals creation time (acceptable for agricultural data).

Decision 2: GeoID vs Lat/Lon

Options:

  1. Lat/Lon coordinates (WGS84)
  2. WKT (Well-Known Text) in header
  3. GeoJSON geometry in header
  4. GeoID (AgStack standard)

Decision: GeoID (with WKT stored elsewhere)

Rationale:

  • Stable identifiers: Field boundaries change; GeoID persists
  • Space-efficient: 64-char hash vs 1000+ char WKT
  • S2 geometry: Behind the scenes (automatic spatial indexing)
  • Standard: AgStack Asset Registry provides resolution

Example:

{
  "geoid": "63f764609b85eb356d387c1630a0671d3a8a56ffb6c91d1e52b1d7f2fe3c4213"
}

Alternative rejected: Embedding WKT in every BITE

{
  "geometry": "POLYGON((-95.5487 41.5381, ...))"  // Too verbose
}

Trade-off: Requires Asset Registry lookup to get geometry (acceptable, one-time cost).

Decision 3: SHA-256 vs SHA-512

Options:

  1. MD5 (128-bit, fast)
  2. SHA-1 (160-bit, deprecated)
  3. SHA-256 (256-bit, standard)
  4. SHA-512 (512-bit, more secure)
  5. BLAKE3 (Modern, fast)

Decision: SHA-256

Rationale:

  • Security: Not cryptographically broken (unlike MD5, SHA-1)
  • Performance: 1ms per BITE (acceptable)
  • Ubiquity: Every language has SHA-256
  • Sufficient: 2^256 hash space (collision-resistant for content addressing)

SHA-512 rejected:

  • Overkill: 512 bits = 128 hex characters (verbose in footer)
  • Speed: Slower on 32-bit systems (rare, but exists in IoT)

BLAKE3 rejected:

  • New: Fewer libraries (adoption barrier)
  • Benefit: 10x faster (but 1ms → 0.1ms not impactful)

Decision 4: ISO 8601 vs Unix Epoch

Options:

  1. Unix epoch (seconds since 1970-01-01, e.g., 1698854400)
  2. ISO 8601 (e.g., 2024-11-01T10:30:00Z)
  3. RFC 3339 (ISO 8601 profile)

Decision: ISO 8601 (UTC)

Rationale:

  • Human-readable: 2024-11-01T10:30:00Z vs 1698854400
  • Timezone clarity: Z suffix = UTC (no ambiguity)
  • Sortable: Lexicographic string comparison = chronological order
  • Standard: IETF, W3C, ISO all endorse

Unix epoch rejected:

  • Readability: Debugging requires conversion
  • Year 2038 problem: 32-bit overflow (still an issue in embedded systems)

Trade-off: 24 bytes vs 10 bytes (acceptable).

Decision 5: Required vs Optional Fields

Philosophy: Minimize required fields to maximize adoption.

Required in Header:

  • id: Uniqueness
  • geoid: Location
  • timestamp: Time
  • type: Interpretation

Optional in Header:

  • source: Provenance (not always known, e.g., imported legacy data)

Nothing required in Body:

  • Why: Body structure is type-specific
  • Validation: Type definitions specify requirements (e.g., “observation” type requires crop)

Nothing required in Footer (except hash):

  • tags: Nice to have, not mandatory
  • references: Only if relationships exist

Benefit: Low barrier to entry (minimal BITEs are valid).


Security & Immutability

Threat Model

Agricultural data faces unique security challenges:

  1. Tampering: Changing yield data to inflate insurance claims
  2. Forgery: Creating fake organic certification records
  3. Deletion: Removing evidence of pesticide misuse
  4. Replay: Re-submitting old data to trigger duplicate payments
  5. Privacy: Exposing farm locations to competitors

Immutability by Design

Principle: Once created, a BITE never changes.

Implementation:

  1. Hash in footer: Any modification invalidates the hash
  2. Timestamp in header: Establishes creation time (cannot backdate)
  3. ULID: Time-ordered, reveals creation sequence

Example: Detecting tampering

def validate_bite(bite):
    # Extract
    header = bite["Header"]
    body = bite["Body"]
    claimed_hash = bite["Footer"]["hash"]
    
    # Recompute
    canonical = canonicalize(header, body)
    actual_hash = sha256(canonical)
    
    # Compare
    return actual_hash == claimed_hash  # False if tampered

Use case: Organic certification audit

  • Inspector retrieves all BITEs for a farm
  • Verifies every hash matches content
  • Any modified BITE detected instantly

Content Addressing

Concept: The hash IS the BITE’s address (like IPFS, Git).

Benefit: Deduplication

  • Two identical observations → One storage entry
  • Reference by hash: bite://sha256:a3f5b2...

Implementation:

CREATE TABLE bites (
    id TEXT PRIMARY KEY,
    hash TEXT UNIQUE NOT NULL,  -- Content address
    ...
);

Cryptographic Signing (Future: BITE v2.0)

Current BITE: Hash provides integrity (data not tampered).

Future: Digital signatures provide authenticity (who created it).

Proposed addition (Footer):

{
  "hash": "a3f5b2...",
  "signature": {
    "algorithm": "Ed25519",
    "public_key": "0x1234...",
    "signature": "0xabcd..."
  }
}

Use case: Regulatory compliance

  • EPA requires proof of pesticide application
  • Farmer signs BITE with private key
  • Regulator verifies with public key (non-repudiation)

Why not in v1.0?

  • Complexity: Key management is hard (PKI, key rotation, revocation)
  • Adoption: Many users don’t need it yet
  • Privacy: Signatures link BITEs to identities (may be undesirable)

Privacy Considerations

Issue: BITEs are transparent; how to protect sensitive data?

Strategies:

1. Pseudonymization

{
  "Header": {
    "source": {
      "agent": "agent-7a3f"  // Hash of real identity
    }
  }
}

2. Selective disclosure

  • Store sensitive data off-chain
  • BITE contains only hash:
{
  "Body": {
    "financial_data_hash": "sha256:xyz..."
  }
}

3. Encryption

  • Encrypt Body (AES-256), store key separately
  • Footer hash includes encrypted Body
{
  "Body": {
    "encrypted": true,
    "ciphertext": "base64..."
  }
}

4. Access control

  • PANCAKE storage enforces permissions
  • BITE itself is public, but retrieval is restricted

Trade-off: Privacy vs transparency (choose based on use case).

Audit Trails

Use case: Track pesticide applications for compliance.

BITE sequence:

  1. Observation BITE: Disease detected
  2. Recommendation BITE: Agronomist suggests pesticide (references observation)
  3. Purchase BITE: Farmer buys product (references recommendation)
  4. Application BITE: Spraying performed (references purchase)
  5. Verification BITE: Inspector confirms (references application)

Query: Trace lineage

WITH RECURSIVE lineage AS (
  SELECT * FROM bites WHERE id = '01HQXYZ'  -- Start
  UNION
  SELECT b.* FROM bites b
  JOIN lineage l ON b.id = ANY(l.footer->'references')
)
SELECT * FROM lineage;

Benefit: Complete audit trail (who did what, when, why).


Schema Evolution

The Agricultural Data Challenge

Reality: Agriculture evolves faster than standards.

Examples:

  • New crops: Hemp legalized (2018) → new data types needed
  • New sensors: LIDAR on tractors (2020) → 3D point clouds
  • New regulations: Carbon credits (2023) → emissions tracking
  • New diseases: Novel pathogens → new observation types

Traditional databases: Schema changes = downtime, migrations, coordination.

BITE: Add new fields, consumers adapt gradually.

Backward Compatibility Principles

Rule 1: Never remove required fields

  • Safe: Add optional fields to Body
  • Unsafe: Remove Header.geoid

Rule 2: Never change field semantics

  • Safe: Add Body.nitrogen_ppm_v2 (new measurement method)
  • Unsafe: Change Body.nitrogen_ppm units (mg/L → ppm)

Rule 3: Use schema_version for breaking changes

  • BITE v1.0: Current spec
  • BITE v2.0: If we must break compatibility (e.g., move to Protobuf)

Rule 4: Old parsers ignore unknown fields

def parse_observation(bite):
    body = bite["Body"]
    crop = body["crop"]  # Required
    severity = body.get("severity", "unknown")  # Optional, default if missing
    # New field "subspecies" ignored if not in code

Versioning Strategy

Three levels:

1. Spec version (Footer.schema_version)

  • Major: Breaking changes (v1 → v2)
  • Minor: New types, fields (v1.1, v1.2)
  • Patch: Clarifications, examples (v1.0.1)

2. Type version (in type name)

  • Example: observation_v1, observation_v2
  • Use: When body structure changes incompatibly

3. Field version (in field name)

  • Example: nitrogen_ppm → nitrogen_ppm_v2
  • Use: When measurement method changes

Extension Patterns

Pattern 1: Optional fields

{
  "Body": {
    "crop": "coffee",
    "variety": "arabica",  // Added later
    "cultivar": "gesha"    // Added even later
  }
}

Old parsers work; new parsers get richer data.

Pattern 2: Vendor extensions

{
  "Body": {
    "vendor": "acme-ag",
    "acme_proprietary": {
      "calibration_id": "xyz",
      "sensor_serial": "123"
    }
  }
}

Vendor-specific fields namespaced (avoid collisions).

Pattern 3: External schemas

{
  "Body": {
    "schema_uri": "https://schemas.acme-ag.com/soil_v2.json",
    "data": { ... }
  }
}

Reference external definitions (advanced).

Type Registries (Future)

Problem: Who defines canonical BITE types?

Proposed: Community registry (like NPM, Crates.io)

  • URL: https://bite-registry.agstack.org/types/observation_v1
  • Contents: JSON Schema, examples, changelog
  • Governance: Open-source, community-maintained

Example type definition:

type: observation_v1
description: Field observation by human scout
required_fields:
  - crop
  - observation_type
optional_fields:
  - disease
  - severity
  - notes
examples:
  - { "crop": "coffee", "disease": "rust", "severity": "high" }

Benefit: Standardization without centralized control.


Comparison with Alternatives

GeoJSON

What it is: JSON format for geographic features (RFC 7946).

Strengths:

  • Widely adopted (web maps, GIS tools)
  • Simple for geometries

Weaknesses for agriculture:

  • No time: No standard timestamp field
  • No provenance: No source tracking
  • No integrity: No hash/signature
  • Map-centric: Assumes everything is a map feature

BITE improvement:

  • Time is first-class (Header.timestamp)
  • Provenance built-in (Header.source)
  • Integrity via hash (Footer.hash)
  • Geometry optional (not all ag data is spatial)

When to use GeoJSON: Interchange with GIS tools (convert BITE ↔ GeoJSON).

SensorThings API (OGC)

What it is: OGC standard for IoT sensor data.

Strengths:

  • Comprehensive (Things, Locations, Datastreams, Observations)
  • International standard

Weaknesses for agriculture:

  • Complexity: Steep learning curve (entities, relationships)
  • API-first: Assumes always-online (farms have poor connectivity)
  • Overkill: Small farms don’t need full OGC stack

BITE improvement:

  • Simpler (3 sections, not 7 entity types)
  • Offline-friendly (files, not just APIs)
  • Easier adoption (JSON, not OGC/ISO specs)

When to use SensorThings: Large-scale IoT deployments with dedicated IT.

ADAPT (AgGateway)

What it is: Precision agriculture data exchange standard (XML-based).

Strengths:

  • Industry-backed (John Deere, CNH, AGCO)
  • Comprehensive (field operations, equipment, prescriptions)

Weaknesses for agriculture:

  • XML: Verbose, complex
  • Vendor-controlled: Dominated by equipment manufacturers
  • North America-centric: Limited global adoption
  • Static: Slow to evolve (committee-driven)

BITE improvement:

  • JSON (simpler, web-native)
  • Open governance (no vendor control)
  • Global (not region-specific)
  • Fast evolution (community-driven)

When to use ADAPT: Equipment data interchange (tractors, planters).

Interoperability: ADAPT ↔ BITE converters (best of both worlds).

Vendor APIs (Planet, etc.)

What it is: Proprietary APIs from ag-tech vendors.

Strengths:

  • Optimized for vendor’s use case
  • Rich features (specific to product)

Weaknesses for agriculture:

  • Fragmentation: 100+ APIs, all different
  • Lock-in: Data trapped in vendor systems
  • Instability: APIs change, break integrations

BITE improvement:

  • Universal: Works with any vendor (TAP adapters)
  • Portability: Export data as BITEs, own your data
  • Stability: BITE spec stable; vendor APIs can change

When to use vendor APIs: Direct integration (TAP translates to BITE).

Summary Table

FeatureBITEGeoJSONSensorThingsADAPTVendor APIs
Simplicity✅ High✅ High❌ Low❌ Low⚠️ Varies
Time support✅ Native❌ No✅ Yes✅ Yes✅ Yes
Provenance✅ Built-in❌ No⚠️ Limited⚠️ Limited⚠️ Varies
Integrity✅ Hash❌ No❌ No❌ No❌ No
Extensibility✅ High⚠️ Medium⚠️ Medium❌ Low❌ Vendor-specific
Adoption barrier✅ Low✅ Low❌ High❌ High⚠️ Varies
Open governance✅ Yes✅ Yes✅ Yes❌ No❌ No
Offline-friendly✅ Yes✅ Yes❌ No⚠️ Limited❌ No

Implementation Guidelines

For Data Producers (Creating BITEs)

Step 1: Choose a BITE type

  • Use existing types (e.g., observation, sensor_reading)
  • Or define new (e.g., drone_survey_v1)

Step 2: Generate header

from ulid import ULID
from datetime import datetime

header = {
    "id": str(ULID()),
    "geoid": get_geoid_from_coordinates(lat, lon),  # Asset Registry API
    "timestamp": datetime.utcnow().isoformat() + "Z",
    "type": "observation",
    "source": {
        "agent": "field-scout-maria",
        "device": "mobile-app-v2.1.0"
    }
}

Step 3: Construct body

body = {
    "crop": "coffee",
    "observation_type": "disease",
    "disease": "coffee_rust",
    "severity": "moderate",
    "notes": "Orange pustules visible on leaf undersides"
}

Step 4: Compute hash

import hashlib
import json

def canonicalize(obj):
    return json.dumps(obj, sort_keys=True, separators=(',', ':'))

header_canon = canonicalize(header)
body_canon = canonicalize(body)
hash_val = hashlib.sha256((header_canon + body_canon).encode()).hexdigest()

Step 5: Create footer

footer = {
    "hash": hash_val,
    "schema_version": "1.0",
    "tags": ["disease", "coffee", "urgent"]
}

Step 6: Assemble BITE

bite = {
    "Header": header,
    "Body": body,
    "Footer": footer
}

Step 7: Validate

def validate_bite(bite):
    # Check structure
    assert set(bite.keys()) == {"Header", "Body", "Footer"}
    
    # Check required header fields
    assert "id" in bite["Header"]
    assert "geoid" in bite["Header"]
    assert "timestamp" in bite["Header"]
    assert "type" in bite["Header"]
    
    # Verify hash
    recomputed = compute_hash(bite["Header"], bite["Body"])
    assert recomputed == bite["Footer"]["hash"]
    
    return True

Step 8: Store/transmit

# Save to file
with open(f"{bite['Header']['id']}.bite", "w") as f:
    json.dump(bite, f, indent=2)

# Or send to API
requests.post("https://pancake-server.com/bites", json=bite)

For Data Consumers (Reading BITEs)

Step 1: Retrieve BITE

import json

with open("01HQXYZ.bite", "r") as f:
    bite = json.load(f)

Step 2: Validate

if not validate_bite(bite):
    raise ValueError("Invalid BITE: hash mismatch")

Step 3: Check type

bite_type = bite["Header"]["type"]

if bite_type == "observation":
    handle_observation(bite)
elif bite_type == "sensor_reading":
    handle_sensor_reading(bite)
else:
    print(f"Unknown type: {bite_type}, skipping")

Step 4: Extract data

def handle_observation(bite):
    body = bite["Body"]
    crop = body["crop"]
    disease = body.get("disease")  # May not exist
    severity = body.get("severity", "unknown")  # Default value
    
    print(f"{crop} has {disease} (severity: {severity})")

Step 5: Handle unknown fields gracefully

def safe_extract(bite, field_path):
    """Extract nested field, return None if missing"""
    obj = bite["Body"]
    for key in field_path.split("."):
        if key in obj:
            obj = obj[key]
        else:
            return None
    return obj

nitrogen = safe_extract(bite, "nutrients.nitrogen_ppm")

Best Practices

1. Always validate

  • Check hash on every BITE you receive
  • Reject invalid BITEs immediately

2. Be liberal in what you accept

  • Ignore unknown fields (forward compatibility)
  • Provide defaults for missing optional fields

3. Be conservative in what you send

  • Follow type specifications exactly
  • Don’t add fields without documentation

4. Use libraries, don’t roll your own

  • Hash computation is error-prone (key ordering matters)
  • Use battle-tested libraries

5. Log provenance

  • Always populate Header.source
  • Future audits will thank you

6. Tag generously

  • More tags = better searchability
  • Use common vocabulary (avoid vendor jargon)

7. Reference related BITEs

  • Build graph of relationships
  • Enables impact analysis

Future Considerations

BITE v2.0 (Potential Features)

1. Cryptographic Signatures

{
  "Footer": {
    "hash": "...",
    "signature": {
      "algorithm": "Ed25519",
      "pubkey": "0x...",
      "sig": "0x..."
    }
  }
}

Use case: Regulatory compliance, non-repudiation.

2. Compression

{
  "Header": { ... },
  "Body_compressed": {
    "algorithm": "zstd",
    "data": "base64..."
  }
}

Use case: Large imagery BITEs (reduce size 10x).

3. Multipart BITEs

{
  "Header": {
    "multipart": true,
    "parts": ["bite://sha256:abc...", "bite://sha256:def..."]
  }
}

Use case: Splitting large datasets (satellite tiles).

4. Linked Data (RDF)

{
  "Header": {
    "@context": "https://schema.org/",
    "@type": "AgricultureEvent"
  }
}

Use case: Semantic web, knowledge graphs.

5. Smart Contracts

{
  "Footer": {
    "blockchain": {
      "network": "ethereum",
      "tx_hash": "0x...",
      "block": 12345
    }
  }
}

Use case: Immutable audit trail on-chain.

Ecosystem Development

1. BITE Libraries

  • Python: pip install bite-ag
  • JavaScript: npm install bite-ag
  • Java: Maven package
  • Go: go get github.com/agstack/bite-go

2. BITE Validators

  • Online tool: https://bite-validator.agstack.org
  • CLI: bite-cli validate myfile.bite

3. BITE Type Registry

  • Community-maintained catalog
  • JSON Schema definitions
  • Examples and best practices

4. BITE Converters

  • GeoJSON ↔ BITE
  • ADAPT ↔ BITE
  • SensorThings ↔ BITE

5. BITE Storage (PANCAKE)

  • Reference implementation (PostgreSQL + pgvector)
  • Cloud-hosted (AWS, GCP, Azure)
  • Edge devices (Raspberry Pi, Arduino)

Governance Model

Proposed: Open-source, community-driven (like Python PEPs, Rust RFCs).

Process:

  1. Proposal: Submit BITE Enhancement Proposal (BEP)
  2. Discussion: Community feedback (GitHub Issues, mailing list)
  3. Prototype: Reference implementation
  4. Vote: Steering committee (elected from contributors)
  5. Adoption: Released as new version

Steering Committee:

  • 5-7 members
  • Elected annually
  • Representation: farmers, vendors, researchers, NGOs

Principles:

  • Transparency: All discussions public
  • Inclusivity: Global participation (translations, accessibility)
  • Meritocracy: Contributions matter, not credentials
  • Sustainability: Funded by grants, sponsorships (no vendor control)

Conclusion

BITE is more than a data format—it’s a vision for agricultural data interoperability. By learning from the success of IP packets, JSON, and open standards, BITE provides a simple, extensible, and secure foundation for the next generation of agricultural technology.

Key Takeaways:

  1. Simplicity: 3 sections (Header, Body, Footer) anyone can understand
  2. Universality: Works for any agricultural data type
  3. Immutability: Cryptographic integrity enables trust
  4. Extensibility: Evolves without breaking existing systems
  5. Openness: No vendor control, no licensing fees

Next Steps:

  • Implement BITE in your ag-tech product
  • Contribute to the BITE specification
  • Join the community (mailing list, GitHub, workshops)

The future of agricultural data is open, interoperable, and BITE-sized. 🌱


Document Status: Living specification (v1.0 POC)
Last Updated: November 2024
Feedback: https://github.com/agstack/bite/issues
License: CC BY 4.0 (Creative Commons Attribution)