BITE: Bidirectional Interchange Transport Envelope
agstack/pancake
·
docs/BITE.mdEdits 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
- Overview
- The Problem: Agricultural Data Fragmentation
- Design Philosophy
- The IP Packet Metaphor
- Three-Section Architecture
- Why JSON?
- Polyglot Data Strategy
- Design Rationale
- Security & Immutability
- Schema Evolution
- Comparison with Alternatives
- Implementation Guidelines
- 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 Component | BITE Equivalent | Purpose |
|---|---|---|
| Source Address | Header.source | Who/what created this data? |
| Destination | (implicit: consumer) | Who should process this? |
| Protocol | Header.type | How to interpret the payload? |
| Timestamp | Header.timestamp | When was this data captured? |
| Payload | Body | The actual agricultural data |
| Checksum | Footer.hash | Is 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
geoidfor 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.
- Header: Metadata (unchanging facts about the data)
- Body: Content (the actual agricultural observations/measurements)
- 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.jsonfiles (like JSON Schema) - Reason: Adds complexity, requires file management
- Solution: Type-specific documentation (e.g., “observation BITE spec”)
Section 3: Footer
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
Bodywith 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:
- JSON (JavaScript Object Notation)
- XML (eXtensible Markup Language)
- Protocol Buffers (Google’s binary format)
- MessagePack (binary JSON alternative)
- CBOR (Concise Binary Object Representation)
- 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_readingstable - Disease observations →
observationstable - Satellite imagery → External files (not in DB)
- Lab results →
lab_resultstable
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
observationstable (id, geoid, timestamp)observation_texttable (observation_id, note)observation_photostable (observation_id, photo_url)observation_geometrytable (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:
- Alter
lab_resultstable (add columns) - Update application code
- Migrate existing records (backfill NULLs)
- Update documentation
- Retrain users
BITE approach:
- New BITE includes new fields:
{
"Body": {
"nitrogen_ppm": 45,
"phosphorus_ppm": 12,
"zinc_ppm": 2.3, // New field
"boron_ppm": 0.8 // New field
}
}
- Old parsers ignore new fields
- 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
- Export observations → CSV
- Export sensor data → CSV
- Export weather data → CSV
- Join on (geoid, timestamp) → Nightmare
- Handle missing data, schema mismatches
BITE approach: All data already in JSON
- Filter BITEs by type and date range
- Extract features from BITE Bodies
- 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:
- UUID v4 (Random, 128-bit)
- UUID v7 (Time-ordered, 128-bit)
- ULID (Time-ordered, lexicographically sortable)
- 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:
- Lat/Lon coordinates (WGS84)
- WKT (Well-Known Text) in header
- GeoJSON geometry in header
- 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:
- MD5 (128-bit, fast)
- SHA-1 (160-bit, deprecated)
- SHA-256 (256-bit, standard)
- SHA-512 (512-bit, more secure)
- 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:
- Unix epoch (seconds since 1970-01-01, e.g.,
1698854400) - ISO 8601 (e.g.,
2024-11-01T10:30:00Z) - RFC 3339 (ISO 8601 profile)
Decision: ISO 8601 (UTC)
Rationale:
- Human-readable:
2024-11-01T10:30:00Zvs1698854400 - Timezone clarity:
Zsuffix = 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: Uniquenessgeoid: Locationtimestamp: Timetype: 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 mandatoryreferences: Only if relationships exist
Benefit: Low barrier to entry (minimal BITEs are valid).
Security & Immutability
Threat Model
Agricultural data faces unique security challenges:
- Tampering: Changing yield data to inflate insurance claims
- Forgery: Creating fake organic certification records
- Deletion: Removing evidence of pesticide misuse
- Replay: Re-submitting old data to trigger duplicate payments
- Privacy: Exposing farm locations to competitors
Immutability by Design
Principle: Once created, a BITE never changes.
Implementation:
- Hash in footer: Any modification invalidates the hash
- Timestamp in header: Establishes creation time (cannot backdate)
- 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:
- Observation BITE: Disease detected
- Recommendation BITE: Agronomist suggests pesticide (references observation)
- Purchase BITE: Farmer buys product (references recommendation)
- Application BITE: Spraying performed (references purchase)
- 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_ppmunits (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
| Feature | BITE | GeoJSON | SensorThings | ADAPT | Vendor 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:
- Proposal: Submit BITE Enhancement Proposal (BEP)
- Discussion: Community feedback (GitHub Issues, mailing list)
- Prototype: Reference implementation
- Vote: Steering committee (elected from contributors)
- 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:
- Simplicity: 3 sections (Header, Body, Footer) anyone can understand
- Universality: Works for any agricultural data type
- Immutability: Cryptographic integrity enables trust
- Extensibility: Evolves without breaking existing systems
- 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)