geth/docs/compatibility.md

5.5 KiB

Compatibility Policy

geth has not shipped a stable release yet. Until the first deployment tag, contracts may still change, but changes must be deliberate, documented in this file, and reflected in docs/production-readiness-roadmap.md.

After the first deployment tag, compatibility is defined by the surfaces below.

CLI Commands

Commands documented in README.md are the automation-facing CLI surface. Existing command names, positional argument meanings, flag names, and default behavior should remain compatible within a major release.

Backward-compatible CLI changes include:

  • Adding optional flags with defaults that preserve previous behavior.
  • Adding new subcommands.
  • Adding fields to human-readable output when --json is not requested.
  • Improving error messages while preserving the error kind and recovery path.

Breaking CLI changes include:

  • Renaming or removing a documented command or flag.
  • Changing the meaning of an existing argument or default.
  • Changing exit success for the same observable result.
  • Requiring a daemon, network path, privileged service, or host mutation where a documented local command did not previously require one.

Prototype commands must be called out in README.md or feature docs with an explicit migration expectation before scripts depend on them.

JSON Output

--json and --jsonl are mutually exclusive machine-output modes:

  • --json writes exactly one pretty-printed JSON document for the command result. Embedded newlines are formatting whitespace, so consumers should parse the complete stdout stream as one document.
  • --jsonl writes each result as one compact JSON object followed by a newline. Current non-streaming commands emit one physical line; a future streaming command may emit multiple records, one complete object per line.

Machine output does not mix operator prose into stdout. Successful direct CLI workflows and local-control responses include a stable top-level type field; type and action names use lowercase kebab-case. For stable commands, the top-level response variant name and existing field names keep their meaning within a major release.

Execution failures in --json or --jsonl mode return a stable JSON error object on stdout with type: "error", a machine-readable code, a human message, full detail, and an optional hint derived from operator recovery output, and exit nonzero. The envelope is pretty-printed in --json mode and occupies exactly one physical line in --jsonl mode. Common codes include daemon_unavailable, unauthorized, peer_not_found, resource_not_found, invalid_input, and command_failed.

Backward-compatible JSON changes include:

  • Adding nullable or optional fields.
  • Adding enum variants for new commands or new explicit states.
  • Adding elements to arrays where ordering was not documented as stable.

Breaking JSON changes include:

  • Removing or renaming fields.
  • Changing field type, unit, or identifier format.
  • Reusing a field for a different semantic meaning.
  • Changing a documented stable ordering.

Automation should ignore unknown fields. Tests that assert JSON fixtures should cover the subset intended as stable rather than every incidental field.

Local Control JSONL

The local daemon control endpoint uses newline-delimited JSON request and response messages from geth-control. Its carrier is a Unix socket on Linux/macOS and a per-home Windows named pipe. This protocol is local-only and not a remote trust boundary, but local automation may still rely on it.

Stable local-control variants follow the same compatibility rules as --json output. Unknown variants, malformed JSON, and messages without a trailing newline must fail without daemon state mutation. Local control must not become a remote transport.

Peer Wire Protocols

Node-to-node geth communication is Iroh-only. Peer protocols are grouped by explicit ALPNs:

  • /geth/control/1 for authenticated peer-control requests.
  • /geth/pipe/1 for authorized pipe byte streams.
  • /geth/overlay/1 for authorized overlay packets.

Within a major release, existing peer request and response variants should keep their field names, field meanings, nonce behavior, peer-card validation, and endpoint-binding validation. New variants must use explicit capabilities and resource IDs. Discovery data remains untrusted and never grants capabilities.

Breaking peer-wire changes require a version bump, migration note, and tests showing old messages fail closed rather than being interpreted as a different operation.

SQLite Metadata

The metadata database is product state. Schema changes must be represented as ordered migrations before deployment. Fresh opens and repeated opens must be idempotent, and multi-table writes that represent one logical operation should commit atomically.

Stable table columns may be added through migrations. Removing or repurposing a column requires a migration note and a recovery path. SQLite state is an implementation detail for scripts unless a table is explicitly documented as stable.

Signed Operations

Signed keychain, auth, SSH certificate, and SSH revocation payloads are durable compatibility surfaces. Signed payloads must use deterministic canonical encoding with explicit namespaces and versions. A verifier must never treat arbitrary JSON serialization as signed canonical data.

Changing a signed payload shape, namespace, or verification rule requires a new versioned namespace or a documented migration that preserves validation of existing signed history.