Kernel security patches for Armbian: gathered, verified, and the missing ones backported.
A standalone tool for Linux kernel security patches, tracking 6.18 LTS and 7.3 (configurable).
- Gather. Collects fixes not yet in a stable release, from the stable queue and from mainline.
- Verify. Checks that they apply as a series, with no fuzz, on the current stable head.
- List. Reports every known security issue that is still unpatched.
- Make the missing patches. Builds work packages for them, so they can be backported with Claude.
It is not tied to any build system. The output is plain patch sets that anything can consume.
./fasttrack.sh sync # pull sources; linux.git is shallow from v<oldest series>
./fasttrack.sh run # gather + verify; see report.md
./fasttrack.sh cve --unpatched --claude # backport every unpatched issue with Claude
| Source | Gives | Role |
|---|---|---|
stable-queue.git |
stable maintainers' backports queued for the next point release | preferred; days ahead of the release |
vulns.git |
CVE records: mainline fix commits, per-series affected / fixed-in | decides what is a security fix and what is still open |
linux.git master + linux-X.Y.y |
the base to verify on; mainline commits to pick | — |
kernel.org releases.json |
latest release per series | warns when the mirror is stale |
| CISA KEV | actively exploited CVEs | urgency (issue in CI) |
linux.git is a full clone of armbian/linux (about
4 GB). It mirrors linux-stable, with master and the linux-X.Y.y branches, and its heads
match kernel.org. Full history means every fix commit has its parent, so patches come
straight from git. shallow: true is still available; it cuts history at the oldest
series' release tag. Fixes that sit on that cut are then downloaded as patches from
git.kernel.org. A configured series with no stable branch yet
(trunk before its first point release) is skipped with a warning.
Writes to the repository itself (out: .), and CI commits the result:
patches/6.18/series.conf apply order
patches/6.18/0001-<sha12>-<slug>.patch upstream patch + X-Fasttrack-* provenance headers
patches/6.18/base stable commit the set was verified on
patches/6.18/fasttrack.json provenance (CVE, source, KEV)
report.md what was included, what was not, and why
unpatched.json open security issues: CVE, fix commit, files, prerequisite hints
Each candidate gets one verdict:
| Verdict | Meaning |
|---|---|
accepted |
applies with no fuzz on the stable head plus all earlier accepted patches |
needs-backport |
does not apply unchanged; goes to unpatched.json |
already-present |
reverse-applies: stable has it even if the CVE record lags |
commit-missing |
fix commit not in the mirror yet |
run never adapts anything. The patch set contains only upstream bytes.
Nothing accumulates. patches/<series>/ is rebuilt from scratch on every run, so a
fix drops out as soon as the stable head carries it, in either of two ways:
- The CVE record says so: it lists a fixed-in version at or below the stable head.
- The stable tree shows it: the patch reverse-applies on the stable head. This catches fixes that shipped while the CVE record still lags.
A series directory with nothing left is deleted. report.md records each drop as a
retired event.
fasttrack prune does the same for incident results under cve/<ID>/<series>/. It runs
hourly in CI, and a set is deleted once every patch in it reverse-applies on the stable
head, or once the head reaches the version that carries the fix.
./fasttrack.sh cve CVE-2026-1234 CVE-2026-5678 # by id
./fasttrack.sh cve --commit <sha> --id my-incident # no CVE record yet: work from the fix commit
./fasttrack.sh cve --unpatched # everything the last run left open
--series 6.18 only this series
--adapt also try a 3-way cherry-pick (marked "adapted", for review)
--package write a backport work package for what still does not apply
--claude work package + run Claude Code on it (implies --adapt)
--retry run Claude again where attempts.json already has a result
Per series it goes down a ladder and stops at the first rung that works:
- Not affected, or already fixed at the stable head: nothing to do.
- Stable-queue backport, applied unchanged.
- Mainline commit, applied unchanged.
- A backport the author posted to the stable mailing list, applied unchanged (
stable_list: true). - 3-way cherry-pick (
--adapt). - A Claude backport in a work package (
--claude). With--unpatched, only for fixes older thanclaude_min_age_days(default 21): stable usually takes newer ones itself.
Results are written to cve/<ID>/<series>/ (same layout as patches/), with a summary in cve-report.md.
Each result has one outcome: patched, patched-needs-review, partial, unpatched, fixed-in-tree, not-affected, already-present or no-fix-known. The exit code is 1 while anything is still unpatched.
work/<ID>/<series>/ contains:
tree/: a worktree at the stable head, with every part of the fix that did apply already committedupstream-<sha12>.patch: the fix that does not applyBRIEF.md: the CVE description, the conflicting files, and mainline commits on those files that the series lacks (likely prerequisites). It also gives the rules: smallest change, keep upstream logic, one commit per upstream commit withcommit <sha> upstream.and a[ Backport: ... ]note, compile-check touched files, and writeRESULT.md.
You can use it in two ways:
- Interactive.
cd work/CVE-…/6.18 && claude, then ask it to follow BRIEF.md. You stay in the loop. - Headless.
--clauderunsclaude_cmdfromfasttrack.yaml(default:claude -p, edits allowed, shell limited to git/make/grep/sed/ls/cat). Set--claude-modelto choose the model.
attempts.json on main records every Claude run: date, outcome and cost. A CVE that Claude already tried is skipped, so a rerun of --unpatched pays only for new issues. Pass --retry to run it again.
Either way, the tool then takes Claude's commits and re-verifies the whole series, with no fuzz, on a fresh tree. It marks them claude-backport and the outcome patched-needs-review. A Claude backport is never mixed into patches/. It stays under cve/ until a human reviews it. In CI it arrives as a PR.
.github/workflows/fasttrack.yml runs on a standard GitHub-hosted runner. No special runner or persistent disk is needed.
- Cache:
data/(the shallowlinux.git,vulns.git,stable-queue.git) lives in the Actions cache. A fresh cache entry is made each ISO week, and between those every run starts from the last entry and fetches only what is new. Changingfasttrack.yamlstarts a new cache. - Hourly: sync,
run, thenprune. Commitspatches/,cve/deletions,report.md,unpatched.json,state.jsonandevents.jsontomainwhen they changed. A run with nothing new commits nothing. Opens an issue labelledsecuritywhen a pending CVE is actively exploited. - Manual dispatch: takes CVE ids, or
unpatched, plus an optional fix commit and a Claude toggle. Results go tocve/on a branch, with a PR intomainfor review. Claude in CI needs theANTHROPIC_API_KEYsecret.
python3 -m unittest discover -s tests
The tests build a synthetic kernel history covering every verdict and outcome, including a 3-way adapt and a work package completed by a stand-in for claude.
- "Applies" is not "correct". Verification proves textual application on the stable head. Compile and boot testing belong to the consumer. Claude is asked to compile-check, and reports what it could not verify.
- Prerequisite hints are heuristic. They list mainline commits on the same files whose subject is not in stable.
- Claude backports need human review. Read
RESULT.mdand the[ Backport: ]notes before shipping one. series:is explicit. Update it when trunk moves on.