mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-22 07:48:26 +00:00
Add build version metadata, changelog, and version display
Embed git commit hash, dirty flag, and target triple in all binaries via a zero-dependency build.rs. Wire clap short/long version output so -V shows "0.1.0 (rev abc1234)" and --version adds the target triple. Log version at daemon startup. Add version field to show_status control socket API response. Show the daemon's version in fipstop's tab bar title and Runtime section. Add CHANGELOG.md in Keep a Changelog format with the 0.1.0-alpha release (2026-02-24) and unreleased work since then.
This commit is contained in:
59
CHANGELOG.md
Normal file
59
CHANGELOG.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Ethernet transport with beacon discovery and auto-connect
|
||||||
|
- TCP transport with configurable bind address and static peer support
|
||||||
|
- Docker sidecar deployment for containerized services
|
||||||
|
- Comprehensive node and transport statistics via control socket
|
||||||
|
- fipstop TUI monitoring tool with smoothed metrics and quality indices
|
||||||
|
- ECN congestion signaling and transport congestion detection
|
||||||
|
- Persistent identity with key file management (`fipsctl keygen`)
|
||||||
|
- Periodic Noise rekey with fresh DH for forward secrecy (FMP + FSP)
|
||||||
|
- Systemd service packaging with tarball installer
|
||||||
|
- Build version metadata: git commit hash, dirty flag, and target triple
|
||||||
|
embedded in all binaries via `--version`
|
||||||
|
- CHANGELOG.md following Keep a Changelog format
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Spanning tree coordinate loop: reject parents whose ancestry contains us
|
||||||
|
- PMTUD per-destination path MTU check and ICMPv6 MTU field width
|
||||||
|
- Ethernet AEAD decryption failures caused by minimum-frame padding
|
||||||
|
- Link-dead detection skipping peers that never send data
|
||||||
|
- FMP version check added to TCP stream reader
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- GitHub repository moved from `jmcorgan/fips` to `fips-network/fips`
|
||||||
|
|
||||||
|
## [0.1.0-alpha] - 2026-02-24
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Core mesh routing protocol with greedy coordinate-based forwarding
|
||||||
|
- Noise IK handshake at FMP (link layer) and Noise XK at FSP (session layer)
|
||||||
|
- UDP transport with configurable bind address and static peers
|
||||||
|
- TUN-based virtual network interface (fips0) with ICMPv6 Packet Too Big
|
||||||
|
- DNS resolver for .fips domain names (port 5354)
|
||||||
|
- Spanning tree construction with cost-based parent selection and
|
||||||
|
flap dampening
|
||||||
|
- Bloom filter-based identity discovery protocol with reverse-path routing
|
||||||
|
- MMP (Mesh Management Protocol) for link and session layer measurement
|
||||||
|
- Hybrid coordinate warmup (CoordsWarmup message and proactive fallback)
|
||||||
|
- Handshake retry with exponential backoff (link and session layer)
|
||||||
|
- Link-layer heartbeat and liveness timeout for dead peer detection
|
||||||
|
- Epoch-based peer restart detection
|
||||||
|
- Per-link MTU support and reactive MtuExceeded error signal
|
||||||
|
- Session idle timeout and identity cache expiry
|
||||||
|
- Unix domain control socket for runtime observability (fipsctl)
|
||||||
|
- Docker test harness with static and stochastic topologies
|
||||||
|
- CI with GitHub Actions (x86_64 and aarch64, unit and integration tests)
|
||||||
|
- Design documentation suite covering all protocol layers
|
||||||
39
build.rs
Normal file
39
build.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Git commit hash (short)
|
||||||
|
let git_hash = Command::new("git")
|
||||||
|
.args(["rev-parse", "--short=10", "HEAD"])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.filter(|o| o.status.success())
|
||||||
|
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
||||||
|
.unwrap_or_default();
|
||||||
|
println!("cargo:rustc-env=FIPS_GIT_HASH={git_hash}");
|
||||||
|
|
||||||
|
// Dirty working tree
|
||||||
|
let dirty = Command::new("git")
|
||||||
|
.args(["status", "--porcelain"])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.filter(|o| o.status.success())
|
||||||
|
.map(|o| !o.stdout.is_empty())
|
||||||
|
.unwrap_or(false);
|
||||||
|
if dirty {
|
||||||
|
println!("cargo:rustc-env=FIPS_GIT_DIRTY=-dirty");
|
||||||
|
} else {
|
||||||
|
println!("cargo:rustc-env=FIPS_GIT_DIRTY=");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build target triple
|
||||||
|
if let Ok(target) = std::env::var("TARGET") {
|
||||||
|
println!("cargo:rustc-env=FIPS_TARGET={target}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rebuild when commits change
|
||||||
|
println!("cargo:rerun-if-changed=.git/HEAD");
|
||||||
|
println!("cargo:rerun-if-changed=.git/refs/");
|
||||||
|
|
||||||
|
// Support reproducible builds (Debian packaging)
|
||||||
|
println!("cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH");
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use fips::config::{resolve_identity, IdentitySource};
|
use fips::config::{resolve_identity, IdentitySource};
|
||||||
|
use fips::version;
|
||||||
use fips::{Config, Node};
|
use fips::{Config, Node};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing::{error, info, warn, Level};
|
use tracing::{error, info, warn, Level};
|
||||||
@@ -11,7 +12,12 @@ use tracing_subscriber::{fmt, EnvFilter};
|
|||||||
|
|
||||||
/// FIPS mesh network daemon
|
/// FIPS mesh network daemon
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(name = "fips", version, about)]
|
#[command(
|
||||||
|
name = "fips",
|
||||||
|
version = version::short_version(),
|
||||||
|
long_version = version::long_version(),
|
||||||
|
about
|
||||||
|
)]
|
||||||
struct Args {
|
struct Args {
|
||||||
/// Path to configuration file (overrides default search paths)
|
/// Path to configuration file (overrides default search paths)
|
||||||
#[arg(short, long, value_name = "FILE")]
|
#[arg(short, long, value_name = "FILE")]
|
||||||
@@ -32,7 +38,7 @@ async fn main() {
|
|||||||
|
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
info!("FIPS starting");
|
info!("FIPS {} starting", version::short_version());
|
||||||
|
|
||||||
// Load configuration
|
// Load configuration
|
||||||
info!("Loading configuration");
|
info!("Loading configuration");
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use fips::config::{write_key_file, write_pub_file};
|
use fips::config::{write_key_file, write_pub_file};
|
||||||
|
use fips::version;
|
||||||
use fips::{encode_nsec, Identity};
|
use fips::{encode_nsec, Identity};
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Write};
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
@@ -13,7 +14,12 @@ use std::time::Duration;
|
|||||||
|
|
||||||
/// FIPS control client
|
/// FIPS control client
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(name = "fipsctl", version, about = "Query a running FIPS daemon")]
|
#[command(
|
||||||
|
name = "fipsctl",
|
||||||
|
version = version::short_version(),
|
||||||
|
long_version = version::long_version(),
|
||||||
|
about = "Query a running FIPS daemon"
|
||||||
|
)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
/// Control socket path override
|
/// Control socket path override
|
||||||
#[arg(short = 's', long)]
|
#[arg(short = 's', long)]
|
||||||
|
|||||||
@@ -7,13 +7,19 @@ use app::{App, ConnectionState, SelectedTreeItem, Tab};
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use client::ControlClient;
|
use client::ControlClient;
|
||||||
use event::{Event, EventHandler};
|
use event::{Event, EventHandler};
|
||||||
|
use fips::version;
|
||||||
use ratatui::crossterm::event::{KeyCode, KeyModifiers};
|
use ratatui::crossterm::event::{KeyCode, KeyModifiers};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
/// FIPS mesh monitoring TUI
|
/// FIPS mesh monitoring TUI
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(name = "fipstop", version, about = "Monitor a running FIPS daemon")]
|
#[command(
|
||||||
|
name = "fipstop",
|
||||||
|
version = version::short_version(),
|
||||||
|
long_version = version::long_version(),
|
||||||
|
about = "Monitor a running FIPS daemon"
|
||||||
|
)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
/// Control socket path override
|
/// Control socket path override
|
||||||
#[arg(short = 's', long)]
|
#[arg(short = 's', long)]
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ fn draw_runtime(frame: &mut Frame, data: &serde_json::Value, area: Rect) {
|
|||||||
let inner = block.inner(area);
|
let inner = block.inner(area);
|
||||||
frame.render_widget(block, area);
|
frame.render_widget(block, area);
|
||||||
|
|
||||||
|
let version = helpers::str_field(data, "version");
|
||||||
let pid = helpers::u64_field(data, "pid");
|
let pid = helpers::u64_field(data, "pid");
|
||||||
let exe = helpers::str_field(data, "exe_path");
|
let exe = helpers::str_field(data, "exe_path");
|
||||||
let uptime_secs = data.get("uptime_secs").and_then(|v| v.as_u64()).unwrap_or(0);
|
let uptime_secs = data.get("uptime_secs").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||||
@@ -52,7 +53,9 @@ fn draw_runtime(frame: &mut Frame, data: &serde_json::Value, area: Rect) {
|
|||||||
|
|
||||||
let lines = vec![
|
let lines = vec![
|
||||||
Line::from(vec![
|
Line::from(vec![
|
||||||
Span::styled(" pid: ", label),
|
Span::styled(" ver: ", label),
|
||||||
|
Span::raw(version.to_string()),
|
||||||
|
Span::styled(" pid: ", label),
|
||||||
Span::raw(pid),
|
Span::raw(pid),
|
||||||
Span::styled(" uptime: ", label),
|
Span::styled(" uptime: ", label),
|
||||||
Span::raw(uptime),
|
Span::raw(uptime),
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ pub fn draw(frame: &mut Frame, app: &mut App) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_tab_bar(frame: &mut Frame, app: &App, area: Rect) {
|
fn draw_tab_bar(frame: &mut Frame, app: &App, area: Rect) {
|
||||||
let block = Block::default().borders(Borders::ALL).title(" fipstop ");
|
let title = format!(" fipstop {} ", fips::version::short_version());
|
||||||
|
let block = Block::default().borders(Borders::ALL).title(title);
|
||||||
let inner = block.inner(area);
|
let inner = block.inner(area);
|
||||||
frame.render_widget(block, area);
|
frame.render_widget(block, area);
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ pub fn show_status(node: &Node) -> Value {
|
|||||||
let fwd = node.stats().snapshot().forwarding;
|
let fwd = node.stats().snapshot().forwarding;
|
||||||
|
|
||||||
json!({
|
json!({
|
||||||
|
"version": crate::version::short_version(),
|
||||||
"npub": node.npub(),
|
"npub": node.npub(),
|
||||||
"node_addr": hex::encode(node.node_addr().as_bytes()),
|
"node_addr": hex::encode(node.node_addr().as_bytes()),
|
||||||
"ipv6_addr": format!("{}", node.identity().address()),
|
"ipv6_addr": format!("{}", node.identity().address()),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
//! A distributed, decentralized network routing protocol for mesh nodes
|
//! A distributed, decentralized network routing protocol for mesh nodes
|
||||||
//! connecting over arbitrary transports.
|
//! connecting over arbitrary transports.
|
||||||
|
|
||||||
|
pub mod version;
|
||||||
pub mod bloom;
|
pub mod bloom;
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|||||||
38
src/version.rs
Normal file
38
src/version.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
//! Build version information for FIPS binaries.
|
||||||
|
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
/// Package version from Cargo.toml.
|
||||||
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
/// Short git commit hash (empty if not available).
|
||||||
|
const GIT_HASH: &str = env!("FIPS_GIT_HASH");
|
||||||
|
|
||||||
|
/// Dirty flag ("-dirty" or empty).
|
||||||
|
const GIT_DIRTY: &str = env!("FIPS_GIT_DIRTY");
|
||||||
|
|
||||||
|
/// Build target triple.
|
||||||
|
const TARGET: &str = env!("FIPS_TARGET");
|
||||||
|
|
||||||
|
/// Short version string for `-V`: `0.1.0 (rev abc1234567)`
|
||||||
|
#[allow(clippy::const_is_empty)]
|
||||||
|
static SHORT_VERSION: LazyLock<String> = LazyLock::new(|| {
|
||||||
|
if GIT_HASH.is_empty() {
|
||||||
|
VERSION.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{VERSION} (rev {GIT_HASH}{GIT_DIRTY})")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Long version string for `--version` with build metadata.
|
||||||
|
static LONG_VERSION: LazyLock<String> = LazyLock::new(|| {
|
||||||
|
format!("{}\ntarget: {TARGET}", *SHORT_VERSION)
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn short_version() -> &'static str {
|
||||||
|
&SHORT_VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn long_version() -> &'static str {
|
||||||
|
&LONG_VERSION
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user