Bindings
Bare names and the trust-evaluation moment. Project bindings, global bindings, and how resolution works.
Esker datasets are addressed by <owner>/<name>@<version>. Two different owners can publish the same <name> — the platform never picks a winner. So how does your code stay readable when every dataset has an owner prefix?
The same way every package manager solves the same problem: bind once, then live in bare-name space.
The moving parts
Three files. All TOML. Same shape as package.json + package-lock.json or pyproject.toml + uv.lock.
Project bindings — pyproject.toml:
[tool.esker]
owner = "statcan" # default publish owner
[tool.esker.datasets]
"ca.corporations.registry" = "statcan/ca.corporations.registry"
"us.sec.companies" = "sec-foundation/us.sec.companies"
Lockfile — esker.lock (committed):
# generated; do not edit
[[dataset]]
name = "ca.corporations.registry"
owner = "statcan"
version = "1.0.0"
schema_version = "1.0.0"
content_hash = "sha256:..."
lineage_hash = "sha256:..."
resolved_at = "2026-04-26T12:00:00+00:00"
Global bindings — ~/.esker/config.toml (off-project fallback):
[datasets]
"ca.corporations.registry" = "statcan/ca.corporations.registry"
The resolution rule
When you write esker.get("ca.corporations.registry") or esker pull ca.corporations.registry:
- If the input is a full ref (
<owner>/<name>[@<version>]), use it directly. - Otherwise look up the bare name in project
[tool.esker.datasets]. - Otherwise look up in global
[datasets]. - Otherwise:
no binding for <name> · run 'esker add <owner>/<name>' or use a full ref.
When a project binding resolves, the lockfile (if present) pins the version. Bare names never auto-resolve when a project has no binding, even if only one publisher exists. Strictness is a feature: behavior should not change the day a second publisher arrives.
Pinned bindings
A binding can include @<version> to pin to an exact version:
[tool.esker.datasets]
# tracks latest — esker.lock supplies the pin
"us.treasury.yields" = "archie/us.treasury.yields"
# exact pin — durable across `esker upgrade`
"us.treasury.yields" = "archie/us.treasury.yields@1.0.0"
A pinned binding short-circuits the lockfile entirely — pyproject.toml is durable intent. esker upgrade refuses to bump pinned bindings.
The workflow
Bind a dependency. The trust-evaluation moment.
$ esker search ca.corporations.registry
statcan/ca.corporations.registry 12,847 records · 4.2k pulls/30d · verified
community/ca.corporations.registry 8,231 records · 142 pulls/30d
$ esker add statcan/ca.corporations.registry
ca.corporations.registry → statcan/ca.corporations.registry@1.0.0
pyproject.toml · esker.lock
Reconcile the cache to the lockfile (mirror of bun install or uv sync):
$ esker sync
ca.corporations.registry · statcan/ca.corporations.registry@1.0.0
Re-resolve to the latest hub version, rewriting the lockfile:
$ esker upgrade ca.corporations.registry
ca.corporations.registry · 1.0.0 → 1.1.0
Drop the binding entirely:
$ esker remove ca.corporations.registry
removed ca.corporations.registry
Project root walk
Project bindings are scoped to the nearest pyproject.toml. The resolver walks up from the current working directory until it finds one. Inside a workspace this picks up the inner project's bindings, not the workspace root's.
If there is no pyproject.toml in the cwd or its parents, esker add errors:
$ esker add statcan/ca.corporations.registry
no pyproject.toml in cwd or parents · use --global to bind
Surgical TOML edits
add and remove use line-pattern regex on the binding lines, not full TOML round-tripping. They preserve user formatting, comments, blank lines, and key ordering outside the touched block.
The cost: a multi-line value or unusual spacing could break the regex. Standard one-line bindings work fine.
Why
Three reasons.
- The owner choice is one explicit moment, not a thousand.
esker addis the only place you have to think about whichstatcan/ca.corporations.registryyou're trusting. Everything downstream — code, CLI, notebooks — uses the bare name. - Reproducibility is a checkbox, not a discipline.
esker.lockpins the exact content hash. Clean clone +esker syncreproduces your cache deterministically. - The platform stays infrastructure. Esker doesn't decide which
ca.corporations.registryis "the canonical one." You do, once, in your project. The disambiguation page atesker.so/<name>ranks publishers by mechanical signal (verification, pull volume, recency) — never by editorial judgment.
See also
- Lockfile —
esker.lockformat and lifecycle - Reading —
esker.getcalling resolve - CLI bindings —
add,remove,sync,upgrade - Handles — what an owner is