RFC: Configurable / default `lake cache` scopes
Nobody has claimed this yet.
- Dominant language
- Lean
- Stars
- 9.2k
- Forks
- 990
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 175
Description
Proposal
A custom S3 cache service can only be used by passing --scope/--repo on every cache get. This RFC proposes a per-service Boolean config field, scoped. A service with scoped = false uses no scope: lake cache get --service <name> then works without a scope flag.
Background
Scope is the path prefix under which a service stores artifacts ({endpoint}/{scope}/{hash}.art) and mappings ({endpoint}/{scope}/{rev}.jsonl). The scope kind also selects the revision layout. A repository scope (--repo) inserts platform and toolchain segments into the revision path: …/{scope}/pt/{platform}/tc/{toolchain}/{rev}.jsonl (Cache.lean:1004-1016). These segments are empty when the package sets platformIndependent := true and fixedToolchain := true (Main.lean:455-459). A string scope (--scope) is a pure prefix.
Today the scope comes only from the CLI for S3 services, or is derived from package fields for Reservoir (reservoirScope). A flat content-addressed bucket has no meaningful scope, yet the CLI still demands one. The flat layout is in fact not expressible today: an empty --scope argument is accepted but produces a double slash in the URL (Main.lean:299; compare --toolchain, which maps an empty argument to "none", Main.lean:311).
Current state
For a custom (non-Reservoir) service, cache get requires --scope or --repo; with neither it errors (Main.lean:565-566). The service config carries endpoints and kind, but no scope (LakeConfig.lean:30-42).
The change
Add scoped to CacheServiceConfig, default true:
scoped = true: the current behavior.cache getandcache putrequire--scopeor--repo.scoped = false: the service uses no scope. Artifacts live at{endpoint}/{hash}.artand mappings at{endpoint}/{rev}.jsonl, forgetandputalike. A fixed prefix, if wanted, is written into the endpoint URLs.
Three rules keep this safe:
- A
--scope/--repoflag directed at ascoped = falseservice is an error. The flag is not ignored, and it is never appended after a prefix inside the endpoint. - An empty argument to
--scopeor--repobecomes an error. Today it is accepted and produces a malformed URL. scopedon a Reservoir service is a configuration error: Reservoir derives its scope per package and has no flat form.
Configuration sketch
[[cache.service]]
name = "forks"
kind = "s3"
artifactEndpoint = "https://cache.example.org/forks/artifacts"
revisionEndpoint = "https://cache.example.org/forks/revisions"
scoped = false # flat: no prefix, no scope flag
[[cache.service]]
name = "master"
kind = "s3"
artifactEndpoint = "https://cache.example.org/artifacts/leanprover-community/mathlib4"
revisionEndpoint = "https://cache.example.org/revisions/leanprover-community/mathlib4"
scoped = false # fixed prefix, written into the endpoints
lake cache get --service forks then needs no scope flag.
Security
Scope is the operator describing where to read, which is already their responsibility; reads remain verified by hash
Limitation and future work
scoped = false always produces plain paths. A service with the platform/toolchain revision layout therefore still needs --repo on every call: Lake computes those segments per call, so they cannot be written into an endpoint URL. This matters only for packages outside the platformIndependent/fixedToolchain regime, and no current consumer needs it. When one does, the extension is per-service value fields (scope = "prefix", repo = "owner/name") that join scoped = false as mutually exclusive alternatives, with at most one of the three set, and a package-to-scope derivation for custom services.
Backwards compatibility
Flows that pass --scope/--repo are unaffected, and scoped defaults to true, which is changes are visible: an empty --scope/--repo argument becomes an error instead of amalformed URL, and a scope flag directed at a scoped = false service is an error.
User Experience
A service declares once, in config, that it takes no scope; lake cache get --service <name> works with no scope flag. Fixed prefixes live in the endpoint URLs.
Beneficiaries
Anyone running an S3-backed Lake cache who would rather configure the layout once than pass --scope on every call. Flat content-addressed buckets that have no scope to pass. Chain members in the companion read
chain RFC (#14152), which need services that resolve without flags.
Maintainability
Small: one Boolean config field, one CLI check relaxed, two error checks added.
Impact
Add 👍 to issues you consider important. If others benefit from the changes in this proposal being added, pleaseask them to add 👍 to it.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Lake/Config/LakeConfig.lean for CacheServiceConfig, then read the scope handling and validation in Lake/CLI/Main.lean and the layout logic in Lake/Config/Cache.lean. Done means the proposed scoped configuration, flag validation, empty-argument errors, and Reservoir restriction are implemented while preserving the default behavior.
Written by the indexing model from the issue text.
Assessment
- Domain
- build-system, cli
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100