Add structs/logging docs, wiki/07-notes with Spew architecture, fix Home index

- src/config/structs.rs: full file header + rustdoc on every struct and field
- src/logging.rs: full file header + rustdoc on init_from_config(), inline comments
- wiki/07-notes.md: design notes page — Spew/no-nazi-twitter concept documented
  (Justin Bieber fan-out problem, KWIC moderation gate, IPFS media storage,
   timeline assembly tradeoffs, open questions, architecture diagram)
- wiki/Home.md: corrected broken 07-broker-calls.md link; added notes section
- README.md: added wiki/07-notes.md to wiki index

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 20:48:21 -07:00
parent e8fdb39ea2
commit f50396b390
5 changed files with 352 additions and 6 deletions

View File

@@ -1,78 +1,189 @@
//! # config/structs.rs — BEDS Configuration Structs
//!
//! Typed configuration structs for all sections of `beds.toml`. Every struct
//! derives `serde::Deserialize` so the `config` crate can deserialise the
//! merged TOML directly into strongly-typed values. No runtime string lookups.
//!
//! ## Calling Agents
//! - `config/mod.rs` — deserialises into `BedsConfig` via `try_deserialize()`
//! - All service modules — receive sub-structs as `&cfg.broker_services`, etc.
//!
//! **Author:** mks
//! **Version:** 1.0
//!
//! ## History
//! * `2026-04-02` - mks - original coding
//! * `2026-04-04` - mks - added RecNodeConfig, RelNodeConfig, RelInstanceConfig
use serde::Deserialize;
use std::collections::HashMap;
/// Top-level BEDS configuration. Deserialised from the merged
/// `beds.toml` + `env_{name}.toml` at IPL step 1.
#[derive(Debug, Deserialize)]
pub struct BedsConfig {
/// Node identity — env name, version string, wbid
pub id: IdConfig,
/// Enables verbose debug output when true
pub debug: bool,
/// Enables journald syslog output when true
pub syslog: bool,
/// When true, log events also echo to stdout even when syslog is active
pub syslog_mirror_console: bool,
/// Enables audit trail logging for all data mutations
pub audit_on: bool,
/// Enables journal event recording
pub journal_on: bool,
/// RabbitMQ broker connection settings
pub broker_services: BrokerServicesConfig,
/// MongoDB (REC) node configs keyed by service name (e.g. "app_server")
pub rec_services: HashMap<String, RecNodeConfig>,
/// MariaDB (REL) node configs keyed by service name (e.g. "app_server")
pub rel_services: HashMap<String, RelNodeConfig>,
}
/// Node identity block. Identifies this node's role and environment.
#[derive(Debug, Deserialize)]
pub struct IdConfig {
/// Environment name — matches the env override file suffix (dev, qa, production)
pub env_name: String,
/// Application version string
pub version: String,
/// BEDS node identifier — unique name for this node in the cluster
pub wbid: String,
}
/// RabbitMQ broker connection settings and broker pool sizing.
#[derive(Debug, Deserialize)]
pub struct BrokerServicesConfig {
pub queue_tag : String,
/// Queue name prefix — prepended to all queue names to isolate environments.
/// Example: "prod_" produces queues like "prod_rec.read", "prod_log"
pub queue_tag: String,
/// RabbitMQ virtual host — isolates traffic between environments
pub vhost: String,
/// Timer violation threshold in seconds — broker tasks exceeding this are logged
pub timer_violation: u32,
/// Maximum records transferred per AMQP message
pub records_per_xfer: u32,
/// Enables TCP keepalive on the broker connection
pub keepalive: bool,
/// AMQP heartbeat interval in seconds
pub heartbeat: u32,
/// Enables TLS on the broker connection
pub use_ssl: bool,
/// Path to TLS certificate file (only used when use_ssl is true)
pub cert_path: String,
/// Connection config for the primary broker node
pub app_server: BrokerNodeConfig,
}
/// Connection and pool sizing config for a single RabbitMQ node.
#[derive(Debug, Deserialize)]
pub struct BrokerNodeConfig {
/// Broker hostname or IP address
pub host: String,
/// AMQP port (default: 5672)
pub port: u16,
/// RabbitMQ management API port (optional — used for health checks and admin)
pub api_port: Option<u16>,
/// AMQP authentication username
pub user: String,
/// AMQP authentication password
pub pass: String,
/// Records per instance — used for per-broker throughput calculations
pub rpi: u32,
/// Broker instance counts for this node
pub instances: BrokerInstancesConfig,
}
/// Number of concurrent broker task instances per type on a single node.
///
/// Each instance is a separate Tokio task. Increase these values when a node
/// has spare CPU/memory and a queue is saturating.
#[derive(Debug, Deserialize)]
pub struct BrokerInstancesConfig {
/// Number of read broker (rBroker) instances
pub r_broker: u32,
/// Number of write broker (wBroker) instances
pub w_broker: u32,
/// Number of migration/object broker (mBroker) instances
pub m_broker: u32,
}
/// Connection config for a single MongoDB (REC) node.
#[derive(Debug, Deserialize)]
pub struct RecNodeConfig {
/// MongoDB hostname or IP address
pub host: String,
/// MongoDB port (default: 27017)
pub port: u16,
/// Authentication username
pub user: String,
/// Authentication password
pub pass: String,
/// Default database name for this node
pub database: String,
/// Enables TLS on the MongoDB connection
pub use_ssl: bool,
}
/// Config for a MariaDB (REL) node. A node has a required master instance
/// and an optional secondary (read replica).
#[derive(Debug, Deserialize)]
pub struct RelNodeConfig {
/// Primary master instance — all writes go here
pub master: RelInstanceConfig,
/// Optional read replica — absence or unreachability is non-fatal
pub secondary: Option<RelInstanceConfig>,
}
/// Connection config for a single MariaDB instance (master or secondary).
#[derive(Debug, Deserialize, Clone)]
pub struct RelInstanceConfig {
/// MariaDB hostname or IP address
pub host: String,
/// MariaDB port (default: 3306)
pub port: u16,
/// Authentication username
pub user: String,
/// Authentication password
pub pass: String,
/// Default database name
pub database: String,
}

