Engineering & Technical

The Engineer

The Signal

The TeamPCP arrests ended the worm, not the 500,000 credentials it already took.

The propagation path ran through Trivy, KICS, LiteLLM and Telnyx. Those four sit inside CI, holding registry tokens, cloud keys and model-provider PATs. Roughly 78,000 of those secrets from 2,200 organizations were published in July, which makes the remaining question how wide your rotation has to go.

In Play

  1. Build Runners Were The Target, Not The Packages

    The Australian Federal Police arrested two alleged TeamPCP operators in Perth, but the crew's self-propagating worm had already compromised Trivy, KICS, LiteLLM and Telnyx and harvested more than 500,000 credentials, per Risky.Biz. Those are CI-resident scanners plus a proxy that concentrates every model-provider key in one process, so the loot was registry tokens, cloud keys and PATs. About 78,000 secrets from roughly 2,200 organizations already leaked publicly in July. That makes this a retrospective rotation problem, not a patch.

    Ask Clarity
    Try
  2. Exploited Bugs That Carry No Severity Score

    CISA added nine actively exploited CVEs between August 18 and 25, including Gitea CVE-2026-60004 at CVSS 9.8 and an MLflow tracking-server SSRF, per SANS reporting. Gitea turns plain repository write access into code execution as the service account by planting an executable Git hook, and upgrading does not remove hooks already written to disk. Several of those entries carry no NVD score at all, so a severity-sorted remediation queue ranks confirmed exploitation below a medium dependency warning.

    Ask Clarity
    Try
  3. Prefix Caches Miss On Chunk Order

    Anthropic's prompt cache walks backward a maximum of 20 blocks looking for an earlier write, so pushing more conversation than that between two calls stops hits entirely — no error, no warning header, just a bigger bill. vLLM keys prefix blocks by a hash chain over token IDs, which makes matching order-sensitive by construction: two RAG requests retrieving the identical document set in a different order share exactly zero blocks. Your single hit-rate dashboard mixes prefill savings with a semantic cache that can return a wrong answer with a 200.

    Ask Clarity
    Try
  4. Write Cost Decided GraphRAG's Fate

    LinkedIn replaced GraphRAG in its production hiring assistant with a hierarchical summarization tree because incremental update cost, not retrieval quality, was the binding constraint. Absorbing one new user signal in a graph means entity resolution plus edge re-linking across an unbounded neighborhood, while a tree re-summarizes a single path root-ward. Benchmarks published in the same window put RocksDB write amplification at 4.56x for random inserts versus effectively none for sequential keys — the same lesson at the storage layer.

    Ask Clarity
    Try
  5. SNI-Based Egress Policy Loses Its Signal

    Google is shipping Encrypted ClientHello at the OS level in Android 17, covering all device traffic, per Risky.Biz. Any egress allowlist, DLP rule or CI network guardrail that reads the plaintext SNI field to decide what to permit loses that input, and it will not raise an error — it will simply stop seeing destinations. The named migration paths are explicit forward-proxy CONNECT logging or DNS-layer policy with pinned resolvers, and doing it on your schedule beats discovering the gap in an incident review.

    Ask Clarity
    Try

Deep Dives

Credential Density Was The Targeting Function

Signature checks, SBOM diffing and vulnerability scanners all passed clean through this campaign, which is why rotation scope — not patch speed — is the decision in front of you.

Risky.Biz places the compromised releases of Trivy, KICS, LiteLLM and Telnyx in a January-to-August 2026 exposure window. The Hacker News dates the Trivy and Checkmarx compromise to March 2026. That disagreement is the rotation window, and the two accounts do not overlap cleanly. Neither states the entry vector per project. Trivy ships as a Go binary and container image rather than a language-registry package, so compromised release automation is at least as plausible as a stolen registry account. Treat January 2026 as the start date until a per-project advisory narrows it.

The worm never touched the artifact layer. It waited for install on a developer machine, stole stored credentials, then republished itself into other libraries the same maintainer owned. The release is signed by the real publisher and the SBOM version checks out, so no scanner has a CVE to match. The attack traverses the human trust graph while almost every supply-chain control on the invoice watches artifacts.

Kill chain stageControl that breaks itControl that does not
Delivery of a signed releaseDelayed-adoption quarantine on new versionsSignature verification, SBOM diffing
Activation at install timeInstall-script disablement, hermetic offline resolutionStatic dependency scanning
Credential theftShort-lived OIDC federation, per-job token scopingRepo secret scanning
ExfiltrationDefault-deny egress with proxy allowlistEndpoint AV on the runner
Self-propagationPublish only from a hardened CI identity2FA on the registry account alone

The two exploited bugs sitting in the same blast radius

