Skip to content

Configure applications with defineConfig - #514

Draft
tuler wants to merge 1 commit into
claude/cartesi-cli-library-api-2m3q2qfrom
claude/define-config-function-7hq2t0
Draft

Configure applications with defineConfig#514
tuler wants to merge 1 commit into
claude/cartesi-cli-library-api-2m3q2qfrom
claude/define-config-function-7hq2t0

Conversation

@tuler

@tuler tuler commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

Replaces cartesi.toml with a cartesi.config.ts file that exports its configuration through defineConfig, in the style of rolldown and tsdown, so an application can be configured without reaching for the programmatic API.

import { defineConfig } from "@cartesi/cli/config";

export default defineConfig({
    drives: { data: { builder: "empty", size: "64Mi", mount: "/mnt/data" } },
    machine: { entrypoint: "dapp", ramLength: "256Mi" },
    run: { epochLength: 10, services: ["explorer"] },
});

defineConfig does nothing at runtime: it exists so the configuration is type checked and completed by the editor, without any annotation. A configuration file may also export a function, which receives the command being run and the mode, so a project can configure itself differently for build and run:

export default defineConfig(async ({ command, mode }) => ({
    machine: { envFile: `.env.${mode}` },
    run: { epochLength: command === "run" ? 10 : 720 },
}));

Based on #512, which it builds directly on top of.

Key Changes

  • defineConfig (src/config/user.ts), exported from a new @cartesi/cli/config subpath so a configuration file does not pull in the rest of the CLI. Also re-exported from the main entrypoint.

  • Configuration subsystem (src/config/): the old src/config.ts becomes a module with types.ts (resolved types), user.ts (input types and defineConfig), normalize.ts (validation and defaulting), load.ts (discovery and loading), merge.ts, size.ts, errors.ts, and toml.ts for the deprecated format.

  • Applications not written in TypeScript describe the very same configuration as plain data. The configuration file of a project is the first of these that exists:

    File Format
    cartesi.config.ts, .mts, .cts, .js, .mjs, .cjs module exporting defineConfig({ ... })
    cartesi.config.json JSON
    cartesi.config.yaml, cartesi.config.yml YAML
    cartesi.config YAML, which accepts JSON as well
    cartesi.toml deprecated

    Because the data formats are not type checked, the whole configuration is validated at load time, whichever format it came from. A $schema key is accepted and ignored.

  • run section: project level defaults of the local development environment (epochLength, services, blockTime, forkUrl, projectName, ...), so they do not have to be repeated on every cartesi run. Command line options take precedence, which is why the commander defaults moved into run itself; the layering is a pure resolveRunOptions function.

  • cartesi.toml keeps working, and is read when a project has no other configuration file, but is deprecated and prints a warning. Its parser moves to config/toml.ts and is otherwise left frozen: it stays snake_case and has no [run] section.

Notable Implementation Details

  • TypeScript configuration files are imported by the runtime itself wherever it can handle them, which covers the standalone binaries (bun) and node from the version that strips types. Older versions of node fall back to jiti, kept external in the bundle because it lazily requires its own transform at runtime and cannot be bundled. Both paths were verified against the built artifacts.
  • path.extname reads a file with no format in its name, such as cartesi.config, as .config, which is handled as YAML.
  • A resolved Config is a valid UserConfig, so it can be given straight back to resolveConfig, and normalizing it again is idempotent. withdrawal is the name in the input, with withdrawalConfig accepted for that reason.
  • The module cache is busted per load, so a configuration file edited between two builds of the same process is picked up, which the interactive run shell relies on.

Behavior Changes

  • resolveConfig is now asynchronous, since a configuration file has to be imported, and its config option accepts a configuration written inline, in the same shape a cartesi.config.ts file exports.
  • -c/--config defaults to looking the configuration file of the project up, rather than to cartesi.toml. An explicitly given file that does not exist is an error, as before.
  • The --default-block warning moved from the run command into run(), where the resolved value is known, and is silenced along with the rest of the progress output.

Fixes Found Along the Way

  • Sizes are parsed strictly and understand the IEC units, so "64Mi" is 64 MiB instead of being silently read as 64 bytes: bytes.parse falls back to parseInt on strings it does not recognize, which made a drive several orders of magnitude too small. This drops the bytes dependency, now unused.
  • Errors of an asynchronous command action are reported without a stack trace, as was intended. They surfaced as unhandled rejections, which had no handler, and the handler that did exist was dead code because the bundler replaces process.env.NODE_ENV with its build time value.

Testing

Linting, type checking and 217 unit tests pass, up from 154. The new tests cover defineConfig, normalization and validation of every section, size parsing, file discovery and precedence, loading each supported format, merging a list of files, and the layering of the run options.

End to end, against the built artifacts rather than the sources: a TypeScript configuration exporting a function was loaded both by dist/lib.js under node and by the compiled linux-x64 binary, along with the YAML, bare cartesi.config and deprecated TOML files, and the error paths were checked to print a single line. Docker was not available in the development environment, so the build itself was only exercised up to the point where it shells out.

Two things left out, both easy to add later: a generated JSON Schema for $schema driven completion in the data formats, and the application templates, which live in another repository.

https://claude.ai/code/session_01BHFyMTf1k57qtDwcbwVjrs


