Compatibility¶
Draft until 1.0.0-RC1
lockrot is 0.x, so a minor release may change anything on this page; the changelog says what changed. The page becomes binding with 1.0.0-RC1. From 0.13.0, lockrot follows how a release may change a verdict as project practice.
- Decided at 1.0.0-RC1: the default thresholds, the Docker tag scheme and the PHP and Composer floor for 1.x.
- Planned:
--schema=1in 2.x, a configuration file named byextendsand the floor-rise warning.
Key your integration on the Contract column. The Not contract column can change in any release, patches included; verdicts and priorities change only as Verdict changes allows.
| Surface | Contract for all of 1.x | Not contract |
|---|---|---|
| Command line | The commands, their options, the --fail-on values and the exit codes |
The wording of its messages |
| Configuration | extra.lockrot keys and the environment variables |
The testing hooks |
| Machine output | json, sarif, gitlab and github, the baseline file, and the report embedded in html |
table, markdown, the look of html, the --explain text and the install-time summary |
| Inside documents | Ids, codes, keys and data fields | Every human-readable sentence: summaries, notes, evidence, messages |
| Closed sets | The closed sets and their order | Which verdict and priority a package gets |
| Distribution | The PHAR asset names and verification path, lockrot-action@v1 and its inputs, self-update staying within its major and on a PHP it runs on |
The Docker tag scheme, until 1.0.0-RC1 chooses it |
| PHP platform | — | The PHP and Composer floor, until 1.0.0-RC1 chooses the one for 1.x |
| PHP code | — | The classes under src/ |
CI owners: pin the lockrot version, and read a release's Verdict changes before you move the pin.
What 1.0 freezes¶
Documents¶
-
The report, the explanation, the baseline file and the configuration (report-1, explain-1, baseline-1 and config-1) only gain fields within 1.x, under the rule in schema.md.
-
Their schemas stay published under
https://lockrot.dev/schema/for all of 1.x. The schema bundled with the release you run is authoritative. -
Within 1.x no field becomes required, narrower or removed: a document valid against an earlier 1.x schema validates against every later one.
-
Planned: lockrot 2.x keeps writing report-1 behind
--schema=1. -
Key on ids and data fields: for a run's notes, on
note_details(Run notes), never onnotes.
Closed sets and their order¶
Nothing is added to these sets, removed from them or reordered within 1.x:
- Verdicts, most severe first:
abandoned,silent,pinned,left-behind,old-promise,stale,unknown,finished,ok. - Priorities, highest first:
critical,high,medium,low,none. - Signal levels, lowest first:
info,warn,high. - A finding's standing against the baseline:
known,new,worsened. - A
priority_basisstep'sfromandto: the priority order withoutnone.
The document number lockrot.schema is 1 for all of 1.x. A new verdict, priority, level or
standing needs report-2 and lockrot 2.0. verdicts.md says what each
value means.
-
The flagged verdicts are
abandonedtostale: the onesrun.flagged_verdictslists, a baseline entry holds and--fail-onaccepts. -
finishedandokshare the lowest severity: no threshold and no comparison tells them apart. Where lockrot lists verdicts —counts,run.flagged_verdicts, the schema enums, this page —finishedcomes beforeok; the SARIF rules appear in the order the results first use them.
The order decides:
--fail-on: a finding fails the run at or above the threshold.- The baseline: a finding is
worsenedwhen its verdict is more severe than the one the baseline accepted. - The report's order: priority, then verdict, then direct before transitive, then package name.
- The SARIF
rank, which follows the priority.
Open sets¶
The sets listed under Open sets grow in minor releases; the closed
sets do not. Read a value you do not know as described there. A
priority_basis step whose reason you do not know still shows its direction: compare from and
to. In lockrot's own input an unknown format name is an error: --format, --output and
extra.lockrot.format accept only the names the release knows.
A minor release may split a libyears_unmeasured reason, no_stable_release_date included, into
narrower ones, which moves findings out of the old libyears.unmeasured key.
Run notes¶
What a run could not see is in the report and the explanation twice: as sentences in notes, and
typed in note_details (notes.md).
Frozen for 1.x:
note_detailshas one entry pernotesstring, at the same index, with the sametext. Every document from 0.13.0 on carries it.- Each entry's keys (
code,text,docs_url,sets_network_failures,data) and each code'sdatakeys, as the schemas list them. - A code's meaning. A new meaning gets a new code; a retired code is never removed or reused.
sets_network_failures, and the rule that the report'snetwork_failuresis true exactly when an entry's is.- Every
docs_urla release has written resolves for all of 1.x. - The specific reasons in
data, in the table below.
| Note codes | Frozen reasons | Catch-all |
|---|---|---|
metadata_unavailable, monorepo_parent_unavailable |
offline, install_time_budget, no_versions |
fetch_failed |
advisories_not_checked |
offline, composer_too_old, install_time_budget |
none |
repository_activity_not_checked |
install_time_budget |
none |
Not frozen:
- The wording of
text. - Every
messageindata: a repository's, a host's or Composer's own words. - Which page a
docs_urlpoints at. A page that moves stays behind as a stub that keeps every id. - Which notes a run writes, and in what order.
- A catch-all reason: a minor release may move cases out of it into reasons of their own.
A new code or reason arrives only in a minor release, and changes no verdict or priority. Under
--strict-network, a new code whose sets_network_failures is true can make a run exit 1
(what trips it).
Package origins¶
A finding's origin says where its lock entry came from
(schema.md). Frozen for 1.x:
- Its keys
kind,registry,package_urlandlocal, on every finding from 0.13.0 on, and the finding's booleanfrom_composer_repository. - The rules schema.md gives for
kind,registry,localandfrom_composer_repository. A new case gets a new kind, never a new meaning for an old one. - A minor release can add a registry lockrot names or links, never withdraw one.
package_urland a finding'sreplacement_urlare written by lockrot, never built by a reader: link one only where it is a string.replacement_urlis null when lockrot keeps no page for the registry that named the replacement, and always null whenreplacementis.
Not frozen:
- Which further registries lockrot names and links.
- Which kind an entry gets after a minor release that learns more: a new kind may take entries out
of
unknown. - Which registries a
replacement_urlis written for, and whether a registry still keeps the page a URL points at.
A new kind or registry arrives only in a minor release, and changes no verdict, priority or exit code.
Finding identity¶
- lockrot reports one finding per package per lock.
- The baseline key is the package name.
- GitLab Code Quality:
check_nameislockrot/<verdict>, andfingerprintis the sha256 oflockrot|<package>|<verdict>. The verdict is part of the fingerprint and not of the baseline key, so a finding whose verdict changes gets a new fingerprint. - SARIF:
ruleIdislockrot/<verdict>, andpartialFingerprintsholdslockrot/packagewith the package name. - GitHub annotations: the title is
lockrot: <verdict> (<priority>), on the package's line of the analysed lock.
Severity mapping¶
The github, sarif and gitlab columns of
How each format marks a finding, and its rules for SARIF
rank and a SARIF rule's default level, are frozen for 1.x.
Command line¶
- The commands:
composer lockrot(aliascomposer rot), and the PHAR'slockrotandself-update(aliasselfupdate). - The options in CLI options, and
self-update's--check,--force,--offlineand--allow-major(phar.md). - Every format name a release has shipped keeps its meaning for all of 1.x, and new names may arrive (Open sets); the table says whose contents are contract.
- The
--fail-onvalues. - Exit codes: the closed set
0,1and2, as ci.md defines them.self-updategives them meanings of its own.- lockrot's own commands exit
2on every usage or configuration error. - In a check or
--generate-baselinerun,0and1follow the report'sgate.fails, with its causes ingate.tripped_by; a run that then cannot write an--outputfile or the baseline exits2instead. - An exit
1from Composer or Symfony before lockrot runs is not lockrot's (ci.md).
- lockrot's own commands exit
- Messages about lockrot itself (a failure, a warning, a deprecation) go to stderr, never into the report on stdout. A report's own notes are part of the report.
Configuration¶
extra.lockrotkeys are never removed within 1.x.- The variables in Environment overrides are frozen with the keys; the testing hooks are not.
- Precedence, where the first that sets a value wins: CLI options, environment variables,
extra.lockrot, the defaults. The one exception: when Composer already holds a credential header for a host (auth.json,COMPOSER_AUTH), lockrot sends that header instead of its own token (Which credentials). - A key lockrot does not know, at the top level or inside an
ignoreentry, prints one warning on stderr and changes nothing else, as Unknown keys describes. The reserved names never warn. - The default thresholds are chosen at 1.0.0-RC1, and a later change to one is a
Verdict change. To hold the numbers fixed, set them in
extra.lockrot. - Planned: a configuration file named by
extends, read afterextra.lockrotand before the defaults.
Blocking at install time¶
The install-time-strict key and its effect, stopping a composer require, update or install
when a finding reaches fail-on, are frozen with the rest of the configuration
(install-time.md).
Distribution¶
- The Composer plugin, the signed PHAR, the Docker image
ghcr.io/somework/lockrotandsomework/lockrot-actionrun the same lockrot code and write documents to the same schemas. - The PHAR, and the image and the Action that run it, bundle a Composer new enough for every check,
so only the plugin, which runs on the project's Composer, checks less under an older one
(
advisories_not_checked, which advisory settings it reads). - The PHAR's asset names and the verification path are stable.
lockrot-action@v1follows lockrot 1.x, and its inputs follow Semantic Versioning. lockrot 2.0 means action v2.self-updatestays within the running major version unless--allow-majoris given, and passes over a release that needs a newer PHP than the running one or is signed with a key the archive does not carry (which release it installs).
PHP platform¶
- The PHP and Composer floors are in the install requirements; the floor for 1.x is chosen at 1.0.0-RC1.
- Planned: within 1.x the floor rises only in a minor release, never in a patch, with a warning on stderr one minor release ahead.
What is not contract¶
- Human-readable output: the formats in the table's Not contract column.
The
htmlpage embeds the report-1 document under itsreportkey, and that document is contract; the rest of the page's payload is internal to lockrot and its renderer. - Which verdict and priority a package gets: thresholds and their defaults, heuristics, the priority rules, the curated package data lockrot ships, the repository-host clients, and S10's reasons. These change under Verdict changes.
- The exposure cap: the value of
exposure_rule.max_fan_in, and with it which flagged transitive packages count inexposureand S7 and which inunattributed(verdicts.md). The fields' shape is contract; the number is not. The report states the value it used, and a change gets a changelog line and is not a verdict change. - The PHP classes under
src/: internal, and may change in any release (CONTRIBUTING.md).
Verdict changes¶
Semantic Versioning covers shapes and names, not which verdict a package gets. A release changes verdicts only under these rules:
- A change that can alter the verdict or the priority a package gets, or what
--fail-on=uncheckedmatches (a new S10 reason, a newly supported host), ships in a minor release, never in a patch. The changelog lists it under Verdict changes. - The one patch exception is a curated-data fix that moves a package to
finishedorok. - A new signal that decides verdicts ships for one minor release as evidence only: it appears in the report and decides nothing until the next minor release.
A committed baseline does not make an upgrade silent: a package a release starts flagging is new,
one whose verdict worsens is worsened, and either one fails the run when it reaches --fail-on.
An unchanged lock can cross a threshold too, as its packages' releases age
(baseline.md).
Extending lockrot¶
1.0 ships no plugin API. Use one of these routes instead:
- JSON out. Every distribution writes report-1 and explain-1 from the same
code. Dashboards, fleet summaries, other formats and organisation policy (a
jq -egate) are programs over those documents. - Composer configuration in. lockrot reads extra GitLab hosts from Composer's
gitlab-domainsand has no host keys of its own (hosts). - A pull request for code. New signals, S10 reasons, hosts and fields go into lockrot itself, in minor releases, under the additive rule and Verdict changes.
Names reserved for extensions¶
S<n>belongs to lockrot. A retired signal keeps its number, and no number is reused.- A signal, run note code, origin kind or format that does not come from lockrot is named
<vendor>:<name>. The vendor and the name are each lower-case letters, digits,_,.and-, starting with a letter or a digit.acme:licencefits;Acme:Licenceandacme:lint:licencedo not, and the published schemas reject them. No name lockrot ships contains a colon. - lockrot never gives a meaning of its own to:
extensionsat the top level ofextra.lockrot;- any key that starts with
x-, at the top level ofextra.lockrotor inside anignoreentry; - any environment variable that starts with
LOCKROT_X_.
- The PHP namespace
Lockrot\Extension\is reserved and declares nothing.
Deprecation¶
- Nothing in the contract is removed within 1.x.
- An option, environment variable, configuration key, format name or Action input can be
deprecated in a minor release. From then on it:
- keeps working unchanged until the next major version;
- prints one line to stderr when used;
- is listed under
Deprecatedin the changelog and in the register below; - is removed only in the next major version, and no sooner than six months after it was deprecated.
- Exit codes and format names never take on a new meaning.
- A JSON field is never deprecated on its own. It keeps being written, the schema marks it
x-deprecated: true, and it disappears only with report-2. A draft-04 validator ignoresx-deprecated, as it ignoresx-known-values. - A signal is retired, never removed.
- A surface marked Experimental on its page can change in any minor release, with a line in the changelog.
The register lists every deprecated or Experimental surface:
| Surface | Status | Since | Replacement | Removed no earlier than |
|---|---|---|---|---|
| none | — | — | — | — |
What lockrot does not do¶
lockrot never writes composer.json or composer.lock, opens no pull or merge request, and has no
hosted service or telemetry. Every file it writes, every host it contacts and where each credential
goes:
SECURITY.md.
Related¶
- schema.md — the schemas, their number, and how a validator reads the open sets
- verdicts.md — what each verdict, priority and signal means
- ci.md — what each exit code means, and each output format
- configuration.md — every key, variable and option this page freezes
- notes.md — every run note code and what to do about it
- phar.md — verifying the PHAR, and the
self-updaterules - changelog.md — each release's Verdict changes and deprecations