Skip to main content
Glama
nickjoven
by nickjoven
README.md
# erdgraph

A static graph of Elden Ring's executable, served to agents over MCP. It is the
mapping layer for an open-source co-op mod: find the code that enforces session
rules, name it with evidence, and keep those names alive across game patches.

It works on any MSVC-built PE64, but every default and example targets `eldenring.exe`.

## What it extracts

Ingest takes about 8 seconds and needs no disassembler install.

| Source in the binary | What you get |
|---|---|
| Exception table (`.pdata`) | Exact function bounds, with cold fragments linked to their parent |
| Call targets without unwind data | ~18k leaf functions, bounds found by forward decode |
| MSVC RTTI | ~12k classes with demangled template names, hierarchy and offsets, ~10k vtables |
| Code sweep (capstone) | Calls, tail jumps, IAT calls, and RIP-relative reads, writes and address-takes |
| Base relocations | Every pointer from data into code: initializer tables, callback tables, vtables |
| Data sections | ASCII and UTF-16 strings, joined to the functions that use them |
| Export and import tables | 560 named Scaleform and Wwise exports, Steam and Win32 imports |

The second executable section is the protection layer. It has no unwind data,
so it is not part of the function graph.

## Labels and provenance

Names are claims, so they go through a lifecycle instead of being written as facts.

1. An agent gathers evidence and calls `propose_label` with a rationale and a confidence.
2. A separate pass calls `review_label` with `confirms` or `refutes`.
3. A game patch arrives: ingest the new exe, then `port_labels` re-finds each name in the new build.