Gitea CVE-2026-60004 is the same mechanism from the other side. An attacker with repository write access, from a leaked PAT or an over-permissioned bot, posts a malicious patch to the diffpatch endpoint. Gitea writes an executable file into the repo's hooks directory, and the next matching Git operation runs it as the Gitea service account. That account holds every repo on disk plus webhook secrets, runner registration tokens and usually registry push credentials. Admin is never required. SANS is explicit that upgrading does not remove hooks already planted, so the order is patch, diff every hooks directory against defaults, rotate.

The MLflow SSRF is the ML-platform variant, and it sits next to an ISC diary on mass scanning for cloud metadata services. Read together, the chain closes: reach an unauthenticated tracking server inside the VPC, coerce it into fetching 169.254.169.254, collect the instance-profile credentials it carries for artifact-store access. IMDSv2 with hop limit 1 and default-deny egress break that chain on an unpatched tracking server. The work lands in the metadata service config and the subnet egress rules.

Two arrests closed the case in Western Australia, but the stolen credentials still authenticate and the planted hooks still execute, so rotation and hook diffing are still owed.

The move

Sources agree on the sequence and disagree only on scope. The Hacker News makes the sharpest structural point: OIDC-federated short-lived credentials need no rotation at all, which is the argument for the migration that keeps getting deferred. The rest is mechanical. Runners get destroyed per job, sub-hour OIDC tokens replace every static secret, and scanners run in a separate job with no deploy credentials and egress limited to the vulnerability-database endpoints. Pin scanner images by SHA256 digest while in there, because aquasec/trivy:latest inside a job holding a cloud role is the exact configuration this campaign was designed against.

What to do

  1. Rotate every long-lived credential readable from a CI runner or developer workstation between January and August 2026 — registry tokens, static cloud keys, PATs, model-provider keys, webhook secrets — starting with jobs that invoked Trivy or KICS.

  2. Patch self-hosted Gitea past CVE-2026-60004, then diff every repository's .git/hooks against defaults and rotate the Gitea service account's runner tokens and registry credentials before Friday.

  3. Front every MLflow tracking server with authentication and enforce IMDSv2 with hop limit 1 plus default-deny egress on all ML infrastructure this sprint.

Four Caches, One Dashboard, One That Can Be Wrong

Three of the caches in your inference path can only cost money; the fourth silently changes answers — and your RAG layer is probably defeating the cheap one for free.

The mechanism that makes prefix caching fragile decides where the prompt-assembly boundary goes. vLLM stores KV in fixed 16-token blocks, each identified by a hash over the parent block's hash plus its own token IDs. That chaining turns a block lookup into a prefix lookup: a block matches only if everything before it matched. The scheduler walks incoming blocks in order and stops at the first miss; the rest is fresh allocation and fresh prefill. Only complete blocks are indexed, so the block straddling stable prefix and variable content is recomputed every request and unshareable. Prefix cache and running batch draw from the same GPU memory pool, so a bigger cache buys fewer concurrent sequences.

The write premium is not the exposure

Do the arithmetic before arguing about the 1.25x write surcharge. For N requests over a shared prefix of T tokens, uncached costs N·T and cached costs T·(1.25 + 0.1·(N−1)). Crossover is N ≈ 1.28; one reuse inside the TTL pays for the write. The leak is writes that are never read: a flat 25% surcharge for nothing, from TTL expiry, misplaced breakpoints and invalidation. The invalidation list is longer than most prompt-review checklists.

  1. Any variable ahead of stable content, such as a timestamp, request ID or username in the system prompt, invalidates every block after it.
  2. Tool schemas usually render ahead of the system prompt, so nondeterministic serialization order nukes the whole cache.
  3. Web search, citations, thinking config and tool_choice toggles rewrite the rendered prompt. Feature flags carry an inference bill.
  4. A/B testing two reasoning efforts splits the cache in two, and nobody bills that to the experiment.
  5. Summarizing history rewrites the prefix and pays cold price; truncating tool outputs in place keeps it byte-identical.
  6. Entries are model-scoped, so routing a long conversation to a cheaper model re-prefills the full history cold.

Where the two sources converge

The independent PagedAttention reporting lands in the same place from the serving side: ~96% cache utilization, copy-on-write sharing of identical prefixes, 2–4x more users per GPU with byte-identical attention outputs, and it fires only on exact prefix matches. That kernel already ships in vLLM and SGLang, so the remaining lever is what you feed it. For RAG, sort retrieved chunks by a stable content hash or doc ID instead of relevance score, and pad the stable region to a block boundary. That trades positional quality for cache determinism, so A/B it on answer quality and TTFT.

Capacity planning changes too. Decode is memory-bandwidth-bound: the whole KV cache streams from HBM every step, and roughly 40 GB of KV covers one 128K context on a 70B BF16 model. On 640 GB of HBM with ~140 GB of resident weights, that is about a dozen concurrent max-context requests. Model concurrency-at-context, not FLOPs.

