mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-06-17 06:04:14 +00:00
90 lines
3.1 KiB
Zig
90 lines
3.1 KiB
Zig
/// Top-level Guardian struct — orchestrates all subsystems.
|
|
///
|
|
/// The Guardian is the main runtime object that ties together:
|
|
/// - HTTP server (client-facing, port 7500)
|
|
/// - Peer protocol (guardian-to-guardian, port 7501)
|
|
/// - Node membership (via RQLite or static config)
|
|
/// - Heartbeat/health management
|
|
/// - Storage operations
|
|
const std = @import("std");
|
|
const log = @import("log.zig");
|
|
const config = @import("config.zig");
|
|
const node_list = @import("membership/node_list.zig");
|
|
const quorum = @import("membership/quorum.zig");
|
|
const heartbeat = @import("peer/heartbeat.zig");
|
|
|
|
pub const Guardian = struct {
|
|
cfg: config.Config,
|
|
nodes: node_list.NodeList,
|
|
allocator: std.mem.Allocator,
|
|
/// Random server secret for HMAC-based auth (generated at startup)
|
|
server_secret: [32]u8,
|
|
/// Share count cache (refreshed periodically)
|
|
share_count: u32,
|
|
|
|
pub fn init(allocator: std.mem.Allocator, cfg: config.Config) !Guardian {
|
|
// Generate server secret
|
|
var secret: [32]u8 = undefined;
|
|
std.crypto.random.bytes(&secret);
|
|
|
|
// Try to load node list from RQLite, fall back to self-only
|
|
var nodes = node_list.fetchFromRqlite(allocator, cfg.rqlite_url, cfg.client_port) catch blk: {
|
|
log.warn("failed to fetch node list from RQLite, running in single-node mode", .{});
|
|
const self_addr = [_][]const u8{cfg.listen_address};
|
|
break :blk try node_list.fromStatic(allocator, &self_addr, cfg.client_port);
|
|
};
|
|
|
|
// Mark self as alive
|
|
if (nodes.nodes.len > 0) {
|
|
nodes.self_index = 0;
|
|
nodes.nodes[0].state = .alive;
|
|
nodes.nodes[0].last_seen_ns = std.time.nanoTimestamp();
|
|
}
|
|
|
|
const share_count = heartbeat.countShares(cfg.data_dir);
|
|
|
|
return Guardian{
|
|
.cfg = cfg,
|
|
.nodes = nodes,
|
|
.allocator = allocator,
|
|
.server_secret = secret,
|
|
.share_count = share_count,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *Guardian) void {
|
|
self.nodes.deinit();
|
|
// Zero out server secret
|
|
@memset(&self.server_secret, 0);
|
|
}
|
|
|
|
/// Get current write quorum requirement.
|
|
pub fn writeQuorum(self: *const Guardian) usize {
|
|
return quorum.writeQuorum(self.nodes.aliveCount());
|
|
}
|
|
|
|
/// Get current Shamir threshold (read quorum).
|
|
pub fn readThreshold(self: *const Guardian) usize {
|
|
return self.nodes.threshold();
|
|
}
|
|
|
|
/// Refresh share count from disk.
|
|
pub fn refreshShareCount(self: *Guardian) void {
|
|
self.share_count = heartbeat.countShares(self.cfg.data_dir);
|
|
}
|
|
};
|
|
|
|
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
|
|
test "guardian: init and deinit" {
|
|
const allocator = std.testing.allocator;
|
|
const cfg = config.Config{
|
|
.data_dir = "/tmp/nonexistent-vault-test",
|
|
};
|
|
|
|
var g = try Guardian.init(allocator, cfg);
|
|
defer g.deinit();
|
|
|
|
try std.testing.expectEqual(@as(u32, 0), g.share_count);
|
|
}
|