Veydrin Community Network

VCN Protocol Spec v1.0

The technical standard governing how VCN-enabled applications discover each other, sign and route data packets, synchronize state across devices, and operate without any central server. Built for the world — starting where infrastructure is scarce.

VCN-PROTOCOL v1.0 AGPL-3.0 STABLE
§ 1

Overview & Design Philosophy

The VCN Protocol governs the technical layer of the Veydrin Community Network — how devices running VCN-enabled applications find each other, exchange signed data packets, and form a regional mesh without any server that the Veydrin Order operates. The one external infrastructure dependency is the Tier 3 bootstrap endpoint (Cloudflare Workers + KV), used only during early regional adoption before the peer mesh fills in. Its source is published and it can be self-hosted by any community.

The zero-infrastructure principle. A network that works at a market with no signal is a network that serves people who have been excluded from every other network. The primary sync mechanism requires no internet, no data plan, no server, and no configuration. Two phones with a VCN-enabled app installed are sufficient to form a network.

The protocol has two concerns addressed in separate sections:

This spec governs software behavior. The organizational governance layer — the nine rules, node federation, trust badge, and enforcement framework — is a separate document: see the VCN Community Spec.

Open participation. Participation in the VCN mesh requires no organizational membership. Any application implementing this protocol is a valid node. Installing a VCN-enabled app makes a device part of the network. No Veydrin approval, no account, no registration.

Relationship to VODS

All VCN member applications must implement VODS v1.0 as the data interoperability baseline. VODS defines the document envelope, privacy floor, and export schema that VCN packets carry as payloads. The VCN Protocol defines how those packets move. They are siblings — neither depends on the other, but both are required for full compliance.

§ 2

Node Identity

Key generation

On first launch of any VCN-enabled application, the vcn_node package generates an Ed25519 key pair locally on the device. No network connection is required or used. No server is involved at any point.

Node ID format

The node ID is the Base64-URL-encoded Ed25519 public key, 44 characters in length without padding. This is the key itself — no hash, no truncation, no information loss.

node_id  ::=  base64url( ed25519_public_key )
example  ::=  "X9kLm2vQpR7wN4yZ1cFbDhEaG8sT0uIo3jK6xV5nMeA"
Self-certifying identity. No central authority can issue, vouch for, or revoke a node identity. Any device can generate a valid node ID. Trust is established through packet signing verification, not through a registry.

Packet signing

Every packet emitted by a node is signed with that node's Ed25519 private key. The signature covers the canonical JSON serialization of the packet body (all fields except the signature field itself). Recipients verify the signature before processing any packet. Packets that fail signature verification are silently discarded — no error is surfaced to the user.

Identity export and migration

A user may export their node identity to migrate to a new device or back up their keys. The export format is a JSON file encrypted with a user-chosen passphrase using AES-256-GCM:

{
  "vcn_identity_version": "1.0",
  "node_id":              "Base64-URL public key",
  "private_key_enc":      "AES-256-GCM encrypted private key (Base64-URL)",
  "nonce":               "AES nonce (Base64-URL)",
  "created_at":          unix_ms
}

The passphrase is never stored or transmitted. Import requires the correct passphrase. The existing key pair is replaced on import — apps must warn users before overwriting an existing identity.

Registered source_app identifiers

The source_app envelope field identifies the originating application. The following identifiers are registered for v1.0. Third-party VCN implementations must use a distinct identifier not listed here:

source_app valueApplication
eluvorimEluvorim — Community Ledger
apiaraApiara — Beekeeping Management
chinaraChinara — Soilless Cultivation
numakaNumaka — Permaculture
arcaloxArcalox — Personal Resilience Logging
vcnVCN infrastructure packets (bulletin, update)

source_app is set at compile time as a package constant in each consuming app's vcn_node configuration. It is not user-configurable.

§ 3

Packet Format

Envelope schema

Every VCN packet, regardless of originating app or content type, uses the following envelope. The payload field is app-specific; its schema is determined by packet_type (see §4).

{
  "vcn_version":   "1.0",               // REQUIRED — protocol version
  "source_app":    "eluvorim",          // REQUIRED — registered app ID
  "source_node":   "X9kLm2vQ...",       // REQUIRED — Base64-URL Ed25519 public key
  "packet_id":     "uuid-v4",           // REQUIRED — globally unique, UUID v4
  "packet_type":   "goods",             // REQUIRED — registered type string
  "area_tag":      "ph_cebu",           // REQUIRED — routing region identifier
  "timestamp":     1741478400000,       // REQUIRED — Unix milliseconds UTC
  "ttl":           72,                  // REQUIRED — hours, decremented per hop
  "location":      { /* see §5 */ },      // OPTIONAL — geohash location schema
  "payload":       { /* type-specific */ }, // REQUIRED — app data
  "deep_link":     "veydrin://...",     // OPTIONAL — in-app navigation URI
  "signature":     "Base64-URL..."      // REQUIRED — Ed25519 signature
}

