Cura persists API responses as JSON files on disk. Subsequent runs serve data from the local cache, avoiding redundant network calls and reducing external API consumption. The implementation has no native dependencies and runs on macOS, Linux, and Windows.
~/.cura/cache/
aggregated/ <- AggregatedPackageData per package
dio.json
provider.json
...
The directory hierarchy is created automatically on first run.
When a package is fetched for the first time (or its cache entry has expired), Cura:
- Calls the relevant APIs (pub.dev, GitHub, OSV.dev)
- Aggregates the raw data into an
AggregatedPackageDatapayload - Writes the payload as
~/.cura/cache/aggregated/<packageName>.jsonwith anexpiresAttimestamp - Returns the fresh data to the scorer
Writes use the write-then-rename pattern (<key>.json.tmp → <key>.json)
so a crash mid-write never leaves a corrupted file.
On subsequent runs, before touching any API:
- Cura opens
~/.cura/cache/aggregated/<packageName>.json - If the file exists and
expiresAtis in the future, the payload is returned - The result is tagged
fromCache: trueso presenters can display a cache-hit indicator - If the file is absent, expired, or unparseable, it is treated as a cache miss
When Cura starts, cleanupExpired() runs automatically and:
- Deletes
.jsonfiles whoseexpiresAthas passed - Removes orphaned
.json.tmpfiles older than 1 hour
Each cache file follows this envelope (schemaVersion 1):
{
"schemaVersion": 1,
"key": "dio",
"cachedAt": "2026-02-24T10:00:00.000Z",
"expiresAt": "2026-02-25T10:00:00.000Z",
"data": {
"package_info": { ... },
"github_metrics": { ... },
"vulnerabilities": []
}
}| Field | Description |
|---|---|
schemaVersion |
Format version; bumped on breaking schema changes |
key |
Package name (matches the file name without .json) |
cachedAt |
UTC ISO-8601 timestamp when the entry was written |
expiresAt |
UTC ISO-8601 timestamp after which the entry is stale |
data |
The AggregatedPackageData payload |
Cache lifetime scales with package popularity, measured by the
popularityScore returned by pub.dev (0–100):
| Popularity tier | Condition | TTL |
|---|---|---|
| Very high | score >= 90 |
24 h |
| High | score >= 70 |
12 h |
| Normal | score >= 40 |
6 h |
| Low | score < 40 |
3 h |
Popular packages receive shorter TTLs because they publish updates frequently and are more likely to have new CVEs. Low-popularity packages change rarely; a longer TTL reduces unnecessary API calls.
cura cache statsPrints valid (non-expired) entry counts per namespace:
Cache Statistics:
Aggregated cache : 43 entries
──────────────────────────────
Total : 43 entries
cura cache cleanupRemoves expired entries and orphaned .tmp files. Valid entries are untouched.
cura cache clearPrompts for confirmation, then deletes all .json files across all cache
namespaces. Use this to force a fully fresh analysis (e.g. after a security
incident or to verify a fix).
| Key | Default | Description |
|---|---|---|
enable_cache |
true |
Enable or disable the file cache entirely |
cache_max_age_hours |
— | Override TTL (hours) for all packages |
auto_update |
true |
Sweep expired entries on startup |
cura config set enable_cache falseWhen disabled, every run fetches live data from all three APIs. Latency and API consumption increase accordingly.
cura config set cache_max_age_hours 2Sets a flat 2-hour TTL for all packages, ignoring the popularity-based strategy.
Persist the ~/.cura/cache/ directory between pipeline runs to reduce API
calls and execution time.
- uses: actions/cache@v4
with:
path: ~/.cura/cache
key: cura-${{ hashFiles('pubspec.lock') }}
restore-keys: cura-The cache key is tied to pubspec.lock and is invalidated whenever
dependencies change.
cache:
key: cura-cache
paths:
- ~/.cura/cache/- API integration — what data is fetched and cached
- Configuration reference — full config key list
- CI/CD integration — pipeline cache examples