Config surface — per-project config introspection, coherence verification, and
audited mutation without destroying NCL structure (ADR-008):
- crates/ontoref-daemon/src/config.rs — typed DaemonNclConfig (parse-at-boundary
pattern); all section structs derive ConfigFields + config_section(id, ncl_file)
emitting inventory::submit!(ConfigFieldsEntry{...}) at link time
- crates/ontoref-derive/src/lib.rs — #[derive(ConfigFields)] proc-macro; serde
rename support; serde_rename_of() helper extracted to fix excessive_nesting
- crates/ontoref-daemon/src/main.rs — 3-tuple bootstrap block (nickel_import_path,
loaded_ncl_config: Option<DaemonNclConfig>, stdin_raw); apply_ui_config takes
&UiConfig; NATS call site typed; resolve_asset_dir cfg(feature = "ui")
- crates/ontoref-daemon/src/api.rs — config GET/PUT endpoints, quickref, coherence,
cross-project comparison; index_section_fields() extracted (excessive_nesting)
- crates/ontoref-daemon/src/config_coherence.rs — multi-consumer coherence;
merge_meta_into_section() extracted; and() replaces unnecessary and_then
NCL contracts for ontoref's own config:
- .ontoref/contracts.ncl — LogConfig (LogLevel, LogRotation, PositiveInt) and
DaemonConfig (Port, optional overrides); std.contract.from_validator throughout
- .ontoref/config.ncl — log | C.LogConfig applied
- .ontology/manifest.ncl — contracts_path, log/daemon contract refs, daemon section
with DaemonRuntimeConfig consumer and 7 declared fields
Protocol:
- adrs/adr-008-ncl-first-config-validation-and-override-layer.ncl — NCL contracts
as single validation gate; Rust structs are contract-trusted; override-layer
mutation writes {section}.overrides.ncl + _overrides_meta, never touches source
on+re update:
- .ontology/core.ncl — config-surface node (28 practices); adr-lifecycle extended
to adr-007 + adr-008; 6 new edges (ManifestsIn daemon, DependsOn ontology-crate,
Complements api-catalog-surface/dag-formalized/self-describing/adopt-ontoref)
- .ontology/state.ncl — protocol-maturity blocker and self-description-coverage
catalyst updated for session 2026-03-26
- README.md / CHANGELOG.md updated
220 lines
6 KiB
Rust
220 lines
6 KiB
Rust
/// Typed representation of `.ontoref/config.ncl`.
|
|
///
|
|
/// Every section that the daemon reads from its own config file is captured
|
|
/// here with `#[derive(ConfigFields)]` so that `inventory` registers the
|
|
/// consumed fields at link time. The coherence endpoint can then compare
|
|
/// these registrations against the live NCL export without needing a
|
|
/// hand-maintained `fields` list in `manifest.ncl`.
|
|
///
|
|
/// All fields carry `#[serde(default)]` — a project may omit any section and
|
|
/// the daemon degrades gracefully rather than failing to start.
|
|
use ontoref_ontology::ConfigFields;
|
|
use serde::Deserialize;
|
|
|
|
/// Full deserialized view of `.ontoref/config.ncl`.
|
|
#[derive(Debug, Deserialize, Default)]
|
|
pub struct DaemonNclConfig {
|
|
#[serde(default)]
|
|
pub nickel_import_paths: Vec<String>,
|
|
#[serde(default)]
|
|
pub ui: UiConfig,
|
|
#[serde(default)]
|
|
pub log: LogConfig,
|
|
#[serde(default)]
|
|
pub mode_run: ModeRunConfig,
|
|
#[serde(default)]
|
|
pub nats_events: NatsEventsConfig,
|
|
#[serde(default)]
|
|
pub actor_init: Vec<ActorInit>,
|
|
#[serde(default)]
|
|
pub quick_actions: Vec<QuickAction>,
|
|
#[serde(default)]
|
|
pub daemon: DaemonRuntimeConfig,
|
|
#[cfg(feature = "db")]
|
|
#[serde(default)]
|
|
pub db: DbConfig,
|
|
}
|
|
|
|
/// `ui` section — template and asset paths, optional TLS cert overrides.
|
|
#[derive(Debug, Deserialize, Default, ConfigFields)]
|
|
#[config_section(id = "ui", ncl_file = ".ontoref/config.ncl")]
|
|
pub struct UiConfig {
|
|
#[serde(default)]
|
|
pub templates_dir: String,
|
|
#[serde(default)]
|
|
pub public_dir: String,
|
|
#[serde(default)]
|
|
pub tls_cert: String,
|
|
#[serde(default)]
|
|
pub tls_key: String,
|
|
#[serde(default)]
|
|
pub logo: String,
|
|
#[serde(default)]
|
|
pub logo_dark: String,
|
|
}
|
|
|
|
/// `log` section — structured logging policy.
|
|
#[derive(Debug, Deserialize, ConfigFields)]
|
|
#[config_section(id = "log", ncl_file = ".ontoref/config.ncl")]
|
|
pub struct LogConfig {
|
|
#[serde(default = "default_log_level")]
|
|
pub level: String,
|
|
#[serde(default = "default_log_path")]
|
|
pub path: String,
|
|
#[serde(default = "default_rotation")]
|
|
pub rotation: String,
|
|
#[serde(default)]
|
|
pub compress: bool,
|
|
#[serde(default = "default_log_archive")]
|
|
pub archive: String,
|
|
#[serde(default = "default_max_files")]
|
|
pub max_files: u32,
|
|
}
|
|
|
|
impl Default for LogConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
level: default_log_level(),
|
|
path: default_log_path(),
|
|
rotation: default_rotation(),
|
|
compress: false,
|
|
archive: default_log_archive(),
|
|
max_files: default_max_files(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn default_log_level() -> String {
|
|
"info".into()
|
|
}
|
|
fn default_log_path() -> String {
|
|
"logs".into()
|
|
}
|
|
fn default_rotation() -> String {
|
|
"daily".into()
|
|
}
|
|
fn default_log_archive() -> String {
|
|
"logs-archive".into()
|
|
}
|
|
fn default_max_files() -> u32 {
|
|
7
|
|
}
|
|
|
|
/// `nats_events` section — NATS JetStream integration.
|
|
#[derive(Debug, Deserialize, Default, ConfigFields)]
|
|
#[config_section(id = "nats_events", ncl_file = ".ontoref/config.ncl")]
|
|
pub struct NatsEventsConfig {
|
|
#[serde(default)]
|
|
pub enabled: bool,
|
|
#[serde(default = "default_nats_url")]
|
|
pub url: String,
|
|
#[serde(default)]
|
|
pub emit: Vec<String>,
|
|
#[serde(default)]
|
|
pub subscribe: Vec<String>,
|
|
#[serde(default)]
|
|
pub handlers_dir: String,
|
|
#[serde(default)]
|
|
pub nkey_seed: Option<String>,
|
|
#[serde(default)]
|
|
pub require_signed_messages: bool,
|
|
#[serde(default)]
|
|
pub trusted_nkeys: Vec<String>,
|
|
#[serde(default)]
|
|
pub streams_config: String,
|
|
}
|
|
|
|
fn default_nats_url() -> String {
|
|
"nats://localhost:4222".into()
|
|
}
|
|
|
|
/// `mode_run` section — per-actor mode execution ACL.
|
|
#[derive(Debug, Deserialize, Default, ConfigFields)]
|
|
#[config_section(id = "mode_run", ncl_file = ".ontoref/config.ncl")]
|
|
pub struct ModeRunConfig {
|
|
#[serde(default)]
|
|
pub rules: Vec<ModeRunRule>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Default)]
|
|
pub struct ModeRunRule {
|
|
#[serde(default)]
|
|
pub when: ModeRunWhen,
|
|
#[serde(default)]
|
|
pub allow: bool,
|
|
#[serde(default)]
|
|
pub reason: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Default)]
|
|
pub struct ModeRunWhen {
|
|
#[serde(default)]
|
|
pub mode_id: Option<String>,
|
|
#[serde(default)]
|
|
pub actor: Option<String>,
|
|
}
|
|
|
|
/// One entry in the `actor_init` array.
|
|
#[derive(Debug, Deserialize, Default)]
|
|
pub struct ActorInit {
|
|
#[serde(default)]
|
|
pub actor: String,
|
|
#[serde(default)]
|
|
pub mode: String,
|
|
#[serde(default)]
|
|
pub auto_run: bool,
|
|
}
|
|
|
|
/// One entry in the `quick_actions` array.
|
|
#[derive(Debug, Deserialize, Default)]
|
|
pub struct QuickAction {
|
|
#[serde(default)]
|
|
pub id: String,
|
|
#[serde(default)]
|
|
pub label: String,
|
|
#[serde(default)]
|
|
pub icon: String,
|
|
#[serde(default)]
|
|
pub category: String,
|
|
#[serde(default)]
|
|
pub mode: String,
|
|
#[serde(default)]
|
|
pub actors: Vec<String>,
|
|
}
|
|
|
|
/// `daemon` section — overrides for CLI defaults set at startup.
|
|
#[derive(Debug, Deserialize, Default, ConfigFields)]
|
|
#[config_section(id = "daemon", ncl_file = ".ontoref/config.ncl")]
|
|
pub struct DaemonRuntimeConfig {
|
|
#[serde(default)]
|
|
pub port: Option<u16>,
|
|
#[serde(default)]
|
|
pub idle_timeout: Option<u64>,
|
|
#[serde(default)]
|
|
pub invalidation_interval: Option<u64>,
|
|
#[serde(default)]
|
|
pub actor_sweep_interval: Option<u64>,
|
|
#[serde(default)]
|
|
pub actor_stale_timeout: Option<u64>,
|
|
#[serde(default)]
|
|
pub max_notifications: Option<usize>,
|
|
#[serde(default)]
|
|
pub notification_ack_required: Vec<String>,
|
|
}
|
|
|
|
/// `db` section — SurrealDB connection (feature-gated).
|
|
#[cfg(feature = "db")]
|
|
#[derive(Debug, Deserialize, Default, ConfigFields)]
|
|
#[config_section(id = "db", ncl_file = ".ontoref/config.ncl")]
|
|
pub struct DbConfig {
|
|
#[serde(default)]
|
|
pub enabled: bool,
|
|
#[serde(default)]
|
|
pub url: String,
|
|
#[serde(default)]
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub username: String,
|
|
#[serde(default)]
|
|
pub password: String,
|
|
}
|