Each event becomes a [ket](https://github.com/nickjoven/ket) DAG node. Proposals hang
off the build node with a `proposes` edge, and reviews hang off the proposal with
`confirms` or `refutes`. Ported labels link back with `derives`. Every event is also
appended to `labels.log` as JSONL.

Porting tries an AOB signature first, with RIP-relative and branch operands
wildcarded. Many functions share a shape, so labels also store a string anchor: the
longest string referenced only by that function. Strings survive patches far
better than code bytes do. Ported labels come in as `ported` and still need review.

## Harvests

Two structural patterns name about 1,700 functions automatically, as proposals:

- **Lua bindings.** Static initialisers bind native handlers to Lua event names
  such as `HostDead` or `StartClientLeaveAroundHost`. These come in at confidence 0.7.
- **Qualified-name strings.** Debug macros embed names like
  `CS::FieldArea::IsEnableFastTravel`. A string used by exactly one function names
  it, at confidence 0.55.

`docs/coop-map.md` applies these to the co-op session rules.

## Install

You need [uv](https://docs.astral.sh/uv/) and your own copy of Elden Ring. uv
fetches Python 3.12 and prebuilt wheels, so there is nothing to compile.

```sh
uv tool install git+https://github.com/nickjoven/erdgraph
erdgraph ingest                 # finds eldenring.exe in your Steam libraries
claude mcp add --scope user erdgraph -- erdgraph serve
```

`ingest` reads Steam's `libraryfolders.vdf` on Windows, WSL and Linux. If the
game lives elsewhere, pass the path to `eldenring.exe`.

[ket](https://github.com/nickjoven/ket) is optional. With it on PATH, every build
and label gets a content ID and lineage. Without it, everything else works and
erdgraph says once that provenance is off. Set `ERDGRAPH_KET` to use a ket
binary under another name.

To work on erdgraph itself:

```sh
git clone https://github.com/nickjoven/erdgraph && cd erdgraph
uv sync
uv run erdgraph ingest
claude mcp add --scope user erdgraph -- uv --directory "$PWD" run erdgraph serve
```

Other commands:

```sh
erdgraph builds                 # ingested builds
erdgraph harvest --apply        # bulk proposals; about 2 minutes with ket, mostly ket writes
erdgraph port <old> <new>       # carry labels to a new game build
```

## Tools

| Tool | Purpose |
|---|---|
| `builds`, `overview` | What has been ingested, counts, label status |
| `find` | Substring search over classes, labels, exports, strings, imports |
| `class_info` | Bases, derived classes, vtables with named slots, constructor and destructor candidates |
| `function_info` | Bounds, callers, callees, imports, strings, globals, vtable slots, data pointers |
| `disassemble` | Annotated listing with names resolved inline |
| `xrefs_to`, `callgraph` | Who references an address, and call neighbourhoods |
| `read_memory` | Static bytes with pointer naming |
| `make_signature`, `scan_signature` | Unique AOB patterns for hooks and for patch survival |
| `propose_label`, `review_label`, `labels`, `port_labels` | The naming lifecycle |
| `global_info` | Writers, readers, and the classes whose vtables the writers take, which identifies singletons |
| `harvest_names` | Bulk proposals from Lua bindings and embedded qualified-name strings |
| `sql` | Read-only SQL over the whole graph |

Most tools accept a name where they take an address: a label, an export, a class
name for its primary vftable, `Class::vfN` for a virtual slot, or `sub_XXXXXXXX`.

## Workspace

`$ERDGRAPH_HOME`, default `~/.local/share/erdgraph`:

```
.ket/                          shared ket store across builds
labels.log                     append-only label events
builds/<blake3[:16]>/image.bin read-only copy of the analysed exe
builds/<blake3[:16]>/graph.sqlite
builds/<blake3[:16]>/manifest.json   hashes, Steam build id, PDB GUID, ket CIDs
```

The workspace contains a copy of the game executable. Keep it local. The
repository holds no game data, and the schema history is in
`src/erdgraph/schema.changelog`.

## Limits

- Static only. Replication timing, packet ordering and authority rules need a
  runtime harness on the Windows side, which is the next layer.
- Indirect calls through registers and virtual dispatch are not resolved to
  targets. Vtable slots and data pointers cover much of that ground.
- No decompiler. Disassembly is what agents read today. A Ghidra backend needs
  JDK 21 and is a planned addition.
- About 2% of RTTI names use forms the demangler skips, mostly member-function
  pointers inside `std::_Binder`. They stay mangled and searchable.

## Tests

```sh
uv run pytest                                          # unit and synthetic
ERDGRAPH_TEST_EXE=/path/to/eldenring.exe uv run pytest # plus real-binary checks
```

## Scope and legal

erdgraph is an independent project. It is not affiliated with or endorsed by
FromSoftware or Bandai Namco. It ships no game files: it analyses a copy of the
executable you already own, and that copy and everything derived from it stay in
your local workspace.

It does static analysis only. It does not touch Easy Anti-Cheat, connect to
FromSoftware's servers, or modify the game. Anything built on these findings
should run offline, as every Elden Ring mod loader already does.

## License

MIT. See `LICENSE`.

TDQS

B3/5.0

Scored across 18 tools

Disambiguation4/5

Most tools target clearly distinct operations (e.g., disassemble vs. callgraph vs. xrefs_to), but there is minor overlap: find can search labels that labels also lists, and propose_label vs. harvest_names both create proposed labels. Descriptions clarify the boundaries, so confusion is limited.

Naming Consistency3/5

Tool names mix noun-style (builds, overview, find, class_info, sql) with verb_noun-style (port_labels, make_signature, scan_signature, propose_label, review_label, harvest_names). The pattern is not consistent, though all names remain readable and understandable.

Tool Count4/5

18 tools is slightly above the typical 3–15 range, but each tool covers a distinct aspect of reverse-engineering graph analysis (builds, classes, functions, disassembly, xrefs, callgraph, globals, memory, signatures, labels, SQL). The count is reasonable for the domain, though not minimal.

Completeness4/5

The surface covers the core lifecycle: build overview, search, class/function/global inspection, disassembly, xrefs, callgraph, signatures, and label proposal/review/listing/porting. Minor gaps exist, such as no explicit label deletion or update operation and no xrefs_from tool, but these are workable via SQL or new proposals.

Maintenance

ActivityMaintained
ResponsivenessNo issues