Skip to content

feat(minibf): add optional base_path configuration#872

Open
adrian1-dot wants to merge 4 commits intotxpipe:mainfrom
adrian1-dot:feat/add-api-v0-base-path
Open

feat(minibf): add optional base_path configuration#872
adrian1-dot wants to merge 4 commits intotxpipe:mainfrom
adrian1-dot:feat/add-api-v0-base-path

Conversation

@adrian1-dot
Copy link

@adrian1-dot adrian1-dot commented Feb 6, 2026

Add optional base_path configuration for Blockfrost API spec compliance

Problem

The Blockfrost OpenAPI specification defines all API endpoints under the /api/v0 base path (see servers section in the official spec). However, Dolos minibf currently serves endpoints directly at the root level (e.g., /network, /blocks/latest), which breaks compatibility with tools and libraries that expect canonical Blockfrost endpoint paths.

This incompatibility causes integration issues with:

  • blockfrost-haskell: Client library with hardcoded /api/v0 base path
  • Other Blockfrost-compatible tooling may also expect standard endpoint structure

Solution

Add an optional base_path configuration field to MinibfConfig that allows nesting all routes under a custom prefix.

Configuration example:

[serve.minibf]
listen_address = "[::]:3000"
base_path = "/api/v0"  # Optional - omit for root-level routes

When configured:

  • All routes become accessible at {base_path}/{endpoint} (e.g., /api/v0/network)
  • Original root-level routes are no longer accessible

Changes

  1. crates/core/src/config.rs: Added base_path: Option<String> field to MinibfConfig
  2. crates/minibf/src/lib.rs: Modified build_router() to conditionally nest all routes when base_path is set (~7 lines)
  3. crates/minibf/README.md: Added documentation for the new configuration option
  4. docs/content/apis/minibf.mdx: Updated API documentation
  5. src/bin/dolos/init.rs: Added base_path to default configuration initialization

Implementation Details

The implementation uses Axum's Router::nest() to wrap the entire route tree under the custom base path when configured:

if let Some(base_path) = &cfg.base_path {
    Router::new().nest(base_path, app).layer(NormalizePathLayer::trim_trailing_slash())
} else {
    app.layer(NormalizePathLayer::trim_trailing_slash())
}

This approach minimizes code changes while providing complete Blockfrost spec compliance.

Backwards Compatibility

  • Fully backwards compatible - when base_path is not configured, all routes remain at root level exactly as before.

