
Security News
Deno Goes the Way of the Dinosaur
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.
@deepfirstsearch/agent-pay
Advanced tools
Policy-gated x402 payments for AI agents: deterministic spend policy outside the model, prompt-injection guards, per-merchant payers and on-chain Agent Safe budgets on Base.
x402 payments for AI agents (v2, plus v1 for merchants you opt in) where the model proposes and the owner's policy decides.
npm install @deepfirstsearch/agent-pay viem
npx @deepfirstsearch/agent-pay demo # 10-second offline tour: one honest payment, four attacks refused
Beta, unaudited. Agent Safe is live on Base Sepolia and in a capped mainnet beta; an independent audit is being arranged. Use small amounts and at your own risk.
Project setup. The samples are ESM with top-level await: run npm pkg set type=module in your project (or name the file .ts and run it with npx tsx file.ts). Node 20 or later. Install viem next to the SDK whenever a sample imports it (pnpm doesn't hoist it for you).
import { hexToBytes, type Hex } from "viem";
import { createAgentPay, MerchantRegistry, burnerPayers } from "@deepfirstsearch/agent-pay";
const ownerSeed = hexToBytes(process.env.AGENT_PAY_BURNER_SEED as Hex); // 32 random bytes, never the owner key
const registry = new MerchantRegistry([
{ origin: "https://api.pricing-intel.io", payTo: "0x…", network: "eip155:8453", maxPerTx: 50_000n, pricePin: 10_000n },
]);
const pay = createAgentPay({
registry,
policy: { allowedNetworks: ["eip155:8453"], approvalThreshold: 1_000_000n, periodBudget: { amount: 20_000_000n, periodMs: 86_400_000 } },
payer: burnerPayers(ownerSeed, vaultAddress), // one payer address per merchant
session: { readsUntrustedInput: true, accessesSensitiveData: false, canPay: true },
approve: askHumanViaPasskey, // a channel the model cannot write to
ensureFunded: topUpFromAgentSafe, // BudgetVault.fundBurner, bounded on-chain
});
// 1. Seal the plan BEFORE the agent reads anything untrusted.
const plan = pay.commitPlan([{ origin: "https://api.pricing-intel.io", maxSpend: 100_000n }], 10 * 60_000);
// 2. Use it like fetch. A 402 is paid only if every check passes.
const res = await pay.fetch("https://api.pricing-intel.io/v1/prices", {}, { plan });
The official client (@x402/fetch, @x402/core) caps each payment with spendControls. Agent Safe adds what a single cap can't: the payee pinned for each origin, a price pin, a sealed plan per session, period budgets and refusal codes. Keep your client and plug the guard into its hooks:
import { wrapFetchWithPayment, x402Client } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { agentSafeGuard, commitPlan, MerchantRegistry } from "@deepfirstsearch/agent-pay";
import { startMockServer } from "@deepfirstsearch/agent-pay/testing";
// A local merchant and an attacker copy of it, so this runs offline.
const MERCHANT = "0x1111111111111111111111111111111111111111";
const shop = await startMockServer({
"/data": { price: 1_000n, payTo: MERCHANT },
"/hijacked": { price: 1_000n, payTo: MERCHANT, tamper: (r) => ({ ...r, payTo: "0x9999999999999999999999999999999999999999" }) },
});
const registry = new MerchantRegistry([{ origin: shop.url, payTo: MERCHANT, network: "eip155:84532", maxPerTx: 5_000n, pricePin: 1_000n }]);
const plan = commitPlan(registry, [{ origin: shop.url, maxSpend: 10_000n }], 3_600_000);
const guard = agentSafeGuard({ registry, plan, policy: { allowedNetworks: ["eip155:84532"] } });
const client = guard.install(x402Client.fromConfig({ schemes: [{ network: "eip155:*", client: new ExactEvmScheme(privateKeyToAccount(generatePrivateKey())) }] }));
const fetchWithPayment = guard.fetch(wrapFetchWithPayment(fetch, client));
console.log((await fetchWithPayment(`${shop.url}/data`)).status); // 200: paid 0.001 USDC to the pinned payee
await fetchWithPayment(`${shop.url}/hijacked`).catch((e) => console.log(e.message)); // ...[payee_mismatch]..., nothing signed
await shop.close();
For a client that wraps its own fetch around one base URL (an OpenAI-style client such as @qntx/openai), pass origin: "https://llm.example.com" and hand it the guarded client (x402Client: guard.install(...)): payments are then judged against that configured origin.
guard.fetch matters: the hooks only see the 402, whose resource is the server's own claim, so the guard records the origin each request was actually made to and judges payments against it. A payment made outside it is refused. Budgets are reserved before the signature, so parallel requests can't overspend them, and are held in this process; for a budget shared across processes and enforced even if the agent's machine is compromised, add a BudgetVault. createAgentPay goes further on settlement: it never signs twice for one purchase and confirms unreadable receipts on-chain.
payTo, asset, network, EIP-712 domain, scheme or timeout differ from owner configuration.x402Versions: [1, 2] (see below), malformed or oversized headers, and 402s reached through cross-origin redirects.policy.timeoutBounds), unless that merchant sets maxTimeoutSeconds (10 to 86400) in the registry.The 402 is read from PAYMENT-REQUIRED, or from X-PAYMENT-REQUIRED when the standard header is absent.
A refusal throws PaymentDeniedError. Its reasons are human-readable and may be reworded; its codes (one per reason) are stable and machine-readable, code is the primary one, and action says what the agent should do. The same codes are recorded in the payment.denied audit event.
import { PaymentDeniedError } from "@deepfirstsearch/agent-pay";
try {
await pay.fetch(url, {}, { plan });
} catch (e) {
if (e instanceof PaymentDeniedError && e.action === "report") alertOwner(e.code, e.reasons); // possible redirection: never retry
}
| code | action | meaning |
|---|---|---|
payee_mismatch | report | the 402's payTo is not the merchant's registered address |
network_mismatch | report | an allowed network, but not the merchant's |
asset_mismatch | report | asset or EIP-712 domain is not the pinned USDC |
scheme_unsupported | report | a scheme or transfer method the SDK does not sign |
sanctioned_payee | report | the payee failed sanctions screening (or screening was unavailable) |
invalid_402 | report | malformed or oversized 402, no options, a non-positive amount, or a cross-origin redirect |
price_changed | ask_owner | the price is above the owner's pin (plus tolerance) |
human_refused | ask_owner | a human declined the approval (or no approval channel exists) |
over_cap | ask_owner | above the merchant's maxPerTx |
policy_denied | ask_owner | generic refusal (default when no specific code applies) |
unknown_merchant | fix_config | the origin is not in the registry (or is not https) |
not_in_plan | fix_config | the merchant is not in the sealed plan |
network_not_allowed | fix_config | a network outside allowedNetworks (or an unmapped x402 v1 network) |
asset_not_pinned | fix_config | no USDC pinned for the merchant's network |
timeout_out_of_bounds | fix_config | maxTimeoutSeconds outside the accepted bounds |
version_not_allowed | fix_config | an x402 version not enabled for that merchant |
plan_exhausted | retry_later | not enough left in the sealed plan |
plan_expired | retry_later | the sealed plan expired; seal a new one |
budget_exhausted | retry_later | not enough left in the period budget |
Actions: report (possible redirection or tampering: don't retry, report it), ask_owner (only the owner can decide), fix_config (the owner's configuration does not allow it), retry_later (a limit was reached: retry later or ask for a bigger plan), and, for blocks only, resend_same (request the same resource again: the SDK resends the same signed authorization and never signs a new one). With several reasons, code is the most cautious one. REFUSAL_CODES, RefusalCode and RefusalAction are exported.
Kill switch, rate limits and settlement problems are not refusals: they throw PaymentBlockedError, which carries a code and an action too (BLOCK_CODES, BlockCode, BlockAction are exported).
| code | action | meaning |
|---|---|---|
settlement_pending | resend_same | no receipt confirmed the payment; it may still settle. Request the same resource again: the same authorization is resent, nothing new is signed. Never re-sign |
settled_not_delivered | report | it was paid (found on-chain, or the merchant answered a resend with "already used", e.g. 402 payment_invalid / tx_already_used) but the resource was not delivered: never pay again, report it |
settlement_unknown | ask_owner | an earlier authorization for this resource expired unconfirmed and nothing can tell whether it executed (no confirmAuthorization, or the RPC failed): nothing new is signed, since that could pay twice. Enable confirmAuthorization, or call pay.forgetUnsettled(url) after checking yourself |
rate_limited | retry_later | the payment rate limit was reached |
kill_switch | ask_owner | payments are stopped by the owner's kill switch |
blocked | ask_owner | generic block (default when no specific code applies) |
One signed authorization per resource, and never a second one while the first could still have paid:
settlement_pending → resend_same). Resends are bounded by its validity window (the 402's maxTimeoutSeconds, capped by your timeout bounds).validBefore to cover chain clock skew) nothing is sent or signed: settlement_pending.confirmAuthorization): never used → a new authorization is signed; used → settled_not_delivered, and that resource is not paid again. Without an on-chain check, or if the RPC fails: settlement_unknown, until the owner clears it with pay.forgetUnsettled(url).pay.forgetUnsettled(url) is an owner action: it forgets the unconfirmed (or paid-but-undelivered) payments recorded for that exact URL, so the next request may sign a new authorization. Call it only after checking the earlier payment yourself (for example, its nonce on a block explorer). It returns how many entries it cleared and records a payment.forgotten audit event.
import { PaymentBlockedError } from "@deepfirstsearch/agent-pay";
try {
await pay.fetch(url, {}, { plan });
} catch (e) {
if (e instanceof PaymentBlockedError && e.code === "settlement_unknown") {
// Owner side, after confirming the earlier authorization was never used on-chain:
const cleared = pay.forgetUnsettled(url); // 1
await pay.fetch(url, {}, { plan }); // signs a new authorization
}
}
Merchants without a standard receipt can say how settlement went with X-Payment-Settled on a 2xx: true (settled) or queued (in flight). The SDK accepts the delivered resource once, without resending or calling the chain, and reports it as res.payment.merchantSettled. The mapping, shared with sellers that render it:
| seller says | buyer state |
|---|---|
| 402, no payment yet | challenge (unpaid) |
2xx + X-Payment-Settled: queued | delivered, settlement pending (merchantSettled: "queued") |
2xx + X-Payment-Settled: true | delivered (merchantSettled: "true") |
any replay refusal on the payment's own scheme (e.g. 402 payment_invalid with tx_already_used, nonce_already_used_locally, nonce_replayed_local) | settled_not_delivered (report, never re-sign) |
Real merchants send more than the spec. What the SDK tolerates, always without using it to sign or echoing it back:
chainId and networkV1 (accepted only when they agree with network), Bazaar's outputSchema, x402 v1 resource metadata (resource, description, mimeType), v1 alias fields (currency, maxAmountRequired, recipient, only when they agree), free-form extra, and options on other chains (skipped).PAYMENT-RESPONSE whose network is "base" or "base-sepolia" is mapped through the fixed table and checked against the network that was signed.X-PAYMENT-RESPONSE, and X-Payment-Settled: true | queued on a 2xx without a standard receipt (accepted once, reported as res.payment.merchantSettled; a well-formed X-Payment-Tx becomes settlement.transaction).tx_already_used, nonce_already_used_locally, nonce_replayed_local, "nonce already used"): classified settled_not_delivered, never re-signed.What it still rejects:
accepts (payTo, amount, asset, network, to, recipient, …): the whole 402 is refused. Every option inside accepts stays strict.payTo (a fresh deposit address per 402, as Browserbase does): it never matches the registered payee, so it is refused as payee_mismatch.Some live merchants still speak x402 v1 (Heurist Mesh, for example): the 402 comes as a JSON body, networks have short names ("base"), the price is maxAmountRequired, and the payment goes in X-PAYMENT. v1 is off by default and there is no global switch. Allow it for one merchant in its registry entry:
new MerchantRegistry([
{ origin: "https://mesh.heurist.xyz", payTo: "0xA112c9C8BF655c678c768B6fD42a1C6FbfeD7D60", network: "eip155:8453",
maxPerTx: 5_000n, pricePin: 1_000n, x402Versions: [1, 2] }, // default [2]; [1] allows only v1
]);
x402Versions; nothing is signed.base → eip155:8453, base-sepolia → eip155:84532. Options on any other network (e.g. solana) are skipped; if none is left, the 402 is refused.payTo, pinned USDC and EIP-712 domain (never taken from extra), price pin, maxPerTx, plan and period budgets, timeout bounds, allowedNetworks.X-PAYMENT ({ x402Version: 1, scheme: "exact", network: "base", payload: { signature, authorization } }, base64 JSON), the receipt is read from X-PAYMENT-RESPONSE with the same checks as v2, and res.payment.settlement.network is reported as CAIP-2. One signature per payment; retries resend the same header; confirmAuthorization works the same.payTo changes on every request (Browserbase hands out a fresh deposit address per 402) cannot be paid with a pinned payee and stays refused ("payTo … is not the merchant's registered address").dryRun reads a seller's answer the way pay.fetch does and returns what the policy would do. Nothing is signed and no key is needed. Every outcome is a value, so a seller that shows no terms doesn't look like a crash:
import { MerchantRegistry, commitPlan, dryRun } from "@deepfirstsearch/agent-pay";
const registry = new MerchantRegistry([{ origin: "https://api.example.com", payTo: "0x…", network: "eip155:8453", maxPerTx: 25_000n }]);
const plan = commitPlan(registry, [{ origin: "https://api.example.com", maxSpend: 100_000n }], 3_600_000);
const url = "https://api.example.com/v1/data";
const verdict = await dryRun(await fetch(url), { url, policy: { allowedNetworks: ["eip155:8453"] }, registry, plan });
// verdict.kind: "no_challenge" (no terms shown to this caller; not the same as free) | "invalid_402" | "allow" | "needsApproval" | "deny"
The same check from a terminal, with the registry as a JSON file (amounts in atomic units). It exits with code 1 on a refusal:
npx @deepfirstsearch/agent-pay check https://api.example.com/v1/data --registry registry.json
# allow: would pay 1000 atomic units (exact, eip155:8453) to 0x…
Sellers reprice and rotate wallets, and a pin refuses the moment they do. Run the check on a schedule to find out before your agent does. When the address or price no longer matches, it prints the registry edit that would accept the change, and never applies it: a 402 that names a new address is also what a hijacked server looks like, so confirm it through a channel that server doesn't control (the seller's announcement, a message from them). --manifest also reads the seller's /.well-known/x402 and says whether it lists the same terms as the 402. It's served by the same server, so agreement shows consistency, not that the change is genuine; disagreement means don't edit:
npx @deepfirstsearch/agent-pay check https://api.example.com/v1/data --registry registry.json --manifest
# deny: price_changed
# amount 3000 is above the pinned price 2000
# if the seller really changed its terms, the edit to merchant 0 (https://api.example.com) in registry.json would be:
# pricePin: 2000 -> 3000
# the 402 alone can't tell a rotation from a hijack: confirm it through a channel this server doesn't control before editing.
# manifest https://api.example.com/.well-known/x402: payTo 0x…, price 3000 atomic units
# agrees with the 402 (same server as the 402: that shows consistency, not that the change is genuine).
A lower price always passes, so only an increase or a new address stops a payment.
By default a payment echoes the 402's resource and nothing else: extensions such as Bazaar's are dropped, because they only leak metadata. Some sellers need one back. A seller that declares a Base Builder Code (ERC-8021) only gets its attribution into the settlement if the buyer echoes the builder-code extension in the payment. Allow that per merchant:
new MerchantRegistry([
{ origin: "https://402.com.tr", payTo: "0x973a31858f4d2125f48c880542da11a2796f12d6", network: "eip155:8453",
maxPerTx: 150_000n,
echoExtensions: ["builder-code"], // echo only this key, as the merchant declared it
builderCodes: ["bc_yourcode"] }, // optional: your own service codes, added as info.s
]);
notEchoed on payment.signed, and the payment goes ahead.builderCodes go first in info.s, then the merchant's own codes, with duplicates removed: the same order as the official client (@x402/core with BuilderCodeClientExtension). This is checked against a live 402 in the tests.accepts.commitPlan (plan-then-execute)taint / isTrusted (provenance)quarantinedExtract (dual LLM with schema validation)spotlight (datamarking)requiresHumanForEveryPayment (Rule of Two)RateLimiter, KillSwitchAuditLog / verifyChainscreen (optional sanctions screening, fails closed): screen: anyScreen(staticListScreen(list), oracleScreen(publicClient, oracle))confirmAuthorization (optional on-chain confirmation when a receipt is unusable): usdcAuthorizationCheck({ "eip155:8453": publicClient })@deepfirstsearch/agent-pay/testing exports the offline x402 merchant used by the demo and our own tests. Routes can be honest or hostile (tamper rewrites the 402), and it verifies the EIP-3009 signatures like a facilitator would:
import { startMockServer } from "@deepfirstsearch/agent-pay/testing";
const payTo = "0x1111111111111111111111111111111111111111";
const merchant = await startMockServer({
"/data": { price: 10_000n, payTo, body: '{"ok":true}' },
"/evil": { price: 10_000n, payTo, tamper: (r) => ({ ...r, payTo: "0x9999999999999999999999999999999999999999" }) },
});
// merchant.url, merchant.received (payloads it got), await merchant.close()
A route with x402Version: 1 speaks x402 v1 instead (402 in the body, X-PAYMENT, X-PAYMENT-RESPONSE); its payloads land in merchant.receivedV1, and tamperV1 rewrites the whole v1 402 body (to add a Solana option, say).
export AGENT_PAY_BURNER_SEED=0x… # the same 32-byte seed your agent's SDK uses for payer addresses
npx @deepfirstsearch/agent-pay owner create-vault --keystore ~/.foundry/keystores/owner
npx @deepfirstsearch/agent-pay owner fund --vault 0x… --amount 5 --keystore ~/.foundry/keystores/owner
npx @deepfirstsearch/agent-pay owner budget --vault 0x… --merchant 0x… --agent 0x… \
--per-tx 0.05 --per-day 0.50 --keystore ~/.foundry/keystores/owner
npx @deepfirstsearch/agent-pay owner status --vault 0x…
--network base-sepolia (default) or --network base. On Base mainnet every transaction asks for confirmation and warns that this is an unaudited beta.--keystore, password prompted) or AGENT_PAY_OWNER_KEY; never from arguments.budget derives the merchant's payer address from AGENT_PAY_BURNER_SEED, signs the budget, proposes it, and prints the intentId plus a ready-to-paste entry for the MCP server config.pause, unpause, revoke --intent, withdraw --amount, flush-fees, and --json for scripts.The official addresses and ABIs are exported too: AGENT_SAFE.base.factory, BUDGET_VAULT_FULL_ABI, BUDGET_VAULT_FACTORY_ABI, FEE_JAR_ABI.
Some merchants answer a paid request without a usable receipt (no transaction in PAYMENT-RESPONSE). The SDK then reports the payment as unconfirmed rather than guessing. To let the chain decide, pass confirmAuthorization: on that failure path only, it reads USDC's authorizationState for the exact authorization you signed.
import { createPublicClient, http } from "viem";
import { base } from "viem/chains";
import { createAgentPay, usdcAuthorizationCheck } from "@deepfirstsearch/agent-pay";
const pay = createAgentPay({
// ...registry, policy, payer, session
confirmAuthorization: usdcAuthorizationCheck({ "eip155:8453": createPublicClient({ chain: base, transport: http() }) }),
});
// res.payment.confirmedOnChain === true when the receipt was unusable but the authorization was used on-chain.
If the authorization was used but the merchant didn't deliver, you get a clear error and nothing is resent.
examples/real-merchants.ts (in the repository; examples are not shipped in the npm package) pays three public x402 APIs (Blockchain.com, Spraay, CoinGecko) through the SDK, 0.012 USDC per run, then shows an injected 402 for 5 USDC being refused before anything is signed. The payer needs a few cents of USDC on Base and no ETH.
git clone https://github.com/DeepFirstHQ/deepfirstsearch && cd deepfirstsearch/sdk && npm ci
PAYER_KEY=0x… npx tsx examples/real-merchants.ts # a throwaway key, never a wallet that matters
Thirty-four merchants, each tested with a real payment, have a step-by-step guide at deepfirstsearch.com/developers.
examples/sepolia-live.ts runs a real x402 payment end to end with your own testnet keys: it creates a vault, signs a budget, waits out the timelock, then an agent pays a local x402 endpoint and the public facilitator settles it on-chain.
cast wallet new, and any merchant address.git clone https://github.com/DeepFirstHQ/deepfirstsearch && cd deepfirstsearch/sdk && npm ci
export OWNER_PK=0x… AGENT_PK=0x… MERCHANT=0x… BURNER_SEED=0x$(openssl rand -hex 32) \
FACTORY=0xf245d3cb8700a804432ea50b253a923b4b32c0c7 STATE_FILE=./sepolia-state.json
npx tsx examples/sepolia-live.ts setup # vault + funding + signed budget; the agent gets gas from the owner
# wait for the 1 h timelock printed by setup
npx tsx examples/sepolia-live.ts pay # prints the settlement transaction
Never reuse these keys on mainnet.
| Network | BudgetVaultFactory | FeeJar | Status |
|---|---|---|---|
| Base Sepolia (84532) | 0xf245d3cb8700a804432ea50b253a923b4b32c0c7 | 0x4ae59cf9462d1601de4fc5aa4538d376e93a79f2 | v0.4, testnet |
| Base (8453) | 0xDe17e1B889efa4671852e0b268e100967A7a257E | 0xa375245D25bdB557801Ad07A50c19b3442cA3Ae4 | v0.4, unaudited beta: small amounts only |
Full list and transactions: docs/DEPLOYMENTS.md. Questions or bugs: open an issue; security reports go through SECURITY.md.
The owner, never the agent, signs one budget per merchant. The budget names the agent key, the merchant and the only payer address the vault may top up:
import { hexToBytes, type Hex } from "viem";
import { burnerAddress, signIntent } from "@deepfirstsearch/agent-pay";
const ownerSeed = hexToBytes(process.env.AGENT_PAY_BURNER_SEED as Hex); // the same seed the agent's payment process uses
const intent = {
agent: agentAddress,
counterparty: merchant.payTo,
burner: burnerAddress({ ownerSeed, vault, chainId: 8453, counterparty: merchant.payTo }),
token: USDC,
maxPerTx: 50_000n, // 0.05 USDC
maxPerPeriod: 500_000n, // 0.50 USDC per day
trancheCap: 100_000n, // the payer never holds more than 0.10 USDC
period: 86_400,
validAfter: 0n,
expiry: BigInt(Math.floor(Date.now() / 1000) + 30 * 86_400),
nonce: BigInt(Date.now()),
};
const signature = await signIntent(owner, vault, 8453, intent);
// Anyone can relay it: vault.proposeIntent(intent, signature). It activates after the vault's timelock.
Who holds what:
maxPerTx, maxPerPeriod and trancheCap, and each payer never holds more than trancheCap. Revoke the intent and rotate the seed (epoch).vaultFunder({ agent, publicClient, vault, usdc, intents, tranche, dataSuffix }) returns an ensureFunded hook that tops up each merchant's payer from the BudgetVault right before a payment. The vault enforces every limit on-chain, and each top-up counts as one transaction: keep tranche at or below both the intent's trancheCap and its maxPerTx, or fundBurner reverts with OverPerTx/OverTranche. dataSuffix appends your Base Builder Code (ERC-8021 attribution) to those transactions.
createFundingService({ registry, payerFor, withdraw, limits }) runs in the owner's process and holds the exchange credentials. The agent uses remoteFunder(url, token) as its ensureFunded hook and can only ask "top up the payer for merchant X". The service derives the payer address itself and applies a per-top-up limit and a daily limit. On-chain, the public sees "exchange → fresh payer" instead of "your vault → fresh payer". The exchange and lawful authorities can still see it. You provide withdraw(to, amount) for your exchange's API.
npm ci
npm test # includes an anvil end-to-end test when Foundry and ../contracts/out exist
npm run demo
npm run build
FAQs
Policy-gated x402 payments for AI agents: deterministic spend policy outside the model, prompt-injection guards, per-merchant payers and on-chain Agent Safe budgets on Base.
The npm package @deepfirstsearch/agent-pay receives a total of 109 weekly downloads. As such, @deepfirstsearch/agent-pay popularity was classified as not popular.
We found that @deepfirstsearch/agent-pay demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.

Security News
Anthropic’s free OSS Scanner uses Mythos and other Claude models to find open source vulnerabilities and send maintainers reports with proposed fixes.

Security News
On Front Lines’ Early Adopters: CISO podcast, Andrew Becherer shares lessons from Datadog, weighs the long-term costs of AI-built tools, and explains how founders can actually gain a CISO’s trust.