View File

@@ -1,26 +1,78 @@
//! # logging.rs — BEDS Structured Logging Initialiser
//!
//! Initialises the `tracing` subscriber at IPL step 2. Supports two output
//! targets: journald (syslog) and console (stdout). Either or both may be
//! active depending on the config flags passed in.
//!
//! The subscriber must be initialised before any `tracing::info!` /
//! `tracing::warn!` / `tracing::error!` calls — the macros are no-ops until
//! a subscriber is registered.
//!
//! ## Calling Agents
//! - `ipl()` in main.rs — calls `init_from_config()` as the second IPL step
//!
//! ## Inputs
//! - `syslog: bool` — from `BedsConfig.syslog`
//! - `mirror_console: bool` — from `BedsConfig.syslog_mirror_console`
//!
//! ## Output Behaviour
//! | syslog | mirror_console | journald | console |
//! |--------|----------------|----------|---------|
//! | false | false | — | yes |
//! | false | true | — | yes |
//! | true | false | yes | — |
//! | true | true | yes | yes |
//!
//! **Author:** mks
//! **Version:** 1.0
//!
//! ## History
//! * `2026-04-02` - mks - original coding
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
/// Initialises the `tracing` subscriber from BEDS config flags.
///
/// Registers a global subscriber with up to two output layers — journald
/// and/or console — based on the `syslog` and `mirror_console` flags. The
/// log level is read from the `RUST_LOG` environment variable; defaults to
/// `info` if not set.
///
/// Must be called exactly once, before any tracing macros are used.
/// Calling it a second time will panic (tracing enforces a single global
/// subscriber).
///
/// # Arguments
///
/// * `syslog` — enable journald output
/// * `mirror_console` — also write to console when journald is active
///
/// # History
///
/// * `2026-04-02` - mks - original coding
pub fn init_from_config(syslog: bool, mirror_console: bool) {
let registry = tracing_subscriber::registry()
.with(EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info")));
// journald layer — active when syslog is enabled in config
let journald_layer = if syslog {
tracing_journald::layer().ok()
} else {
None
};
// console layer — active when syslog is off, or when mirroring is on
let console_layer = if !syslog || mirror_console {
Some(tracing_subscriber::fmt::layer()
.with_target(false)
.compact())
}else {
} else {
None
};
registry
.with(journald_layer)
.with(console_layer)
.init();
}
}