Development setup
This guide helps you set up a development environment for contributing to Barbacane.
Prerequisites
- Rust 1.94+ - Install via rustup. This is the project MSRV (enforced by CI); the dependency floor (wasmtime/cranelift) requires it.
- Git - For version control
- Node.js 20+ - For the UI (if working on the web interface)
- PostgreSQL 14+ - For the control plane (or use Docker)
- Docker - For running PostgreSQL locally (optional)
Optional:
- cargo-watch - For auto-rebuild on file changes
- wasm32-unknown-unknown target - For building WASM plugins (
rustup target add wasm32-unknown-unknown) - tmux - For running multiple services in one terminal
Quick Start with Makefile
The easiest way to get started is using the Makefile:
# Start PostgreSQL in Docker
make db-up
# Build all WASM plugins and seed them into the database
make seed-plugins
# Start the control plane (port 9090)
make control-plane
# In another terminal, start the UI (port 5173)
make ui
Then open http://localhost:5173 in your browser.
Makefile Targets
| Target | Description |
|---|---|
| Build & Test | |
make | Run check + test (default) |
make test | Run all workspace tests |
make test-verbose | Run tests with output |
make test-one TEST=name | Run specific test |
make clippy | Run clippy lints |
make fmt | Format all code |
make check | Run fmt-check + clippy |
make build | Build debug |
make release | Build release |
make plugins | Build all WASM plugins |
make compile | Compile spec to artifact.bca |
make seed-plugins | Build plugins and seed registry |
make clean | Clean all build artifacts |
make coverage | Line coverage of the unit tests |
make coverage-integration | Coverage including the integration suite |
make coverage-html | Coverage as a browsable HTML report |
make coverage-lcov | Coverage as lcov.info |
| Development | |
make control-plane | Start control plane server (port 9090) |
make ui | Start UI dev server (port 5173) |
make dev | Show instructions to start both |
make dev-tmux | Start both in tmux session |
| Docker | |
make docker-build | Build both Docker images |
make docker-build-gateway | Build data plane image |
make docker-build-control | Build control plane image |
make docker-up | Start full stack (compose) |
make docker-down | Stop full stack |
make docker-run | Run data plane standalone |
make docker-run-control | Run control plane only |
| Database | |
make db-up | Start PostgreSQL container |
make db-down | Stop PostgreSQL container |
make db-reset | Reset database (removes all data) |
Override the database URL:
make control-plane DATABASE_URL=postgres://user:pass@host/db
Getting Started
Clone the Repository
git clone https://github.com/barbacane/barbacane.git
cd barbacane
Build
# Build all crates
cargo build --workspace
# Build in release mode
cargo build --workspace --release
Test
# Run all tests
cargo test --workspace
# Run tests for a specific crate
cargo test -p barbacane-compiler
# Run tests with output
cargo test --workspace -- --nocapture
# Run a specific test (routing trie lives in the barbacane crate)
cargo test -p barbacane router::trie::tests::static_takes_precedence_over_param
Coverage
Coverage uses cargo-llvm-cov, which instruments the build rather than sampling, so a line is covered only if it actually executed.
cargo install cargo-llvm-cov
rustup component add llvm-tools-preview
# Unit tests only: fast, needs no services
make coverage
# Adds the integration suite, which boots the gateway as a child process
make coverage-integration
# Browsable report at target/llvm-cov/html/index.html
make coverage-html
make coverage-integration measures the gateway process too. cargo llvm-cov show-env puts the instrumented build under CARGO_TARGET_DIR, and the test
harness resolves the gateway binary from there, so the process the tests drive
is the instrumented one. Set BARBACANE_TEST_BINARY to point the harness at a
specific binary instead.
The targets wrap scripts/coverage.sh, which CI calls directly in the
Coverage job, so local and CI runs measure the same thing. The job
publishes the summary to the run page, uploads lcov.info as an artifact and
fails if line coverage drops below the floor, currently 60%. Raise that floor
when the real number rises; do not lower it to turn a red build green.
./scripts/coverage.sh --help
Run
# Dev server (auto-compiles and hot-reloads on save)
cargo run --bin barbacane -- dev --manifest tests/fixtures/barbacane.yaml --spec tests/fixtures/minimal.yaml
# Or the manual workflow:
# Validate a spec
cargo run --bin barbacane -- validate --spec tests/fixtures/minimal.yaml
# Compile a spec
cargo run --bin barbacane -- compile --spec tests/fixtures/minimal.yaml \
--manifest tests/fixtures/barbacane.yaml --output test.bca
# Run the gateway
cargo run --bin barbacane -- serve --artifact test.bca --listen 127.0.0.1:8080 --dev
Project Structure
barbacane/
├── Cargo.toml # Workspace definition
├── Makefile # Development shortcuts
├── Dockerfile # Data plane container image
├── Dockerfile.control # Control plane container image
├── docker-compose.yml # PostgreSQL for local dev
├── docker/ # Docker support files
│ ├── nginx-control.conf # Nginx config for control plane UI
│ └── control-entrypoint.sh
├── LICENSE
├── CONTRIBUTING.md
├── README.md
│
├── crates/
│ ├── barbacane/ # Data-plane binary + CLI (serve/compile/validate/dev)
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── main.rs
│ │ ├── router/ # Prefix-trie routing (trie.rs)
│ │ ├── validator.rs
│ │ └── mcp/ # MCP server
│ │
│ ├── barbacane-control/ # Control plane server (REST API, PostgreSQL)
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── main.rs
│ │ ├── server.rs
│ │ └── db/
│ │
│ ├── barbacane-compiler/ # Spec compilation + parser + .bca format
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── artifact.rs
│ │ └── spec_parser/ # OpenAPI/AsyncAPI parsing
│ │
│ ├── barbacane-wasm/ # WASM plugin runtime (wasmtime), host functions
│ ├── barbacane-telemetry/# OpenTelemetry tracing + Prometheus metrics
│ ├── barbacane-plugin-sdk/ # Plugin SDK (types + log/http/errors/jwt helpers)
│ ├── barbacane-plugin-macros/ # #[barbacane_middleware] / #[barbacane_dispatcher]
│ ├── barbacane-sigv4/ # AWS SigV4 signing (s3 / lambda dispatchers)
│ └── barbacane-test/ # Integration tests (+ adversarial security suite)
│
├── plugins/ # Built-in WASM plugins
│ ├── http-upstream/ # HTTP reverse proxy dispatcher
│ ├── mock/ # Mock response dispatcher
│ ├── lambda/ # AWS Lambda dispatcher
│ ├── kafka/ # Kafka dispatcher (AsyncAPI)
│ ├── nats/ # NATS dispatcher (AsyncAPI)
│ ├── rate-limit/ # Rate limiting middleware
│ ├── cors/ # CORS middleware
│ ├── cache/ # Caching middleware
│ ├── jwt-auth/ # JWT authentication
│ ├── apikey-auth/ # API key authentication
│ └── oauth2-auth/ # OAuth2 token introspection
│
├── ui/ # React web interface
│ ├── package.json
│ ├── vite.config.ts
│ └── src/
│ ├── pages/ # Page components
│ ├── components/ # UI components
│ ├── hooks/ # Custom React hooks
│ └── lib/ # API client, utilities
│
├── tests/
│ └── fixtures/ # Test spec files
│ ├── minimal.yaml
│ ├── train-travel-3.0.yaml
│ └── ...
│
├── docs/ # Documentation
│ ├── index.md
│ ├── guide/
│ ├── reference/
│ └── contributing/
│
└── adr/ # Architecture Decision Records
├── 0001-*.md
└── ...
Development Workflow
Making Changes
-
Create a branch
git checkout -b feature/my-feature -
Make changes and test
cargo test --workspace -
Format code
cargo fmt --all -
Check lints
cargo clippy --workspace -- -D warnings -
Commit
git commit -m "feat: add my feature"
Code Duplication
CI fails when copy-paste duplication across crates/ and plugins/ exceeds the
threshold in the duplication job of .github/workflows/ci.yml. Reproduce it
locally with the same settings:
cargo install jscpd --locked
jscpd crates plugins \
--format rust \
--ignore "**/target/**,crates/barbacane-test/**" \
--min-lines 10 --min-tokens 70 \
--threshold 3.5
crates/barbacane-test is excluded because repeated setup across integration
tests says nothing about the shipped code. Unit tests in #[cfg(test)] modules
are counted, as they cannot be separated by path.
The threshold guards against growth rather than demanding a cleanup. Lower it when a change reduces duplication, so the reduction is kept. Raising it needs a reason in the pull request.
Commit Messages
Follow Conventional Commits:
feat:- New featurefix:- Bug fixdocs:- Documentationrefactor:- Code refactoringtest:- Adding testschore:- Maintenance
Examples:
feat: add cache middleware
fix: handle empty path in router
docs: add middleware configuration guide
refactor: extract trie traversal logic
test: add integration tests for 405 responses
Adding a New Crate
-
Create the crate directory:
mkdir -p crates/barbacane-mycrate/src -
Create
Cargo.toml:[package] name = "barbacane-mycrate" description = "Description here" version.workspace = true edition.workspace = true license.workspace = true [dependencies] # Use workspace dependencies serde = { workspace = true } -
Add to workspace in root
Cargo.toml:[workspace] members = [ # ...existing crates... "crates/barbacane-mycrate", ] [workspace.dependencies] barbacane-mycrate = { path = "crates/barbacane-mycrate" } -
Create
src/lib.rs://! Brief description of the crate. //! //! More detailed explanation.
Testing
Unit Tests
Place unit tests in the same file:
// src/parser.rs
pub fn parse(input: &str) -> Result<Spec, Error> {
// implementation
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_minimal() {
let result = parse("openapi: 3.1.0...");
assert!(result.is_ok());
}
}
Integration Tests
Use barbacane-test crate for full-stack tests:
use barbacane_test::TestGateway;
#[tokio::test]
async fn test_my_feature() {
let gateway = TestGateway::from_spec("tests/fixtures/my-fixture.yaml")
.await
.expect("failed to start gateway");
let resp = gateway.get("/my-endpoint").await.unwrap();
assert_eq!(resp.status(), 200);
}
Test Fixtures
Add test spec files to tests/fixtures/:
# tests/fixtures/my-feature.yaml
openapi: "3.1.0"
info:
title: Test
version: "1.0.0"
paths:
/test:
get:
x-barbacane-dispatch:
name: mock
config:
status: 200
UI Development
The web interface is a React application in the ui/ directory.
Setup
cd ui
npm install
Development Server
# Using Makefile (from project root)
make ui
# Or manually
cd ui && npm run dev
The UI runs at http://localhost:5173 and proxies API requests to the control plane at http://localhost:9090.
Testing
cd ui
npm run test # Run tests once
npm run test:watch # Watch mode
Key Directories
ui/src/
├── pages/ # Page components (ProjectPluginsPage, etc.)
├── components/ # Reusable UI components
│ └── ui/ # Base components (Button, Card, Badge)
├── hooks/ # Custom hooks (useJsonSchema, usePlugins)
├── lib/
│ ├── api/ # API client (types, requests)
│ └── utils.ts # Utilities (cn, formatters)
└── App.tsx # Main app with routing
Adding a New Page
-
Create page component in
src/pages/:// src/pages/my-feature.tsx export function MyFeaturePage() { return <div>...</div> } -
Add route in
src/App.tsx:<Route path="/my-feature" element={<MyFeaturePage />} />
API Client
Use the typed API client from @/lib/api:
import { useQuery } from '@tanstack/react-query'
import { listPlugins } from '@/lib/api'
function MyComponent() {
const { data: plugins, isLoading } = useQuery({
queryKey: ['plugins'],
queryFn: () => listPlugins(),
})
// ...
}
Debugging
Logging
Use eprintln! for development logging:
if cfg!(debug_assertions) {
eprintln!("debug: processing request to {}", path);
}
Running with Verbose Output
# Gateway with dev mode
cargo run --bin barbacane -- serve --artifact test.bca --dev
# Compile with output
cargo run --bin barbacane -- compile --spec api.yaml --output api.bca
Integration Test Debugging
# Run single test with output
cargo test -p barbacane-test test_gateway_health -- --nocapture
Performance Profiling
Benchmarks
Criterion benchmarks are available for performance-critical components:
# Run all benchmarks
cargo bench --workspace
# Run router + validator benchmarks (both live in the barbacane crate)
cargo bench -p barbacane --bench routing
cargo bench -p barbacane --bench validation
Router benchmarks (crates/barbacane/benches/routing.rs):
router_lookup- Measures lookup performance for static paths, parameterized paths, and not-found casesrouter_insert- Measures route insertion performance at various route counts (10-1000 routes)
Validator benchmarks (crates/barbacane/benches/validation.rs):
validator_creation- Measures schema compilation timepath_param_validation- Validates path parameters against schemasquery_param_validation- Validates query parametersbody_validation- Validates JSON request bodiesfull_request_validation- End-to-end request validation
Benchmark results are saved to target/criterion/ with HTML reports.
Flamegraph
cargo install flamegraph
cargo flamegraph -p barbacane -- --artifact test.bca
Documentation
Doc Comments
All public APIs should have doc comments:
/// Parse an OpenAPI specification from a string.
///
/// # Arguments
///
/// * `input` - YAML or JSON string containing the spec
///
/// # Returns
///
/// Parsed `ApiSpec` or error if parsing fails.
///
/// # Example
///
/// ```
/// let spec = parse_spec("openapi: 3.1.0...")?;
/// println!("Found {} operations", spec.operations.len());
/// ```
pub fn parse_spec(input: &str) -> Result<ApiSpec, ParseError> {
// ...
}
Generate Docs
cargo doc --workspace --open
Release Process
- Update version in workspace
Cargo.toml - Update CHANGELOG.md
- Create git tag:
git tag v0.1.0 - Push:
git push origin main --tags - CI builds and publishes
Getting Help
- Open an issue on GitHub
- Check existing ADRs for design decisions
- Read the architecture docs