
Product
Introducing Socket Scanning for VS Code Marketplace Extensions
Socket now scans VS Code extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.
prooflane-harness
Advanced tools
The prooflane verify lane: a stack-agnostic evidence harness that derives done from evidence rather than an agent's word. Profile loader, lane runner, receipts, hash-locked region, Stop hook, spec-to-citation binding and the Rule 0/1/2 instruments. Depend
The verify lane every create-cmp app carries — the machine-owned half of a stamped project, published as the single source of truth it is vendored from.
A create-cmp project contains app-owned files (screens, specs, golden baselines, approvals, e2e flows — the app) and machine-owned files (the lane — engine code, byte-identical in every app ever stamped, carrying no app content). The boundary is mechanical, with no list to maintain:
machine-owned == the .mjs files directly under qa/ and qa/lib/
src/lib/harness-region.mjs is that rule as code. Three properties hang off it:
create-cmp.json.qa/verify.mjs
to force every step green and the receipt still validates, because the
edited checker is just part of the hashed surface.create-cmp upgrade --harness swaps the whole
region. Three-way-merging a derived artifact was the prior failure: engine
code produced conflict noise in every app while containing nothing
app-specific to preserve.Generated projects never npm install this. The scaffold drops a
byte-identical copy of src/ into the project's qa/, so
node qa/verify.mjs runs offline, in CI, and air-gapped, with no install
step and no registry reachable. This package exists so that copy has a
published, citable source of truth — and so the region's integrity mechanism
is installable on its own (below). In the create-cmp repo,
node scripts/sync-harness.mjs re-vendors and parity tests pin
byte-equality across package ↔ template ↔ fresh scaffold.
npm install prooflane-harness
The part worth consuming standalone is the machine-owned-region integrity
check — the same check qa/verify.mjs runs as its first step in every
profile, as plain functions over a directory tree:
import { isHarnessFile } from "prooflane-harness/harness-region";
import {
writeHarnessLock,
checkHarnessIntegrity,
describeIntegrity,
} from "prooflane-harness/lib/harness-lock.mjs";
isHarnessFile("qa/verify.mjs"); // true — machine-owned
isHarnessFile("qa/approvals.json"); // false — app state, never part of the region
writeHarnessLock(projectRoot, { version: "1.0.0" });
describeIntegrity(checkHarnessIntegrity(projectRoot));
// "prooflane-harness 1.0.0 — 2 files verified"
// …edit a lane file in place, then check again:
// "prooflane-harness 1.0.0 — 1 modified"
checkHarnessIntegrity returns { status: "intact" | "modified" | "unlocked", name, version, modified, missing, extra, fileCount } — naming exactly which
files drifted, so an upgrade knows what it is replacing. hashHarnessRegion(root)
gives { sha256, fileCount, files } (a per-file digest map) with no lock to
compare against. A corrupt or truncated lock reads as unlocked, never as
intact.
A secret scanner may flag qa/harness.lock.json. Every value in the lock's files map (one
per lane file) and its top-level sha256 (the whole region) is a lowercase 64-character hex
sha256 digest — the shape a generic secret rule (gitleaks' generic-api-key, for one) reads as a
key. They are not credentials, and they are load-bearing: without them the lock cannot name the
file that changed. Allowlist the path rather than the values, which change with every upgrade and
relock — for gitleaks, paths = ['''qa/harness\.lock\.json'''] under [allowlist] in
.gitleaks.toml. The receipts under qa/evidence/ carry digests of the same shape.
src/verify.mjs is the lane's CLI entry (what a vendored copy runs as
qa/verify.mjs), composed from the src/lib/*.mjs modules: spec-coverage
scanning, the approvals gate, golden-tree / a11y / conformance wiring, the
device lease, the step cache, the flight recorder, the evidence-receipt
writer. Each is individually importable (prooflane-harness/lib/<name>.mjs,
extension optional), but they are written to run inside a scaffolded
Compose Multiplatform project — they expect composeApp/, specs/, and
gradlew on disk. They are not a general-purpose toolkit.
Two modules are the exception by design: lib/inputs-hash.mjs and
lib/receipt-validate.mjs are byte-identical vendored copies of
prooflane-receipts
(parity-tested), kept so this package needs no npm dependency either. If you
only want receipt validation, depend on prooflane-receipts directly.
The lane is two things (evidence-economics S8): a spine and a step pack.
verify.mjs plus lib/lane-runner.mjs, lib/step-outcomes.mjs,
lib/receipt-validate.mjs, lib/inputs-hash.mjs, lib/approvals.mjs, lib/spec-coverage.mjs,
lib/spec-model.mjs, lib/harness-manifest.mjs, lib/profile-loader.mjs,
lib/flight-recorder.mjs, lib/evidence-level.mjs, lib/walk.mjs, lib/plan.mjs,
receipt-check.mjs, approve.mjs, plan.mjs, walk-status.mjs, retrospective.mjs. It
parses arguments, runs steps under a deadline with a pulse, turns a throw or a timeout into
one ERROR row, derives the verdict and the evidence rung, writes the receipt bound to the
inputs hash, journals the run, and refuses "done" without a PASS. It knows nothing about
Gradle, adb, Maestro or composeApp/. What it needs to know about a stack it reads from
the profile the manifest names (qa/lib/profiles/<id>/index.mjs): steps(ctx) — the
pack — plus layout (where specs, sources, tests and flows live) and tiers (which test
tiers exist, which run host-only, which satisfy a clause's [tier: …]), which
lib/spec-model.mjs validates into the scanner's model; and, optionally, artifacts(root)
— what a human signs, in definition order, each entry a path set with an optional
hash(root) of its own — composed from the core's helpers (featureBriefArtifacts,
featureDesignArtifacts, featureSpecArtifacts, architectureArtifact) plus the
stack's own entries, and governable(root) to refuse recording signatures in a tree
that is not a real project. A profile with no artifacts still governs its feature
briefs. lib/approvals.mjs keeps the mechanic and signs whatever list it is handed.lib/steps-cmp.mjs. Every Compose Multiplatform step, behind one factory:
createCmpSteps(ctx) returns { stepsForProfile, DEVICE_STEPS, FAST_EXCLUDED_NAMES, STEP_FN_BY_NAME, stepDeterminism, releaseLease }. It reads no argv and writes no receipt.To verify a Kotlin backend, a web service, anything — from this package alone:
npm i -D prooflane-harness
npx prooflane init [--profile <id>]
No scaffolder, no Compose, nothing else installed — that independence is the point, and
node scripts/stage1-gate.mjs in the create-cmp repo proves it by installing this tarball
into a Go repo that has never heard of us and running the lane. (If you already have the
Compose scaffolder, npx create-cmp-cli harness init delegates to exactly this code; the two
differ only in which command names they print back at you.)
It vendors the spine, writes qa/harness-manifest.json, generates a working profile at
qa/lib/profiles/<id>/index.mjs, seeds qa/verified-surface.json from your own tree, takes
the lock, and then runs qa/framework-check.mjs so you watch the lane refuse and recover
before you trust it. A repo with sources, a spec and a test goes from that to a green lane
and a passing Rule 0 with nothing hand-edited.
The generated profile is the specification. Prose about the protocol drifts from
lib/profile-loader.mjs silently; a skeleton that has to load cannot. Read the file init
writes rather than a description of it — the five required exports are real code with their
reasons, and three of the four optional ones (artifacts, governable, plants) are
present as commented blocks carrying their true field names. The fourth is ladder, and it
is the one shape this file cannot promise in advance: init asks a short menu, and what a
human answers decides whether the block arrives commented — nobody asked — or LIVE, naming
the rungs they said this project has.
Two steps ship in it, chosen because they prove something on a stack nobody has seen:
harnessIntegrity (this lane is the one that was locked) and specCoverage (every promise
is cited from a test that can observe it). Add your build and test steps beside them.
Everything under qa/lib/profiles/<id>/ is yours. Everything else under qa/ is
machine-owned, hash-locked, and vouched for on every receipt. If you find yourself needing to
edit a core file, that is a defect in the harness worth reporting — not a local patch. A
forked spine receives no upstream fix, and harnessIntegrity will say so on every run.
Your profile is inside the lock too — an edited step pack that could certify itself is the
same hole as an edited spine — so the first edit you make to it FAILs harnessIntegrity until
you re-take the lock:
npx prooflane relock
A core fix reaches your tree by one command — never by a package manager moving a number,
because the lane is vendored and a lane in node_modules is outside the lock that vouches
for it:
npm i -D prooflane-harness@latest
npx prooflane upgrade
It re-vendors every machine-owned file, rewrites qa/harness-source.json (which artifact you
now carry, and where it came from), re-takes the lock, and leaves your profile and your two
declarations untouched. Run node qa/framework-check.mjs afterwards — the lane changed, so
prove it still refuses — then commit the lane, the lock and the provenance record together.
Hold more than a couple of repos and the thing you need is not the ability to type that command
ten times — you have a shell. It is that the ten upgrades come from one resolved harness, so
the fleet ends up on one version instead of on whatever each directory's node_modules happened
to hold. A shell loop cannot promise that; one process can.
Declare the fleet in a file you write, and name each repo by a path:
{
"schema": "prooflane-fleet/1",
"repos": [
{ "id": "cart-service", "path": "../cart-service" },
{ "id": "web-bff", "path": "../web-bff" }
]
}
npx prooflane upgrade --fleet ./fleet.json
Paths resolve against the manifest, never your cwd, so the file can be committed and still
mean the same thing from anywhere. Every repo runs even after one fails — stopping at the first
would report one problem when there are three and leave the fleet half-upgraded either way — and
the command exits non-zero if any did. --dry-run works the same, for the whole fleet.
A repo named by url rather than path is refused: an upgrade writes to a working tree, and
this command will not put a clone on your disk at a path it chose for you.
It re-hashes the region and rewrites qa/harness.lock.json, only when every difference is
a file you own (qa/lib/profiles/<id>/**, qa/verified-surface.json, qa/harness-manifest.json).
A machine-owned file among them and it refuses by name: that is a fork, and create-cmp upgrade --harness restores it. It never changes the harness version the lock records — re-locking is
not an upgrade. Commit the lock with the edit that caused it.
A step is a function returning { name, verdict: "PASS"|"FAIL"|"SKIP"|"ERROR", reason?, durationMs, details?, skipKind?, layer? }. Borrow ctx.sh (it throws StepTimeout past the
step's deadline — never catch that) and push degraded-path notes onto ctx.DEGRADED_PATHS.
ERROR means the step COULD NOT RUN and is never FAIL: "I could not check this" is not an
accusation about the change. Tag a step with fn.layer = "backend" and the Evidence page
groups by it; tag fn.timeoutHint and the runner says where to look when it times out.
Init derives your source roots from the tree and writes them into the manifest's
citationRoots, so a wrong guess is one visible line rather than a buried scanner default.
Correct it there, then harness relock — the manifest and the surface are declarations the
lane READS, so they are locked like the code that reads them. The manifest's specs and
citationRoots override the profile's layout field by field, so the lane and the console
read the same paths.
The seeded qa/verified-surface.json is what the receipt attests. It lives inside the
surface, so changing it invalidates receipts — which is correct, the coverage changed. Do not
leave it undeclared: a surface matching none of your tree is refused loudly, but one
matching some of it resolves to a valid, smaller hash, and that is how a receipt comes to
attest a fraction of a project while looking complete.
The full lane (every profile but smoke, scaffold and --fast) drives a device: tokenDrift,
e2eSmoke (every flow in qa/e2e/, debug build), androidChecks (connectedDebugAndroidTest).
With nothing attached it boots a headless emulator (lib/device-provider.mjs) and shuts it
down when the lane exits. Environment variables:
| Variable | Effect |
|---|---|
CMP_AVD=<name> | the AVD to boot; else the doctor's cmp_pixel, else the only AVD; several and no way to choose is a refusal naming them |
CMP_KEEP_DEVICE=1 | leave a booted emulator running for the next lane |
CMP_DEVICE=none | the ONE explicit opt-out (CI runners without an emulator). The device rows SKIP with skipKind: "environment", and receipt-check.mjs refuses that receipt as done-evidence |
A device that cannot be provisioned within the boot bound is an ERROR row, and the lane FAILs: a device that never came up is a failure to test, not a gap to record.
node_modules will not verify the project that installed it. It works
vendored into a project's qa/, which create-cmp does at scaffold and
upgrade time.prooflane-harness@X?" needs a
comparison against the published package's own digests, which is the
upgrade flow's job, not the lock's.qa/evidence/latest.json
on disk; committing or hosting them is the project's choice.This package versions independently of create-cmp-cli. The lane changes
far more often than the template's app shape; coupling the two forced an
app-shape release for every lane fix. A generated project records both — the
engine version that stamped its shape and the harness version that issues its
verdicts — and can upgrade either without the other.
FAQs
The prooflane verify lane: a stack-agnostic evidence harness that derives done from evidence rather than an agent's word. Profile loader, lane runner, receipts, hash-locked region, Stop hook, spec-to-citation binding and the Rule 0/1/2 instruments. Depend
The npm package prooflane-harness receives a total of 819 weekly downloads. As such, prooflane-harness popularity was classified as not popular.
We found that prooflane-harness 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.

Product
Socket now scans VS Code extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.

Research
/Security News
Socket uncovered two malicious VS Code themes in a GlassWorm-linked cluster with thousands of installs across VS Code Marketplace and Open VSX.

Security News
/Company News
Capital One is partnering with Socket to proactively secure its open source supply chain.