Skip to content

Commit 1efc9a9

Browse files
Jonathan D.A. Jewellclaude
andcommitted
feat(config): add configuration file support
- Add wharf-core config module with TOML loading - Define YachtAgentConfig and WharfCliConfig structs - Implement ConfigLoader for hierarchical config discovery (project-local > user > system > defaults) - Integrate config loading into yacht-agent main - CLI args take precedence over config file values - Add example config files in examples/ - Update STATE.scm with session progress Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 1a1100a commit 1efc9a9

8 files changed

Lines changed: 682 additions & 25 deletions

File tree

Cargo.lock

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

STATE.scm

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
((version . "1.0.0")
1010
(schema-version . "1")
1111
(created . "2025-01-10T13:50:29+00:00")
12-
(updated . "2025-01-12T06:00:00+00:00")
12+
(updated . "2025-01-12T07:00:00+00:00")
1313
(project . "project-wharf")
1414
(repo . "https://github.com/hyperpolymath/project-wharf")))
1515

@@ -20,16 +20,16 @@
2020

2121
(current-position
2222
((phase . "Alpha Development")
23-
(overall-completion . 55)
23+
(overall-completion . 60)
2424
(components
2525
((wharf-core
2626
((status . "functional")
27-
(completion . 60)
28-
(notes . "PolicyEngine, IntegrityChecker, SyncManager implemented")))
27+
(completion . 70)
28+
(notes . "PolicyEngine, IntegrityChecker, SyncManager, ConfigLoader implemented")))
2929
(yacht-agent
3030
((status . "in-progress")
31-
(completion . 60)
32-
(notes . "DB proxy, API, eBPF loader, nftables fallback complete")))
31+
(completion . 65)
32+
(notes . "DB proxy, API, eBPF loader, nftables fallback, config loading complete")))
3333
(wharf-cli
3434
((status . "in-progress")
3535
(completion . 35)
@@ -43,15 +43,16 @@
4343
"SQL AST parsing for zone classification"
4444
"BLAKE3 file integrity manifests"
4545
"eBPF XDP firewall loader (userspace)"
46-
"rsync-based file synchronization"))))
46+
"nftables fallback firewall"
47+
"rsync-based file synchronization"
48+
"Configuration file loading (TOML)"))))
4749