Hit rate is not one metric. Split it into prefill saved and wrong answers served, because only one of those has a severity.

That second half is the semantic cache. Its failure modes are geometric rather than threshold-tunable: negated sentences sit close together in embedding space, and two prompts sharing a sentence frame but differing in one operational value score as near-identical because the frame dominates the vector. Published thresholds span 0.75 to 0.97. The number is a property of your traffic.

What to do

  1. Instrument per-route, per-turn cache accounting from provider usage payloads this sprint — cache_creation versus cache_read versus uncached input tokens — and alert when read tokens collapse mid-conversation or the creation:read ratio approaches 1:1.

  2. Change RAG chunk rendering to a deterministic stable-hash order, pad the stable prefix to a 16-token multiple, and A/B it against relevance ordering on both answer quality and TTFT before the next serving-capacity review.

  3. Gate every semantic cache hit behind an exact normalized-key lookup plus a parameter guard on numbers, IDs, dates, entities and negation tokens, and shadow-sample a percentage of hits against live calls to report wrong-answer rate.

LinkedIn Cut GraphRAG On The Write Path, Not The Read Path

Two independent accounts of agent memory land on the same conclusion: retrieval quality is not what production bills you for, and key ordering is a 4.5x lever hiding in your state store.

The architecture is the part of LinkedIn's decision worth copying. Its hiring assistant runs four memory types: conversational, episodic, semantic, procedural. Behind them sit three dedicated services: ingestion, consolidation and retrieval, each with access control, traceability and a hard latency budget. Most teams shipping agent memory build ingestion and retrieval and skip consolidation. Consolidation is the service that keeps memory from degrading into an append-only landfill of stale preferences.

The trade should be priced before it is copied. A tree gives up the arbitrary multi-hop relational queries a graph hands you for free. Picking it is a bet that personalization needs freshness more than relational reach. Where the differentiating queries run three hops deep, the same cost analysis lands somewhere else.

The same finding, one layer down

An independent line of analysis reaches the write path from the multi-tenant side. Agent memory as knowledge graphs produces one graph per user, per team, per project. That is millions per enterprise customer, most cold at any instant, with each query reading exactly one. General-purpose graph engines assume the opposite: one large resident graph, with a parse and optimizer pass ahead of every request. On sparse workloads cost then tracks provisioned memory rather than reads served, and tenant isolation collapses into a filter over one shared graph. That is a unit-economics problem sitting one bad predicate away from cross-tenant exposure. Vendor latency claims in that space arrive without disclosed graph sizes, query shapes or hardware. Take the architecture; make them reproduce the benchmark.

Then check the state backend

Flink and Kafka Streams both use RocksDB as the state backend, so anyone running either already owns this problem. Benchmarked across workload shapes:

Workload shapeMeasured effectWhat it costs you
Random inserts (UUID or hash-prefixed keys)4.56x write amplificationDisk throughput, compaction CPU, NVMe endurance
Sequential keysEffectively unamplifiedNothing — the lever is key design alone
Delete/TTL-heavy1.58x transient space amplificationAn 80% disk alarm fires too late to prevent a compaction stall
Growing L0 file countsRead latency near flat with bloom filtersReads are not where your problem is

A 4.5x I/O and SSD-wear lever living in the key schema is the cheapest performance work available to a streaming team. The space-amplification number also says the usual disk-headroom alarm threshold is miscalibrated. TTL-heavy stores want at least 40% free.

What not to act on yet

Two adjacent items are evaluation criteria, not mandates. LogDrive decomposes the shared log into an AtomicLog sequencing layer plus a durability layer of flat single-value registers with weakTail, which makes striping and quorum replication reusable over S3, S3 Express or DynamoDB. It is the clean formal skeleton under the diskless-Kafka wave, and it is good work. weakTail is the word to circle: approximate tail visibility is a real semantic change for any consumer that assumes strictly monotonic tail reads. Read it as a vendor questionnaire, not a migration plan.

Production bills for incremental update cost. Benchmark cost-per-update before you benchmark recall.

What to do

  1. Audit the key schema of every Flink or Kafka Streams RocksDB state store for random versus sequential insert order this sprint, re-benchmark write amplification, and raise disk headroom to at least 40% on TTL and tombstone-heavy stores.

  2. If an agent-memory design is in flight, prototype a hierarchical-tree summarization index alongside the graph option this quarter and compare cost-per-update, not retrieval quality alone.

The bottom line

Attackers and invoices are collecting on the same thing: state your systems leave lying around between jobs. Credentials that outlive the process that needed them, sessions with no expiry, prefixes assembled in whatever order a retriever happened to return, keys chosen for convenience rather than locality — all of it ambient, and ambient state is exactly what neither your scanner nor your cost dashboard can see. Assign one owner to inventory every place a build job, an agent, or an inference request inherits state it did not create, then give each of those a lifetime and a deterministic identity.