Skip to content

Signature Database

github-actions[bot] edited this page Jul 16, 2026 · 1 revision

This document was generated from 'src/documentation/wiki-signature-database.ts' on 2026-07-15, 09:42:41 UTC presenting an overview of flowR's bundled signature database that resolves library() calls (v2.12.0, using R v4.6.0). Please do not edit this file/wiki page directly.

Signature Database

flowR ships a database of the complete history of all exports in every version of all CRAN packages so it can resolve calls into the packages you load. After library(ggplot2), a call to ggplot() resolves to ggplot2::ggplot. The same database qualifies bare names and backs various components like the dependencies and call-context queries as well as the undefined symbol rule.

Configuration

The exports come from versions:sigdb, which reads bundled databases. It is enabled by default (see configuring flowR).

Which version's exports get resolved is decided by the version-reading plugins that pin the packages a project uses.

function usePackageDatabase(parser: KnownParser) {
	const sigdb = new FlowrAnalyzerPackageVersionsSigDbPlugin('/path/to/sigs.manifest.json.br');
	return new FlowrAnalyzerBuilder().setParser(parser).registerPlugins(sigdb).build();
}

Defined at src/documentation/wiki-signature-database.ts#L20

File sources load lazily on the first package load, so a script with no library() or use() calls never pays to parse them. Set solver.sigdb.eagerlyLoad to mount the database up front instead, or solver.sigdb.enabled to false to switch it off entirely. For a compressed (.br) or manifest source, preload it before analysis to mount it.

The base-R packages (base, stats, graphics, ...) resolve against an assumed R version, which defaults to 4.5.3 (solver.sigdb.assumedRVersion, or "auto" to detect the local R). So library(stats) attaches that release's exports, and a bare sd() qualifies to stats::sd even without attaching the base namespaces to the graph. Set solver.sigdb.linkBaseR to also link them as dataflow edges.

No signature shards are committed: the base.* floor (self-contained base-R signatures, a few hundred KB), the current.* scope (every package's latest version) and history.* (every older version) all live as assets on the free solver.sigdb.downloadRepo GitHub release. The only committed file is a tiny link file, src/data/sigdb/sigdb.remote.json, which records the release tag and each shard's sha256 and size, so :signature download builds the direct release-CDN URL (no API rate limit), verifies every shard by content hash, and skips any already cached. Because the link file is versioned, a git pull that updates it re-syncs only the shards whose hash changed — and with solver.sigdb.autoSync that check runs on startup and re-downloads in the background; npm run build bakes the shards in as well. The richest downloaded scope is used (order full > current > base), so once fetched library(stats) resolves. Any path in solver.sigdb.additionalPaths (or $FLOWR_SIGDB_DIR) is searched alongside the default, so a downloaded bundle stays mounted on every start.

Bundled Databases

The default bundle is not a single file but a set of shards that a manifest routes between (see SigDatabaseSet). Nothing is read when the manifest opens. The first lookup of a package decompresses only the one shard that holds it, plus the shared dictionary once. The following ship with this build; the load column is the decompression time measured at generation time.

Shard Contents Versions kept Packages Versions Size (.br) Load (first touch)
base-current base-R packages (base, stats, graphics, ...) latest only 23 23 95 KB ≈ 2 ms
base-full base-R packages (base, stats, graphics, ...) full history 23 1,626 418 KB ≈ 6 ms
current-top the 1,000 most-downloaded CRAN packages latest only 1,000 1,000 1.9 MB ≈ 31 ms
current-rest the remaining CRAN packages latest only 22,742 22,742 14.0 MB ≈ 520 ms
history-rest the remaining CRAN packages full history 18,466 140,128 27.6 MB ≈ 1000 ms
current.dict 1,394,438 shared strings - - - 6.2 MB ≈ 180 ms
base.dict 10,866 shared strings - - - 48 KB ≈ 1 ms
history.dict 1,402,879 shared strings - - - 6.5 MB ≈ 190 ms

Which shard answers a lookup follows from the package and the version asked for. A base-R package comes from base-current, one of the 1,000 most-downloaded CRAN packages from current-top, and anything else from current-rest. The *-full and history-* shards hold every historical version and are only touched when an older, pinned version is requested, so a normal analysis never decompresses them. Each scope carries its own shared dictionary that its shards depend on, so it is decompressed the first time any of its packages is looked up and then reused. The flowR Docker images ship this dictionary already decompressed, so a container reads it in place and skips that step (the load column above is the cost a plain npm install pays).

Every shard, dictionary, and manifest is published in both brotli (.br) and zstd (.zst, faster to decompress) compression, and flowR uses whichever the runtime supports: .zst when the Node version exposes zstd (Node ≥ 22.15), otherwise .br. :signature download fetches only that one variant per file, and :version reports the format each loaded database resolved to.

Format

The on-disk format is flowr-sigdb (schema 4). Beyond each version's exports it records, per version, every function's signature (parameters, whether each is forced or optional, and its default) and call graph, together with that version's declared dependencies (Depends, Imports, ... with their version qualifiers). The layout is NDJSON: a header, then a shared string dictionary, then one self-contained blob per package, next to a sidecar .idx. A reader (SigDatabase) therefore loads the dictionary once and then seeks straight to the packages it needs, never reading the rest. The bundle is written by SigDbBuilder and can be split into several small shards (current-only versus full history, top-N versus the rest) that a flowr-sigdb-manifest routes transparently (SigDatabaseSet), and which information gets stored is selectable (SigDbFeatures). crawlr produces the bundle from its analysis of CRAN.

Performance

The dictionary is read once, the reader then seeks straight to each requested package, and consumers cache what they derive (the base-package list is precomputed when flowR is bundled, so it costs nothing at analysis time). After the one-time load a per-package lookup is O(1), so each library() or :: a script uses is a single cached lookup.

Clone this wiki locally