Platform Embed Integration
Verify software before the agent acts on it. Embed Attestd at the action boundary in an agent pipeline, sandbox, or automation platform. Same mental model every time: before install, execute, recommend, deploy, expose, or upgrade. Prototype with a Free or Solo key. Move to Platform when you need embedding rights, volume, or a contract SLA.
Six moments to call Attestd
You already secure agent behaviour. Attestd gives you the software-integrity signal without you building vulnerability normalisation, supply-chain monitoring, and package-name integrity. Call it at these boundaries.
| Moment | What to verify |
|---|---|
| Before install | Agent or IDE about to run pip/npm/install. Reject typosquats and compromised packages before anything lands on disk. |
| Before execute | Sandbox or automation about to run a binary or script. Verify the version that will execute. |
| Before recommend | Assistant about to suggest a dependency. Check the name and version before the user copies it into a lockfile. |
| Before deploy | CI/CD about to ship to production. Fail on critical risk_state or supply_chain.compromised. |
| Before expose | Provisioner about to open ingress or attach a public endpoint. Pair with actively_exploited and remote_exploitable. |
| Before upgrade | Automation about to bump a version pin or roll a fleet. Verify the target version before the change runs. |
Synchronous gate at the action boundary
Your handler calls GET /v1/check (or the SDK equivalent) before the irreversible action, then branches on structured fields. Do not ask an LLM to interpret the response. Branch on booleans and risk_state.
Your platform request handler
│
▼
attestd.check(product, version) ← before the irreversible action
│
├─ risk_state: critical | high → block
├─ supply_chain.compromised: true → block
├─ typosquat.detected: true → reject + surface resembles
├─ outside_coverage / unsupported → apply your fallback policy
└─ risk_state: none (and clean SC) → allowUse a synchronous gate for before install, execute, recommend, deploy, expose, and upgrade. Use an async side-channel (Team webhooks on supply_chain.compromise) for continuous monitoring after admission. Webhooks do not replace the pre-action check.
For name integrity, treat typosquat.detected: true as a hard reject of the requested name. Prefer resembles / likely_intended. Outside coverage is unknown risk, not a clearance.
Latency expectations
Attestd does not publish formal p50 / p95 SLOs on self-serve tiers. Observed behaviour for a single /v1/check from a warm client is typically well under one second. Batch requests of up to 100 items stay in a similar range because synthesis is precomputed. Under rate limit pressure you receive 429 with Retry-After rather than a slow queue.
| Condition | What to expect |
|---|---|
| Single check, cache miss | Typically under 1 s end-to-end. Precomputed risk_state; no on-request NVD fan-out. |
| SDK cache hit (runtime) | Local memory only. Does not bill. Default TTL is 5 minutes. |
| Batch ≤ 100 items | One HTTP round trip. Each item bills as one call if the request succeeds. |
| 429 / 5xx | Fail fast. Retry 429 using Retry-After. Retry 500 with exponential backoff. |
For production embeds, use cache_policy="runtime" so repeated checks of the same product@version within five minutes do not consume quota. See Best Practices.
Batch behaviour
POST /v1/check/batch accepts 1 to 100 { product, version } items. Quota is checked before any results are returned. If the batch would exceed your remaining allowance, the API returns 429 and bills nothing.
curl -X POST "https://api.attestd.io/v1/check/batch" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "product": "log4j", "version": "2.14.1" },
{ "product": "nginx", "version": "1.20.0" },
{ "product": "litellm", "version": "1.82.7" }
]
}'| Rule | Detail |
|---|---|
| Max size | 100 items per request. |
| Billing | One call per item on success. No partial billing on 429. |
| Per-minute (Free) | 60/min. Solo / Team / Platform have no per-minute cap. |
| Monthly caps | Free 1,000; Solo 10,000; Team 100,000; Platform unlimited under contract. |
| SDK coalesce | AsyncClient with batch_window_ms coalesces concurrent check() calls into one batch. |
Full request and response shapes: API Reference.
When the API is unavailable
Decide fail-open vs fail-closed before you ship. Security-sensitive gates (agent install, sandbox admit, production deploy) usually fail closed. Non-blocking enrichment can fail open and log.
| Status | Meaning | Recommended action |
|---|---|---|
| 400 | Missing or invalid product / version params | Fix the request. Do not retry. |
| 401 | Missing or invalid API key | Fix credentials. Do not retry. |
| 422 | Version string could not be parsed | Normalize the version. Do not retry as-is. |
| 429 | Rate limit or monthly quota exceeded | Respect Retry-After. Back off. Upgrade tier if persistent. |
| 500 | Transient internal error | Retry with exponential backoff. Then apply fail-open / fail-closed. |
import os
import attestd
from attestd import AttestdAPIError, AttestdUnsupportedProductError
# fail_closed=True blocks when Attestd is unreachable or returns 5xx.
# fail_closed=False (fail-open) allows the action and logs a warning.
FAIL_CLOSED = os.environ.get("ATTESTD_FAIL_CLOSED", "true").lower() == "true"
client = attestd.Client(
api_key=os.environ["ATTESTD_API_KEY"],
cache_policy="runtime", # 5-minute TTL; cache hits do not bill
)
def gate(product: str, version: str) -> None:
try:
result = client.check(product, version)
except AttestdUnsupportedProductError as e:
if e.typosquat and e.typosquat.detected:
raise PermissionError(
f"name integrity failed for {product}: "
f"prefer {e.typosquat.resembles}"
)
# Outside coverage: treat as unknown risk.
if FAIL_CLOSED:
raise PermissionError(f"outside coverage: {product}")
return
except AttestdAPIError as e:
# 429, 5xx, timeout, network. Decide per your policy.
if FAIL_CLOSED:
raise PermissionError(f"attestd unavailable: {e}") from e
return
if result.risk_state in ("critical", "high"):
raise PermissionError(f"{product}@{version} risk_state={result.risk_state}")
if result.supply_chain and result.supply_chain.compromised:
raise PermissionError(f"{product}@{version} supply chain compromised")Network timeouts and DNS failures should follow the same FAIL_CLOSED path as 500.
supply_chain.provenance
npm only. Nested under supply_chain. Values:
| Value | Meaning |
|---|---|
| true | This version has a valid attestation against the package provenance baseline. |
| false | The package has a provenance baseline, but this version lacks attestation. Alert condition. |
| null | No baseline known. Absence of attestation is not a signal. |
Recommended default: treat false as a warning in sandbox / agent UIs. Reserve hard blocks for supply_chain.compromised: true, risk_state: critical|high, and typosquat hits. Field semantics: Response Fields.
How fresh is the data
| Source | Cadence |
|---|---|
| NVD + CISA KEV | Ingested every 6 hours. Responses may be up to cache_ttl_seconds (default 1 hour) stale due to in-memory caching. |
| Supply chain | OSV and registry sources checked on each monitoring run. last_updated on the response is the package-level freshness clock. |
| EPSS | Daily FIRST.org snapshot. max_epss / per-CVE epss may be null for CVEs published after the latest snapshot. |
For time-sensitive decisions, read last_updated on the check response (and the X-Attestd-Knowledge-Age header when present). There is no guaranteed fixed lag from OSV publish to API availability; treat last_updated as the authority for that package.
SLA availability
Free, Solo, and Team do not include a published availability or accuracy SLA. Risk assessments are bounded by upstream NVD, CISA KEV, OSV, and registry completeness.
Platform arrangements can include availability commitments, invoicing, and commercial embedding terms. Volume is unlimited under contract. Start the conversation from the pricing page:
Three working patterns
1. Agent security gateway (before install / execute)
Call before an agent installs or executes a package. Blocks on critical / high CVE risk, supply chain compromise, and name integrity failures.
"""Agent security gateway: call before an agent installs or executes a package."""
import os
import attestd
from attestd import AttestdUnsupportedProductError
class SecurityGatewayError(Exception):
pass
client = attestd.Client(
api_key=os.environ["ATTESTD_API_KEY"],
cache_policy="runtime",
)
def allow_install(product: str, version: str) -> None:
"""Raise SecurityGatewayError if the package must not be installed."""
try:
result = client.check(product, version)
except AttestdUnsupportedProductError as e:
if e.typosquat and e.typosquat.detected:
raise SecurityGatewayError(
f"hallucinated or typosquat name '{product}'. "
f"prefer {e.typosquat.resembles} "
f"(likely_intended={list(e.typosquat.likely_intended)})"
)
raise SecurityGatewayError(
f"no Attestd coverage for '{product}'. treat as unknown risk"
)
if result.supply_chain and result.supply_chain.compromised:
raise SecurityGatewayError(
f"{product}@{version} supply_chain.compromised=true "
f"sources={result.supply_chain.sources}"
)
if result.risk_state in ("critical", "high"):
raise SecurityGatewayError(
f"{product}@{version} risk_state={result.risk_state} "
f"fixed_version={result.fixed_version}"
)
# Example: agent about to install a dependency
allow_install("log4j", "2.14.1") # raises: risk_state=critical2. Sandbox provider pre-flight (before execute / install)
Admit a dependency into a sandbox only after a structured check. Provenance gaps become warnings. Compromised and critical / high remain hard denies. Batch up to 100 packages per call.
/** Sandbox provider: pre-flight before admitting a dependency into the sandbox. */
import { Client, AttestdAPIError, AttestdUnsupportedProductError } from '@attestd/sdk';
const client = new Client({
apiKey: process.env.ATTESTD_API_KEY!,
cachePolicy: 'runtime',
});
export type PreflightResult = {
allowed: boolean;
reason: string;
warnings: string[];
};
export async function preflightDependency(
product: string,
version: string,
): Promise<PreflightResult> {
const warnings: string[] = [];
try {
const result = await client.check(product, version);
if (result.supplyChain?.compromised) {
return {
allowed: false,
reason: `supply_chain.compromised for ${product}@${version}`,
warnings,
};
}
if (result.riskState === 'critical' || result.riskState === 'high') {
return {
allowed: false,
reason: `risk_state=${result.riskState} for ${product}@${version}`,
warnings,
};
}
// npm provenance: false means the package has a baseline but this version
// lacks attestation. Surface as a warning; do not hard-block by default.
if (result.supplyChain?.provenance === false) {
warnings.push(
`provenance=false for ${product}@${version}: version lacks attestation`,
);
}
return { allowed: true, reason: 'ok', warnings };
} catch (err) {
if (err instanceof AttestdUnsupportedProductError) {
if (err.typosquat?.detected) {
return {
allowed: false,
reason: `name integrity failed; prefer ${err.typosquat.resembles}`,
warnings,
};
}
return {
allowed: false,
reason: `outside coverage for ${product}`,
warnings,
};
}
if (err instanceof AttestdAPIError) {
// Propagate so the caller can apply fail-open / fail-closed.
throw err;
}
throw err;
}
}
/** Admit many packages at once (max 100 per batch call). */
export async function preflightBatch(
deps: { product: string; version: string }[],
) {
const results = await client.checkBatch(deps);
return deps.map((d, i) => ({ dep: d, result: results[i] }));
}3. Automation platform CI gate (before deploy / expose)
Fail the pipeline when any SBOM item has supply_chain.compromised: true or risk_state in your fail set. Configure strictness with ATTESTD_FAIL_ON.
"""Automation platform CI gate: fail the pipeline on critical risk or compromise."""
import os
import sys
import attestd
# Comma-separated risk_state values that fail the build.
# Default: critical,high. Set ATTESTD_FAIL_ON=critical to only block critical.
FAIL_ON = set(
os.environ.get("ATTESTD_FAIL_ON", "critical,high").lower().split(",")
)
# Parsed from your SBOM / lockfile. Each item is (product, version).
DEPS = [
("nginx", "1.20.0"),
("log4j", "2.25.5"),
("litellm", "1.82.7"),
]
client = attestd.Client(
api_key=os.environ["ATTESTD_API_KEY"],
cache_policy="ci", # infinite TTL within a single CI run
)
failed = False
results = client.batch_check(DEPS)
for (product, version), result in zip(DEPS, results):
if result is None:
print(f" FAIL [OUTSIDE] {product}@{version}", file=sys.stderr)
failed = True
continue
sc = result.supply_chain
if sc is not None and sc.compromised:
print(
f" FAIL [COMPROMISED] {product}@{version} sources={sc.sources}",
file=sys.stderr,
)
failed = True
continue
if result.risk_state in FAIL_ON:
print(
f" FAIL [{result.risk_state.upper()}] {product}@{version} "
f"fixed_version={result.fixed_version}",
file=sys.stderr,
)
failed = True
continue
print(f" PASS [{result.risk_state}] {product}@{version}")
sys.exit(1 if failed else 0)