Canonical serialization for signing

The signature is computed over the canonical form of the packet. Canonical form is defined as:

canonical_input  ::=  json_compact_sorted( packet \ { signature } )
signature        ::=  base64url( ed25519_sign( private_key, canonical_input ) )

Deduplication

Every node maintains a seen_packets set of packet_id values. Before processing or forwarding any received packet, the node checks this set. If the packet_id is present, the packet is silently discarded. If not, the packet_id is added and processing proceeds. The set is pruned of entries older than max_ttl (720 hours) to prevent unbounded growth.

TTL propagation

When a node forwards a packet, it decrements the ttl field by 1. The signature is not recomputed on forward — the original emitter's signature remains intact. The TTL decrement is the only permitted modification to a packet in transit. At ttl = 0 the packet is not forwarded further.

TTL integrity is advisory. TTL can be decremented in transit and cannot be verified against the original signature. This is an accepted trade-off. Treat TTL as a routing hint, not a security property.

Default TTL values

Packet typeDefault TTL (hours)Max TTL (hours)
goods / services / skills / need / groupbuy72720
surplus720 (30 days)2160 (90 days)
harvest / hive / observation168 (7 days)720
bulletin / update168720

All TTL values are user-configurable within the stated max. Apps should expose TTL as a simple duration selector, not a raw number.

surplus — TTL vs expires field. The surplus payload includes an expires unix timestamp set by the originator. TTL governs how many hops a packet will travel before it stops propagating. expires governs whether the listing is still active at the application layer. Both must be checked: a packet that has not expired but has reached TTL 0 stops propagating; a packet that is still propagating but whose expires has passed must not be displayed as an active listing. Receiving apps discard surplus packets whose expires timestamp is in the past.
§ 4

Packet Type Map & Payload Schemas

Type map — registered types v1.0

packet_typeHandler appDeep link scheme
goodsEluvorimveydrin://eluvorim/listing/{id}
servicesEluvorimveydrin://eluvorim/listing/{id}
skillsEluvorimveydrin://eluvorim/listing/{id}
needEluvorimveydrin://eluvorim/need/{id}
groupbuyEluvorimveydrin://eluvorim/groupbuy/{id}
surplusEluvorim / Numaka / Apiara / Chinaraveydrin://{app}/surplus/{id}
harvestChinaraveydrin://chinara/harvest/{id}
hiveApiaraveydrin://apiara/hive/{id}
observationNumakaveydrin://numaka/observation/{id}
bulletinVCN (any app)veydrin://vcn/bulletin/{id}
updateVCN (any app)veydrin://vcn/update/{id}

Area tag format

Area tags are human-readable routing region identifiers. Format rules:

Area tag filtering

The type map lookup is the first delivery filter. The second is the area tag. Even if packet_type maps to an installed handler, the packet is only delivered if the receiving node's configured area_tag matches the packet's area_tag field. Matching is exact string equality in v1.0. No wildcards. No hierarchical prefix matching. See §6 for the forwarding and sync logic that governs how packets move between nodes before delivery.

Deep link — handler not installed

When a Veydrin app fires a deep link and the target app is not installed, the firing app must catch the failed intent and present a non-intrusive prompt offering the target app's Codeberg release page or F-Droid listing. It must not error, crash, or surface a system-level "no app" dialog.

Payload schemas — normative v1.0

All string fields are UTF-8. Fields marked optional may be omitted entirely — receiving apps must tolerate their absence gracefully.

goods / services / skills

{ "title": string, "value": number|null, "notes": string|null,
  "currency": string|null, "image_hash": string|null }

need

{ "title": string, "notes": string|null, "offer": string|null }

groupbuy

{ "title": string, "min_count": integer, "unit_value": number|null,
  "deadline": unix_ms|null, "notes": string|null }

surplus (shared across apps)

{ "species": string, "quantity": string, "unit": string|null,
  "contact": string, "expires": unix_ms, "intent": "trade"|"sell"|"give"|"barter"|"ask",
  "notes": string|null, "image_hash": string|null }