Testing

  • All 110 existing tests pass (cargo test --workspace --all-features)
  • No clippy warnings (cargo clippy --workspace --all-features)
  • Successfully tested with local atlas server using /api/v0 base path
  • Verified endpoints accessible at /api/v0/* with base_path configured
  • Verified endpoints at root level when base_path is omitted

Impact

This change enables Dolos minibf to serve as a drop-in replacement for Blockfrost API in tools requiring canonical endpoint paths, significantly improving ecosystem compatibility while maintaining flexibility for users who prefer root-level routes.

Summary by CodeRabbit

  • New Features

    • Added optional base_path to serve all API endpoints (including health and metrics) under a custom path prefix (e.g., /api/v0); when omitted, endpoints remain at root.
  • Bug Fixes

    • Added validation for base_path format with clear error messages for invalid values to prevent misconfiguration.
  • Documentation

    • Updated guides and examples to explain base_path usage, defaults, route namespacing, and sample configuration.

@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Added an optional base_path: Option<String> to MinibfConfig; router construction now validates and, when set, nests all minibf routes under that path (with trailing-slash normalization). Documentation and default initialization were updated to include the new field.

Changes

Cohort / File(s) Summary
Configuration Structure
crates/core/src/config.rs
Added pub base_path: Option<String> to MinibfConfig with docs describing route prefixing and default behavior.
Routing Implementation
crates/minibf/src/lib.rs
Router build now validates cfg.base_path (must start with /, not / alone, not empty, no wildcards) and nests the app under the base path when Some, applying NormalizePathLayer::trim_trailing_slash() inside the nested router; preserves prior behavior when None.
Configuration Initialization
src/bin/dolos/init.rs
MinibfConfig literal updated to include base_path: None as the default.
Documentation
crates/minibf/README.md, docs/content/apis/minibf.mdx
Added Blockfrost spec compliance guidance and examples; introduced base_path option in config docs and TOML example; clarified endpoint prefixing (health, metrics included).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I found a cosy path beneath the tree,
Routes hop in line — /api/v0 or free,
I twitch, I code, I bind them with a charm,
No more root confusion, everything’s calm,
A tiny rabbit cheer for one small change!

🚥 Pre-merge checks | ✅ 2 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (122 files):

⚔️ .github/workflows/release.yml (content)
⚔️ CHANGELOG.md (content)
⚔️ Cargo.lock (content)
⚔️ Cargo.toml (content)
⚔️ crates/cardano/Cargo.toml (content)
⚔️ crates/cardano/src/cip68.rs (content)
⚔️ crates/cardano/src/estart/loading.rs (content)
⚔️ crates/cardano/src/estart/mod.rs (content)
⚔️ crates/cardano/src/estart/reset.rs (content)
⚔️ crates/cardano/src/estart/work_unit.rs (content)
⚔️ crates/cardano/src/ewrap/commit.rs (content)
⚔️ crates/cardano/src/ewrap/loading.rs (content)
⚔️ crates/cardano/src/ewrap/mod.rs (content)
⚔️ crates/cardano/src/ewrap/refunds.rs (content)
⚔️ crates/cardano/src/ewrap/rewards.rs (content)
⚔️ crates/cardano/src/ewrap/work_unit.rs (content)
⚔️ crates/cardano/src/ewrap/wrapup.rs (content)
⚔️ crates/cardano/src/hacks.rs (content)
⚔️ crates/cardano/src/lib.rs (content)
⚔️ crates/cardano/src/model.rs (content)
⚔️ crates/cardano/src/pots.rs (content)
⚔️ crates/cardano/src/rewards/formulas.rs (content)
⚔️ crates/cardano/src/rewards/mod.rs (content)
⚔️ crates/cardano/src/roll/accounts.rs (content)
⚔️ crates/cardano/src/roll/assets.rs (content)
⚔️ crates/cardano/src/roll/batch.rs (content)
⚔️ crates/cardano/src/roll/epochs.rs (content)
⚔️ crates/cardano/src/roll/mod.rs (content)
⚔️ crates/cardano/src/roll/pools.rs (content)
⚔️ crates/cardano/src/roll/proposals.rs (content)
⚔️ crates/cardano/src/roll/txs.rs (content)
⚔️ crates/cardano/src/rupd/deltas.rs (content)
⚔️ crates/cardano/src/rupd/loading.rs (content)
⚔️ crates/cardano/src/rupd/mod.rs (content)
⚔️ crates/cardano/src/rupd/work_unit.rs (content)
⚔️ crates/cardano/src/utils.rs (content)
⚔️ crates/cardano/src/validate.rs (content)
⚔️ crates/core/Cargo.toml (content)
⚔️ crates/core/src/async_query.rs (content)
⚔️ crates/core/src/config.rs (content)
⚔️ crates/core/src/mempool.rs (content)
⚔️ crates/core/src/submit.rs (content)
⚔️ crates/core/src/sync.rs (content)
⚔️ crates/minibf/Cargo.toml (content)
⚔️ crates/minibf/README.md (content)
⚔️ crates/minibf/src/hacks.rs (content)
⚔️ crates/minibf/src/lib.rs (content)
⚔️ crates/minibf/src/mapping.rs (content)
⚔️ crates/minibf/src/pagination.rs (content)
⚔️ crates/minibf/src/routes/accounts.rs (content)
⚔️ crates/minibf/src/routes/addresses.rs (content)
⚔️ crates/minibf/src/routes/assets.rs (content)
⚔️ crates/minibf/src/routes/blocks.rs (content)
⚔️ crates/minibf/src/routes/epochs/mod.rs (content)
⚔️ crates/minibf/src/routes/genesis.rs (content)
⚔️ crates/minibf/src/routes/governance.rs (content)
⚔️ crates/minibf/src/routes/metadata.rs (content)
⚔️ crates/minibf/src/routes/network.rs (content)
⚔️ crates/minibf/src/routes/pools.rs (content)
⚔️ crates/minibf/src/routes/tx/submit/mod.rs (content)
⚔️ crates/minibf/src/routes/txs.rs (content)
⚔️ crates/minibf/src/routes/utxos.rs (content)
⚔️ crates/redb3/src/indexes/mod.rs (content)
⚔️ crates/testing/Cargo.toml (content)
⚔️ crates/testing/README.md (content)
⚔️ crates/testing/src/blocks.rs (content)
⚔️ crates/testing/src/lib.rs (content)
⚔️ crates/testing/src/toy_domain.rs (content)
⚔️ crates/trp/Cargo.toml (content)
⚔️ crates/trp/src/lib.rs (content)
⚔️ crates/trp/src/methods.rs (content)
⚔️ dist-workspace.toml (content)
⚔️ docs/content/apis/minibf.mdx (content)
⚔️ docs/content/configuration/schema.mdx (content)
⚔️ examples/custom-network/dolos.toml (content)
⚔️ src/adapters/mod.rs (content)
⚔️ src/bin/dolos/bootstrap/mithril.rs (content)
⚔️ src/bin/dolos/common.rs (content)
⚔️ src/bin/dolos/daemon.rs (content)
⚔️ src/bin/dolos/data/cardinality_stats.rs (content)
⚔️ src/bin/dolos/data/clear_state.rs (content)
⚔️ src/bin/dolos/data/compute_nonce.rs (content)
⚔️ src/bin/dolos/data/compute_spdd.rs (content)
⚔️ src/bin/dolos/data/copy_wal.rs (content)
⚔️ src/bin/dolos/data/dump_blocks.rs (content)
⚔️ src/bin/dolos/data/dump_entity.rs (content)
⚔️ src/bin/dolos/data/dump_logs.rs (content)
⚔️ src/bin/dolos/data/dump_state.rs (content)
⚔️ src/bin/dolos/data/dump_wal.rs (content)
⚔️ src/bin/dolos/data/export.rs (content)
⚔️ src/bin/dolos/data/find_seq.rs (content)
⚔️ src/bin/dolos/data/housekeeping.rs (content)
⚔️ src/bin/dolos/data/import_archive.rs (content)
⚔️ src/bin/dolos/data/prune_chain.rs (content)
⚔️ src/bin/dolos/data/prune_wal.rs (content)
⚔️ src/bin/dolos/doctor/catchup_stores.rs (content)
⚔️ src/bin/dolos/doctor/reset_wal.rs (content)
⚔️ src/bin/dolos/doctor/rollback.rs (content)
⚔️ src/bin/dolos/doctor/update_entity.rs (content)
⚔️ src/bin/dolos/doctor/wal_integrity.rs (content)
⚔️ src/bin/dolos/eval.rs (content)
⚔️ src/bin/dolos/init.rs (content)
⚔️ src/bin/dolos/serve.rs (content)
⚔️ src/bin/dolos/sync.rs (content)
⚔️ src/lib.rs (content)
⚔️ src/serve/grpc/submit.rs (content)
⚔️ src/sync/emulator.rs (content)
⚔️ src/sync/submit.rs (content)
⚔️ tests/cardano/harness/compare.rs (content)
⚔️ tests/cardano/harness/dump.rs (content)
⚔️ tests/external/smoke.rs (content)
⚔️ xtask/src/bootstrap.rs (content)
⚔️ xtask/src/config.rs (content)
⚔️ xtask/src/ground_truth/delegation.rs (content)
⚔️ xtask/src/ground_truth/epochs.rs (content)
⚔️ xtask/src/ground_truth/eras.rs (content)
⚔️ xtask/src/ground_truth/generate.rs (content)
⚔️ xtask/src/ground_truth/query.rs (content)
⚔️ xtask/src/ground_truth/rewards.rs (content)
⚔️ xtask/src/ground_truth/stake.rs (content)
⚔️ xtask/src/test_instance/create.rs (content)
⚔️ xtask/src/util.rs (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(minibf): add optional base_path configuration' clearly and specifically describes the main change: introducing an optional base_path configuration field to the minibf service.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch feat/add-api-v0-base-path
  • Post resolved changes as copyable diffs in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@crates/core/src/config.rs`:
- Around line 572-575: Update the doc comment for the `base_path:
Option<String>` field in crates/core/src/config.rs to reflect the actual
behavior in `build_router()` (lib.rs): when `base_path` is set, all
routes—including `/health` and `/metrics`—are nested under that base path;
remove or correct the line that says "/health and /metrics remain at root" so
the comment matches the implementation (or, if you prefer the other approach,
modify `build_router()` to exclude `/health` and `/metrics` from nesting under
`base_path`).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@crates/minibf/src/lib.rs`:
- Around line 442-446: The config validation for cfg.base_path is too narrow and
must also reject values missing a leading '/' (and optionally containing
wildcards) to avoid Axum's opaque panic when calling
Router::new().nest(base_path, app); update the check around cfg.base_path in
lib.rs to: ensure base_path is not empty, starts with '/', is not just "/" (as
already done), and does not contain '*' (or other invalid characters you choose
to forbid); if validation fails, panic or return a clear error message
referencing cfg.base_path so callers see a helpful configuration error before
reaching Router::nest (which is where NormalizePathLayer::trim_trailing_slash()
is applied).

Comment on lines +445 to +447
panic!(
"base_path must start with '/', must not be just '/', and must not contain wildcards; got: \"{}\"",
base_path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should avoid a panic and instead bubble up an error, ideally a "config" error variant.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've addressed the panic by adding a ConfigError(String) variant to ServeError in dolos-core and changing build_router to return Result<Router, ServeError>. Invalid base_path values now return Err(ServeError::ConfigError(...)) instead of panicking, propagating through Driver::run consistently with BindError and ShutdownError.

I've also rebased onto the current main to resolve the CI conflicts.

adrian1-dot and others added 4 commits March 6, 2026 10:48
Allows nesting all routes under a custom prefix (e.g., /api/v0) for
Blockfrost OpenAPI spec compliance. Backward compatible when unset.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- Check for leading '/' to prevent Axum panic
- Reject wildcard characters
- Improve error message with actual value
@adrian1-dot adrian1-dot force-pushed the feat/add-api-v0-base-path branch from 4da0643 to f1a5f18 Compare March 6, 2026 09:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants