Project configuration (bino.toml)
Every bino reporting project is defined by a bino.toml file at its root.
This file marks the project boundary and stores project-level configuration.
Purpose
Section titled “Purpose”The bino.toml file serves several important purposes:
-
Project root marker: Commands like
bino build,bino preview,bino lint, and the VS Code extension use this file to locate the project root. Similar to how Git uses.git/, bino searches upward from the current directory until it findsbino.toml. -
Project identity: The
report-idfield provides a stable, unique identifier for the project. -
Future extensibility: As bino evolves, additional project-level settings will be added to this file.
File format
Section titled “File format”The bino.toml file uses TOML format. Here's the current structure:
# Bino project configuration
# This file marks the root of a bino reporting project.
report-id = "550e8400-e29b-41d4-a716-446655440000"
engine-version = "v0.37.0"Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
report-id | string | A unique identifier for this reporting project. Generated as a UUID by bino init. |
engine-version | string | Optional. The template engine version to use (e.g., v0.37.0). If not specified, the latest locally installed version is used. |
Template engine versioning
Section titled “Template engine versioning”The engine-version field allows you to pin a specific template engine version for your project. This is useful for:
- Reproducible builds: Ensure all team members and CI/CD pipelines use the same template engine version.
- Gradual upgrades: Test new engine versions on a project-by-project basis before rolling out globally.
- Stability: Prevent unexpected behavior changes from automatic engine updates.
When engine-version is set, bino will use that specific version for rendering. If the version is not installed locally, you can download it with:
bino setup --template-engine --engine-version v0.37.0If engine-version is omitted, bino uses the latest locally installed version. Run bino update to download the latest version.
Lifecycle hooks
Section titled “Lifecycle hooks”You can define shell commands that run at specific pipeline checkpoints. Shared hooks apply to all commands, while per-command hooks override them for the same checkpoint:
[hooks]
pre-datasource = ["python scripts/fetch_data.py"]
[build.hooks]
pre-build = ["./scripts/validate_env.sh"]
post-build = ["./scripts/upload.sh"]
[preview.hooks]
pre-preview = ["python scripts/generate_fixtures.py"]
[serve.hooks]
pre-serve = ["./scripts/warmup.sh"]Each checkpoint maps to an array of shell commands executed sequentially. Commands run through sh -c on Unix and cmd /C on Windows.
See Hooks for the full reference of checkpoints, environment variables, and error handling.
Lint rules
Section titled “Lint rules”The [lint] table decides which lint findings you see and how bino grades them:
[lint]
disable = ["table-sum-title-unused", "myplugin/deprecated-api"]
[lint.severity]
text-content-required = "error"
i18n-code-unused = "info"| Field | Type | Description |
|---|---|---|
lint.disable | array | Rule IDs whose findings are never reported. |
lint.severity | table | Rule ID mapped to "error", "warning" or "info". |
disable removes a finding from everything bino prints, writes to the lint
log, or sends to the editor — bino lint, bino build, bino preview --lint
and the LSP alike. It hides the finding; it does not repair what the finding
was about.
severity re-grades a finding everywhere it is reported: bino lint prints
the severity in front of the finding, and the editor squiggles it in the
matching colour.
Exit codes
Section titled “Exit codes”Only a severity you set here affects an exit code.
"error"makesbino lintexit non-zero on its own, without--fail-on-warnings."info"keeps the finding visible but exempts it from--fail-on-warnings.- A rule that reports
Errorin the rules table by itself stays advisory and changes no exit code. That has always been true, and[lint]does not change it — name the rule in[lint.severity]if you want it to fail the command.
bino build is not affected at all. It prints the findings that survive
[lint], but no lint finding has ever failed a build, and [lint] neither
makes one fail nor makes bino build accept a bundle it cannot render.
disable also never repairs a bundle. With
disable = ["schema-validation", "manifest-load"], manifests that fail to load
or fail schema validation no longer produce a line anywhere, but bino lint
still exits non-zero: the bundle really could not be read, and that is not a
finding you can switch off.
Rule IDs you can name
Section titled “Rule IDs you can name”Every rule in the lint rules appendix, plus four IDs that findings carry although no rule of that name declares them:
| ID | Covers |
|---|---|
schema-validation | One finding per manifest that failed schema validation. |
manifest-load | One finding per manifest that could not be loaded at all. |
engine-version-incompatible | The engine-version pin check. |
missing-required-reference | The dangling-reference finding, which page-layout-slots-used emits while it walks the children of a page. The appendix documents it under its own heading. |
Disabling engine-version-incompatible turns that check off completely: the
finding disappears and so does the non-zero exit it forced, in bino lint and
in the editor. Disabling schema-validation or manifest-load only hides the
lines, as described above.
The first three take disable only. How much they weigh follows the state of
the bundle, not a severity you pick, so a [lint.severity] entry on them is
rejected with a warning instead of quietly doing nothing.
Plugin rules are named "<plugin>/<rule>", for example
"myplugin/deprecated-api". Bino cannot know a plugin's rule set before the
plugin has run, so entries containing a / are taken as written.
Unknown entries
Section titled “Unknown entries”A rule ID that no rule in this project declares, a severity outside error,
warning and info, and a severity on an ID that cannot honour one are
ignored and reported by bino lint:
bino.toml: unknown rule id "no-such-rule" in [lint] disable
bino.toml: invalid severity "fatal" for rule id "text-content-required" in [lint] severity (want error, warning or info)
bino.toml: severity is not configurable for "schema-validation"; use [lint] disable to silence itSuch an entry is never silently honoured — it disables nothing and re-grades nothing.
Not covered by [lint]
Section titled “Not covered by [lint]”- Data validation warnings from
bino lint --execute-queries. They are not lint findings and carry no rule ID. - Unresolved
${VAR}environment variables and the YAML syntax errors the editor reports on their own. No rule declares them, so naming them in[lint]is an unknown rule ID.
Registry and dependencies
Section titled “Registry and dependencies”Projects can consume predef packages from a bino registry. The registry
connection lives in the [registry] table, and dependencies are declared in
the [dependencies] table:
[registry]
url = "https://registry.bino.bi" # optional; this is the default
token = "${ACME_REGISTRY_TOKEN}" # optional; literal or ${ENV_VAR}
[dependencies]
"@acme/quarterly-report" = "1.2.3" # exact version = pinned
"@acme/base-layout" = "latest" # a tag name = follows the tag| Field | Type | Description |
|---|---|---|
registry.url | string | Optional. Registry base URL; overrides BINO_REGISTRY_URL and the global ~/.bino/config.toml; defaults to the public registry. |
registry.token | string | Optional. Access token, either literal or a ${ENV_VAR} reference expanded at runtime. |
dependencies.* | string | One entry per package (@scope/name), mapping to an exact version or a tag. |
The [dependencies] table is normally maintained by bino registry add and
bino registry remove; resolved versions are pinned in bino.lock next to
bino.toml. See the Registry and dependencies guide
and the bino registry command reference.
Predef packages
Section titled “Predef packages”A [package] table turns an ordinary project into a predef project: a project
that not only renders reports, but also authors a reusable package other projects
can install. The table itself is the marker — there is no type key and no second
config file. report-id and [package] coexist, so a project can keep rendering
its own reports while it publishes a kit.
report-id = "550e8400-e29b-41d4-a716-446655440000"
[package]
name = "@acme/starter-kit"
description = "A reusable kit: an IBCS table, the style it wears and a logo asset."
tags = ["starter", "ibcs"]
category = "components"
visibility = "private"
compat-engine = ">=1.0.0"
include = ["components", "styles", "resources"]
preview = "mocks/preview.yaml#starter-kit-preview"| Field | Type | Description |
|---|---|---|
name | string | Required. The package name, @scope/name, with lowercase a-z0-9_- segments. |
description | string | Optional. One-line summary shown in the registry. |
tags | array | Optional. Free-form keywords for registry search. |
category | string | Optional. Registry category. |
visibility | string | Optional. public or private; defaults to private. |
compat-engine | string | Optional. Semver range of template engine versions this package works with. |
compat-cli | string | Optional. Semver range of bino CLI versions this package works with. |
include | array | Optional. Project-relative files and directories that make up the package. Defaults to every canonical manifest folder. |
preview | string | Optional. path#definition-name — the artefact or page that demonstrates the package. |
The include set
Section titled “The include set”Without an explicit include list a package publishes every canonical manifest
folder:
components datasets datasources i18n manifests
pages resources scaling secrets signing stylesAn entry matches a file of exactly that name or anything below a directory of
that name, so include = ["components", "styles/corporate.yaml"] publishes the
whole components/ tree plus one style manifest.
Three directories are never part of a package, whatever include says:
mocks/— the sample data and the preview harness that make the package demonstrable locally. This is where the artefact and the mockDataSourcebelong.reports/— the artefacts that render your reports..bino/— machine-managed state, including the packages you installed yourself.
Naming inside a package
Section titled “Naming inside a package”Documents in the include set carry the package name: exactly @acme/starter-kit
for the single main definition, or @acme/starter-kit/<definition> for every
other one. A DataSource is the exception — its name becomes a DuckDB view name
and is limited to two segments — so mock data sources belong in mocks/.
Run bino init predef to scaffold a project that already carries a [package]
table, a namespaced component, a style, an asset and a working mocks/ preview.
The wizard asks for the scope, name, description, visibility, tags and category
and writes them into the table and into every namespaced metadata.name.
Creating a project
Section titled “Creating a project”When you run bino init, a bino.toml file is automatically created in the target directory with a freshly generated UUID for the report-id:
bino init --directory my-reportsThis creates my-reports/bino.toml along with the starter manifests.
Project root discovery
Section titled “Project root discovery”When you run a bino command, the CLI searches for bino.toml starting from your current working directory and walking up the directory hierarchy:
/home/user/projects/my-reports/pages/
└── (no bino.toml, keep searching...)
/home/user/projects/my-reports/
└── bino.toml ← Project root found!
/home/user/projects/
/home/user/
/home/
/This means you can run bino build or bino preview from any subdirectory within your project.
Nested projects
Section titled “Nested projects”Bino supports nested projects. If you have a parent project with its own bino.toml and a subdirectory that also contains a bino.toml, each is treated as a separate project:
/parent-project/
└── bino.toml ← Parent project root
└── reports/
└── ...
└── nested-project/
└── bino.toml ← Nested project root
└── reports/
└── ...When you run bino commands from within nested-project/, they operate on the nested project, not the parent.
VS Code extension
Section titled “VS Code extension”The VS Code extension for Bino Reports activates automatically when it detects a bino.toml file in any workspace folder. The extension:
- Searches each workspace folder for
bino.tomlto identify project roots - Supports multi-root workspaces with multiple bino projects
- Determines the active project based on the currently open file
If no bino.toml is found, the extension's features remain disabled. Create a project with bino init to enable full extension functionality.
Error messages
Section titled “Error messages”If you run a bino command outside of a project (no bino.toml found), you'll see:
bino.toml not found: not inside a bino project (run 'bino init' to create one)This error indicates you need to either:
- Navigate to a directory within an existing bino project
- Create a new project with
bino init - Manually create a
bino.tomlfile