e8e230b4a5
nomos+web: streaming, provider routing, event gap-fill, embedded UI; fix approval FK & session context
...
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
Agent (cmd/nomos):
- Stream LLM tokens via NewStreaming; emit text_delta then final text.
- OpenRouter provider routing: data_collection=deny (ZDR) + require_parameters;
NOMOS_PROVIDER_SORT opt-in; Exacto via model suffix.
- Multi-turn: reload session history into context; UI passes session id.
- Fix agent_activity logging (agent_id/session_id) and mcpClient data race.
Events (live control-room feed):
- approval.created (mcp), approval.decided (api), execution.completed/failed
(approved-action path), signal.raised/resolved + health.changed (scheduler,
transition-gated).
Fixes:
- createApproval FK violation (reuse execution entity) — the agent's only
write path; log the previously-swallowed errors.
Web UI:
- Embed web/dist via //go:embed (single binary); Dockerfile builds SPA into
the Go stage; committed .gitkeep placeholder keeps backend-only builds green.
- Caddy: Authentik-gated /agent/* -> nomos so the UI reaches the agent
same-origin in production.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-07-08 15:22:27 +02:00
2b3aa248b1
N0: rename Hermes → Nomos (standalone commit)
...
Problem: "Hermes" collides with Nous Researchs unrelated product;
unclear identity for the resident agent.
Change: Rename the live service identity across 39 files:
- cmd/hermes/ → cmd/nomos/ (binary, env vars NOMOS_*)
- internal/config/ server.go (NomosAgentSlug, nomosAgentID)
- compose/hermes/ → compose/nomos/ (Dockerfile, service name)
- hermes/ → nomos/ (SOUL.md, config.yaml, skills/)
- .agents/HERMES.md → NOMOS.md (persona)
- tools/setup-hermes-soul.sh → setup-nomos-soul.sh
- seeds/inventory.yaml (agent:hermes → agent:nomos)
- migrations/014_rename_agent_hermes_to_nomos.up.sql
- Caddy vhost hermes.hubris.network → nomos.hubris.network
- All referencing docs, scripts, ADR notes
History preserved: archive/, plans/done/, ADRs not rewritten.
Matrix @hermes notifier account and Legacy bin/hermes on LXC 129
intentionally untouched (out of scope).
Risk: N0 is identity-only rename; zero behavioral changes.
Verification: go build ./... passes; docker compose --profile full
resolves nomos service; grep -ri hermes (excluding archive/plans)
returns only intentional refs (LLM model name, Matrix user).
2026-07-08 14:14:56 +02:00
7c6cffb5f5
complete MCP tool surface — Matrix approval webhook loop + token verification
...
Plan #6 (MCP Tool Completion / bin/homelab Migration) done.
- Approval records created for gated request_execution actions
- Notifier sends Matrix messages with HMAC approval tokens
- Stores matrix_event_id, polls /relations/{id}/m.annotation for ✅ /❌
- Reaction detection triggers DecideApproval API call
- Token verification added to DecideApproval endpoint
- Migration 013: matrix_event_id + alert_sent_at on approvals
- AGENTS.md: 21-tool surface documented, stale homelab CLI refs removed
- Plan index updated, audit cross-reference refreshed
2026-07-08 11:02:06 +02:00
5b22f2367b
test: e2e client lifecycle + ADRs with sequence diagrams
...
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
- client_lifecycle_test.go: full end-to-end integration test
planned → provisioning (enroll) → active → migrating → active →
deprecated → failed. Validates age keypair generation, attrs,
context/secrets endpoints, invalid transition blocking, compute
entity provisioning with relationship edges and status tracking.
Also tests enrollment rejection for invalid states and duplicate
slug rejection for provisioning.
- adr/0011-client-lifecycle-flows.md: workstation self-enrollment,
compute entity provisioning, deprecation/destruction flows with
Mermaid sequence diagrams. Full lifecycle state diagram. Transition
check enforcement documentation.
- adr/0012-hermes-oikos-interactions.md: Hermes ↔ Oikos interaction
flow through OODA loop phases. Thin client bootstrap. Internal
component interactions (scheduler, actuator, notifier). Complete
30-tool ownership matrix.
- Fix: migration 012 FK reference (executions.id → executions.entity_id)
- Fix: provision handler null attributes JSONB
- Fix: provisioning steps use entity_id for execution FK
All 3 integration tests pass, go vet clean.
2026-07-08 00:56:16 +02:00
44e1e421e1
feat: client enrollment API and compute entity provisioning
...
Phase 1 implementation from the client-lifecycle plan.
- Migration 012: provisioning_steps table, context_version, context_files,
enrolled_at column, slug+type index for machine entities
- API endpoints (openapi.yaml + generated code):
POST /clients/enroll — age key issuance, Infisical identity, state transition
GET /clients/{slug}/context — agent file delta polling (replaces git pull)
GET /clients/{slug}/secrets — scoped secret listing
POST /entities/provision — compute entity creation with constraint validation
GET /entities/{slug}/provision/status — step-by-step provisioning progress
- Handlers in impl.go: enrollment with state validation and age key generation,
provisioning with execution tracking and relationship creation,
context endpoint with since-based delta queries
- Server struct extended with secretsBackend interface for key storage
- All tests pass, build clean
2026-07-08 00:24:32 +02:00
6b75f7302d
db as source of truth: wiki→seeds, archive old artifacts, knowledge ingestion
...
- Migrations 010 (content_hash) + 011 (search tsvector column)
- new: internal/knowledge/seed.go — knowledge seed ingest engine
- new: internal/httpapi/knowledge.go — SearchKnowledge + GetEntityKnowledge
- wire knowledge ingest into oikos seed pipeline
- convert all 36 wiki docs + 6 investigations + 12 runbooks → seeds/knowledge.yaml
- archive: knowledge/wiki/→archive/, oikos/cards/→archive/, .hermes/plans/→archive/
- delete: 9 superseded Python kernel files, ledger/, mcp/build_host_files.py
- remove empty knowledge/ directory tree
2026-07-07 20:22:30 +02:00
095a3967c4
phase 3: control loop — scheduler, actuator, learning, notifier, policy, API endpoints
...
Implemented the full OODA control loop:
Scheduler:
- Check_defs runner with bounded worker pool (errgroup)
- Signal dedup via partial unique index (UpsertSignal)
- Recovery auto-resolves open signals
- Metrics writing (InsertMetricSample) and entity_status updates
- Housekeeping (idempotency-key prune)
- Graceful shutdown via ctx cancellation
Actuator:
- Auto-act signal consumer with FOR UPDATE SKIP LOCKED pattern
- Per-target serialization with pg_advisory_xact_lock
- Circuit breaker per target host (N consecutive failures → open)
- Autonomy kill-switch (global.auto_act, never_auto_act.<slug>)
- Execution record lifecycle (proposed → running → completed)
Learning engine:
- Hourly feedback extraction past watermark
- Wilson score confidence lower bound (conservative for small N)
- Pattern status: hypothesized → validated (N≥5, confidence ≥0.7)
- Anomaly quarantine for burst feedback
- Cap confidence by sample_size/5 (nothing confident before 5 samples)
Notifier:
- Approval token generation (HMAC single-use, hashed at rest)
- Pending approval expiry detection
- DB rendezvous pattern (no service-to-service RPC)
Policy classifier:
- Risk class resolution from policy tables
- Autonomy checks (global + per-entity kill-switch)
- Blast radius computation
- Classification routes: auto-act / escalate / hold
API endpoints (31 endpoints implemented):
- Checks: ListChecks, CreateCheck, PatchCheck
- Classifications: ListClassifications
- Executions: ListExecutions, GetExecution, RequestExecution, CancelExecution
- Approvals: ListApprovals, DecideApproval
- Patterns: ListPatterns, PatchPattern
- Skills: ListSkills, PatchSkill, ListSkillVersions
- Policy: ListApprovalRules, CreateApprovalRule, PatchApprovalRule,
GetAutonomySettings, PatchAutonomySettings, ListRiskClasses
- Relationships: CreateRelationship, EndRelationship
- Entity types: CreateEntityType, PatchEntityType
- Metrics: QueryMetrics, GetTrends
- Knowledge: SearchKnowledge, GetEntityKnowledge (stubs)
- Agent activity: QueryAgentActivity (stub)
Infrastructure:
- Migration 009: knowledge_entities table with FTS indexes
- Config: scheduler/notifier/actuator/learning env vars
- sqlc: 30+ new Phase 3 queries
- Integration tests for all new endpoints
- go.sum updated with golang.org/x/sync
2026-07-07 15:19:25 +02:00
c9975d60a5
phase 2 (part 2): sqlc queries, audit/event helpers, event NOTIFY trigger
...
- sqlc.yaml + internal/db/queries/*.sql: typed queries for entities,
relationships, ontology, operations (signals, events, audit,
idempotency, entity_status)
- internal/db/sqlcgen/: generated Go from sqlc (pgx/v5)
- internal/observability/record.go: Audit() and Event() helpers that
write in the caller's transaction (SG10). actorLabel is interim text
identity in detail JSON until OIDC resolution lands; actor_id column
exists but is not yet populated
- migrations/008: post-commit pg_notify trigger on events table for
SSE fan-out (SG8/SG10)
2026-07-07 08:49:59 +02:00
1b04683639
phase 1 review fixes: dedup edges, real export, validation, tests
...
Review of aa2ca0a found and fixed:
- re-ingest duplicated ALL edges (upsert conflicted on valid_from=now(),
never fired) — migration 007 dedupes + partial unique index on current
edges; upsert now targets it. Regression-tested.
- export was a stub that overwrote seeds/*.yaml with 11-byte "version: 1"
files — implemented real deterministic export (ontology/inventory/policy,
cognition-layer excluded); round-trip is byte-stable (tested)
- DB password leaked in startup logs (slog JSON bypasses String()) —
Config now implements slog.LogValuer; regression-tested
- docker-compose had literal '***' as DB password — env-interpolated
- uuid.New() (v4) → uuid.NewV7() per ADR-0005
- no ontology validation on ingest — internal/ontology TypeTree: abstract
instantiation rejected, relationship endpoints hierarchy-validated,
cardinality enforced in-transaction, lifecycle states checked, default
state applied (Phase 1 gate items, R3-1)
- getOrCreateEntityID swallowed non-ErrNoRows errors
- migration runner now holds a session advisory lock on one connection
- Makefile: hardcoded /opt/homebrew/bin/go → go; test-db target
Tests: 4 unit suites + 7 integration tests (env-guarded, throwaway DB per
run): migrate idempotent, seed idempotent + no dup edges, abstract/edge/
cardinality rejection, blast_radius cycle termination, export round-trip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-07-07 08:11:01 +02:00
aa2ca0ae6f
phase 1: Go foundation — module, migrations, domain, seed ingest
...
Core deliverables:
- Go module github.com/dtoro/oikos (Go 1.26.3)
- cmd/oikos: single binary with role subcommands (migrate, seed, export)
- 6 SQL migrations: ontology meta-schema, entity instances (UUID+slug,
blast_radius recursive function), operations (signals/checks/approvals),
cognition (classifications/executions/feedback/patterns/skills), policy,
observability (TimescaleDB hypertables + CAGGs + retention)
- Domain layer: entity, signal, execution, classification, pattern, skill,
approval, check types + 11 sentinel errors + lifecycle state machines
- DB layer: pgx pool, SQL splitter (handles 94436 and -- comments), migration
runner, seed ingest (ontology+inventory+policy) with content-hash dedup
- Config: env-based with defaults, secrets redaction
- Observability: slog JSON logger with debug mode
- Infrastructure: Makefile, docker-compose.yml, multi-stage Dockerfile
(distroless, CGO_ENABLED=0)
Verified end-to-end against timescale/timescaledb:2.17.2-pg16:
- 6 migrations applied (65 SQL statements)
- Seeds ingested: 6 lifecycles, 59 entity types, 46 relationship types,
111 entities, 144 relationships, 4 risk classes, 27 approval rules,
9 autonomy settings
- Idempotent: second seed run is a no-op (content hash matches)
Bugs fixed during implementation:
- TimescaleDB CAGGs can't run in a transaction -> splitSQL() executes
statements individually
- Semicolons in -- comments treated as separators -> comment handling
- YAML keys source/target didn't match code's source_type/target_type
- yaml.Marshal produced YAML for JSONB columns -> json.Marshal
2026-07-07 01:07:26 +02:00