harvest (Chinara)

{ "crop": string, "quantity": number, "unit": string,
  "value": number|null, "harvest_date": unix_ms, "notes": string|null,
  "image_hash": string|null }

hive (Apiara)

{ "product": string, "quantity": number, "unit": string,
  "value": number|null, "notes": string|null, "ohds_ref": string|null }

observation (Numaka)

{ "guild_type": string, "layer": string, "succession_state": string|null,
  "notes": string|null, "image_hash": string|null, "opds_ref": string|null }

bulletin / update

{ "title": string, "body": string,
  "target_app": string|null, "version": string|null, "url": string|null }
§ 5

Location Schema & Privacy Model

Location is a first-class field in the VCN packet format. Every app that attaches location to a packet uses this schema. Exact GPS coordinates never appear in any VCN packet under any circumstances — the geohash is the privacy floor.

What a geohash is

A geohash converts GPS coordinates into a short text code. Longer codes describe smaller, more precise areas. The code for any location is a prefix of the code for any smaller area it contains — enabling natural geographic hierarchy without central configuration.

PrecisionCode lengthApprox. areaUse in VCN
Province / large region3 chars~150 km radiusRural / sparse network fallback
Metro / greater area4 chars~40 km radiusDefault routing key
District / neighborhood5 chars~5 km radiusDense urban opt-in
Street level6 chars~1 kmNot used in routing

Location packet schema

{
  "location": {
    "geohash":           "w3gv",     // routing key at chosen precision
    "geohash_precision": 4,          // number of chars used
    "coords_available":  false       // exact coords never in packet unless explicit opt-in
  }
}

Privacy rules — normative

Edge cell handling

Geohash cells have hard edges. A device near a cell boundary should store and forward packets for all 8 neighboring cells automatically, in addition to its own cell. This prevents listings from being invisible to users just across a geohash boundary. No user configuration required — this is automatic behavior in vcn_node.

OSM rendering

All VCN-enabled apps that display location use OpenStreetMap tiles via flutter_map. Offline tile caching is required — users must be able to download their region's tiles for use without connectivity. The app-level community map is an emergent property of VCN packets with location fields: each app registers which packet types it renders as map pins. The shared OSM layer makes the mesh visible.

§ 6

Sync Transport Tiers

All six tiers use the same packet format, signing, verification, deduplication, and type-map routing logic. They are complementary, not alternatives. A device uses all available tiers simultaneously. Tiers are listed in priority order for the target population.

TLS baseline

All VCN communication over any network transport (Tiers 2, 3, 5, and 6) must use TLS 1.3 minimum. Certificate validation is enforced. No fallback to plaintext under any circumstances. Tier 1 (physical proximity via Android Nearby Connections API) and Tier 4 (QR) do not use TLS — they are local channel transports that operate entirely off-network.

Forward vs deliver

Forward ≠ Deliver. A node must forward packets it cannot deliver locally. A phone running only Eluvorim will receive Apiara hive packets — it has no delivery handler, but it must still relay them to peers. Discarding at delivery is correct. Discarding at forwarding is a protocol violation that silently breaks the mesh.
// FORWARDING — happens first, regardless of local handler
forward_packet(p):
  if p.ttl <= 0: drop
  if p.packet_id in seen_packets: drop
  seen_packets.add(p.packet_id)
  deliver_packet(p)         // deliver BEFORE decrement — handler sees original TTL
  p.ttl -= 1
  if p.ttl > 0:
    queue_for_all_transports(p)

// DELIVERY — only for packets we can handle locally
deliver_packet(p):
  handler = type_map[p.packet_type]
  if handler == null: return       // no handler — not an error, packet still forwarded
  if p.area_tag != local_area_tag: return
  if handler_app_not_installed: return
  route_to(handler, p)

Sync negotiation — P2P Bloom filter handshake (Tiers 1, 2, 4)

When two nodes connect via a peer-to-peer tier (physical proximity, dedicated node, or QR), they negotiate what to exchange using a Bloom filter handshake. This avoids retransmitting packets the other node already has. This applies to Tiers 1, 2, and 4 only — Tier 3 (Cloudflare Workers) uses incremental HTTP polling via ?since=unix_ms and does not use this handshake.

// Both sides exchange their seen_packets Bloom filters
A → B: SYNC_HELLO { area_tag, bloom_filter(seen_packet_ids), node_id }
B → A: SYNC_HELLO { area_tag, bloom_filter(seen_packet_ids), node_id }

