crates/dwarves-embed/proto/dwarf.proto
// dwarf.proto — a TRIMMED copy of dwarves' `Dwarf` per-run-report read surface.
//
// This is the thin backend-(2) client's ONLY proto: the read RPCs an embed pane
// needs to render a LIVE forge run — `History` (pick the newest run when no id is
// pinned) and `GetRunReport` (the parent RunRow + a BuildRow per step, with the
// step's `needs` DAG edges). Field numbers, message names, and the `dwarf.v1`
// package MUST match `crates/dwarves/proto/dwarf.proto` exactly (the gRPC method
// paths are `/dwarf.v1.Dwarf/History` and `/dwarf.v1.Dwarf/GetRunReport`), so this
// client talks to a live `dwarves-server` WITHOUT linking the `dwarves` server
// feature (which would build the whole tonic router + skidbladnir lifecycle) — and
// with NO `nornir` dep (that would be a cycle: nornir → dwarves already).
//
// Auth: both RPCs carry `authorization: Bearer <DWARVES_SERVER_TOKEN>` in their grpc
// metadata (enforced by the server's shared nornir-service interceptor).
syntax = "proto3";
package dwarf.v1;
// A historized pipeline run summary (the wire twin of PipelineRunRow). Field
// numbers mirror the server's `RunRow` exactly.
message RunRow {
string run_id = 1;
string pipeline = 2;
string project = 3;
string trigger = 4;
string status = 5;
uint32 steps = 6;
uint32 passed = 7;
uint64 duration_ms = 8;
string finished_at = 9;
}
// `pipeline` (when set) narrows the runs to that pipeline's.
message HistoryRequest { uint32 limit = 1; string pipeline = 2; }
message HistoryReply { repeated RunRow runs = 1; }
// One step's log, by its run and its row's index in the run (`seq`). `tail` = the last
// N lines (0 = the whole log, its end, at most 20000 lines).
message StepLogRequest { string run_id = 1; uint32 seq = 2; uint32 tail = 3; }
message StepLogReply {
repeated string lines = 1;
uint64 total = 2; // lines the log holds
bool found = 3; // false = the step kept no log
}
// One REGISTERED pipeline on the forge (the wire twin of the server's `PipelineInfo`).
// Field numbers mirror `crates/dwarves/proto/dwarf.proto` exactly.
message PipelineInfo {
string project = 1;
string name = 2;
uint32 steps = 3;
}
// The forge's render-ready dump: the REGISTERED pipeline roster + the recent runs, in
// ONE round trip. This pair is exactly what a status BOARD needs — a pipeline that has
// never run still belongs on the board (as a grey "unknown" chip), and `History` alone
// can never name one, because a pipeline with no run contributes no row.
message ShowReply {
repeated PipelineInfo pipelines = 1;
repeated RunRow recent_runs = 2;
}
// Ask for one run's full report by id (the server reuses `RunStatusRequest`).
message RunStatusRequest { string run_id = 1; }
// One executed step's wire record + its DAG `needs` edges (mirrors the server's
// `BuildRow`). Enough to reconstruct BOTH the RunReport rows and the build DAG.
message BuildRow {
string run_id = 1;
string step = 2;
string stage = 3;
string backend = 4;
string matrix = 5;
string status = 6;
string summary = 7;
string artifact_id = 8;
string job_ref = 9;
uint64 duration_ms = 10;
string finished_at = 11;
repeated string needs = 12;
// A Runa `BuildWorkspace` step's units (dwarves `run_units::StepUnits` as JSON:
// counts, per repo, failed units + their errors); empty for every other step.
string units_json = 13;
}
// The full per-run report: parent RunRow + a BuildRow per step (mirrors the
// server's `RunReport`).
message RunReport {
RunRow run = 1;
repeated BuildRow steps = 2;
// a release run's state, statement, artifacts and signing command (JSON)
string release_json = 3;
}
// An empty request (the wire twin of the server's `Empty`) — AgentPool takes no args.
message Empty {}
// Queue a run (`release`/`release-build` with `request`: a release request, TOML).
message TriggerRequest {
string project = 1;
string pipeline = 2;
string trigger = 3;
string request = 4;
}
message TriggerReply {
string run_id = 1;
string status = 2;
}
// The owner's signed bundle for a release run.
message ReleaseBundleRequest {
string run_id = 1;
bytes bundle = 2;
}
// One registered worker agent (the wire twin of the server's `AgentRow` = a jera
// `AgentSpec` + liveness). Field numbers mirror `crates/dwarves/proto/dwarf.proto`
// exactly so this thin client reads the live `dwarves-server`'s `AgentPool` reply.
message AgentRow {
string id = 1;
string group = 2;
repeated string caps = 3; // required-capability labels the agent advertises
uint32 cores = 4;
uint64 mem_mb = 5;
string state = 6; // idle|busy|... (jera AgentState) — liveness/online
}
// The forge's worker fleet as data — every agent the scheduler dispatches to.
message AgentPoolReply {
repeated AgentRow agents = 1;
}
// Where the server finds holger's checkout for the holger build target: a LOCAL repo
// root the server already has on disk (e.g. the side-by-side `../holger`). Empty ⇒ the
// server's default (`$DWARVES_HOLGER_REPO`, else `../holger`). Field number mirrors the
// server's `HolgerBuildRequest` exactly.
message HolgerBuildRequest {
string holger_root = 1;
}
// One resolved holger deployment tier (the wire twin of the server's `HolgerTier` =
// brokkr::resolve::ResolvedTarget). When `resolved` is false the tier names a build
// target holger's build-space does not declare (a dropped/renamed executable) and the
// bin/package/command_line are empty. Field numbers mirror the server's exactly.
message HolgerTier {
string tier = 1; // the tier/variant name (embedded|mini|full)
string step = 2; // the pipeline step id (build-<tier>)
bool resolved = 3; // true iff the tier names a declared build target
string bin = 4; // the tier's executable name (when resolved)
string package = 5; // the tier's cargo package (when resolved)
string command_line = 6; // the materialized `cargo build …` argv (when resolved)
}
// The 3-tier resolution: a per-tier row plus the completeness verdict. `complete` is
// false when any tier is unwired; `found` is false when holger declares no build-space
// at `holger_root` (the plan is empty, NOT an error). Field numbers mirror the server's.
message HolgerBuildReply {
repeated HolgerTier tiers = 1;
uint32 resolved = 2;
uint32 missing = 3;
bool complete = 4;
bool found = 5;
}
// The read RPCs this thin pane consumes. The full dwarves `Dwarf` service has more
// (Health/Show/Register/Trigger/Webhook/RunStatus/Badge); this client needs only these —
// list runs to pick the newest, fetch its per-step report, read the worker fleet for the
// live Jobs + Agent-fleet cockpit pane, and resolve holger's 3-tier build matrix.
service Dwarf {
rpc History (HistoryRequest) returns (HistoryReply);
rpc GetRunReport (RunStatusRequest) returns (RunReport);
// One Runa step's log (everything the step printed), filed on its run.
rpc StepLog (StepLogRequest) returns (StepLogReply);
// Queue a run; a release request rides `TriggerRequest.request`.
rpc Trigger (TriggerRequest) returns (TriggerReply);
// The owner's signed bundle for a release run, verified before it is stored.
rpc ReleaseBundle (ReleaseBundleRequest) returns (TriggerReply);
// The forge's render-ready dump — registered pipelines + recent runs in ONE round
// trip. The Status BOARD's feed: `History` alone cannot name a registered pipeline
// that has never run, and a board that silently drops those is a board that reports
// only on what already worked. Added 2026-09-10 when the board's fixture was deleted.
rpc Show (Empty) returns (ShowReply);
// The worker/agent roster + heartbeat/online state (the multi-machine fleet).
rpc AgentPool (Empty) returns (AgentPoolReply);
// holger's 3-tier (embedded·mini·full) build MATRIX: each tier mapped onto its build
// target (bin · package · materialized `cargo` argv) + which resolve + completeness.
// COMPILE-FREE (the server resolves the plan; it never builds holger in the RPC).
rpc ResolveHolgerBuild (HolgerBuildRequest) returns (HolgerBuildReply);
}