Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Using the library

The binary is a thin shell over the library in the same crate. Everything the CLI does is available to a bot, a GitHub App, a dashboard, or your own settlement backend.

Signatures and types live on docs.rs/dedalo, which publishes the reference for each released version. This chapter is the shape of the thing, not the shape of every function.

Depending on it

[dependencies]
dedalo = { version = "0.1", default-features = false }

default-features = false drops the cli feature, and with it clap, tokio, toml_edit, tracing-subscriber and libc. What is left is the pipeline.

FeatureDefaultBrings
clionThe command-line interface and the runtime it needs.
testingoffdedalo::testing, for building throwaway repositories with real merges.

Everything under dedalo::cli is private except Cli, Command and the entry points. Terminal output is not API.

The short path

Engine ties a repository, its config and its ledger together, and is the shortest route through all four stages:

use dedalo::{Engine, money::Amount};

let engine = Engine::discover(".")?;
let merges = engine.scan(None)?;                 // unpaid merges
let attribution = engine.attribute(&merges);     // contribution weights
let plan = engine.plan(&merges, &attribution, Amount::from_base_units(1_000_000))?;

for item in plan.contributors() {
    println!("{:>12} → {}", plan.asset.format_amount(item.amount), item.handle);
}

Engine::discover walks up from a path looking for dedalo.toml, the way git finds .git. Engine::new assembles one from parts, for tests or for an alternative git backend.

The modules

ModuleResponsibility
gitGitBackend trait and CliGit, which drives the git binary.
attributionMerge history → integer contribution weights.
attribution::identityGit emails → payable wallets.
moneyAmount, Asset, and exact splitting.
money::treasuryThe fee schedule and the protocol/treasury/contributor split.
payoutPayoutPlan, its content hash, and its invariants.
chain::walletValidated, checksummed addresses.
chain::merkleThe claim tree a round is deposited against.
chain::vaultThe rules a deployed contract enforces, as pure functions.
chain::settlementThe Settlement trait, and the dry-run and EVM backends.
storage::ledgerThe hash-chained event log and the payout cursor.
storage::objectsThe content-addressed object store.
configdedalo.toml, parsed and validated.
errorError and Result.

Each is usable on its own. money has no idea git exists; attribution has no idea money does.

Substituting a backend

Two traits are meant to be implemented from outside.

GitBackend

Four methods: the repository root, the current branch, resolving a revision, and listing merges matching a query.

use dedalo::git::{GitBackend, HistoryQuery, MergeEvent};

struct MyBackend { /* … */ }

impl GitBackend for MyBackend {
    fn root(&self) -> &std::path::Path { /* … */ }
    fn current_branch(&self) -> dedalo::Result<String> { /* … */ }
    fn resolve(&self, rev: &str) -> dedalo::Result<String> { /* … */ }
    fn merges(&self, query: &HistoryQuery) -> dedalo::Result<Vec<MergeEvent>> { /* … */ }
}

Implement it to read from libgit2, from a forge’s API, or from a version control system that is not git. Everything downstream sees MergeEvent values and never knows the difference — which is the groundwork for running on more than git.

Settlement

Implement it to add a chain, or to route a plan through your own custody process. The contract is narrow on purpose: a settlement re-verifies the plan before acting, and returns a receipt or an error.

Careful — if you implement this, do not return a receipt for something that did not happen. The shipped evm backend returns NotImplemented rather than a plausible-looking success, and that is the standard to hold.

Testing against real repositories

The testing feature builds throwaway repositories with real merge commits, which is why nothing in this project mocks git:

[dev-dependencies]
dedalo = { version = "0.1", features = ["testing"] }
use dedalo::testing::TempRepo;

let repo = TempRepo::new("example");
repo.merge_feature("feature-a", ("Ada", "ada@example.com"), 40);
repo.merge_feature("feature-b", ("Bea", "bea@example.com"), 40);

A mock would only test the mock. git log --merges has enough surface — first parents, trailers, empty merges, octopus merges — that a fake of it tests a version of git nobody runs.

Determinism is your problem too

If you build on the library, the guarantee that makes plans checkable is only as strong as the code around it. Two rules:

  • Do not introduce I/O into stages 1 to 3. A price feed, a contributor list fetched from an API, anything with a clock — each turns a reproducible computation into a snapshot nobody else can reproduce.
  • Do not reformat amounts through floats. Amount is a u128 of base units, and Asset::format_amount is for display. A round trip through f64 loses base units above 2^53.