The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup --gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
16 KiB
Changelog
All notable changes to ontoref are documented here.
ADRs referenced below live in adrs/ as typed Nickel records.
[Unreleased]
Auth & Session Model (ADR-005)
Unified key-to-session token exchange across all surfaces. All work gated on #[cfg(feature = "ui")].
KeyEntrygainslabel: String(#[serde(default)]) — audit trail for key-based sessions. NCL schemainstall/resources/schemas/ontoref-project.nclupdated accordingly.verify_keys_listreturnsOption<KeyMatch { role, label }>instead ofOption<Role>.SessionEntrygainsid: String— stable public identifier distinct from the bearer token, safe to expose in list responses. Prevents session enumeration by admins.SessionStoregains secondaryid_index: DashMap<id, token>for O(1)revoke_by_id.- New
SessionStoremethods:list_for_slug,list_all,revoke_all_for_slug,revoke_by_id(id, acting_slug, acting_role) -> RevokeResult. POST /sessions— key → UUID v4 token exchange. Accepts project keys or daemon admin password (project: "_daemon"). Rate-limited. No authentication required (it is the credential exchange endpoint).GET /sessions?project=slug— list active sessions for a project (viewer+). Without?project=param requires daemon admin and returns all sessions.DELETE /sessions/{id}— revoke by public id. Project admin: own project only. Daemon admin: any.require_session()helper: validates UUID v4 Bearer →SessionEntry, error boxed (Box<Response>) to satisfyclippy::result_large_err.check_primary_authfast-path: UUID v4 bearer → session lookup (O(1)) before argon2 fallback (~100ms).project_update_keys(PUT /projects/{slug}/keys) now callssessions.revoke_all_for_slugin addition to actor deregistration. All in-flight UI sessions for the rotated project are immediately invalidated.- Daemon admin:
ONTOREF_ADMIN_TOKEN_FILE(preferred — hash not visible inps aux) orONTOREF_ADMIN_TOKEN. Sessions use virtual slug"_daemon". manage_login_page/manage_login_submit/manage_logouthandlers for/ui/manage/loginand/ui/manage/logout.AdminGuardredirects to/ui/manage/loginwhendaemon_admin_hashis set.
CLI Bearer Token
bearer-argsexported fromreflection/modules/store.nu: returns["-H" "Authorization: Bearer $token"]whenONTOREF_TOKENis set,[]otherwise.http-get,http-post-json,http-delete(new) instore.nuuse...$auth— Bearer injected transparently, no behavior change whenONTOREF_TOKENis unset.notify-daemon-project-addandnotify-daemon-project-removeinreflection/bin/ontoref.nuusebearer-args.
Project Setup & Onboarding
ontoref setupis now the primary onboarding command (replaces manualcp templates/pattern).--kind <K>flag:Service(default) |Library|DevWorkspace|PublishedCrate|AgentResource|Mixed.--parent <path>flag: generates manifest withimplementationlayer +<slug>-frameworklayer and<slug>-browseop mode for implementation children.- Logo auto-detection:
setupscansassets/for<slug>-logo.svg,<slug>.svg,logo.svg(and.pngvariants); insertsui.logointo generatedconfig.nclwhen found. --gen-keys ["admin:label" "viewer:label"]flag: idempotent bootstrap — skips ifrole =already present inproject.ncl. Hashes viaontoref-daemon.bin --hash-password; prints passwords once to stdout.- All
mkdircalls in setup guarded byif not (path | path exists).
Self-Description — on+re Update
.ontology/core.ncl — 3 new Practice nodes, updated ontoref-daemon description and artifact_paths:
| Node | Pole | Description |
|---|---|---|
unified-auth-model |
Yang | Key→session token exchange; session.id ≠ bearer; revoke on key rotation |
project-onboarding |
Yang | ontoref setup — idempotent scaffold, --kind, --parent, --gen-keys |
ontoref-daemon node updated: auth/session management added to description and artifact_paths
(session.rs, auth.rs, login.rs).
New edges: unified-auth-model → ontoref-daemon, unified-auth-model → no-enforcement
(Contradicts/Low — auth is opt-in), ontoref-daemon → unified-auth-model (Contains),
project-onboarding → unified-auth-model (DependsOn),
project-onboarding → adopt-ontoref-tooling (Complements),
project-onboarding → daemon-config-management (DependsOn).
.ontology/state.ncl — self-description-coverage transitions to current_state = "fully-self-described".
Blocker resolved: reflection/backlog.ncl created, ADR-005 recorded.
reflection/backlog.ncl — created with 6 items: bl-001 (ontoref.dev), bl-002 (first external project),
bl-003 (ontoref keys CLI), bl-004 (session UI views), bl-005 (Syntaxis migration),
bl-006 (ADR-001 acceptance).
Previous state: 4 axioms, 2 tensions, 13 practices. Current: 4 axioms, 2 tensions, 15 practices.
Protocol
- ADR-004 accepted: NCL pipe bootstrap pattern —
nickel export config.ncl | ontoref-daemon.bin --config-stdin. Stages: Nickel evaluation → optional SOPS/Vault merge → binary via stdin. (adr-004) - ADR-005 accepted: unified key-to-session auth model across CLI, UI, and MCP. Opaque UUID v4 tokens,
session.id ≠ bearer, revocation on key rotation, daemon admin via_daemonslug. (adr-005)
Install Infrastructure
install/directory reorganized: binaries, bootstrapper, global CLI, resources, and validation scripts co-located.- Binary installed as
ontoref-daemon.bin; public entrypoint bootstrapper installed asontoref-daemon. Users never call.bindirectly. install/ontoref-daemon-boot(renamed fromontoref-daemon-start) — NCL pipe bootstrapper implementing ADR-004. Stages:nickel export config.ncl→ optional SOPS/Vault secret merge →ontoref-daemon.bin --config-stdin. Supports--dry-run,--sops,--vault.install/ontoref-daemon-bootsetsNICKEL_IMPORT_PATH(config dir + platform data dir schemas) andNATS_STREAMS_CONFIG(default~/.config/ontoref/streams.json) before launching the binary.install/install.nu— installs binary, bootstrapper, globalontorefCLI (ONTOREF_ROOT baked in at install time), UI assets, config skeleton, and global NATS topology. Hash-checked — unchanged files are not overwritten.install/config-setup.nu— standalone validation script:nickel typecheck+nickel export, path existence checks, DB and NATS liveness probes (nc -z -w2).install/check-config-sync.nu— CI guard asserting that everynickel_path-bearing field inreflection/forms/config.nclhas a matching{{ name }}reference inreflection/forms/config.ncl.j2, and vice versa. Wired intojust ci-lintandjust ci-full.
Config Management
install/resources/config.ncl— default global config skeleton with full Nickel contracts (Port,LogLevel,Rotation,Actor,Severity). Covers: daemon, db, nats_events, log, cache, ui, mode_run, actor_init, quick_actions, nickel_import_paths.install/resources/streams.json— global default NATS JetStream topology: ECOSYSTEM stream (ecosystem.>, 30-day retention), no project-specific consumers. Installed to~/.config/ontoref/streams.json.nats/streams.json— ontoref project-local topology withdaemon-ontorefandcli-notificationsconsumers on ECOSYSTEM stream.reflection/forms/config.ncl+reflection/forms/config.ncl.j2— typedialog roundtrip for browser-based config editing (ontoref config-edit). Form populates fields from existing config vianickel_path; Tera template reconstructs full NCL with all contracts on save.reflection/nulib/bootstrap.nu— Nu bootstrapper helper updated:nats-streams-configfunction resolvesNATS_STREAMS_CONFIGdefault; env var passed to daemon process viawith-env.- Daemon
nats.rs: emptystreams_configstring →None, activatingTopologyConfig::loadfallback toNATS_STREAMS_CONFIGenv var. Projects with a localnats/streams.jsonsetstreams_configexplicitly in their config.
Daemon Fixes
--config-stdinnow exclusively skips.ontoref/config.ncl— project config is never loaded when stdin config is active. Previously both paths could run.- DB connection (
stratum-db) only established whendb.enabled = truein config. Previously connected regardless ofenabledflag. - NATS connection (
platform-nats) only established whennats_events.enabled = true. "Connecting to NATS..." log moved after the enabled check. NatsPublisher::connectsignature changed fromconfig_path: &PathBuf(re-read file) toconfig: Option<&serde_json::Value>(already-loaded JSON). Eliminates double file read and ensures NATS uses the same config source as the rest of the daemon.load_config_overridesreturns(Option<String>, Option<serde_json::Value>)— nickel import path and parsed config JSON returned together.apply_stdin_configreturnsserde_json::Valuedirectly.
Self-Description — on+re Update
.ontology/core.ncl — 1 new Practice node, updated ontoref-daemon description and artifact_paths:
| Node | Pole | Description |
|---|---|---|
daemon-config-management |
Yang | Install + config form/template roundtrip, CI guard, global NATS topology, config-setup validation |
New edges: daemon-config-management → ontoref-daemon (DependsOn),
daemon-config-management → adopt-ontoref-tooling (Complements).
.ontology/state.ncl — operational-mode dimension description updated to reference ADR-004 bootstrap
and NATS_STREAMS_CONFIG mechanism. protocol-maturity blocker updated to reflect install pipeline
completion.
.ontology/manifest.ncl — tooling layer paths updated to include install/ and nats/.
Previous state: 4 axioms, 2 tensions, 12 practices. Current: 4 axioms, 2 tensions, 13 practices.
Protocol
- ADR-001 accepted: ontoref extracted as a standalone protocol project, independent of
stratumiops versioning and release cycle. Consumer projects adopt via
scripts/ontorefwrapper +.ontoref/config.ncl. (adr-001) - ADR-002 accepted:
ontoref-daemonintroduced as optional persistent daemon for NCL export caching (keyed by path+mtime), actor registry (developer/agent/CI), and notification barrier (pre-commit hook, fail-open). Supersedes stratumiops ADR-007. (adr-002) - ADR-003 accepted: Q&A and accumulated operational knowledge persist to
reflection/qa.ncl— typed NCL, git-versioned, accessible via MCP tools and HTTP endpoints. localStorage eliminated. Q&A entries survive session boundaries and are queryable by any actor. (adr-003)
Crates
ontoref-ontology: Rust crate for loading.ontology/*.nclas typed structs (Core,Gate,State). Zero stratumiops dependencies.ontoref-reflection: Rust crate for loading, validating, and executing Reflection modes as NCL DAG contracts. Optionalnatsfeature (path dep:platform-nats).ontoref-daemon: Rust crate providing HTTP API (axum), DashMap-backed NCL export cache, notify-based file watcher, and actor registry. Optionaldbfeature (path dep:stratum-db) andnatsfeature (path dep:platform-nats).
Daemon — Q&A NCL Persistence (crates/ontoref-daemon/src/ui/qa_ncl.rs)
Line-level NCL surgery for reflection/qa.ncl — same pattern as backlog_ncl.rs. No AST parsing,
no nickel-lang-core dependency.
add_entry— appends a typedQaEntryblock before],array close; generates sequentialqa-NNNidsupdate_entry— in-place field mutation via bidirectional scan (question + answer fields)remove_entry— removes the full block by id using backward scan for{and forward scan for},
HTTP endpoints (all under #[cfg(feature = "ui")] except read-only GET):
GET /qa-json— export all Q&A entries as JSON (read-only, always enabled)POST /qa/add— append new entry; returns generated idPOST /qa/delete— remove entry by id; invalidates NCL cachePOST /qa/update— mutate question + answer fields by id; invalidates NCL cacheGET /actions/run/POST /actions/run— execute a quick action by id; spawns./ontoref <mode>
Server-side hydration: qa.html receives entries as Tera context variable, embeds
SERVER_ENTRIES JSON literal in the page <script> — no fetch round-trip on load.
Daemon — MCP Tools (crates/ontoref-daemon/src/mcp/mod.rs)
Four new MCP tools exposed to AI agents:
| Tool | Description |
|---|---|
ontoref_qa_list |
List Q&A entries with optional filter substring match. Never triggers ontology sync. |
ontoref_qa_add |
Append a new Q&A entry to reflection/qa.ncl; invalidates NCL cache. |
ontoref_action_list |
List all quick actions from .ontoref/config.ncl export. |
ontoref_action_add |
Create a new reflection mode at reflection/modes/<id>.ncl and register it as a quick action. |
Constraint: ontoref_qa_list and ontoref_qa_add never trigger apply steps or modify .ontology/
files (enforced by ADR-003).
Daemon — Passive Drift Observation (crates/ontoref-daemon/src/ui/drift_watcher.rs)
Background observer bridging Yang code artifacts with Yin ontology declarations:
- Watches
crates/,.ontology/,adrs/,reflection/modes/vianotifyfile watcher - 15-second debounce window before triggering scan
- Spawns
./ontoref sync scan && ./ontoref sync diffas read-only subprocesses - Parses stdout for MISSING / STALE / DRIFT / BROKEN markers
- Emits
ontology_driftnotification viapush_customwhen any drift is found - Never applies changes automatically —
applyremains a deliberate human or agent act
Started from main.rs under #[cfg(feature = "ui")]; failure to start is non-fatal (logged as warning).
Tooling
reflection/modules/: 16 Nushell operational modules (adr.nu,backlog.nu,coder.nu,describe.nu,sync.nu, etc.)reflection/modes/: 10 NCL DAG operational modes includingadopt_ontoref,sync-ontology,coder-workflow,create-prreflection/forms/: 7 interactive NCL forms for ADR lifecycle, backlog, and adoptiontemplates/: Consumer-facing adoption templates (ontoref-config.ncl,ontology/,scripts-ontoref)./ontoref: Bash entry point with actor auto-detection, advisory file locking, and Nushell version guard (>= 0.110.0)
Self-Description — on+re Update
.ontology/core.ncl updated with 3 new Practice nodes and 9 new edges:
| Node | Pole | Description |
|---|---|---|
qa-knowledge-store |
Yin | Q&A entries as typed NCL — accumulated knowledge queryable by any actor |
quick-actions |
Yang | Runnable shortcuts over reflection modes; configured in .ontoref/config.ncl |
drift-observation |
Spiral | Passive bridge between Yang code artifacts and Yin ontology declarations |
New edges: qa-knowledge-store → dag-formalized, qa-knowledge-store → coder-process-memory,
ontoref-daemon → qa-knowledge-store, quick-actions → reflection-modes,
quick-actions → ontoref-daemon, describe-query-layer → quick-actions,
drift-observation → ontoref-daemon, drift-observation → ontology-vs-reflection,
drift-observation → reflection-modes.
Previous state: 4 axioms, 2 tensions, 9 practices. Current: 4 axioms, 2 tensions, 12 practices.
reflection/schemas/qa.ncl — QaStore and QaEntry types (id, question, answer, actor, created_at,
tags, related, verified).
reflection/qa.ncl — typed store file, conforms to QaStore contract, Nickel typecheck must pass.
ontoref uses its own ADR system to track decisions. Architectural rationale lives in adrs/, not in this file.