Phase log 87: OpenSSF silver and gold work, year roadmap, SPDX headers, CI speed-up, trust badges, 0.56.1
Date: 2026-10-18. Version 0.56.1 (bumped, nothing committed or pushed). Branch at the time of writing: main
(PRs #37 and #38 were merged by the maintainer earlier in the day; v0.56.0 was published at 15:57 UTC).
Everything below is uncommitted in the working tree. Handoffs are gitignored; never put this file in a commit line.
Goal
The session started as "read past conversations for bestpractices.dev data and fill the Silver form" and grew, in the maintainer's order, to:
- Fill OpenSSF Best Practices Silver, then Gold, as truthfully as the repository allows, and fix what could be fixed in code or docs to move criteria to Met.
- A one-year plan (to 1.0.0) naming every remaining phase, put on the README and the website.
- Find the real Rust coverage number without a long local run.
- Why v0.56.0 had no release for 45+ minutes, and make the pipeline faster: tests only for the part that changed, release not 4 to 5 times longer than needed.
- Codecov-style reporting for Rust, the Codecov tree graph, and trust badges on the README and the website.
- Bump to 0.56.1 because code changed.
Current Progress
All numbers below were produced in this session unless marked "not run".
OpenSSF forms (nothing is submitted; the maintainer submits)
~/Code/bestpractices-silver.ps1(47 answers, 3 tabs) and~/Code/bestpractices-gold.ps1(16 answers, 1 tab) are prefilled-URL scripts (/en/projects/15623/{silver,gold}/edit?<id>_status=..&<id>_justification=..), the same method asbestpractices-baseline.ps1. Generators live in the session scratchpad only (gen.py,gen_gold.py); the scripts are the artefact. Regenerating needs the answer table, which is inside those two python files (lost with the session). Live percentages when checked: passing 100, silver 15, gold 13, baseline 1 and 2 at 100, baseline 3 at 95.- Silver answers: 32 Met, 3 N/A (
sites_password_security,installation_standard_variables,vulnerability_report_credit), 7 Unmet (access_continuity,bus_factor,internationalization,build_repeatable,test_statement_coverage80,crypto_algorithm_agility,version_tags_signed).dcobecame Met after the pull-request-template checkbox was added. - Gold answers: Met
copyright_per_file,license_per_file,small_tasks,require_2FA,secure_2FA(maintainer confirmed TOTP),code_review_standards,test_invocation,crypto_used_network,crypto_tls12,security_review,hardening. Unmetbus_factor,contributors_unassociated,two_person_review,build_reproducible,hardened_site(GitHub Pages cannot set CSP, HSTS, nosniff or X-Frame-Options headers; livecurl -Ishows none). Left unanswered on purpose until Rust coverage is measured:test_statement_coverage90,test_branch_coverage80, anddynamic_analysis(the criterion accepts an automated suite with 80 percent branch coverage). small_tasksis only true once~/Code/unenverse-good-first-issues.ps1has run: it creates thegood first issuelabel and seven issues taken from the open findings of phase log 83/84.
Files added or changed (repository)
ASSURANCE_CASE.md(new): security requirements, trust boundaries, secure design principles, common weaknesses with the enforcing place. A path-traversal row was removed because it could not be verified.scripts/spdx.mjs(new) andpackage.json(spdx:check, wired intocheck): adds and verifiesSPDX-License-Identifier: Apache-2.0plusCopyright the UnENVerse contributors.on every tracked .rs/.ts/.js/.mjs/ .py/.sh outsidetests/fixtures,docs/,node_modules,dist,target. 450 files after the merge (369 before it).CONTRIBUTING.md: DCO 1.1 via checkbox, "Good first issues", "How review works" (five checks)..github/pull_request_template.md: DCO checkbox.book/src/roadmap.md(full year plan),website/roadmap/index.html(generated from it by a one-off script, so the two can drift), README## Roadmapsection plus contents entry, sidebar and footer links,NAVentry inscripts/build-site.mjs.- Trust: README badge row (Silver %, Gold %, Baseline 3 %, TypeScript coverage, Rust coverage), the Codecov tree graph
under "Continuous integration",
website/security/index.html"Trust at a glance", the same.badgesrow on the home page and the download page,.badgesrule inwebsite/styles.css. - CI speed:
Cargo.toml(dev-profile optimisation of argon2, blake2, scrypt, pbkdf2, sha2, hmac, chacha20poly1305, curve25519-dalek),.github/workflows/ci.yml(per-crate test selection),.github/workflows/build.yml(desktop and headless packaging no longer wait for the gate),tests/workflows.test.ts(classifier behaviour tests and the changed gate test),.github/workflows/coverage-rust.yml(new),codecov.yml(flags with carryforward). - Version 0.56.1 in every copy
npm run versionowns, and a0.56.1section inCHANGELOG.md.
Verification that ran
npm run -s format:check: clean.npm run -s lint: clean.npm run -s spdx:check: "All 450 source files have a header".npx vitest run tests/workflows.test.ts: 29 passed.tests/site-build.test.ts: 20 passed.- Site:
node scripts/build-site.mjs --out <scratch>thennode scripts/check-site.mjs: "site checks ok: 10 pages x 2 viewports". actionlint .github/workflows/ci.yml build.yml coverage-rust.yml: clean apart from the SC2034 warnings inexporters.ymlthat were already there.cargo test -p unv-cli --test contract_users --locked: 111.11 s before the Cargo.toml change, 9.12 s after.- Classifier proof (ran the real
ci.ymlstep in bash on sample file lists):unv-cli/..->-p unv-cli;vault-core/..andCargo.lock->--workspace;unv-server/..->-p unv-server -p unv-cli -p unenverse; TypeScript only -> no Rust tests; README only -> nothing. - Before the SPDX headers:
cargo fmt --all --checkclean,npm run typecheckclean, six source-reading test files passed (97 tests).
Not run
cargo test --workspace,cargo llvm-cov,npm test(full),npm run preflight, ui-lab, any GitHub Actions run of the changedci.yml,build.ymlor the newcoverage-rust.yml. The expected effect of the Cargo.toml change on the whole Rust job (45 min to roughly 5 to 8 min) is an extrapolation from one test file.- The silver and gold forms, the good-first-issues script.
The bugs that mattered
- The Rust CI job took about 42 minutes of test time (Test ubuntu 14:51 to 15:36). Cause: debug-build Argon2id (64 MiB, t=3) in every test that unlocks a vault; 35 contract files each ran 8 to 12 tests at 5 to 12 s. Fixed by optimising only the key-derivation crates in the dev profile (release unchanged, overflow checks and debug assertions kept). 12 times faster on the one file measured.
- A release took CI plus the slowest build.
desktopandheadlesshadneeds: [meta, ci]although they publish nothing, so packaging started after a 45 minute gate. Nowneeds: [meta];dockerandreleasestill needci. v0.56.0 timeline for reference: gate 14:49 to 15:36, Windows CLI finished 15:54, release 15:57 (68 minutes). - Every change ran every Rust test, including TypeScript-only and single-crate changes. Now selected per crate.
- My earlier work disappeared from the working tree: the maintainer ran
git stash("before-main-pull", including untracked files) before pulling main. Recovered withgit stash pushof today's three edits,git stash pop stash@{1},git checkout -- tests/workflows.test.ts(it only held a header and blocked the second pop), second pop, thennode scripts/spdx.mjs --writefor the 81 files that came in with the merge.
Decisions made
- DCO as a checkbox, not
Signed-off-by: the maintainer's commits are one sentence and he runs them himself; forcing-swould change that. Sign-off is welcome, the checkbox is the mechanism (CONTRIBUTING.md). - Honest Unmet over aspirational Met: bus factor, unassociated contributors, two-person review, reproducible build, hardened site headers, signed tags, algorithm agility and i18n are all Unmet. The roadmap names the path for each.
crypto_used_networkanswered Met becauseunv-serverbinds127.0.0.1by default and plain HTTP is only used on loopback (a node refuses a plain-HTTP hub unless loopback). Refusing a non-loopback bind without--tlswas considered and rejected: it would break the maintainer's own container deployment.- Rust coverage figure: the newest saved measurement is 78.86 percent lines (43,167 of 54,738) from
phase log 86 (src-tauri went from 7.8 percent to tested, about 20 more contract files). No later figure exists; the
run4attempt was killed (exit 143). Measuring now happens on a runner, not locally (maintainer's rule: no long local runs). - Per-crate selection rule: vault-core -> whole workspace; unv-app -> unv-app, unv-server, unv-cli, unenverse;
unv-server -> unv-server, unv-cli, unenverse; unv-cli alone; src-tauri ->
unenverse; anything not clearly one crate or clearly TypeScript (manifests, lockfile,.github,scripts,tests/fixtures, Docker, catalogue) -> whole workspace. The package forsrc-tauriisunenverse. - Release publishing still needs the gate; only the no-publish packaging jobs run beside it. Test in
tests/workflows.test.tsrewritten accordingly (two tests: publishing needsci; packaging must not wait for it). - Trust badges are live dynamic badges (shields.io reading
bestpractices.dev/projects/15623.jsonand Codecov), so no number is typed anywhere. The Codecov tree graph token in the README is a read-only graph token the maintainer supplied; the upload token (CODECOV_TOKEN) is separate and still needs setting and rotating. - No ADR written: nothing here changes a design.
.claude/rules/ci-release.mdgot the permanent CI notes.
What Worked / What Didn't Work
prefilled-URL method; timing one slow test file before and after a profile change; extracting the real ci.yml step into
bash and into a vitest to prove the classifier; actionlint and check-site.mjs as fast gates.
Did not work / traps:
- The first
python3edit ofcoverage-rust.ymlran after the stash had removed the file and aborted before touchingcodecov.yml; redone after the restore. Check files exist after any stash. check-site.mjsrejects an<img>withoutwidthandheight; shields badges needwidth="160" height="28"plus CSSwidth:auto.actionlintprints SC2034 forexporters.yml(pre-existing); filter them when reading output.- A dynamic shields badge shows "invalid" until its service has data (Rust coverage before the first upload).
git addglobs:'*.ts'etc. must excludetests/workflows.test.tsfrom the headers group, which belongs to the CI group (it holds both).
Next Steps
Only the maintainer can:
- Run the commit command at the bottom of this section's companion message, merge the PR (rebase), and let CI cut v0.56.1
(no
v0.56.1tag exists and the version moved, sobuild.ymlpublishes it). - After merge:
gh workflow run coverage-rust.yml --repo darthdemono/UnENVerse, read the job summary, and send the Rust numbers back so the silvertest_statement_coverage80and goldtest_statement_coverage90,test_branch_coverage80,dynamic_analysisanswers can be set. TypeScript is 99.52 statements and 95.35 branches (phase log 85). - Submit the forms:
pwsh -File ~/Code/bestpractices-silver.ps1and...-gold.ps1(review each tab, press Submit), thenpwsh -File ~/Code/unenverse-good-first-issues.ps1. - Set and rotate
CODECOV_TOKEN(pasted into chat earlier); close Dependabot PR #28 (age 0.12.1); enable Dependabot security updates; sign release tags (needs his GPG or SSH key) to moveversion_tags_signed. - Put the site behind a CDN that can set security headers if
hardened_sitematters (his account).
Doable by an agent next:
- Confirm the faster pipeline on the first real PR: read the
Changesjob output (crates=), the Test job time, and the release run's overlap of gate and Windows builds. Tune the optimised-crate list if the Test job is still slow. - A fuzz job (
cargo fuzz60 s per target) as a release gate, to flipdynamic_analysis. - Reproducible build check (
SOURCE_DATE_EPOCH,--remap-path-prefix, build twice and compare) forbuild_reproducible. - Make
website/roadmap/index.htmlgenerated frombook/src/roadmap.mdso they cannot drift. - Roadmap phases themselves (R21 first), see
book/src/roadmap.md.
Notes for whoever picks this up
- The roadmap dates and quarter assignment of phases are my proposal (the dependency order comes from
the session records/roadmap/phases/*.md"Depends on"); R18 to R21 and R24 to R26 have no stated dependency and were placed in Q4 2026. package-lock.jsonstill says 0.42.7;npm run versiondoes not own it andversion:checkdoes not flag it.ASSURANCE_CASE.mdclaims were checked againsttls.rs,tauri.conf.json,THREAT_MODEL.md,AGENTS.mdinvariants and the README; re-verify before citing it elsewhere.- Silver
documentation_currentandregression_tests_added50are attestations about practice, not checked by a tool. - Do not commit this file. Unset
UNV_SERVER_URLandUNV_PASSWORDbefore runningunvby hand.
2 line(s) about the maintainer's own machines were left out of this page.