Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cairn/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
out/
17 changes: 17 additions & 0 deletions .cairn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Boros — Cairn dossier

This tree is maintained under the [Cairn](https://github.com/txpipe) discipline and its layout is framework-defined: derived files are regenerated, never hand-edited; intent lives in the colocated manifests (`WORKSPACE.md`, `src/*/MODULE.md`); decisions live in [`decisions/`](decisions/). `out/` is machine exhaust and never committed.

Boros ("tx omnivore") is a Cardano transaction submission service: it accepts raw transactions over gRPC, queues them with priority and chaining semantics in SQLite, fans them out to Ouroboros tx-submission peers, and tracks their fate on-chain (confirmation, retry, rollback) via UtxoRPC chainsync.

## Reading order

1. [life-map.md](life-map.md) — where the action is: churn × complexity partition, chartering nominations. **Repo dormant since 2025-05, judged "will resume" (human, 2026-08-23).**
2. [topology.md](topology.md) — module graph, instability table, the one root cycle, confirmed layer labels. Rendered crate graph: [crate-graph.svg](crate-graph.svg).
3. The four flows — the tx lifecycle (`Pending → InFlight → Confirmed | Failed`, defined in `src/storage/mod.rs`) is the spine; each flow drives one leg:
- [flows/submit.md](flows/submit.md) — gRPC → validated → queued (`Pending`).
- [flows/fanout.md](flows/fanout.md) — priority drain → sign → broadcast (`InFlight`/`Failed`).
- [flows/confirm.md](flows/confirm.md) — chainsync → `Confirmed`/retry/rollback + cursor. **Flow-contract nominee.**
- [flows/peer-discovery.md](flows/peer-discovery.md) — relay/peer-sharing pool top-up (relay side mocked — [decision 0003](decisions/0003-real-relay-source-for-peer-discovery.md)).

Reference material, consulted rather than read in order: [shape.md](shape.md) (census: size, tests, unsafe, dependency weight), [api/](api/) (internal public-item surface — bin-only crate, so this is the operational-comprehension snapshot, not a semver surface), [context-map.md](context-map.md) (generated from manifest relationships), [decisions/](decisions/) (ADRs 0001–0004: gasket fork, dependency pinning, relay mock, `Validated` status).
171 changes: 171 additions & 0 deletions .cairn/api/boros-internal-surface.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
GENERATED 2026-08-23 by cairn-survey @ fdec4da via `cargo modules structure --bin boros`.
NOTE: internal public-item surface of a bin-only crate — NOT a semver API surface (cargo public-api inapplicable; see shape.md deviation notes).


crate boros
├── struct Config: pub(crate)
│ └── fn new: pub
├── mod ledger: pub(crate)
│ ├── mod relay: pub
│ │ ├── struct MockRelayDataAdapter: pub
│ │ │ ├── async fn get_relays: pub(self)
│ │ │ └── fn new: pub
│ │ └── trait RelayDataAdapter: pub
│ └── mod u5c: pub
│ ├── type ChainSyncStream: pub
│ ├── struct Config: pub
│ ├── enum Event: pub
│ ├── type Point: pub
│ ├── trait U5cDataAdapter: pub
│ ├── struct U5cDataAdapterImpl: pub
│ │ ├── async fn fetch_pparams: pub(self)
│ │ ├── async fn fetch_tip: pub(self)
│ │ ├── async fn fetch_utxos: pub(self)
│ │ ├── fn interceptor: pub(self)
│ │ ├── async fn stream: pub(self)
│ │ └── async fn try_new: pub
│ ├── fn map_conway_pparams: pub(self)
│ └── fn map_multi_era_pparams: pub(self)
├── async fn main: pub(crate)
├── mod network: pub(crate)
│ ├── mod mempool: pub
│ │ ├── struct Event: pub
│ │ ├── struct Mempool: pub
│ │ │ ├── fn acknowledge: pub
│ │ │ ├── fn find_inflight: pub
│ │ │ ├── fn new: pub
│ │ │ ├── fn notify: pub
│ │ │ ├── fn receive: pub(self)
│ │ │ └── fn receive_raw: pub
│ │ ├── enum MempoolError: pub
│ │ ├── struct MempoolState: pub(self)
│ │ ├── struct Tx: pub
│ │ ├── type TxHash: pub(self)
│ │ └── enum TxStage: pub
│ ├── mod mock_ouroboros_tx_submit_server: pub
│ │ └── struct MockOuroborosTxSubmitPeerServer: pub
│ │ ├── async fn init: pub
│ │ ├── fn new: pub
│ │ └── async fn start_background_task: pub(self)
│ ├── mod peer: pub
│ │ ├── struct Peer: pub
│ │ │ ├── async fn collect_transactions: pub(self)
│ │ │ ├── async fn discover_peers: pub
│ │ │ ├── async fn init: pub
│ │ │ ├── fn new: pub
│ │ │ ├── async fn propagate_txs: pub(self)
│ │ │ ├── async fn query_peer_sharing_mode: pub
│ │ │ └── fn start_background_task: pub(self)
│ │ └── enum PeerError: pub
│ └── mod peer_manager: pub
│ ├── struct PeerManager: pub
│ │ ├── async fn add_peer: pub
│ │ ├── async fn connected_peers_count: pub
│ │ ├── async fn init: pub
│ │ ├── async fn is_peer_exist: pub
│ │ ├── fn new: pub
│ │ └── async fn pick_peer_rand: pub
│ ├── struct PeerManagerConfig: pub
│ └── enum PeerManagerError: pub
├── mod pipeline: pub(crate)
│ ├── mod ingest: pub
│ │ ├── struct Stage: pub
│ │ │ └── fn new: pub
│ │ └── struct Worker: pub
│ │ ├── async fn bootstrap: pub(self)
│ │ ├── async fn execute: pub(self)
│ │ └── async fn schedule: pub(self)
│ ├── mod monitor: pub
│ │ ├── struct Config: pub
│ │ ├── struct Stage: pub
│ │ │ └── fn new: pub
│ │ └── struct Worker: pub
│ │ ├── async fn bootstrap: pub(self)
│ │ ├── async fn execute: pub(self)
│ │ └── async fn schedule: pub(self)
│ ├── mod peer_discovery: pub
│ │ ├── enum FanoutError: pub
│ │ ├── struct Stage: pub
│ │ │ └── fn new: pub
│ │ └── struct Worker: pub
│ │ ├── async fn bootstrap: pub(self)
│ │ ├── async fn execute: pub(self)
│ │ └── async fn schedule: pub(self)
│ └── async fn run: pub
├── mod queue: pub(crate)
│ ├── struct Config: pub
│ ├── mod chaining: pub
│ │ ├── type LockEvent: pub(self)
│ │ └── struct TxChaining: pub
│ │ ├── fn is_chained_queue: pub
│ │ ├── async fn is_valid_token: pub
│ │ ├── async fn lock: pub
│ │ ├── fn new: pub
│ │ └── async fn unlock: pub
│ └── mod priority: pub
│ └── struct Priority: pub
│ ├── fn new: pub
│ ├── async fn next: pub
│ └── fn quota: pub(self)
├── mod server: pub(crate)
│ ├── struct Config: pub
│ ├── async fn run: pub
│ └── mod submit: pub(self)
│ └── struct SubmitServiceImpl: pub
│ └── fn new: pub
├── mod signing: pub(crate)
│ ├── struct Config: pub
│ ├── struct Secret: pub(self)
│ ├── trait SigningAdapter: pub
│ ├── enum SigningError: pub
│ ├── mod hashicorp: pub
│ │ └── struct HashicorpVaultClient: pub
│ │ ├── async fn get_mnemonic: pub(self)
│ │ ├── fn new: pub
│ │ ├── async fn sign: pub(self)
│ │ └── async fn verify: pub(self)
│ └── mod key: pub
│ ├── mod derive: pub
│ │ ├── fn from_bip39_mnenomic: pub(self)
│ │ ├── fn generate_account_key: pub
│ │ ├── fn generate_address: pub
│ │ ├── fn generate_address_with_delegation: pub
│ │ ├── fn generate_delegation_keypair: pub
│ │ ├── fn generate_payment_keypair: pub
│ │ ├── fn get_ed25519_keypair: pub
│ │ └── fn to_ed25519_keypair: pub
│ └── mod sign: pub
│ ├── fn sign_transaction: pub
│ └── fn to_built_transaction: pub
├── mod storage: pub(crate)
│ ├── struct Config: pub
│ ├── struct Cursor: pub
│ │ ├── fn from_row: pub(self)
│ │ └── fn new: pub
│ ├── struct Transaction: pub
│ │ ├── fn from_row: pub(self)
│ │ └── fn new: pub
│ ├── struct TransactionState: pub
│ │ └── fn from_row: pub(self)
│ ├── enum TransactionStatus: pub
│ └── mod sqlite: pub
│ ├── struct SqliteCursor: pub
│ │ ├── async fn current: pub
│ │ ├── fn new: pub
│ │ └── async fn set: pub
│ ├── struct SqliteStorage: pub
│ │ ├── async fn migrate: pub
│ │ └── async fn new: pub
│ └── struct SqliteTransaction: pub
│ ├── async fn create: pub
│ ├── async fn find: pub
│ ├── async fn find_to_rollback: pub
│ ├── async fn latest: pub
│ ├── fn new: pub
│ ├── async fn next: pub
│ ├── async fn state: pub
│ ├── async fn update: pub
│ └── async fn update_batch: pub
└── mod validation: pub(crate)
├── async fn evaluate_tx: pub
└── async fn validate_tx: pub
31 changes: 31 additions & 0 deletions .cairn/context-map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Context map

<!-- GENERATED from manifest Relationships and Expectations sections (manual derivation by cairn-charter 2026-10-06; generator tooling pending — regenerate whenever a manifest's Relationships change). DO NOT EDIT BY HAND. -->

```mermaid
flowchart TD
pipeline -->|customer-supplier| storage
pipeline -->|customer-supplier| queue
pipeline -->|customer-supplier| signing
pipeline -->|customer-supplier| network
pipeline -->|conformist| ledger
queue -->|customer-supplier| storage
storage:::openhost
classDef openhost stroke-dasharray: 5 5
```

| Consumer | Relationship | Supplier | Declared in |
|---|---|---|---|
| `queue` | `customer-supplier` | `storage` | `src/queue/MODULE.md` |
| `pipeline` | `customer-supplier` | `storage` | `src/pipeline/MODULE.md` |
| `pipeline` | `customer-supplier` | `queue` | `src/pipeline/MODULE.md` |
| `pipeline` | `customer-supplier` | `signing` | `src/pipeline/MODULE.md` |
| `pipeline` | `customer-supplier` | `network` | `src/pipeline/MODULE.md` |
| `pipeline` | `conformist` | `ledger` (u5c vocabulary) | `src/pipeline/MODULE.md` |
| `storage` | `open-host` (supplier-side declaration) | — | `src/storage/MODULE.md` |

Modules without manifests yet (`ledger`, `network`, `signing`, `server`, `validation`) appear only as referenced suppliers; their consumer-side declarations are pending their own charter.

## Expectations

No manifest declares `Expectations` yet, so there are no consumer → provider links and the reverse index by provider is empty. Collaborator obligations are recorded only as gaps in each manifest's `Boundary allocation`.
18 changes: 18 additions & 0 deletions .cairn/crate-graph.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions .cairn/decisions/0001-return-gasket-to-published-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 0001 — Return gasket to a published release

**Status:** accepted (human decision, 2026-08-23)

## Context

`Cargo.toml` pins `gasket` to a personal git fork (`construkts/gasket-rs`), a supply-chain and bus-factor liability surfaced by the census (`.cairn/shape.md`).

## Decision

The fork is a stopgap. Boros returns to a published upstream/crates.io `gasket` release as soon as one carries what the fork provides.

## Consequences

Until executed, the fork pin is visible debt: `external-policy = "review-new"` plus a future `cargo deny` sources check (SPEC §7.1) will keep flagging it. Executing this decision closes that finding.
15 changes: 15 additions & 0 deletions .cairn/decisions/0002-pin-toolchain-and-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 0002 — Pin toolchain and dependencies

**Status:** proposed (finding from the first cairn survey, 2026-08-23; awaiting execution sign-off)

## Context

A clean checkout of `main` does not compile: `pallas = "1.0.0-alpha.2"` is a floating pre-release requirement that now resolves to `pallas 1.1.1`, whose u5c/pparams API differs from what the code was written against — and `Cargo.lock` is gitignored (the `.gitignore` template's library advice, applied to a binary), so nothing pins the resolution. The first gate run only succeeded after a local lockfile downgrade (`cargo update -p pallas --precise 1.0.0-alpha.2`). `rust-version` (MSRV) is also unpinned.

## Decision (proposed)

Commit `Cargo.lock`; pin `pallas = "=1.0.0-alpha.2"` exactly until upgraded deliberately; declare `rust-version` in `Cargo.toml`.

## Consequences

Builds become reproducible from a clean checkout; dependency upgrades become explicit diffs routed through the gate instead of silent re-resolution during dormancy.
15 changes: 15 additions & 0 deletions .cairn/decisions/0003-real-relay-source-for-peer-discovery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 0003 — Peer discovery requires a real relay source

**Status:** accepted (human decision, 2026-08-23)

## Context

Production wiring injects `MockRelayDataAdapter` into the peer-discovery stage (`src/pipeline/mod.rs:33`); the on-chain half of peer discovery is fake, leaving only config-listed peers and peer-sharing.

## Decision

This is a known gap, not intended design: a real on-chain relay data source must replace the mock before Boros is production-ready.

## Consequences

The `peer-discovery` flow doc notes the gap; the `ledger/relay` adapter trait is the implementation seam. Closing this decision removes the mock from `pipeline::run()`.
15 changes: 15 additions & 0 deletions .cairn/decisions/0004-fate-of-the-validated-status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 0004 — Fate of the `Validated` transaction status

**Status:** open (deliberately undecided, 2026-08-23)

## Context

`TransactionStatus::Validated` is defined in `src/storage/mod.rs` and exercised only by a storage test; no production path ever sets it. The live lifecycle is `Pending → InFlight → {Confirmed, Failed}` (plus retry and rollback re-entries). A dead state in the lifecycle enum invites drift.

## Decision

None yet. Options: retire the variant, or wire it as a real stage (decoupling validation from broadcast in the ingest flow).

## Consequences

Until decided, INV-WS-001 documents the lifecycle without `Validated`, and this ADR is the pointer explaining the extra variant.
38 changes: 38 additions & 0 deletions .cairn/flows/confirm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
+++
schema = "cairn/v0"
flow = "confirm"
short = "CONFIRM"
participants = ["pipeline", "ledger/u5c", "storage"]
+++

# Flow — confirm/rollback (monitor)

<!-- Curated flow doc, drafted 2026-08-23 by cairn-survey @ fdec4da; diagram traced from src/pipeline/monitor.rs. -->

The `monitor` gasket stage follows the chain through the u5c `ChainSyncStream` and drives the tail of the tx lifecycle: `InFlight → Confirmed` on inclusion, `InFlight → Pending` on retry timeout, and un-confirmation on rollback. The chain cursor is persisted after each event for crash recovery.

**⚑ Flow-contract nominee (SPEC §12):** this flow's defining invariants are temporal and multi-module (crash recovery, rollback correctness, retry/confirm races). It is the survey's nomination for the first Quint model, if/when one is written.

```mermaid
sequenceDiagram
participant L as ledger::u5c<br/>ChainSyncStream
participant M as pipeline::monitor<br/>Stage/Worker
participant DB as storage::sqlite<br/>SqliteTransaction
participant CU as storage::sqlite<br/>SqliteCursor

L-->>M: Event::RollForward(Point, Vec<Tx>)
M->>DB: find(InFlight) → Vec<Transaction>
M->>DB: update_batch(included ⇒ Confirmed, slot = block slot)
M->>DB: update_batch(stale beyond retry_slot_diff ⇒ Pending, slot = None)
L-->>M: Event::Rollback(Point)
M->>DB: find_to_rollback(slot)
M->>DB: update_batch(confirmed after slot ⇒ InFlight, slot = None)
M->>CU: set(Cursor { slot, hash }) — after status updates
```

## Invariants

- **INV-CONFIRM-001** `[unverified]` — A transaction is `Confirmed` only when observed in a rolled-forward block; its `slot` then records the confirmation slot.
- **INV-CONFIRM-002** `[unverified]` — An `InFlight` transaction not confirmed within `retry_slot_diff` slots returns to `Pending` (and will be re-broadcast by fanout), so no transaction is stranded `InFlight` forever while the chain advances.
- **INV-CONFIRM-003** `[unverified]` — On rollback to slot S, transactions confirmed after S revert to `InFlight` and are eligible for re-confirmation; no transaction remains `Confirmed` at a slot beyond the new tip.
- **INV-CONFIRM-004** `[unverified]` — The cursor is persisted only after the corresponding status updates, so a crash between the two replays the event (at-least-once) rather than skipping it.
Loading
Loading