Omnigraph
Schema

Edges

Edge types define directed relationships between node types, with optional properties and cardinality constraints.

An edge declaration creates a directed relationship between two node types. Each edge type gets its own storage table with three built-in String columns, id, src, and dst. Plus one column per declared property. The endpoints reference the @key value of the source and destination nodes.

Basic syntax

edge WorksAt: Person -> Company

The identifier after edge is the type name. The source and destination are existing node type names separated by ->.

Edge properties

Edges can carry properties just like nodes. Declare them inside braces:

edge Knows: Person -> Person {
    since:      Date?
    confidence: F64?
}

Property rules are the same as for nodes: each property has a name, a type, and optional annotations. Nullable properties use the ? suffix.

Self-referencing edges

An edge can connect a node type to itself:

edge Knows: Person -> Person
edge DependsOn: Task -> Task

This is how you model recursive structures like social graphs, dependency chains, or org hierarchies.

Cardinality constraints

Use @card(...) on the edge header to limit how many edges of a given type can originate from a single source node.

edge ManagedBy: Employee -> Manager @card(1..1)

The range syntax:

PatternMeaning
@card(1..1)Exactly one edge per source node.
@card(0..1)At most one edge per source node.
@card(1..)At least one edge per source node.
@card(0..)No constraint (default).

Cardinality is checked when edges are inserted or deleted. A mutation that would violate the constraint is rejected.

Edge-level annotations

Annotations that describe the edge type itself appear on the edge header, after any @card(...) annotation and before the optional property block:

edge Authored: Actor -> Decision @description("Records who authored a decision and when.") {
    date: Date
}

Edge types can use @description("..."), @instruction("..."), and @rename_from("OldEdgeName") on the header.

Edge body constraints

Edge bodies only allow @unique and @index. @key, @range, and @check are rejected by the parser on edges. Those are node-only constraints. Inside @unique(...) and @index(...) you can also reference the reserved endpoint identifiers src and dst (in addition to declared property names).

edge Follows: Person -> Person {
    @unique(src, dst)
}

Note that @unique(src, dst) is currently parsed as two separate per-column uniqueness constraints, one on src and one on dst, not a composite "no duplicate (src, dst) pair" guarantee. The engine will accept multiple identical edges with the same src and dst values. Pair-uniqueness has to be enforced in the loader or reviewer until the engine grows true composite edge uniqueness.

Edge storage

Every edge row contains:

ColumnTypeDescription
idStringAuto-generated unique identifier.
srcStringSource-node key (the @key value of the from node).
dstStringDestination-node key (the @key value of the to node).

Plus one column per declared property. The src and dst columns are indexed automatically for efficient traversal in both directions.

Full example

A decision-tracking graph with multiple edge types:

node Actor {
    name: String @key
    role: String
}

node Decision {
    slug:   String @key
    title:  String @index
    status: enum(proposed, accepted, rejected, superseded)
}

node Signal {
    slug:     String @key
    title:    String @index
    strength: enum(strong, moderate, weak)
}

edge Authored: Actor -> Decision @description("Records who authored a decision and when.") {
    date: Date
}

edge Informed: Signal -> Decision @description("Links a signal to the decision it informed.") {
    weight: F64?
}

edge Supersedes: Decision -> Decision @card(0..1) @description("A newer decision that replaces an older one.")

edge Supports: Signal -> Signal @description("One signal corroborating another.") {
    @unique(src, dst)
}

On this page