Documentation
Folders and projects
A project is a row on the deployment and a folder on your disk. This is the piece of string between them.
One folder, one project
graph link writes .vipl/link.json in the current directory. From then on every command run inside that folder knows which project it belongs to, so graph status, graph check and graph agent all need no arguments.
{
"api": "https://graphlit.co",
"project_id": "sketch-odtb84p5",
"name": "Recipe Box"
}.vipl/link.json. No token, no key, nothing private, so commit it, and everyone who clones the repository is attached to the same project.The same binding is also recorded in ~/.vipl/links.json, which is how graph status --all and graph agent --all know what exists on this machine without walking your disk.
What is in `.vipl/`
| File | What it is | Commit it? |
|---|---|---|
link.json | Which project this folder is, and on which deployment | Yes |
graph.json | The synced copy of the Software Graph | Yes. It is the contract CI checks against |
tasks.json | The build plan, and each task's status | Yes |
node_file_map.json | Node → file mapping with a content hash per node. This is what drift compares | Yes |
health.json | Findings from the last scan, with a fingerprint per finding so a known one stays known | Yes |
planned_graph.json | The snapshot a delta plan is diffed against | Yes |
history/ | Previous graph versions, for undo | Optional |
runs/ | Per-run records from builds that happened here | No. Noise in a diff |
agent.lock | Held while a runner is serving this folder | No |
.gitignore already has the second group in it.Several projects on one machine
Link as many folders as you like. graph status --all shows every one and whether a terminal is currently serving it, including a terminal on a different machine.
graph projects asks the deployment the other half of the question: what this account could open, whether or not a folder is attached.
Commands that take a project also accept the name you see on screen, because that is what people remember: graph status invoice works even though the id is g-0c89dc4c. Give a name that does not match, in a terminal, and you get the list to choose from rather than an error.
Moving or renaming a folder
The binding is keyed by absolute path, so a moved folder is a folder the machine no longer recognises. graph link again in the new location. It is instant, and the old entry stops being offered once it no longer exists.
The folder on the server is not this folder
The deployment keeps its own working copy per project. That is what the board reads when nobody's terminal is connected, and what the scaffold writes into when a project is first stood up. Your checkout is yours; the two are synced through the graph and the plan, not by file copying.
Two consequences worth knowing before they surprise you:
- *`graph tasks` reads this folder's copy of the plan and makes no network call.*
graph statusasks the deployment. They can differ, most often when a build ran here but the report did not reach the board. That is a feature oftasks(it works offline, and it is whatgraph runwill actually run) rather than a bug in either. - Each organisation resolves inside its own subtree on the server. Two organisations both drawing a project called Shop do not share a directory, because the org is half of the address.
Starting from code instead of a drawing
An existing repository does not need drawing. Import code… on the board scans it (Next.js routes, FastAPI endpoints, Prisma and SQL schemas) and produces the graph from what is actually there, with every node marked as having come from code.

After an import the sync layer has something to compare against, and Keeping the graph true becomes the interesting chapter.