Lockfile (stacy.lock)
Records installed package versions for reproducible environments. Auto-generated by stacy lock and stacy install.
Purpose
- Reproducibility: Ensures identical packages across machines and over time
- Verification: SHA256 checksums detect corruption or tampering
- Documentation: Records exact sources for each package
- Collaboration: Teammates get the same versions you tested with
Format Specification
The lockfile uses TOML format with a defined schema:
# Auto-generated by stacy. Do not edit manually.
version = "1" # Lockfile format version
[packages.<name>] # One section per package
version = "<version>" # Version string (date or semver)
checksum = "sha256:<hash>" # SHA256 of package contents
[packages.<name>.source] # Where the package came from
type = "SSC" | "GitHub" # Source type
name = "<name>" # Package name (SSC only)
repo = "<owner>/<repo>" # Repository (GitHub only)
tag = "<ref>" # Git ref (GitHub only)
Annotated Example
# Auto-generated by stacy. Do not edit manually.
# Lockfile format version - stacy checks compatibility
version = "1"
# ─────────────────────────────────────────────────────────────
# SSC Package
# ─────────────────────────────────────────────────────────────
[packages.estout]
# Version comes from SSC metadata (typically a date)
version = "2024.03.15"
# SHA256 hash of all .ado and .sthlp files concatenated
checksum = "sha256:14af94e03edd2e5f12021a8967afe1eee2dc7ebd..."
[packages.estout.source]
type = "SSC"
name = "estout"
# ─────────────────────────────────────────────────────────────
# GitHub Package
# ─────────────────────────────────────────────────────────────
[packages.reghdfe]
# Version from git tag or commit
version = "6.12.3"
checksum = "sha256:8f9234ab12cd56ef78901234567890abcdef..."
[packages.reghdfe.source]
type = "GitHub"
repo = "sergiocorreia/reghdfe"
# Tag, branch, or commit SHA
tag = "v6.12.3"
What the lockfile guarantees
The lockfile is an input to stacy install and stacy run, never an output of them:
stacy installinstalls what the lockfile pins. It does not re-resolve versions and it does not writestacy.lock. If the source no longer serves the pinned version, or serves different bytes under it, the install fails and the lockfile is left untouched. This holds with and without--frozen.stacy runchecks the cache against the lockfile before it starts Stata. If a cached package no longer hashes to the locked checksum, or a production package is not installed at all, the run fails instead of executing.- Only
stacy add,stacy update, andstacy lockwritestacy.lock. Moving a pin is an explicit act.
Which packages run requires
stacy install installs the production group by default, so that is the group run requires to be present: a locked production package that is not in the cache fails the run. dev and test packages are installed on request (stacy install --with dev,test), so run does not require them — but it does check them against their locked checksums when they are installed, since every locked package is on the ado-path.
A pinned version can become unfetchable
SSC serves only the current revision of a package. Once a pinned version leaves your package cache, it cannot be downloaded again. A cold-cache install of a superseded pin therefore fails:
estout: stacy.lock pins version 20240315, but SSC serves 20260413
SSC serves only the current revision of a package, so once a pinned version
leaves the package cache it cannot be downloaded again.
hint: run `stacy update estout` to move the pin to 20260413 (your results may
change), or restore 20240315 into the package cache from a machine that
still has it.
This is a real limit of SSC, not something stacy can work around. The choice is to fail loudly or to run a different package than the one you locked; stacy fails.
The version pin is only enforced where the package names its own version. A .pkg manifest without a Distribution-Date line names none — stacy add records the date it fetched the package, which says nothing about the contents — so for those packages the checksum decides whether the pin is satisfied. A cold-cache install of such a package succeeds as long as the bytes still hash to the locked checksum, whatever the date in the lockfile says.
How Checksums Work
Checksums verify that the installed package matches exactly what was recorded:
- On install: stacy downloads the package, computes SHA256 of the contents
- On lock: Computed hash is stored in
stacy.lock - On verify: Cached package is re-hashed and compared to lockfile
The checksum covers all .ado and .sthlp files in the package, sorted and concatenated. This catches:
- Corrupted downloads
- SSC updates that changed the package
- Manual modifications to cached files
Checksums are checked by stacy install and again by stacy run before every run. Verification on run is on by default: it reads and hashes the locked packages, which costs milliseconds against a Stata startup measured in seconds, and a default that silently runs modified code would not be a reproducibility guarantee.
--no-verify turns checksum verification off. It applies to both commands, and it has to: stacy install --no-verify caches whatever the source served, which by definition does not match the locked checksum, so stacy run on that cache needs --no-verify too. Prefer stacy update <package> to re-lock, and expect your results to change.
Fields Reference
| Field | Required | Description |
|---|---|---|
version | Yes | Lockfile format version (currently “1”) |
packages.<name>.version | Yes | Package version string |
packages.<name>.checksum | Yes | SHA256 hash prefixed with sha256: |
packages.<name>.source.type | Yes | "SSC" or "GitHub" |
packages.<name>.source.name | SSC only | Package name on SSC |
packages.<name>.source.repo | GitHub only | owner/repo format |
packages.<name>.source.tag | GitHub only | Git ref (tag, branch, or commit) |
Workflow
Creating a lockfile
# Add packages (creates/updates lockfile automatically)
stacy add estout reghdfe
# Or generate lockfile from existing stacy.toml
stacy lock
Installing from a lockfile
# Clone a project
git clone https://github.com/user/project
cd project
# Install exact versions from lockfile
stacy install
Verifying in CI
# Fails if lockfile doesn't match stacy.toml
stacy lock --check
Updating packages
# Update one package to latest
stacy update reghdfe
# Update all packages
stacy update
Version Control
| File | Commit? | Why |
|---|---|---|
stacy.toml | Yes | Declares dependencies |
stacy.lock | Yes | Ensures reproducibility |
~/.cache/stacy/packages/ | No | Cache, not source |
Always commit both stacy.toml and stacy.lock. The lockfile is what ensures everyone gets the same package versions.
Troubleshooting
“Lockfile out of sync”
The lockfile doesn’t match stacy.toml:
stacy lock # Regenerate
“Checksum mismatch”
The cached package differs from what’s in the lockfile:
stacy cache packages clean # Clear cache
stacy install # Re-download
If the re-download also mismatches, the source changed the package without changing its version. Run stacy update <package> to re-lock it, and expect your results to change.
“The package cache does not match stacy.lock”
stacy run reports this when a locked production package is missing from the cache, or when any locked package has been modified since it was installed:
stacy install # Install the locked packages
stacy cache packages clean # ...or clear a modified cache first
A cached package that no longer hashes to its locked checksum is reported as modified whatever group it is in, since every locked package is on the ado-path.
Merge conflicts in lockfile
After a git merge with conflicts:
# Resolve stacy.toml conflicts first, then:
stacy lock # Regenerate lockfile
See Also
- stacy install - Install packages from lockfile
- stacy lock - Generate/verify lockfile
- stacy add - Add packages
- Project Config - The stacy.toml file