Skip to content

The graph model

A hydrate project is a graph. This page defines its structure, which both the CLI and the v1 API operate on. For the formal, field-by-field reference — the canonical h2o format, its delta grammar, and the invariants the server enforces — see The h2o spec.

A node is a vertex in the graph. There are five kinds:

  • Behavior: a unit of work. The default kind.
  • Boundary: a node that contains other nodes. Boundaries nest, forming a tree of containment over the graph.
  • State: a place shared, mutable data lives. Its input ports are writes and its output ports are reads.
  • I/O: the program’s edge with its environment — where data enters or leaves (standard input, arguments, the caller; standard output, an exit code, a response). An I/O node carries exactly one typed port on one side: an output port makes it a source (data flowing into the program), an input port a sink (data flowing out).
  • Interface: the program’s edge with the callers that depend on it — a piece of the public API surface, such as a function, type, or parameter that outside code links against. Where I/O is the program’s edge with its environment, interface is its edge with its consumers.

A behavior or boundary may be marked external, representing a system outside the graph. External nodes carry an external-kind label (for example, rest-api) and may name a protocol (for example, gRPC).

See Node types in the Quickstart for the shapes and when to reach for each.

Every node has a free-text description. It may also carry:

  • Constraints: a list of free-text strings.
  • Verifications: a list of free-text strings.
  • Aliases: alternate public names the node is also known by.
  • Doc URL: an optional documentation link.

Boundary nodes additionally have a user-kind label, a path prefix, and an optional language — the codegen language the boundary declares, which the nodes inside it inherit.

A node exposes ports. Every port has a name and a type, written name:type; the type is always required. There are three channels:

  • Inputs: ports a node consumes.
  • Outputs: ports a node produces.
  • Config: a third channel that is not connected by edges.

A type is a nominal string, such as HotDog, ShortCode, or Rating. Two ports are compatible when their type strings are equal.

An edge connects an output port to an input port. Config ports cannot be edge endpoints.

The two ports should have the same type, but the type is a hint rather than a contract: the server accepts an edge whose endpoints disagree. It reports the mismatch as a type_mismatch coherence finding, which hydrate validate surfaces.

A type mismatch and an input left unwired are reported rather than enforced: neither prevents a commit, because a graph under construction is legitimately incoherent. hydrate validate is the check you run when you want the answer, and the CLI reference covers what it does and does not gate.

What the server does refuse is a graph it cannot represent at all: an edge to a port that does not exist, a name that collides with a sibling, a cycle in the containment tree.

Nodes and ports are addressed by dotted path, scoped by the boundaries that contain them.

  • Api.Rater is the node Rater inside the boundary Api.
  • Api.Rater.score is the port score on that node.
  • Rater is a top-level node with no enclosing boundary.

A node’s name is unique within its parent scope.

A project has a main branch and any number of working branches. You create a working branch with hydrate fork and make edits on it; main is not edited directly. The graph is stored on the server, and the CLI keeps a local view that hydrate pull refreshes.