Local CodeQL-style code search for Elixir, backed by DuckDB/QuackDB or Postgres and ExAST.

Exograph indexes Elixir source code into normalized Ecto-backed tables: files, AST fragments, comments, definitions, references, package versions, and optional Reach call graph facts. You can then query that index with structural AST patterns, text/regex search, symbol/reference filters, and Ecto-shaped joins.

What is Exograph?

Exograph is:

  • a library for indexing Elixir source code into DuckDB or Postgres
  • a set of Ecto schemas and migrations for normalized code facts
  • a structural search engine using ExAST for exact AST verification
  • an optional Reach-backed call graph index
  • a foundation for CodeQL-like Elixir queries over one project or many package versions

Exograph is not:

  • a hosted search service
  • a language server
  • a general multi-language analyzer
  • a replacement for ExAST or Reach

Why?

Use Exograph when text search is not enough:

  • find functions matching an AST shape
  • search definitions and references across packages
  • ask "which private functions call Repo.transaction/1?"
  • index multiple Hex package versions into one database
  • combine relational filters with exact ExAST verification
  • persist Reach call graph facts for caller/callee queries

Installation

def deps do
  [
    {:exograph, "~> 0.7"}
  ]
end

DuckDB through QuackDB is the default local backend. Postgres remains supported; ParadeDB's pg_search extension is optional and enables BM25-backed text/code-fact retrieval.

Quickstart

Point Exograph at Elixir source. Mix tasks start a managed QuackDB server automatically unless you pass an existing QuackDB-backed Ecto repo:

{:ok, index} =
  Exograph.index("lib",
    repo: MyApp.QuackDBRepo,
    migrate?: true
  )

{:ok, hits} = Exograph.search(index, "Repo.get!(_, _)")

The configured backend retrieves candidates by term index; ExAST verifies the structural match.

Index Hex.pm

Download and index packages directly from Hex.pm:

mix exograph.index.hex --mode latest --duckdb-shards 4 --duckdb-threads 1 --prefix hex

Modes: latest (one version per package), top --limit 5000, all (every version). Resumes automatically — already-indexed packages are skipped.

For large corpora, DuckDB sharding keeps independent shard files and queries them through %Exograph.ShardedIndex{} without a merge step. Use --manifest-path to persist the shard manifest.

Web UI

Exograph includes an embedded web interface for exploring indexes:

mix exograph.web --prefix myindex --port 4200

Add --web to mix exograph.index.hex for a live progress dashboard while indexing runs.

Features:

  • Monaco editor with Elixir syntax highlighting and autocompletion
  • Structural, text, and regex search modes
  • IDE-style error diagnostics
  • Collapsible results grouped by package with code previews
  • Hex.pm links on package names
  • Live progress dashboard at /progress during Hex.pm indexing

JSON API

The web server exposes a JSON API:

POST /api/search    structural, text, or regex search
POST /api/query     DSL query execution
GET  /api/packages  list indexed packages
GET  /api/stats     index statistics

Cursor pagination via cursor/next_cursor. Rate limited (60 req/min).

Query with code facts

Use Exograph.DSL to combine structural AST patterns with indexed code facts:

import Exograph.DSL

query =
  from(f in Fragment,
    join: r in assoc(f, :references),
    where: r.qualified_name == "Repo.transaction/1",
    where: matches(f, "def _ do ... end")
  )

{:ok, hits} = Exograph.all(index, query)

Reach call graph facts can also be queried directly:

Exograph.search_callers(index, "Repo.transaction/1")
Exograph.search_callees(index, "MyApp.Accounts.update_user/2")

How it compares

ToolScopeStorageQuery styleElixir AST-aware?Best for
ripgreplocal text searchnoneregex/textnofast ad-hoc text search
ExASTstructural AST matchingnone/advisory termsAST patterns/selectorsyesexact search and patching
Reachdependence analysisin-memory graph/reportsAPIs / Mix tasksyescall/data/control-flow analysis
CodeQLsemantic code analysisCodeQL databaseQL languagenot first-class Elixirsecurity analysis at scale
Sourcegraphcross-repo searchexternal indextext/structural depending setupnot Elixir-specificorganization-wide search
ExographElixir code fact indexDuckDB/QuackDB or Postgres/ParadeDBExAST + Ecto-shaped DSLyeslocal/self-hosted Elixir code intelligence, large Hex package indexing

Features

  • ExAST-backed structural search with exact verification
  • normalized Ecto-backed storage for files, fragments, comments, definitions, references, packages, versions, and call edges
  • mix exograph.index.hex — streaming pipeline to index all of Hex.pm
  • package/version-scoped indexes for Hex or other source archives
  • text and regex search via DuckDB FTS/BM25 or Postgres/ParadeDB
  • optional Reach call graph extraction
  • ExDNA-powered structural similarity
  • web UI with Monaco editor, live progress dashboard, and JSON API

Documentation

GuideContent
Getting StartedInstallation, backend setup, first index/search
QueryingStructural, text, and regex search; planning/explain
DSLExograph.DSL, joins, selects, predicates
Code FactsDefinitions, references, comments, typed hits
Call GraphReach extraction, callers/callees, call edge DSL
DuckDB and QuackDBRecommended backend, sharding, manifests, tuning
Postgres and ParadeDBPostgres backend, migrations, BM25, performance tuning
Backend benchmarksCurrent DuckDB/Postgres benchmark methodology and results
Postgres COPY stagingDesign notes for a future fair Postgres bulk-ingest path
Package IndexingIndexing Hex.pm and manual package archives
Mix TasksCLI indexing, searching, web UI
Web UIMonaco editor, search modes, progress dashboard
APIJSON API endpoints, pagination, rate limiting
ComparisonsExograph vs ExAST, Reach, CodeQL, Sourcegraph
ArchitectureStorage model, verifier contract, extraction pipeline

License

MIT.