Generated by Claude Code

Replace `cartesi.toml` with a `cartesi.config.ts` file that exports its
configuration through `defineConfig`, imported from `@cartesi/cli/config`.
The helper does nothing at runtime, and exists so the configuration is type
checked and completed by the editor, without any annotation. A configuration
file may also export a function, which receives the command being run and the
mode, so a project can configure itself differently for `build` and `run`.

TypeScript configuration files are read by the runtime itself, which covers
the standalone binaries (bun) and recent versions of node; older versions of
node fall back to jiti, kept external so it can transpile at runtime.

Applications not written in TypeScript or JavaScript describe the same
configuration as plain data, in a `cartesi.config.json`, `cartesi.config.yaml`
or `cartesi.config` file, the last one read as YAML so it accepts JSON too.
Because those are not type checked, the whole configuration is validated at
load time, whichever format it came from.

The configuration gained a `run` section with the project defaults of the
local development environment, so they do not have to be repeated on every
`cartesi run`. Command line options take precedence over it, which is why the
commander defaults were moved into `run` itself.

`cartesi.toml` keeps working, and is read when a project has no other
configuration file, but is deprecated and prints a warning. Its parser moves
to `config/toml.ts` and is otherwise left frozen.

Along the way:

- sizes are parsed strictly and understand the IEC units, so `"64Mi"` is 64
  MiB instead of being silently read as 64 bytes by `bytes.parse`, which falls
  back to `parseInt`. This drops the `bytes` dependency, now unused;
- errors of an asynchronous command action are reported without a stack trace,
  as intended: they surfaced as unhandled rejections, which had no handler, and
  the handler that did exist was dead code because the bundler replaces
  `process.env.NODE_ENV` at build time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BHFyMTf1k57qtDwcbwVjrs
@changeset-bot

changeset-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0471fc9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@cartesi/cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedjiti@​2.7.09710010082100

View full report

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🟢 Lines 76.8% (🎯 0%) 5781 / 7527
🔵 Statements 76.8% 5781 / 7527
🔵 Functions 72.46% 200 / 276
🔵 Branches 0% 0 / 0
📁 File Coverage (20 files)
File Lines Statements Functions Branches Uncovered Lines
apps/cli/src/api/connection.ts 🔴 13.33% 🔴 13.33% 🔴 0% 🔴 0% 59-84
apps/cli/src/api/deposit/common.ts 🟢 93.1% 🟢 93.1% 🟡 71.43% 🔴 0% 90, 94
apps/cli/src/api/deposit/erc1155.ts 🔴 3.33% 🔴 3.33% 🔴 0% 🔴 0% 81-159, 170-262, 271-301
apps/cli/src/api/deposit/erc20.ts 🔴 7.07% 🔴 7.07% 🔴 0% 🔴 0% 33-46, 78-155
apps/cli/src/api/deposit/erc721.ts 🔴 5.51% 🔴 5.51% 🔴 0% 🔴 0% 40-49, 78-187
apps/cli/src/api/deposit/ether.ts 🔴 13.64% 🔴 13.64% 🔴 0% 🔴 0% 30-67
apps/cli/src/api/deposit/index.ts 🟢 100% 🟢 100% 🟢 100% 🔴 0% -
apps/cli/src/api/run.ts 🔴 13.16% 🔴 13.16% 🔴 20% 🔴 0% 182-204, 208-221, 225-270, ...
apps/cli/src/api/send.ts 🟡 71.96% 🟡 71.96% 🟢 80% 🔴 0% 46-47, 74-78, 95, 104, 156-176
apps/cli/src/api/types.ts 🟢 100% 🟢 100% 🟢 100% 🔴 0% -
apps/cli/src/base.ts 🔴 13.79% 🔴 13.79% 🔴 0% 🔴 0% 35, 39-44, 48, 58-64, 70-13...
apps/cli/src/builder/directory.ts 🟢 100% 🟢 100% 🟢 100% 🔴 0% -
apps/cli/src/builder/docker.ts 🟢 86.72% 🟢 86.72% 🟡 66.67% 🔴 0% 75-77, 79, 109-111, 169-178
apps/cli/src/builder/empty.ts 🟢 100% 🟢 100% 🟢 100% 🔴 0% -
apps/cli/src/builder/none.ts 🟢 100% 🟢 100% 🟢 100% 🔴 0% -
apps/cli/src/builder/tar.ts 🟢 100% 🟢 100% 🟢 100% 🔴 0% -
apps/cli/src/compose/anvil.ts 🔴 7.14% 🔴 7.14% 🔴 0% 🔴 0% 12-47, 51-52, 72-85
apps/cli/src/compose/builder.ts 🟢 99.79% 🟢 99.79% 🟢 100% 🔴 0% 228
apps/cli/src/compose/bundler.ts 🔴 4.82% 🔴 4.82% 🔴 0% 🔴 0% 8-40, 44-75, 79-92
apps/cli/src/compose/common.ts 🟢 100% 🟢 100% 🟢 100% 🔴 0% -

@brunomenezes brunomenezes moved this to 🧑‍💻 In Progress in Rollups Tooling Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo
Status: 🧑‍💻 In Progress

Development

Successfully merging this pull request may close these issues.

3 participants