4850
(route-to-mvp
4951
((milestones
5052
((core-hardening
5153
((target-completion . 70)
52-
(items . ("Complete nftables fallback"
53-
"Wire integrity ops in CLI"
54-
"Add configuration file support"))
54+
(items . ("Wire integrity ops in CLI"
55+
"Add config loading to wharf-cli"))
5556
(status . "in-progress")))
5657
(nebula-integration
5758
((target-completion . 85)
@@ -69,20 +70,28 @@
6970
(blockers-and-issues
7071
((critical . ())
7172
(high . ())
72-
(medium . ("CLI integrity commands not wired"
73-
"No configuration file support"))
73+
(medium . ("CLI integrity commands not wired"))
7474
(low . ("Some unused function warnings in wharf-cli"))))
7575

7676
(critical-next-actions
7777
((immediate . ("Wire integrity commands in CLI"
78-
"Add configuration file support"))
78+
"Start Nebula/Mooring integration"))
7979
(this-week . ("Test nftables on production system"
8080
"Test eBPF compilation with bpf-linker"))
81-
(this-month . ("Begin Nebula mesh integration"
81+
(this-month . ("Implement Mooring protocol"
8282
"Performance optimization"))))
8383

8484
(session-history
85-
(((timestamp . "2025-01-12T06:00:00Z")
85+
(((timestamp . "2025-01-12T07:00:00Z")
86+
(session-id . "config-support")
87+
(accomplishments
88+
("Created wharf-core config module with TOML support"
89+
"Added ConfigLoader for hierarchical config discovery"
90+
"Defined YachtAgentConfig and WharfCliConfig structs"
91+
"Integrated config loading into yacht-agent main"
92+
"Config merges with CLI args (CLI takes precedence)"
93+
"Created example config files in examples/")))
94+
((timestamp . "2025-01-12T06:00:00Z")
8695
(session-id . "firewall-blockers")
8796
(accomplishments
8897
("Implemented NftablesManager with full runtime API"

bin/yacht-agent/src/main.rs

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use tokio::sync::{Mutex, RwLock};
3131
use tracing::{error, info, warn, Level};
3232
use tracing_subscriber::FmtSubscriber;
3333

34+
use wharf_core::config::{ConfigLoader, YachtAgentConfig};
3435
use wharf_core::db_policy::{DatabasePolicy, PolicyEngine, QueryAction};
3536
use wharf_core::types::HeaderPolicy;
3637

@@ -175,8 +176,48 @@ impl AgentState {
175176
async fn main() -> anyhow::Result<()> {
176177
let args = Args::parse();
177178

178-
// Set up logging based on verbosity
179-
let log_level = match args.verbose {
179+
// Load configuration file (CLI args override config file values)
180+
let config_loader = ConfigLoader::new();
181+
let config = config_loader.load_yacht_agent_config().unwrap_or_else(|e| {
182+
eprintln!("Warning: Failed to load config file: {}. Using defaults.", e);
183+
YachtAgentConfig::default()
184+
});
185+
186+
// Merge config with CLI args (CLI takes precedence)
187+
let protocol = if args.protocol != "mysql" {
188+
args.protocol.clone()
189+
} else {
190+
config.db_proxy.protocol.clone()
191+
};
192+
let listen_port = args.listen_port;
193+
let shadow_host = if args.shadow_host != "127.0.0.1" {
194+
args.shadow_host.clone()
195+
} else {
196+
config.db_proxy.shadow_host.clone()
197+
};
198+
let shadow_port = if args.shadow_port != 33060 {
199+
args.shadow_port
200+
} else {
201+
config.db_proxy.shadow_port
202+
};
203+
let firewall_mode = if args.firewall_mode != "nftables" {
204+
args.firewall_mode.clone()
205+
} else {
206+
config.firewall.mode.clone()
207+
};
208+
let xdp_interface = if args.xdp_interface != "eth0" {
209+
args.xdp_interface.clone()
210+
} else {
211+
config.firewall.xdp_interface.clone()
212+
};
213+
214+
// Set up logging based on verbosity (CLI overrides config)
215+
let verbosity = if args.verbose > 0 {
216+
args.verbose
217+
} else {
218+
config.logging.verbosity
219+
};
220+
let log_level = match verbosity {
180221
0 => Level::INFO,
181222
1 => Level::DEBUG,
182223
_ => Level::TRACE,
@@ -192,15 +233,15 @@ async fn main() -> anyhow::Result<()> {
192233

193234
info!("Yacht Agent starting...");
194235
info!("Version: {}", wharf_core::VERSION);
195-
info!("Protocol: {}", args.protocol);
196-
info!("Masquerade port: {}", args.listen_port);
197-
info!("Shadow DB: {}:{}", args.shadow_host, args.shadow_port);
198-
info!("Firewall mode: {}", args.firewall_mode);
236+
info!("Protocol: {}", protocol);
237+
info!("Masquerade port: {}", listen_port);
238+
info!("Shadow DB: {}:{}", shadow_host, shadow_port);
239+
info!("Firewall mode: {}", firewall_mode);
199240

200241
// Initialize firewall based on mode
201-
let _firewall: Firewall = match args.firewall_mode.as_str() {
242+
let _firewall: Firewall = match firewall_mode.as_str() {
202243
"ebpf" => {
203-
info!("Attempting to load eBPF XDP firewall on {}", args.xdp_interface);
244+
info!("Attempting to load eBPF XDP firewall on {}", xdp_interface);
204245

205246
// Look for the eBPF object file in standard locations
206247
let ebpf_paths = [
@@ -213,9 +254,9 @@ async fn main() -> anyhow::Result<()> {
213254

214255
match ebpf_path {
215256
Some(path) => {
216-
match ebpf::try_load_shield(path, &args.xdp_interface) {
257+
match ebpf::try_load_shield(path, &xdp_interface) {
217258
Some(shield) => {
218-
info!("eBPF XDP firewall loaded successfully on {}", args.xdp_interface);
259+
info!("eBPF XDP firewall loaded successfully on {}", xdp_interface);
219260
Firewall::Ebpf(shield)
220261
}
221262
None => {

crates/wharf-core/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,8 @@ anyhow = { workspace = true }
2727
# Logging
2828
tracing = { workspace = true }
2929

30+
# Platform-independent directories
31+
dirs = "5.0"
32+
3033
[dev-dependencies]
3134
tempfile = "3.10"

0 commit comments

Comments
 (0)