Best Practices
How to use the Attestd SDKs efficiently. Cache policies, CI/CD patterns, async batching, and session stats so you stay within quota without sacrificing freshness.
Choose a cache policy
Both SDKs cache check results in memory by product and version. The default is runtime. Cached hits do not count against your monthly quota.
| Policy | TTL | Use case |
|---|---|---|
development | 24 h | Local loops. Avoid burning Free-tier quota while iterating. |
runtime | 5 min | Production default. Fresh enough for deploy gates and agents. |
ci | infinite | CI/CD runs. Deduplicate the same product+version within one job. |
none | 0 | Raw mode. Every call hits the API. Useful for tests. |
Python: Client(cache_policy="runtime"). JavaScript: new Client({ cachePolicy: 'runtime' }).
CI vs production
Use cache_policy="ci" in pipelines so a large SBOM does not re-fetch the same package versions. Use runtime in long-lived services.
import attestd
# Deduplicate within a CI run. Same product+version is fetched once.
with attestd.Client(api_key="YOUR_API_KEY", cache_policy="ci") as client:
for product, version in sbom:
result = client.check(product, version)
if result.risk_state in ("critical", "high"):
raise SystemExit(f"blocked: {product} {version}")
print(client.stats()) # api_calls_made, cache_hits, calls_savedimport attestd
# Production default: 5-minute TTL. Fresh enough, quota-efficient.
client = attestd.Client(api_key="YOUR_API_KEY", cache_policy="runtime")import { Client } from '@attestd/sdk';
// Production default: 5-minute TTL.
const client = new Client({
apiKey: process.env.ATTESTD_API_KEY,
cachePolicy: 'runtime',
});Coalesce concurrent checks
Python AsyncClient coalesces concurrent check() calls within batch_window_ms (default 5) into one POST /v1/check/batch. For an explicit list of dependencies, call batch_check (Python) or checkBatch (JavaScript) instead.
import attestd
import asyncio
async def check_many(deps):
async with attestd.AsyncClient(
api_key="YOUR_API_KEY",
cache_policy="runtime",
batch_window_ms=5,
) as client:
# Concurrent check() calls within the window coalesce into one batch.
results = await asyncio.gather(
*(client.check(p, v) for p, v in deps)
)
print(client.stats().batch_saves)
return resultsimport attestd
deps = [("nginx", "1.20.0"), ("log4j", "2.14.1"), ("redis", "6.0.9")]
with attestd.Client(api_key="YOUR_API_KEY") as client:
results = client.batch_check(deps)Session stats
Call stats() at the end of a deploy or CI job. Log calls_saved to confirm caching and batching are working.
stats = client.stats()
print(stats.api_calls_made) # billed API calls this session
print(stats.cache_hits) # results served from cache
print(stats.batch_saves) # check() calls coalesced into a batch
print(stats.calls_saved) # cache_hits + batch_savesconst stats = client.stats();
console.log(stats.apiCallsMade);
console.log(stats.cacheHits);
console.log(stats.callsSaved);When to invalidate
Invalidate a cache entry after a patch deploy, or when a webhook tells you a new advisory landed for that product. The next check fetches fresh data.
client.invalidate_cache("nginx", "1.20.0")
# Next check("nginx", "1.20.0") hits the API again.client.invalidateCache('nginx', '1.20.0');
// Next check('nginx', '1.20.0') hits the API again.