The CPG layer: Joern, interprocedural calls and honest degradation
What Joern (php2cpg) adds, the 12 CPG facts, the torch.sparse backend, the degraded contract, and why a missing Joern is a mode — not an accident.
Dieser Artikel ist noch nicht übersetzt — angezeigt wird die English.
Why a separate layer is needed #
tree-sitter gives intra-file syntax: symbols, calls within a file, SQL literals. But the agent's real questions are interprocedural: "what is reachable from this point", "does the approval route break". For PHP this hurts especially: dynamics (call_user_func, magic methods, include chains) make static reachability nontrivial. RAGRAF does not reinvent an analyzer — it plugs in Joern (Code Property Graph) as an optional L1 extractor and wraps it in its own contract.
What exactly gets added #
Pipeline stage 5 (cpg) starts the Joern server (localhost:9500, auto start/stop) and imports into graph.sqlite:
| Artifact | Contents |
|---|---|
Interprocedural CALL edges | calls across file boundaries — what tree-sitter cannot see |
cfg_edge / dfg_edge | control and data flows inside functions (groundwork for taint analysis — see future) |
| 12 CPG coverage facts | which files/functions are CPG-covered, by which parser (php2cpg), layer version |
joern_used on the revision | 1/0 — a flag visible in status and the panel; never "implied" |
On the pilot corpus (123 PHP files) the CPG stage took 64 s of the 71 s full indexing run — Joern dominates the time, which is why it is optional and switchable (joern_enabled: false).
slice: the only CPG operation #
slice is an interprocedural slice (up/down the flows from a symbol, configurable depth). It is the only operation requiring capability=full. The degradation contract:
$ ragraf -p cfg query slice --name markPaid
exit 3
{"op":"slice","capability":"degraded",
"reason":"CPG layer unavailable: Joern not installed",
"suggest":"install-joern.ps1 -Download, or use callers/impact (tree-sitter graph)"}A refusal with a reason and a suggestion — instead of an empty result the agent would take for "no dependencies". Anti-requirement #4 in action.
The algorithmic backend: torch.sparse #
CPG traversal (reachability, slices) is implemented in graph_algo.py with three backends and an honest cascade:
| Backend | Condition | Character |
|---|---|---|
torch-gpu | torch + CUDA available, graph above the threshold | CSR representation on GPU; batch traversals |
torch-cpu | torch available | the same code, CPU tensors |
none | no torch | pure Python/SQLite traversal — slower but always works |
The selected backend is written into the revision metadata (reason field), never implied. An honest detail: the CSR representation limits on-the-fly graph mutations — a CPG rebuild is a full pass (Joern has no incremental CPG). The tensor-path switch threshold is ~5,000 vertices (see gpu-compute).
Installation and weight #
./scripts/install-joern.ps1 -Download # downloads the distribution, verifies SHA-256, restores the environment on failureRequires JDK 21 + Joern (~5–6 GB). The installer checks the archive hash (protection against corrupted/tampered downloads) and restores the previous state on error — the pipe-deadlock and missing-timeout defects were closed by the refactor (INSTALLER-REFACTOR-NOTES).
Layer boundaries #
- PHP-first: php2cpg is verified on PHP corpora; for other languages the CPG is experimental (Joern supports many, but RAGRAF's contract is verified on PHP).
- Dynamics stay dynamic:
call_user_funcchains are not magically resolved by Joern either — derived edges with confidence 0.60 in a separate answer block (see operations). - Taint analysis is not included: CFG/DFG facts live in the graph as groundwork; product taint operations do not exist in v0.1.0 — a conscious release boundary and the first major future direction (future).
- Dependencies are not declared in pyproject: torch/Joern are external optional components; formalizing extras (
pip install ragraf[gpu]) is roadmap item R-PKG-1.
Related: GPU compute → · architecture → · future directions →