Every evidence row now records its derivation (direct, assertion, operator,
external, prediction, consolidation, inference, reflection, dream,
retrieval), its independent-source family (an actor, an integration, an
operator, a prediction) and its root event or memory ids. Confidence
groups evidence by family: the strongest primary item counts in full, each
further primary item adds a quarter, capped at 1.5x, and derived items
count only when the family has no primary member. Three derived
re-expressions of one utterance therefore weigh exactly what the utterance
weighs, while three independent families still add up.
Producers (conversation, association, consolidation, predictions,
metacognition, dreaming, operator and seed input, contradictions) supply
their ancestry. Existing rows are marked legacy_unknown with family =
source; nothing is invented. The belief page shows per-family
contributions and the replay test checks that provenance is reconstructed
identically in the sandbox. Migration 0017 is additive.
The SQL-injection effect test found that a NUL byte in a query string,
path or JSON body reached PostgreSQL and surfaced as a 500. Requests are
now buffered up to the existing limit and NUL in any form (raw, JSON
escaped, form encoded) is refused with a 400 before a query runs.
TestSQLInjectionHasNoEffect sends metacharacter payloads through every
search, filter and free-text route and asserts effect: users, policies,
sessions and tokens unchanged, seeded content intact, payloads stored
verbatim. TestDynamicSQLIdentifiersAreAllowListed scans for statements
assembled with Sprintf outside the reviewed allow-list.
An archive is data about a mind, not a grant of permissions on this
instance. Import now skips the policies table (forks on the same instance
keep theirs), reports the omitted rows in the audit log and the mind's
history, and the snapshot test proves both paths.
ADR-0002 described the bigserial seq as gap-free. PostgreSQL sequences are
not: values consumed by rolled-back inserts are never reused. The code only
ever relied on monotonic order (seq > cursor), so this is a documentation
correction plus a regression test that burns sequence values and checks
paging, cursor resumption from inside a gap and stats.
- TOTP second factor (RFC 6238, secret encrypted under the master key),
ten hashed single-use recovery codes, two-step login with a pending token
bound to the client address, account enrolment/disable, admin reset
- tests/security: prompt-injection corpus never acts or creates evidence-free
beliefs; roles and CSRF on form and API paths; cross-mind isolation
(demo scenario 5); stored-XSS escaping and CSP across pages; secrets never
echoed (API, pages, database, archives); hostile archive import; session
lifecycle and security headers
- OpenAPI 3.1 document generated from the route table, served at
/api/v1/openapi.json and kept complete by a test
- gosec clean (justified #nosec annotations, cookie/redirect hardening,
bounded multipart parsing, UUID packing without narrowing conversions);
govulncheck clean with toolchain pinned to go1.26.8
- THREAT_MODEL verification pointers, SECURITY.md testing section, README,
CHANGELOG 0.1.0, docs/RELEASE_NOTES.md, PLAN status
- snapshots: checksummed JSON archive of every mind-scoped state table plus
identity/personality/config and the event position; stored, downloadable
- fork: restore into a new mind with all ids remapped; event references are
kept so history before the fork is shared by reference
- export/import: same archive; import is hostile-input validated (size,
allow-listed tables, row caps, column names, checksum, forced mind_id)
- compare: deterministic counts, belief diffs by proposition, personality
and config diffs, event mix
- replay: Runtime.Step for synchronous drivers; sandbox fork driven one
stimulus at a time under a forbidden capability context; recorded model
responses via a replaying router; steps labelled exact / approximate /
diverged with recorded-vs-replayed digests
- experiments: definition (overrides, stimuli, expectations, seed, duration),
background run in a disabled subject mind, stored and exportable results
- migration 0015, API, UI (Snapshots and Replay tabs, Experiments section,
compare page), noemactl snapshots/compare/replay/experiments, docs
- dreaming.Service: deterministic memory replay sample keyed by dream id,
vocabulary-based association of unlinked memory pairs (similar_to edges +
dream_association events), speculative hypotheses as hypothesis-kind beliefs
with two weak memory citations (starts ~0.30, asserted <= 0.35)
- Run marks the context forbidden for capabilities before anything else;
test proves Invoke refuses inside a dream
- per-mind settings: enabled (default off), hours window (wraps midnight),
interval, model-call / memory / hypothesis caps; global pause switch
- optional model wording via new `dream` prompt (role hypothesise)
- promotion adds operator evidence, rejection retracts the belief; both
audited and recorded as operator actions
- migration 0014, API, Dreams tab and page, noemactl dreams, docs/dreaming.md
Phase 12 (ADR-0005). Capabilities are registered in code with parameter
schemas, risk classes and default effects; sudo_shell and delete_database
are locked to DENY and cannot be relaxed. Policies are scoped globally or
per mind with capability, target glob, action kind and optional hour
window; evaluation is deterministic, most specific wins, and ties go to
the stricter effect. Invoke is the single entry point: it refuses
dreaming and replay contexts, validates parameters (unknown keys, non
http URLs and path traversal rejected), evaluates policy and session
grants, hashes the exact request and records a policy_decision event.
ALLOW executes with timeout and panic isolation and turns textual output
into a tool-trust observation; ASK creates an approval request bound to
the hash with an alert notification; DENY is counted and recorded.
Operators approve once, approve for a time-boxed session grant, deny
with a note, or modify parameters (re-validated, re-hashed, original
kept in the append-only decision log). The self-model's capability list
is recomputed from effective policy on start and on every policy change.
The act process maps fetch requests to http.fetch and the action handler
invokes through the layer; replies report capabilities and outcomes.
Policy matrix and approval centre pages, dashboard count, API, CLI
(approvals, policy, invoke) and docs/capabilities.md.
Phase 11. Entities are mind-scoped nodes with canonical matching,
aliases and deterministic kind guessing (hostnames and hyphenated ids
are systems, capitalised names people, file suffixes documents).
Relationships carry a relation from the shared vocabulary, confidence,
source, reinforcement count and the event that produced them; links tie
entities to memories, beliefs, events, goals and predictions.
The perceive and attend handlers upsert the entities a message or
observation mentions and record co-mention edges cited to the event, so
the graph grows only from what was actually seen. Operators can merge
("these are the same": edges and links move, the old name becomes an
alias, the old id still resolves), record that two entities are
different, correct kinds and add typed edges; every correction is an
operator action and a corrections row.
The explorer renders a bounded subgraph on a canvas with a small
force-directed layout in plain JavaScript, with kind filters, focus on
a neighbourhood and a side panel per entity. API, entity page, CLI and
docs/graph.md.
Phase 10. Metacognition is a cognitive process with deterministic
self-checks (insufficient evidence, single-source reliance, model output
without evidence, repeated failed hypotheses, stale contradictions,
calibration overconfidence); its warnings compete for attention and,
when admitted, add counter-evidence or open an investigation goal.
Reflection reviews the period since the last run (prediction errors,
rejected beliefs, corrections, status changes, completed goals,
contradictions, calibration), turns repeated failures into procedural
lessons written once, and records a reflection event; a model may only
word the prose within the mind's budget.
Consolidation clusters unconsolidated episodes by shared stemmed terms,
creates a belief whose confidence is computed from the episodes as
evidence and a semantic memory linked to each episode by derived_from,
and records jobs with inputs and outputs. Wording may come from the
consolidate prompt within a per-run model budget; the job says which.
Operators can run, pause and reject; originals are never touched.
The narrative hook keeps a five-part first-person narrative built from
state and stored with narrative_updated events. The notification hook
applies explicit thresholded rules, aggregates repeats within a cooldown
and records suppressions with reasons. A scheduled job runs
consolidation and reflection per mind, switching the runtime mode while
it works. API, Background tab, notifications page with unread badge,
overview narrative and warnings, CLI commands, docs/cognition.md.
Phase 9. Goals form a mind-scoped tree with kind, priority, progress and
blockers; their deterministic relevance to a candidate feeds the
goal_relevance attention feature and a goal monitor raises stalled goals
and observations that touch one. Drives are a closed set of eight
scalars with operator-configured ranges and personality-derived
baselines; there is no self-preservation, replication or acquisition
drive and no way to add one at runtime. Affect is ten scalars moved by an
explicit rule table over events and decaying towards baseline; both
modulate attention weights each cycle and the effective weights are
recorded with every cycle and shown beside the configured ones.
Predictions carry a window and machine-checkable criteria (event terms
occurring or staying absent, a belief reaching a confidence, or operator
judgement). A resolver job judges them deterministically, keeps an
append-only outcome history for calibration (accuracy, Brier score,
confidence buckets, overconfidence flag), emits prediction_error events
scaled by how confident the miss was, and feeds the outcome back into the
linked belief as evidence. Causal hypotheses derive predictions
automatically. Curiosity turns misses, contradictions and unexplained
observations into questions that compete for attention and become
investigation goals when admitted.
Brain gains after-cycle hooks and goal/motivation state. API, UI (goals,
prediction dashboard, cognitive state panel, effective weights) and CLI
(goal, prediction, state). Demo scenario 4 verified end to end.
Phase 8, completing milestone M1. A user message is stored and appended
as an observation event carrying trust, actor, channel and reference. The
perceive process analyses it deterministically (intent, introspection
kind, entities, claims, urgency) and, within the per-cycle model
allowance, enriches it through the perceive prompt. Its handler writes a
perception event, an episodic memory with participants, turns claims into
beliefs whose evidence is the message, and builds a response intent from
state: beliefs and memories for the topic, evidence rows for why-questions,
the uncertain band, contested beliefs and their links, recent learning,
status changes, and honest declines for subsystems that do not exist yet
or for requests with no capabilities. The respond prompt phrases the
intent when a model is assigned; otherwise the reply is a direct
rendering of the intent. response_generated cites the message, the
perception, every belief and memory used, the episode and the model call.
API: conversations, messages with optional wait, one-shot ask; chat UI
with live refresh and a per-reply intent view. Sample mind Dani as a
strictly decoded seed document with an import endpoint and noemactl mind
import. tests/m1 drives all fourteen milestone steps including restart.
Phase 7 (ADR-0004). Cognitive code asks the router for a role; the
router maps roles to configured models with ordered fallbacks, applies
global and per-mind daily token budgets and a concurrency limit, records
every call (request, response, usage, latency, cost, seed, request hash)
and can serve recordings back for exact replay. Providers are raw HTTP
clients for OpenAI-compatible, Anthropic and Ollama endpoints, with
embedding support for the embed role wired into memory retrieval.
Secrets are references only: env:, file:, or enc: values encrypted with
AES-256-GCM under NOEMA_MASTER_KEY; keys are resolved at call time and
never rendered, logged or exported. Outbound HTTP refuses private and
loopback addresses at dial time unless a provider explicitly allows it,
bounds redirects and caps bodies. Provider errors are scrubbed.
Prompts are named, versioned rows with system and user templates and an
optional JSON schema; untrusted text enters only through delimited data
blocks. Six defaults are seeded and never overwritten. The UI adds the
LLM page (providers, models, role matrix, usage, errors, budgets), a
provider page with connectivity test, and a prompt manager with editor,
test runner, history, restore, clone and diff. CLI: provider, prompt.
Association candidates scored below decayed workspace residents and were
always rejected in a small workspace. Raise their novelty, consequence
and operator-priority features so a new hypothesis outranks a stale
recall; confidence of the resulting belief is unchanged (still computed
from the two co-occurring observations).
Phase 6. Beliefs are propositions with a prior set by epistemic kind and a
confidence computed from typed evidence: sigmoid(logit(prior) + slope ×
(support × diversity − opposition)), where support from a single source
is discounted so repetition cannot manufacture certainty, and bounds keep
the mind away from absolute certainty. Every evaluation stores its
breakdown and appends a version row protected by the append-only
trigger; the belief page shows the arithmetic, evidence for and against,
alternatives, contradictions and history.
Contradiction detection is deterministic first (negation plus token
overlap): a hit emits contradiction_detected, links both beliefs and
makes each counter-evidence for the other so both trend to contested.
Operators can assert beliefs (their statement is the evidence), add
evidence, mark a belief wrong (pinned rejected, history kept) and forbid
an inference; do-not-infer corrections block matching hypotheses.
Retracting a memory retracts the evidence that cited it and re-evaluates
the beliefs concerned.
The associate process proposes low-confidence causal hypotheses from
co-occurring notable observations, and the hypothesis handler turns
admitted ones into beliefs with automatic alternatives, closing demo
scenarios 1 and 2 without any model call.
Phase 5 (ADR-0003). pkg/concepts fixes the epistemic vocabulary
(observation, fact, memory, claim, hypothesis, belief, prediction,
assumption, inference, opinion), memory kinds and relation names.
internal/memory stores episodic, semantic, procedural and
autobiographical memories in one mind-scoped table with trust-derived
credibility, importance, salience and decaying activation. Retrieval
fuses full-text rank (any-term by default), optional pgvector similarity,
recency, activation, importance and credibility, and returns the
component scores so every recall can be explained. Decay lowers
activation and never deletes; retraction and supersession keep rows for
provenance. Relationships are constrained to one mind at insert time.
Embeddings live in a conditional pgvector table keyed by model so vector
spaces are never mixed; without an embedder the system degrades to text
and graph retrieval.
Attention gates encoding: the attend handler turns admitted observations
into episodic memories (operator changes become autobiographical ones)
with memory_created events; the retrieve process proposes recalled
memories as candidates and admitted recalls are reinforced with
memory_retrieved events carrying their scoring components.
Memory explorer UI (search with component breakdown, timeline, filters,
operator knowledge entry), memory detail with provenance, relationships,
citing events and an inaccurate-memory correction; API; noemactl memory
commands; decay and embed-missing background jobs.
Phase 4 (ADR-0001). internal/attention scores thought candidates as a
normalised weighted sum over ten named features with a per-term
breakdown, ranked by (score, id) so ordering is total and replayable.
internal/workspace holds the capacity-limited workspace (refresh by
subject, decayed competition, explained admit/reject outcomes) and the
decaying working memory.
internal/brain runs one runtime per active mind: debounced wake-ups from
the event bus (own output never re-triggers), a settle window so
concurrently committing events are not skipped, bounded concurrent
Observe calls with per-process isolation and timeouts, deterministic
scoring, a loop detector that suppresses recurring thoughts and raises a
metacognition warning, executive dispatch by thought kind, and one
transaction per cycle whose events are published only after commit.
Every candidate, selection, attention shift and broadcast is an event
with provenance back to the triggering observation. Operators can run a
cycle, pause, resume, reset transient state and change attention
weights live; the manager reconciles runtimes with mind status.
cognition/attend is the first process: deterministic candidates from
external events using an urgency lexicon, novelty against context and
trust-based operator priority.
Phase 3. Events are the append-only record of everything a mind does
(ADR-0002). pkg/events defines the closed type vocabulary, categories,
evidence references and the observation payload with explicit trust
levels. The store validates and appends inside the caller's transaction,
publishes to a bounded fan-out bus only after commit, queries by seq
cursor with type/category/source/salience/confidence/time/search
filters, and walks provenance: cause, parents, children, cited evidence,
derived events and correlation groups. Durable per-consumer cursors never
move backwards.
Server-Sent Events streams (per mind and global) subscribe before
replaying history after Last-Event-ID so nothing is missed, apply the
same filter vocabulary live, and heartbeat. Operators can inject
observations tagged with operator trust for demos and tests.
UI: live thought stream with filters and paging, forensic event page
with causal chain and evidence navigation, dashboard important events.
Personality and status changes now leave operator_action events in the
mind's own history. CLI: event inspect/list/tail, observe.
Phase 2. Minds are rows with a slug, status (active/disabled/archived),
cognitive configuration and a pointer to the current personality version.
Personalities are structured (identity, values, traits, communication,
behaviour, free text) with a deterministic Brief() used by prompts and the
editor preview. Every change appends a personality_versions row protected
by the append-only trigger; restore copies an old version forward.
Templates carry reusable personalities; minds record their overrides so a
template can be re-applied without losing mind-specific edits. The
self-model starts with structural limitations the mind cannot drop
(no policy changes, no influence over shutdown, no cross-mind access).
API, UI (list, overview, editor with sliders/diff/history/import/export,
template editor) and noemactl mind/template commands. All mutations are
audited with field-level diffs.
Phase 1 foundation. Local auth per ADR-0006: Argon2id passwords, opaque
hashed session tokens with idle/absolute expiry, CSRF token plus Origin
check on cookie sessions, per-IP token bucket and per-account exponential
lockout, three roles enforced by middleware, personal API tokens for
noemactl. Audit log writer used by every admin mutation.
API: request ids, panic recovery, strict CSP and security headers, body
limits, single JSON error envelope, health/ready/status, users, sessions,
tokens, audit, token-protected /metrics.
UI: embedded html/template pages (login, first-run setup, dashboard,
system, users, account, audit, interpretation boundary), light/dark
theme, dependency-free app.js with CSRF-aware fetch and SSE helper.
Binaries: noemad with graceful shutdown, migrations at start, bootstrap
admin from environment, supervised background tasks; noemactl with
status, migrate, user create and meaningful exit codes.
- config: defaults -> JSON file -> NOEMA_* env with validation and redaction
- observability: slog with secret-redacting handler; Prometheus registry with
the metric set from the plan (events, workspace, memory, LLM, tools, queues)
- scheduler: supervised Group and bounded Pool so no goroutine is unowned
- storage: pgx pool with statement timeout, embedded migration runner
(advisory lock, checksums, up/down/status), per-test schema helper
- migrations/0001: append-only guard trigger, users, sessions, api tokens,
recovery codes, audit log, settings
- pkg/ids: UUIDv7 with monotonic counter