// Each side sends only packets the other node probably lacks
A → B: SYNC_PACKETS [ packets not in B's bloom filter ]
B → A: SYNC_PACKETS [ packets not in A's bloom filter ]

// Session closes
A ↔ B: SYNC_DONE

Bloom filter parameters: 10,000 capacity, 1% false positive rate. False positives cause a missed sync for that packet — it will arrive in the next session. No data loss, only slight delay. Filter is rebuilt from the live seen_packets set on each session open.

Physical proximity first. For the communities VCN is built to serve — markets, neighborhoods, community gatherings across the global south — people are regularly physically near each other. Tier 1 is not a fallback. It is the primary sync event.
Tier 1 — Physical Proximity (BLE + WiFi Direct + mDNS) PRIMARY

Two nodes in physical proximity exchange packets directly, device to device. No internet, no infrastructure, no configuration. Uses Android Nearby Connections API (Bluetooth and WiFi Direct transport) via the nearby_connections Flutter package. mDNS handles discovery on any shared local network (community WiFi, home router, café hotspot).

Packet size constraint: Core text packets must not exceed 1 KB. Images are never included in proximity sync packets (see §8).

Discovery: Nodes advertise their area_tag as the service ID. A node only initiates connection to peers advertising a matching area tag, preventing cross-community leakage at the transport layer.

Session lifecycle: Sessions are opportunistic and short-lived. A session opens when two compatible nodes are discovered, exchanges the outbound packet queue, then closes. No persistent connection is maintained.

Tier 2 — Dedicated Community Nodes (DHT) REGIONAL BACKBONE

Any device running a VCN-enabled app may opt in as a dedicated node — a persistent, always-on regional anchor. Dedicated nodes are the community's own infrastructure. No Veydrin server is involved at any step.

Discovery: Dedicated nodes advertise themselves in a distributed hash table (DHT) keyed by their 4-character geohash. Mobile devices query the DHT to find regional dedicated nodes and sync directly to them. The DHT stores only contact addresses (geohash → current IP:port), not content. DHT entries are ephemeral — no persistent record of who was where.

Activation: Available in Settings when charging is detected. User toggle — off by default. Charging + WiFi required. Battery use when active is negligible (WorkManager, Doze-compliant, fires only during sync windows).

Cold start: A dedicated node in a region solves cold start for all new users — new device connects to WiFi, finds the dedicated node in the DHT, syncs, and immediately has the region's packet history. No physical proximity required.

Old device repurposing: Any Android device (including old phones no longer used as primary) can serve as a dedicated node permanently. Plug in, open app, enable Node Mode. The device becomes a regional anchor for its community. No advanced setup. UPnP handles port forwarding automatically on consumer routers.

Storage cap: User-configurable. Suggested default: 500 MB. Devices store packets for their geohash region and all 8 adjacent cells. Auto-trim by age then by geohash distance when cap approaches.

Tier 3 — HTTPS Endpoint (Cloudflare Workers + KV) BOOTSTRAP / FALLBACK

When a device has internet connectivity and no dedicated node is yet available in its region, it pushes packets to and pulls from a lightweight HTTPS endpoint backed by a Cloudflare Worker and Cloudflare KV storage. This is the mechanism that connects the first users in any region before the dedicated node mesh fills in. It is the only component in the VCN stack with an external infrastructure dependency.

Architecture: One Cloudflare Worker handles both reads and writes. Packets are stored in Cloudflare KV keyed by {area_tag}:{packet_id} with a TTL matching the packet's ttl field. KV entries expire automatically — no manual cleanup. Cloudflare's free tier (100k requests/day, 1 GB KV) is sufficient for bootstrap use across all regions.

Push: POST /packets with the full VCN packet JSON as the request body. The Worker verifies the packet's Ed25519 signature against the source_node public key before storing. Packets with invalid or missing signatures are rejected with HTTP 400. No accounts. No API keys. No sessions. The signature is the authentication.

Pull: GET /packets?area_tag={tag}&since={unix_ms} — returns all stored packets for the given area tag with timestamps after since. Response is a JSON array. Incremental — clients track the timestamp of their last successful pull.

Global coverage: One Worker deployment serves all regions — every packet carries an area_tag and pull requests filter by it. Devices in Cebu receive only Cebu packets. No cross-regional leakage.

Rate limiting: The Worker enforces per-source-node rate limits: maximum 60 packet pushes per hour per source_node. Excess requests receive HTTP 429. This is a spam floor, not an access control mechanism.

Role: Once a region has dedicated nodes, Tier 3 becomes pure redundancy. It does not replace Tier 2 — it precedes it in regional adoption and backs it up permanently. The Worker source code is published in the VCN protocols repository under AGPL-3.0 and may be self-hosted by any community that prefers to eliminate this dependency.

Tier 4 — QR Physical Exchange ZERO INFRASTRUCTURE

Two nodes exchange a batch of packets by encoding them as a QR code (or sequence of QR codes) and scanning with the device camera. No internet, no Bluetooth pairing, no network of any kind. Requires only that both devices be in the same physical location.

Encoding: Base64-URL-encoded, gzip-compressed JSON array of packet objects. A single QR at max density (v40, error correction L) holds ~2.9 KB. Threshold for multi-frame: 2 KB (implementation-defined reference default — adjust after device testing on mid-range Android/GrapheneOS).

Multi-frame: { "frame": 1, "total": 3, "batch_id": "uuid", "data": "..." } — frames assembled in any order, duplicates ignored.

Primary use cases: Transaction records, listing discovery at physical markets, identity exchange, sync bootstrap for new devices with no connectivity of any kind.

Tier 5 — Tor Hidden Service OPTIONAL — PRIVACY

VCN-enabled apps may optionally route communication through Tor hidden services (.onion addresses). This tier provides metadata privacy — the network location of the user is not revealed to destination nodes or network observers. Tor transport is opt-in and user-initiated. Apps must not silently route traffic through Tor without explicit user consent.

When to implement: Applications serving populations with elevated surveillance risk — asylum seekers, domestic violence survivors, political dissidents, LGBTQ+ communities in hostile jurisdictions.

Badge requirement: Required for the enhanced VCN trust badge (see Community Spec §7).

Tier 6 — Pluggable Transports (obfs4 / Snowflake) HIGH-THREAT TIER

Apps may implement pluggable transports (obfs4, Snowflake, or equivalent) that disguise VCN traffic as innocuous traffic, evading deep packet inspection and censorship infrastructure. This tier is for deployments where Tor traffic itself is blocked or surveilled. Must be explicitly enabled by the user — not default.

When required: Applications deployed for communities in censored or authoritarian environments, or any app intending to earn the full VCN trust badge (Community Spec §7).

§ 7

Dedicated Node Mode

Dedicated Node Mode is a first-class feature of the VCN protocol — not a hidden setting, not a developer tool. Any user with a spare Android device can strengthen their community's network by enabling it. The device becomes a persistent regional anchor that serves all VCN-enabled apps in the area.

What a dedicated node does

Activation requirements

Node Mode UX

When Node Mode is active, the app collapses to a minimal status screen showing: packets stored, last sync timestamp, peers seen in the last 24 hours, and storage used. No advanced configuration required. UPnP handles port forwarding automatically on consumer routers — no manual setup.

Old device repurposing. A phone that would otherwise collect dust becomes community infrastructure. Plug it in, open the app, enable Node Mode. That is the entire setup. The community builds its own network without knowing that is what they are doing.

Storage management

Auto-trim runs two passes when storage approaches the configured cap (default 500 MB):

The node's own locally-generated packets are never trimmed regardless of age or cap.

Resource usage

StateRAMCPUNetwork
Idle (advertising only)~15–25 MBNegligibleNear zero
Active sync window~30–50 MBLow burstOnly during sync
Long-running dedicated node~30–50 MBWorkManager bursts onlyPeriodic, not persistent
§ 8

Image Transfer

Images are never included in sync packets propagated through any tier. This is a firm protocol-level constraint, not a recommendation. Packet size limits and low-bandwidth target environments make inline images incompatible with reliable sync.

When a listing or record includes an image, the emitting node stores the image locally and includes only the SHA-256 hex digest (image_hash field) in the packet payload. Image transfer is a separate, out-of-band, bilateral, on-demand operation using direct HTTP over whichever transport tier connects the two nodes:

  1. Receiving node sends an image request to the source node: GET /image/{image_hash}. The request is authenticated by an Ed25519-signed request header: X-VCN-Node: {node_id}, X-VCN-Sig: {base64url(sign(image_hash + timestamp))}.
  2. Source node verifies the request signature, retrieves the image from local storage, and transmits it as the response body with Content-Type: image/*.
  3. Receiving node verifies that SHA-256(received bytes) == image_hash.
  4. On match: image is stored and displayed. On mismatch: image is discarded silently.

Transport: Image requests route via the best available tier — Tier 2 (dedicated node relay) preferred for cross-WiFi requests, Tier 1 (direct device-to-device) for proximity requests. Tier 3 (Cloudflare) is not used for image transfer. If no transport path exists to the source node at the time of request, the request is deferred until a sync session with the source node (or a node that has cached the image) becomes available.

The source node may decline any image request with HTTP 403. Declining does not affect packet delivery or listing visibility. Images are optional — listings without images are fully valid.

§ 9

vcn_node Package Interface

The vcn_node shared Flutter/Dart package is the single implementation of this protocol. All Veydrin apps import it. No app reimplements any part of the protocol directly.

Extraction boundary

All protocol logic lives in vcn_node: identity generation, packet format, signing, type map, transport, sync. All app-specific UX stays in the consuming app: community tab layout, listing rendering, partner card display, deep link handling UI. This boundary must be respected — coupling app UX to protocol logic makes extraction impossible.

Development sequence: vcn_node is built and proven in Eluvorim (first implementation) before extraction as a standalone published package. The extraction boundary defined here must be implemented before Eluvorim scaffolding begins.

VcnConfig (initialization — call before start)

VcnConfig.init({
  required String sourceApp,   // registered source_app identifier (compile-time constant)
  required String areaTag,     // community area tag — set once, persisted in Hive CE
})

sourceApp is a compile-time constant defined per app (e.g., "eluvorim"). areaTag is set during onboarding and stored in the app's local Hive CE box. It is also writable later via VcnSync.setAreaTag(). Both values are automatically attached to every emitted packet envelope — VcnEmitter.emit() does not require them as parameters.

VcnEmitter

VcnEmitter.emit({
  required String packetType,
  required Map<String, dynamic> payload,
  Map<String, dynamic>? location,  // geohash schema — see §5
  String? deepLink,
  int ttl = 72,
})

Builds the envelope, attaches source_node and source_app from local identity, signs, adds to the outbound queue for all active transports, returns the packet_id.

VcnReceiver

VcnReceiver.register(
  String packetType,
  Future<void> Function(VcnPacket packet) handler,
)

Registers a callback for a given packet_type. Multiple handlers may be registered for the same type. Called after area tag filtering and deduplication pass.

VcnSync

VcnSync.start()                    // Begin all available transport tiers
VcnSync.stop()                     // Graceful shutdown
VcnSync.setAreaTag(tag)            // Update active area tag filter
VcnSync.enableNodeMode(cap)        // Activate dedicated node (charging + WiFi required)
VcnSync.disableNodeMode()          // Deactivate dedicated node

// Returns current transport tier availability
VcnSyncStatus VcnSync.status() → {
  tier1_active: bool,   // BLE/WiFi Direct advertising
  tier2_active: bool,   // DHT connected to at least one dedicated node
  tier3_active: bool,   // Cloudflare endpoint reachable
  tier4_active: bool,   // always true — QR available when app is open
  tier5_active: bool,   // Tor circuit established
  tier6_active: bool,   // pluggable transport active
  node_mode:    bool,   // dedicated node mode running
  last_sync:    int,    // unix ms of last successful sync on any tier
}
§ 10

Security Properties & Known Limitations

Properties

Known limitations

Sensitive data. VCN packets are designed for community economic and ecological data — listings, harvests, observations, announcements. They are not designed to carry personally identifying information, private messages, or sensitive individual records. Applications handling sensitive data (Refuge-class apps, health records) must encrypt payloads at the application layer and should implement Tiers 5 and 6.
§ 11

Versioning

VCN Protocol Spec uses semantic versioning (MAJOR.MINOR). The vcn_version envelope field is the designated compatibility mechanism.

Change typeVersion bumpCompatibility
Add optional envelope field; add optional payload schema field; add new packet type; clarify existing behavior MINOR Backward compatible. Old nodes ignore unknown fields.
Change required envelope field; change canonical serialization; change signing algorithm; restructure type map; change transport requirement MAJOR Breaking. Nodes receiving a packet with an unsupported major version drop it silently and log locally. No exotic negotiation.
Correct typographic or formatting error without changing behavior Patch note — no version bump No implementation change required.
Version negotiation — closed. A node receiving a packet with an unsupported MAJOR version drops it silently and logs locally. No handshake, no rejection message, no exotic negotiation. The vcn_version field is sufficient. Minor versions are backward compatible by definition — no negotiation needed.

The VCN Protocol Spec and the VCN Community Spec version independently. Protocol changes do not require community governance changes and vice versa.