Skip to content

Versioning

Protocol versioning and per-function versioning


Mesh has two independent versioning systems:

TypeScopeChanges
Protocol versionEnvelope structure, error format, core semanticsRare, coordinated
Function versionBusiness logic, arguments, return shapePer-team, independent

The protocol field specifies the Mesh protocol version using Semantic Versioning:

<major>.<minor>.<patch>

Examples:

  • 0.1.0 — Draft/experimental
  • 1.0.0 — First stable release
  • 1.2.0 — Minor additions
  • 2.0.0 — Breaking changes

Major version — Breaking changes:

  • Removing or renaming required fields
  • Changing field types
  • Altering error semantics
  • Modifying core behavior

Minor version — Backwards-compatible additions:

  • New optional fields
  • New error codes
  • New optional features

Patch version — Backwards-compatible fixes:

  • Clarifications to specification text
  • Documentation corrections
  • No behavioral changes

Servers MUST:

  • Reject requests with unsupported major versions
  • Accept requests with supported major version, any minor version
  • Return INVALID_PROTOCOL_VERSION for unsupported versions

Clients SHOULD:

  • Send the protocol version they were built for
  • Handle responses from any minor version of the same major
// Request with unsupported version
{
"protocol": { "name": "mesh", "version": "99.0.0" },
"id": "req_123",
"call": { ... }
}
// Response
{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": "req_123",
"result": null,
"errors": [{
"code": "INVALID_PROTOCOL_VERSION",
"message": "Unsupported protocol version: 99.0.0",
"retryable": false,
"details": {
"requested": "99.0.0",
"supported": ["0.1.0"]
}
}]
}

Each function is versioned independently. This differs from REST’s monolithic API versioning:

REST (monolithic):

/api/v1/orders ← Forced to v1 because users changed
/api/v1/users ← The actual change
/api/v1/products ← Unchanged, but still "v1"

Mesh (per-function):

orders.create@1 ← Untouched
users.get@2 ← Evolved independently
products.list@1 ← Untouched
  • Teams evolve functions without coordinating releases
  • Consumers upgrade function-by-function
  • No version sprawl where everything is “v7” but 90% is unchanged
  • Deprecation is surgical: sunset orders.create@1, not “all of v1”

The version field in the call object specifies function version:

{
"call": {
"function": "orders.create",
"version": "2",
"arguments": { ... }
}
}

Type: String. MUST use integer strings: "1", "2", "3"

When a request omits the version field, servers SHOULD route to the latest stable version:

{
"call": {
"function": "orders.create",
"arguments": { ... }
}
}

Resolution rules:

  1. Find all versions with status: "stable" (exclude beta, removed)
  2. Select the highest version number
  3. If no stable versions exist, return VERSION_NOT_FOUND

Example: If a function has versions 1 (stable, deprecated), 2 (stable), and 3 (beta):

  • Omitting version routes to 2 (latest stable)
  • Explicitly requesting 3 routes to beta

Recommendations:

  • Clients SHOULD specify explicit versions in production code for predictability
  • Servers SHOULD support version omission for exploration and development
  • Version discovery: Use mesh.describe to find available versions and their status
// Discover versions before calling
{
"call": {
"function": "mesh.describe",
"version": "1",
"arguments": {
"function": "orders.create"
}
}
}

Implementations MUST increment function version for:

  • Removing or renaming arguments
  • Changing argument types
  • Changing return value structure
  • Altering function behavior

Implementations SHOULD NOT increment for:

  • Adding optional arguments with defaults
  • Adding fields to return value
  • Bug fixes that don’t change the contract

Servers MAY support multiple versions simultaneously:

orders.create@1 ← Legacy, deprecated
orders.create@2 ← Current, recommended
orders.create@3 ← Beta, testing

To deprecate a function version:

  1. Document deprecation with timeline
  2. MAY return warning in meta:
    {
    "meta": {
    "deprecated": {
    "reason": "Use version 2",
    "sunset": "2025-06-01"
    }
    }
    }
  3. Eventually return VERSION_NOT_FOUND

When a client requests an unknown version:

{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": "req_123",
"result": null,
"errors": [{
"code": "VERSION_NOT_FOUND",
"message": "Version 5 not found for function orders.create",
"retryable": false,
"details": {
"function": "orders.create",
"requested_version": "5",
"available_versions": ["1", "2", "3"]
}
}]
}

Clients MAY discover available versions using system functions:

// Request
{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": "req_discover",
"call": {
"function": "mesh.describe",
"version": "1",
"arguments": {
"function": "orders.create"
}
}
}
// Response
{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": "req_discover",
"result": {
"function": "orders.create",
"versions": [
{
"version": "1",
"status": "stable",
"deprecated": {
"reason": "Use version 2",
"sunset": "2025-06-01"
}
},
{
"version": "2",
"status": "stable"
},
{
"version": "3",
"status": "beta"
}
]
}
}

See System Functions for details.


{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": "req_v1",
"call": {
"function": "users.get",
"version": "1",
"arguments": {
"user_id": 42
}
}
}
{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": "req_v1",
"result": {
"id": 42,
"name": "Alice",
"email": "alice@example.com"
}
}
{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": "req_v2",
"call": {
"function": "users.get",
"version": "2",
"arguments": {
"identifier": {
"type": "id",
"value": 42
}
}
}
}
{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": "req_v2",
"result": {
"user": {
"id": 42,
"profile": {
"name": "Alice",
"email": "alice@example.com"
},
"metadata": {
"created_at": "2024-01-01T00:00:00Z"
}
}
}
}