OptixLog Docs
Concepts

Node Types and Schemas

Per-project node schemas, typed fields, the many-to-many node-to-project mapping, and the universal fields every node has.

What is a node type?

A node type defines the shape of a class of workflow nodes in your organization. Each type has a name (e.g. SimulationNode, MeasurementNode, LayoutNode) and a list of typed fields. The OptixLog backend validates payloads against this schema on ingest.

Node types are defined at the organization level and associated with projects through a many-to-many mapping: a single node type can appear in multiple projects, and a project can include many node types.

Universal fields

Every node type gets two universal fields regardless of its per-type schema:

FieldTypeMeaning
namestrHuman-readable name for the node. Always required.
attached_fileslist[str]File references or file IDs to attach to the node.

These fields are present in the generated dataclass alongside any per-type custom fields.

Per-type custom fields

Each node type defines additional fields with a name, a type, a required flag, and optionally a default value or an enum constraint.

Field types

Schema typePython annotation
stringstr
integerint
numberfloat
booleanbool
json / objectMapping[str, JSONValue]
array<T>Sequence[T] (recursive)
enumLiteral[members...]

array<T> is parsed recursively: array<number> becomes Sequence[float], array<string> becomes Sequence[str].

Required and default rules

Given a field with base type T:

  • required: true → name: T (must be supplied at construction).
  • required: false with a default → name: T = <default>.
  • required: false without a default → name: T | None = None.

Example generated dataclass (from the sample schema):

@dataclass(frozen=True, kw_only=True)
class SimulationNode:
    # Universal fields
    name: str
    attached_files: list[str] = field(default_factory=list)

    # Per-type custom fields
    config: Mapping[str, JSONValue]          # required, type=json
    solver: Literal["fdtd", "eme", "varfdtd"] # required, type=enum
    wavelength_nm: float = 1550.0             # optional with default, type=number
    other_param: str | None = None            # optional without default, type=string

    def to_payload(self) -> dict[str, JSONValue]: ...

Many-to-many mapping

The node-to-project association is stored in the schema's project_node_types section:

{
  "project_node_types": {
    "proj_grating_7f3a": ["SimulationNode", "MeasurementNode"],
    "proj_modulator_22b1": ["SimulationNode", "LayoutNode"]
  }
}

Here SimulationNode is shared by both projects — it is emitted once as a single dataclass that appears in both projects' union aliases. MeasurementNode is only in the first project; LayoutNode is only in the second.

Association is derived from the schema, not optixlog.toml

The node-to-project mapping is never stored in optixlog.toml. It is fetched from the server (or fixture) at optixlog generate time and baked into the generated bindings.

How schemas are fetched

optixlog generate fetches the schema from the server's v0.codegen.schema endpoint (or reads it from a --fixture file). It parses the schema with contract.py, maps field types with typemap.py, and emits the Python module with emit.py.

The output is byte-deterministic: identical schema input always produces identical Python output. You can safely commit optixlog_gen.py and diff it.

Accessing schema data at runtime

You do not typically need to access schema metadata at runtime — the generated bindings encapsulate it. If you do need it, use the Management API's workflow node labels collection:

for label in project.workflow_nodes.labels.iter():
    print(label.id, label.name)

See Node Types and Schema reference for the full model definitions.

On this page