roundstorm: a per-launch token, four cleared advisories, and a Tauri shell that compiles in CI
roundstorm’s desktop app now generates
32 random bytes on every launch, hands them to the daemon as ROUNDSTORM_TOKEN
and injects them into its own page, and the daemon refuses /api and /ws
without it — unset, the headless CLI and every script behave exactly as before.
It is narrower than “auth”, and SECURITY.md now says so: it stops another
process on the loopback socket and another user on a shared machine, and makes
ROUNDSTORM_HOST=0.0.0.0 defensible, but not malware running as the user
(17618e7).
deck-lovers: the projector disconnect survives cancellation, and the deploy script reads .env
deck-lovers’ /ws disconnect cleanup
ran unshielded, so a cancelled task could skip telling the audience the
projector had left and leave stale clients behind — Starlette’s TestClient
cancels the app task right after sending the disconnect, which made the
websocket tests hang about four runs in ten. The cleanup is now wrapped in a
shielded anyio cancel scope, two test races are gone, and a regression test
cancels the handler the way the client does
(3c4e938).
cdclkit: the trail copy stops going quadratic on decision-only searches
cdclkit’s target-phase search copied the whole trail into the target at every new deepest trail. The comment said improvements become rare quickly, which is true once conflicts start and false before: a search that makes many decisions without one improves on every decision, so n decisions copied 1, 2, …, n entries — 65,536 declared variables and one unit clause took 144 s in Python, 131,072 took 4.2 s natively, and each 4× in variables cost about 13–19× (e43a4ac). A counter now records how much of the trail the target already holds, only the new suffix is copied, and the target still ends up byte-for-byte what the full copy produced.
dratify 0.1.7 fixes a soundness hole, and says why PySAT's CaDiCaL proofs fail
dratify 0.1.6 fixed the README’s PySAT example — it loaded formulas with PySAT’s own parser, which stops at the % line every SATLIB file ends with — and closed a release path that let workflow_dispatch publish from a branch, with the pypi and crates environments now restricted to v* tags (108dd50, 5d0bb47). 0.1.7 is the security fix: three checker bugs found while tracing why PySAT’s CaDiCaL proofs do not verify, one of them a soundness hole where the pure-Python checker accepted refutations of satisfiable formulas when a list of steps held a negative literal — reachable through check_proof, though not from cdclkit’s own solver, and text proofs were never affected — alongside an unbounded literal that could size arrays for 10¹¹ variables and two false rejections, both checkers refusing valid proofs in which a lemma arrived unit at the root (61165f9). The PySAT failure itself is not the checker’s: the binding reads every CaDiCaL proof before flushing it, and with the documented workaround all four CaDiCaL versions verify 50 of 50, where glucose and Lingeling already did (2823854).
cdclkit 0.1.4 requires the dratify that checks proofs correctly
cdclkit 0.1.4 moves both halves onto dratify>=0.1.7, whose three fixed checker bugs include two that reach this package: --self-check could reject a valid proof, and the pure-Python checker could accept a refutation through check_proof (d33f84a). Publishing is now tag-only — the environment restricted to v* tags, the workflow_dispatch trigger gone, and the version check failing outright on any other ref — and make smoke, broken since the dratify split, now ties its module list to the source tree, with a packaging CI job that builds the wheel, installs it cleanly and runs it (b7b5426). Python 3.15 rc3 joins the test matrix (including a native-engine leg) and the README example is now executed by a test; the runbook’s recorded Sigstore gap was already covered, since every file on PyPI has carried a PEP 740 attestation since the first Trusted Publishing release.
pickmydegree stops tracking its own generated coverage reports
pickmydegree had been committing the
HTML output of its test-coverage run, and CodeQL flagged the Istanbul report’s
sorter.js assets as js/xss-through-dom inside that generated output
(11e72fc). Those files
are test artifacts rather than application code, so coverage/ is now ignored
and the committed reports left the tree — roughly 22,800 lines of generated HTML
and CSS removed and three added, merged as
PR #2. The app source is
untouched; only the repository stops carrying a copy of its own coverage run.
pacenotch fingerprints credentials by expiry instead of hashing the token
pacenotch notices that Claude Code has
refreshed its OAuth token by fingerprinting the credentials it reads, and that
fingerprint used to be a SHA-256 of the access token — which CodeQL flagged as
go/weak-sensitive-data-hashing
(a63ee1f). The hash never
left memory, but hashing the secret was never needed to detect a refresh: the
fingerprint is now claudeAiOauth.expiresAt, a value every refresh moves and
which is not secret at all, while a PACENOTCH_TOKEN cannot change while the
program runs and gets a fixed fingerprint. It is the second CodeQL-driven
correction in pacenotch’s credential layer after the recovery path stopped
re-reading the token.
deck-lovers: the projector password stops living in the browser cookie
deck-lovers’ /login handler set the
proj_auth cookie to the projector password itself, so the plaintext password
lived in every presenter’s browser — which CodeQL reports as
py/clear-text-storage-sensitive-data. The server now issues a random
per-process session token and compares password and cookie in constant time,
which also means a server restart invalidates existing projector cookies; two
converter test assertions that matched the bare fonts.googleapis.com hostname
(py/incomplete-url-substring-sanitization) now check the exact stylesheet URL
built by google_fonts_css_url()
(fa4dfce).
portcullis: Phase 1 stops accepting any SSH host key
portcullis replaced paramiko’s
AutoAddPolicy — which accepts whatever key a host presents — with an explicit
trust-on-first-use policy (25da71b).
Phase 1 is the first contact with a VM created seconds earlier by the same
process, so there is no prior key to compare against and Hetzner does not
publish the fingerprint through its API: the new policy accepts that first key
once, logs its type and fingerprint, and Phase 2 still connects with that exact
key pinned, so a later substitution is detected. The change addresses CodeQL’s
py/paramiko-missing-host-key-validation alert, and the test that asserted the
blanket policy now asserts the new one. The secret-handling pass of 21
September had already pinned the key Phase
2 connects with; the first contact was still an unconditional accept.
portcullis stops printing its own secrets and pins the host key across phases
portcullis hardened its own handling of
the secrets it moves around during provisioning
(f41693f): it no longer
logs the first and last four characters of HCLOUD_TOKEN, keys/id_rsa is
created 0600 from the start rather than being written and then narrowed, and
the smtp.env values are shell-quoted before Phase 2 sources them as root —
passwords containing $, spaces, quotes or backticks had been mangled or
executed, and the file is now chmod 0600 on the VM before credentials are
written into it. Phase 1’s SSH host key is pinned and Phase 2 rejects a different
one immediately, so a swapped host cannot receive the second phase’s credentials,
and preflight validation now runs before any Hetzner resource exists.
A dependency-advisory sweep across the JavaScript, Python and Go repositories
A pass over open dependency advisories bumped the affected packages in every
repository that carried them. On the JavaScript side:
pickmydegree (vitest,
happy-dom, vite, surge),
storygen (vitest 4, vite
6, react-router 7), solids-hunter
(vitest 4, vite 6.4.3), eclipse-3d-sim,
lifechess,
agility-trainer and
they-live-agent
(vitest 4.1.11), platosdf
(vitest and its coverage package, 5.0.1), with lockfile refreshes in
deck-lovers and express
updates in neon-bumper-cars
and collective-starship-game.
portcullis: a two-phase hardened Ubuntu VM for Hetzner Cloud
portcullis is a new public Python
repository: one command creates a hardened Ubuntu 26.04 VM on Hetzner Cloud, and
it drops the gate first — an unprivileged user with key-only SSH on a random high
port, root locked, UFW default-deny and sysctl hardening, all inside about 30
seconds and using only what the stock image already ships — then switches the
Hetzner firewall to the new port and deletes the temporary API key before the
full CIS-style pass (package upgrades, AppArmor, auditd, AIDE, PAM policy,
fail2ban, rkhunter, msmtp alerts, Docker and Podman) runs behind both firewalls.
verify.sh then runs
58 checks on the finished host. Everything runs inside a Podman container so
nothing is installed locally, and teardown deletes a half-provisioned server
together with the keys and firewalls it created. The
README was rebranded as
the repository went public, with a
GitHub Actions test workflow
on actions/checkout@v5 and a
logo.
portcullis: Vim stops stealing the terminal's mouse selection
portcullis hardens a fresh Ubuntu host,
and hardening has to leave the machine pleasant to actually use: Ubuntu’s Vim
defaults enable xterm mouse reporting, so on a remote terminal Vim captures the
selection and ordinary copy/paste stops working. A new Phase 2 section,
1.6a — Terminal editor defaults, now
ships /etc/vim/vimrc.local
with set mouse= (and a note in the README’s Phase 2 list), leaving selection to
the terminal emulator while anyone who wants Vim’s mouse support can opt back in.
kiwifarmit/cra-lab is public: CI/CD pipelines that produce CRA evidence
kiwifarmit/cra-lab is now public — a
small lab in the Kiwifarm org that builds the pipeline half of a Cyber
Resilience Act story instead of describing it. Pull requests go through Semgrep
SAST plus SCA and IaC scanning (Semgrep rather than CodeQL, which needs GitHub
Advanced Security on private repos, and it runs entirely on the runner so the
code never leaves it); a v* tag generates a CycloneDX SBOM with Syft, hashes
it, hands the digest to the SLSA generic generator for provenance, and runs
Trivy over the release SBOM at CRITICAL/HIGH before attaching it to the release
(pr-security.yml,
release-security.yml).
A weekly Trivy and Semgrep sweep covers vulnerability, misconfiguration and
licence scanning on a timer, and Dependabot keeps the actions the workflows
themselves depend on pinned, with each workflow’s comments citing the CRA
Annex I Part II clause it answers.
dash: a serverless DMARC aggregate-report parser for Gmail
dash parses DMARC aggregate reports in a serverless setup for Gmail: it extracts and parses incoming reports, enriches the failing sources, and emails a summary of which senders are failing DMARC and why. The repo is public and actively developed.