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
141 lines
5.3 KiB
Plaintext
141 lines
5.3 KiB
Plaintext
#!/usr/bin/env nu
|
|
# install/config-setup.nu — validate config.ncl and probe external services
|
|
#
|
|
# Run after editing config (or after first install) to confirm everything is wired correctly.
|
|
# Safe to run any time — read-only, no side effects.
|
|
#
|
|
# Usage:
|
|
# nu install/config-setup.nu
|
|
# nu install/config-setup.nu --config /custom/path/config.ncl
|
|
|
|
def main [
|
|
--config: string = "", # path to config.ncl (default: ~/.config/ontoref/config.ncl)
|
|
] {
|
|
let platform = (sys host | get name)
|
|
let is_mac = ((^uname) == "Darwin")
|
|
|
|
let config_path = if ($config | is-empty) {
|
|
$"($env.HOME)/.config/ontoref/config.ncl"
|
|
} else {
|
|
$config
|
|
}
|
|
|
|
let data_dir = if $is_mac {
|
|
$"($env.HOME)/Library/Application Support/ontoref"
|
|
} else {
|
|
$"($env.HOME)/.local/share/ontoref"
|
|
}
|
|
|
|
print "ontoref config-setup"
|
|
print $" config ($config_path)"
|
|
print ""
|
|
|
|
# ── 1. Config file exists ──────────────────────────────────────────────────
|
|
if not ($config_path | path exists) {
|
|
print "✗ config not found"
|
|
print $" copy the example: cp ($data_dir)/nulib/bootstrap.nu ... "
|
|
print $" or: cp (($config_path | path dirname))/config.ncl.example ($config_path)"
|
|
error make { msg: "config not found" }
|
|
}
|
|
print "✓ config file exists"
|
|
|
|
# ── 2. Nickel typecheck ────────────────────────────────────────────────────
|
|
let config_dir = ($config_path | path dirname)
|
|
let import_path = $"($config_dir):($config_dir)/schemas:($data_dir)/schemas:($data_dir)"
|
|
let check = (do {
|
|
with-env { NICKEL_IMPORT_PATH: $import_path } {
|
|
^nickel typecheck $config_path
|
|
}
|
|
} | complete)
|
|
|
|
if $check.exit_code != 0 {
|
|
print "✗ nickel typecheck failed"
|
|
print ($check.stderr | str trim)
|
|
error make { msg: "config schema validation failed" }
|
|
}
|
|
print "✓ nickel typecheck passed"
|
|
|
|
# ── 3. Parse config values ─────────────────────────────────────────────────
|
|
let export = (do {
|
|
with-env { NICKEL_IMPORT_PATH: $import_path } {
|
|
^nickel export --format json $config_path
|
|
}
|
|
} | complete)
|
|
|
|
if $export.exit_code != 0 {
|
|
print "✗ nickel export failed"
|
|
print ($export.stderr | str trim)
|
|
error make { msg: "config export failed" }
|
|
}
|
|
|
|
let cfg = ($export.stdout | from json)
|
|
|
|
# ── 4. Resolve and verify path dirs ───────────────────────────────────────
|
|
let log_path = if ($cfg.log.path | is-empty) {
|
|
if $is_mac { $"($env.HOME)/Library/Logs/ontoref" } else { $"($env.HOME)/.local/state/ontoref/logs" }
|
|
} else { $cfg.log.path }
|
|
|
|
let cache_path = if ($cfg.cache.path | is-empty) {
|
|
if $is_mac { $"($env.HOME)/Library/Caches/ontoref" } else { $"($env.HOME)/.cache/ontoref" }
|
|
} else { $cfg.cache.path }
|
|
|
|
let templates_dir = if ($cfg.ui.templates_dir | is-empty) {
|
|
$"($data_dir)/templates"
|
|
} else { $cfg.ui.templates_dir }
|
|
|
|
let public_dir = if ($cfg.ui.public_dir | is-empty) {
|
|
$"($data_dir)/public"
|
|
} else { $cfg.ui.public_dir }
|
|
|
|
print ""
|
|
print "── paths ──────────────────────────────────────────────"
|
|
|
|
for entry in [
|
|
{ label: "data dir", path: $data_dir },
|
|
{ label: "templates", path: $templates_dir },
|
|
{ label: "public", path: $public_dir },
|
|
{ label: "log dir", path: $log_path },
|
|
{ label: "cache dir", path: $cache_path },
|
|
] {
|
|
let exists = ($entry.path | path exists)
|
|
let mark = if $exists { "✓" } else { "⚠ missing (will be created on first run)" }
|
|
print $" ($mark) ($entry.label) ($entry.path)"
|
|
}
|
|
|
|
# ── 5. Liveness probes ────────────────────────────────────────────────────
|
|
print ""
|
|
print "── services ───────────────────────────────────────────"
|
|
|
|
# DB probe
|
|
if $cfg.db.enabled {
|
|
let db_url = $cfg.db.url
|
|
# Extract host:port from surrealdb://host:port/...
|
|
let addr = ($db_url | parse "surrealdb://{rest}" | get rest.0? | default "" | split row "/" | first)
|
|
let probe = (do { ^nc -z -w2 ...($addr | split row ":") } | complete)
|
|
if $probe.exit_code == 0 {
|
|
print $" ✓ db ($db_url)"
|
|
} else {
|
|
print $" ✗ db ($db_url) — not reachable"
|
|
}
|
|
} else {
|
|
print " — db disabled"
|
|
}
|
|
|
|
# NATS probe
|
|
if $cfg.nats_events.enabled {
|
|
let nats_url = $cfg.nats_events.url
|
|
let addr = ($nats_url | parse "nats://{rest}" | get rest.0? | default "" | split row "/" | first)
|
|
let probe = (do { ^nc -z -w2 ...($addr | split row ":") } | complete)
|
|
if $probe.exit_code == 0 {
|
|
print $" ✓ nats ($nats_url)"
|
|
} else {
|
|
print $" ✗ nats ($nats_url) — not reachable"
|
|
}
|
|
} else {
|
|
print " — nats disabled"
|
|
}
|
|
|
|
print ""
|
|
print $"config-setup complete platform=($platform)"
|
|
}
|