Skip to content

Lockfile ​

securock.lock is a language-agnostic snapshot of dependency trust. Go is the implementation language of Securock, not a constraint on what the lockfile can describe.

Canonical specification source: docs/specification.md in the repository.

Goals ​

The same project inputs must produce the same lockfile bytes.

sha256(securock.lock)

must be stable across machines, clocks, and working directories.

Forbidden fields ​

The lockfile must not contain:

  • timestamps such as generated_at
  • absolute filesystem paths
  • other host-specific metadata
  • advisory modified timestamps from vulnerability databases

Those values change without a trust-relevant change in the project.

Document shape (v2) ​

yaml
version: 3
source:
  ecosystems:
    - npm
  resolvers:
    - pnpm
policy:
  digest: sha256:...
artifacts:
  - subject:
      ecosystem: npm
      name: react
    version: 19.2.0
    digest: sha512:...
    source:
      resolver: pnpm
      registry: https://registry.npmjs.org
    evidence:
      provenance: unknown
      signature: unknown
      vulnerabilities:
        state: unknown
      malicious:
        state: unknown
      capabilities:
        state: unknown
      behavior:
        state: unknown
      ownership:
        state: unknown
      chain:
        state: unknown
    trust:
      status: unknown
      reasons:
        - malicious reports not checked
        - vulnerabilities not checked

Identity ​

Subject identity is ecosystem:name and does not include a version. npm:react@19.1.0 and npm:react@19.2.0 are two artifacts of the same subject npm:react. A bump is a change to one subject, not a delete plus an add.

ecosystem is the package registry family. resolver is the package manager that produced the input lockfile. Switching from pnpm to npm must not rewrite every subject identity.

source.ecosystems and source.resolvers are optional sorted lists. They must not include a path. Artifact source.registry is the package metadata origin. source.artifact is the download URL when it differs, or a Git repository URL. source.kind is registry, workspace, git, file, or url. source.requested and source.resolved record a specifier that redirected (for example a Deno HTTPS import).

A change to source.kind, source.registry, source.artifact, source.requested, or source.resolved is trust drift even when the name, version, and digest stay the same.

Evidence axes ​

capabilities, ownership, behavior, and chain use state: checked / evidence states when collected and unknown when skipped (offline or unsupported ecosystem). A change to any of them is trust drift.

health is optional. It records the release day, deprecation message, and license when the registry answered, and is omitted otherwise. It is informational and not trust drift on its own. See Release health.

With --reachability, each artifact also records scope (runtime, dev, or optional) and dependencies (the artifact IDs it depends on directly), and each vulnerability item records reachable (yes, no, or unknown) and, for an indirect yes, via, the shortest dependency path from a runtime package. These fields are optional and are not trust drift. See Usage.

Each vulnerability item can also carry severity and fixed (the versions that fix it for this package, oldest first), read from the advisory record; both are omitted when OSV gives none. They are not trust drift. See Usage.

Vulnerability evidence is not a bare list. state: checked means OSV was queried. unknown means it was not. An empty items list with state: unknown is not the same as zero vulnerabilities.

URL artifacts may omit version. Their identity is subject.name (the requested URL). Do not encode a URL into version.

yaml
- subject:
    ecosystem: url
    name: https://esm.sh/preact
  source:
    resolver: deno
    requested: https://esm.sh/preact
    resolved: https://esm.sh/preact@10.26.8

When a remote hash is present, version is that hash (not a URL).

Digests ​

PrefixMeaning
sha256: / sha384: / sha512:Content hash of the package artifact (npm SRI, Cargo checksum, PyPI file)
goh1:Go module directory hash from go.sum (h1:)

require_digest: true (including the strict profile) requires a non-empty digest that uses a strong algorithm with a well-formed payload: sha256, sha384, sha512, or goh1. sha1 and opaque strings fail the policy.

PyPI may emit one artifact per wheel or sdist, distinguished by filename. The same ecosystem:name@version with two different digests is an artifact conflict and must fail the scan, except when filename distinguishes PyPI files.

Policy digest ​

policy.digest is a SHA-256 of a canonical JSON encoding of the active policy (version, resolved network mode, registry allowlists, and non-default rules including capability deny lists, ownership change actions, and provenance origin allowlists). It must not include a file path.

v1 migration ​

version: 1 lockfiles from Securock v0.1.x are migrated to version: 3 in memory on read. Kept fields are digest, source, provenance, signature, and vulnerabilities. New evidence axes (malicious, capabilities, behavior, ownership, chain) and trust are set to unknown — a v1 trusted status is not carried forward. Read-only commands do not rewrite the file; run securock lock to persist a refreshed v2 snapshot.

Canonical encoding ​

  • YAML 1.2, 2-space indentation
  • artifacts sorted by subject ecosystem, then name, then version, then resolver
  • vulnerability ids, malicious report ids, ownership maintainers, and trust reasons sorted lexicographically
  • empty optional collections omitted

Evidence states for provenance / signature / chain: unknown, missing, present, verified.

Vulnerability, malicious, capability, behavior, and ownership states: unknown and checked.

Unknown lockfile fields, schema versions, ecosystems, evidence states, and trust statuses are errors.

Commands that use the lockfile ​

CommandRole
lockWrite the file
diff / verifyCompare the tree to the file
scanInspect the tree (default); fails on current policy violations even if already locked

JSON Schemas: schemas/.

Next steps ​

Version history ​

VersionWritten byChange
1v0.1.xFirst format
2v0.2.xEvidence axes for malicious reports, capabilities, behavior, ownership, and chain
3currentOptional release health, advisory severity and fixed, and --reachability fields (scope, dependencies, ...)

Older versions are migrated in memory. A file written at a newer version than the running securock is refused with a message to upgrade, and older releases refuse a version 3 file, because decoding is strict: do not commit a v3 lockfile until every machine and CI job that runs securock has been updated.

Released under the Apache-2.0 License. Pre-1.0: all lockfile resolvers are stable.