Viewert Graph Format (VGF)
VGF is the open, JSON-based standard for defining node relationships in a graph. It separates what a graph means from how it looks — making graphs portable, AI-queryable, and permanently versionable.
The Problem VGF Solves
Every diagram tool invents its own format. Draw.io produces XML that only Draw.io understands. Figma exports JSON that is purely visual — it tells you where a box is, but not what it means. Mermaid is text-only with no visual layer at all. None of them are designed to be stored in a database, queried by an API, or consumed by an AI. The result: your system architecture diagram lives in a .drawio file on someone's Desktop. It goes stale the moment the system changes. It can't be versioned, diffed, searched, or reasoned about by a machine. VGF fixes this. It is a single, open JSON format that encodes topology, visual layout, and semantic meaning together — designed for production storage, API transport, and AI consumption from day one.
Three Layers, One Document
Every VGF document holds three orthogonal layers with a strict hierarchy of authority. You can strip any one layer without losing the others.
node types · relationship kinds · technology · domain · criticality
nodes · edges · groups · children · layerId
x · y · width · height · color · strokeStyle · zIndex
The Document Structure
A VGF file is a single JSON object. The top-level keys are fixed — everything else composes from there.
document
graph
* required field
format + version
"format": "vgf" identifies the file type. "version": "1.1" enables parsers to handle future schema changes with a clear upgrade path. Both are required on every document.
document — Metadata envelope
Identity and provenance: document ID (ULID), title, description, creation/update timestamps, author, tags, revision number, and source tool. Revision increments on every save — enabling optimistic concurrency and a full edit history.
graph — The topology and visual data
Contains nodes[], edges[], groups[], ports[], and layers[]. nodes[] and edges[] hold individual elements; groups[] holds containment relationships (the canonical topology store); layers[] holds visibility filters.
views, styles, resources — Optional richness
views[] define named camera positions (zoom + pan) for saved perspectives. styles holds a design token system for reusable visual themes. resources holds embedded markdown or Vellum references attached to specific nodes.
A Minimal VGF Document
The smallest valid VGF document — two nodes connected by one directed edge.
{
"format": "vgf",
"version": "1.1",
"document": {
"id": "01HZ7K2P3Q4R5S6T7U8V9W0XY1",
"title": "Auth to Database",
"revision": 1
},
"graph": {
"nodes": [
{
"id": "node-api",
"type": "service",
"label": "Auth Service",
"semantic": { "technology": "Go / Fiber" },
"visual": {
"x": 80, "y": 120,
"width": 160, "height": 64,
"color": "#06b6d4"
}
},
{
"id": "node-db",
"type": "database",
"label": "Users DB",
"semantic": { "technology": "PostgreSQL" },
"visual": {
"x": 360, "y": 120,
"width": 160, "height": 64,
"color": "#10b981"
}
}
],
"edges": [
{
"id": "edge-01",
"from": "node-api",
"to": "node-db",
"directed": true,
"label": "reads/writes",
"visual": {
"strokeStyle": "solid",
"arrowEnd": "arrow"
}
}
],
"groups": [],
"layers": [
{
"id": "default",
"name": "Default",
"color": "#06b6d4",
"visible": true
}
]
}
}Why Groups Are the Key Innovation
Most diagram formats treat grouping as a visual concept — a box drawn around other boxes. VGF treats groups as a topology concept: a named relationship between nodes that persists regardless of how the canvas is laid out. Group membership is encoded in graph.groups[].children (the authoritative record) and mirrored as node.groupId (a derived index for O(1) canvas lookups). Writers must keep both in sync. Readers derive groupId from groups[] on load.
// canonical topology
"id": "group-backend",
"label": "Backend Tier",
"children": [
"node-payment",
"node-events"
]✓ Source of truth — read first on load
// O(1) canvas lookup
"id": "node-payment",
"type": "service",
"groupId":
"group-backend"
// ↑ populated at loadDerived from groups[ ] on load — not authoritative
Invariant: For any node n and group g: n.groupId === g.id iff g.children.includes(n.id). Writers must keep both in sync. Readers derive groupId from groups[] on load.
The Full Node Schema
Every node carries all three layers as distinct objects within a single record. The semantic and visual objects are cleanly separated.
{
// stable identity — never regenerate
"id": "node-01",
// controlled vocabulary (see below)
"type": "service",
// display name, max 128 chars
"label": "Auth Service",
// SEMANTIC LAYER — what it means
"semantic": {
"technology": "Go / Fiber",
"description": "Handles JWT issuance",
"domain": "identity",
"criticality": "high"
},
// VISUAL LAYER — how to render it
"visual": {
"x": 200, "y": 150,
"width": 160, "height": 64,
"color": "#06b6d4",
"zIndex": 1
},
// derived topology index (see §Groups)
"groupId": "group-backend",
// visibility layer reference
"layerId": "layer-backend"
}
// Node type vocabulary (partial):
// service · database · api · queue · cache
// cdn · ai_model · worker · external · client
// storage · note · group · postgresql · redis
// openai · kubernetes · docker · github ...The Full Edge Schema
Edges encode directed or undirected relationships. Asynchronous relationships (event buses, queues) use dashed strokes; synchronous calls use solid.
{
"id": "edge-01",
"from": "node-api",
"to": "node-db",
// required — always set explicitly
"directed": true,
"label": "reads/writes",
"semantic": {
// calls · publishes · subscribes · depends-on
"relationshipType": "calls",
"protocol": "SQL/TLS",
"async": false
},
"visual": {
// solid | dashed | dotted
"strokeStyle": "solid",
// arrow | none | both
"arrowEnd": "arrow",
"color": "#06b6d4"
}
}Groups Schema
Groups are first-class topology entities in graph.groups[]. They are not just visual boxes — they define which nodes belong to a named domain, tier, or team.
// In graph.groups[] — authoritative record
{
"id": "group-backend",
"type": "group",
"label": "Backend Tier",
// canonical membership list
"children": [
"node-payment-svc",
"node-event-bus"
],
"visual": {
"x": 200, "y": 140,
"width": 480, "height": 280
},
"semantic": {
"kind": "domain",
"description": "All backend services"
}
}
// On each child node — derived index
{
"id": "node-payment-svc",
"type": "service",
"label": "Payment Service",
// populated at load from groups[]
"groupId": "group-backend"
}Layers: Visibility Filters
Layers are named visibility filters — not Z-order stacking (that is node.visual.zIndex). A layer groups nodes by architectural concern so entire tiers can be shown or hidden independently.
// graph.layers[] — visibility filter definitions
[
{ "id": "layer-edge",
"name": "Edge",
"color": "#f59e0b", "visible": true },
{ "id": "layer-backend",
"name": "Backend",
"color": "#06b6d4", "visible": true },
{ "id": "layer-data",
"name": "Data",
"color": "#10b981", "visible": true },
{ "id": "layer-ai",
"name": "AI",
"color": "#f97316", "visible": true }
]
// On a node — reference the layer by id
{ "layerId": "layer-backend" }
// In a view — override layer visibility
{
"id": "view-backend-only",
"name": "Backend Services",
"layerVisibility": {
"layer-edge": false,
"layer-backend": true,
"layer-data": true,
"layer-ai": false
}
}AI Integration: Semantic-First Design
VGF is designed for AI consumption from the ground up. The semantic layer makes graphs machine-understandable without visual parsing. An AI can read graph.nodes[].semantic to understand what each node is and does, graph.edges[].semantic.relationshipType to understand data flow, and graph.groups[].children to understand domain boundaries. VGF supports a semantic-only projection that strips all visual data, reducing token count by ~50%. In Viewert it is Export → "VGF for AI"; importing it lays the graph out again, so it also round-trips:
// Semantic-only projection
// strips visual layer for AI consumption
{
"format": "vgf",
"version": "1.1",
"projection": "semantic",
"graph": {
"nodes": [
{
"id": "node-api",
"type": "service",
"label": "Auth Service",
"semantic": {
"kind": "service",
"technology": "Go",
"domain": "identity"
}
},
{
"id": "node-db",
"type": "postgresql",
"label": "Users DB",
"semantic": {
"kind": "relational-db",
"technology": "PostgreSQL"
}
}
],
"edges": [
{
"id": "edge-01",
"from": "node-api",
"to": "node-db",
"directed": true,
"semantic": {
"relationshipType": "calls"
}
}
],
"groups": [
{
"id": "group-backend",
"label": "Backend",
"children": ["node-api", "node-db"]
}
]
}
}Editor Data Flow
The Viewert graph editor is fully VGF-canonical. The VGF document is the authoritative state — the canvas is a projection of it.
How VGF Compares to Other Formats
VGF is the only format that holds all three layers simultaneously while remaining portable, versionable, and AI-friendly.
Mermaid / DOT — Topology only
Great for text-based diagram generation. No visual layer (you cannot save positions), no semantic layer (no machine-readable types or relationships), not designed for database storage or API transport.
Excalidraw / draw.io — Visual only
Rich visual layer, some topology. No semantic layer — a box labeled "Auth Service" carries no machine-readable meaning. Not designed for querying, AI reasoning, or long-term versioned storage.
Neo4j / graph databases — Semantic + topology
Excellent semantic and topology representation. No visual layer — you cannot store a layout. Not a document format — not portable as a single file, not embeddable in an API response.
VGF — All three layers
Topology (nodes, edges, groups), visual (x/y/width/height/color), and semantic (type, technology, relationship kind) in one portable JSON file. Versioned, diffable, queryable, storable, AI-consumable.
Versioning and Revision Control
Every VGF document carries a revision integer in document.revision that increments on every save. It enables optimistic concurrency, change tracking, and future collaboration via operational transforms.
{
"document": {
"id": "01HZ7K2P3...",
"title": "Payment Architecture",
// increments on every save
"revision": 14,
"createdAt": "2026-03-01T10:00:00Z",
"updatedAt": "2026-03-16T20:00:00Z",
// AI-generated graphs — provenance
"provenance": {
"generatedBy": "viewert-ai-v1",
"model": "claude-opus-4-5",
"generatedAt": "2026-03-16T20:00:00Z"
}
}
}File Format and API Transport
VGF files use the .vgf extension and the MIME type application/vnd.viewert.graph+json. The backend stores the full document as a JSONB column — it does not reshape it, but it enforces the size limits and strips markup and unsafe colours. The JSON Schemas are published at /schemas/vgf-1.1.json (the complete document) and /schemas/vgf-authoring-1.1.json (what a person or an AI writes — positions and most envelope fields optional).
# File extension and MIME type
.vgf
application/vnd.viewert.graph+json
# What Viewert checks on import — and says, in words, before anything changes
✓ format === "vgf"; version is MAJOR.MINOR (a newer MAJOR is refused)
✓ graph.nodes is an array (graph.edges, graph.groups may be absent)
✓ Node and edge IDs are unique → a repeat is renamed
✓ Edge from/to reference real nodes → an edge to nothing is dropped
✓ Group children reference real nodes, no node in two groups, no cycles
✓ Node types come from the vocabulary → an unknown one is drawn as a service
✓ Colours are hex; text has no markup; URLs are https
✓ Size ≤ 5 MB, ≤ 2,000 nodes, ≤ 6,000 edges
✓ No positions? → the graph is laid out for youWriting VGF with an AI
Any assistant can write VGF that imports cleanly if it is given the guide. The one rule that matters: describe the topology and leave out positions — Viewert lays the graph out left to right and draws every group around its members, which a model's invented coordinates never manage. Give it the guide (https://viewert.com/schemas/vgf-ai-guide.md) or the authoring schema (https://viewert.com/schemas/vgf-authoring-1.1.json, usable as a structured-output or tool schema), then paste what it writes into Graph → Import. Whatever is wrong is listed and repaired; whatever cannot be read says why.
Write a VGF 1.1 document (JSON only, no code fence) that describes:
<your system>
Follow https://viewert.com/schemas/vgf-ai-guide.md. The essentials:
- {"format": "vgf", "version": "1.1", "document": {"title": "..."},
"graph": {"nodes": [...], "edges": [...], "groups": [...]}}
- every node: unique "id", a "type" from the guide's list
(use "service" when nothing fits), a short "label",
and "semantic": {"technology", "description"}
- every edge: "from" and "to" are ids of nodes you defined;
"directed": true; a short "label"; async links get
"semantic": {"async": true} and "visual": {"strokeStyle": "dashed"}
- groups, in graph.groups: {"id", "type": "group", "label",
"children": [node ids]}
- do NOT include positions or sizes — Viewert lays the graph out
Aim for 5–25 nodes a person can read. (Mapping a real
environment? The guide's "Describing a real environment" section
covers up to 2,000 resources: only write what you saw, put the
provider's own id in semantic.externalId and its console page in
semantic.url, mark inferred links with semantic.confidence, and
never include secrets.)Mapping a real environment
Point an assistant that can read your tech stack — through MCP servers or cloud CLIs for AWS, DigitalOcean, Google Cloud, Azure, Kubernetes, Cloudflare, GitHub, Jira and Confluence — at the guide, and ask it for a VGF document. It makes a node for what runs, stores and moves data, groups for accounts, regions, networks and clusters, and an edge for every relationship it found, with the evidence in the edge and a confidence of declared, observed or inferred. Import the result and you get a laid-out, animated, filterable picture: click any node for its identifier (with a copy button), its console link and its properties; use Views to cut a large estate into Production, one cloud at a time, or one team's part. The guide has the resource-to-type tables for each platform and the rules that keep such a graph true (read-only, no secrets, inferred edges marked). A worked example of about 75 resources across AWS, DigitalOcean, Google Cloud, Cloudflare, GitHub, Terraform, Jira and Confluence is at https://viewert.com/schemas/examples/multi-cloud-estate.vgf.json — save it and use Graph → Import to see what the result looks like.