Stage 1 — Foundation:
- Target validation: iptables + systemctl/docker target-type gates
- Async run for long-running commands (sleep/wait/poll loops)
- audit_log.session_id plumbing (SQL, sqlcgen, 18 call sites)
Stage 2 — External agent observe (11 tools):
- get_dashboard_summary, get_ontology, list_checks, list_executions
- get_knowledge_revisions, get_knowledge_duplicates, get_knowledge_orphans
- list_knowledge_tags, list_entity_sessions, find_entities_by
- 3 resource templates: oikos://entity/{slug}, knowledge/{id}, execution/{id}
Stage 3 — Nomos reliability:
- complete_task(success) refused without verification (upgraded from warn)
- sessionHasPlan excludes replaced steps (forces propose_plan after reopen)
- Bash syntax validation in run() (rejects literal \n, flag-space typos)
- Scope gate in SOUL.md (ask before pivoting to unrelated subsystem)
Stage 4 — External agent act (9 mutation tools):
- ack_signal, resolve_signal, mute_signal, cancel_execution
- update_check, delete_knowledge, restore_knowledge
- merge_knowledge, rename_knowledge_tag
67 lines
1.8 KiB
Go
67 lines
1.8 KiB
Go
package observability
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"github.com/dtoro/oikos/internal/db/sqlcgen"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// Audit writes an audit_log entry. Pass a transaction-bound Queries so the
|
|
// entry commits or rolls back atomically with the state change it records.
|
|
//
|
|
// actorLabel is the interim textual actor identity ("operator:dev",
|
|
// "agent:mcp") recorded in detail; actor_id (a Person/Agent entity UUID)
|
|
// starts being populated when OIDC identity resolution lands.
|
|
func Audit(ctx context.Context, q *sqlcgen.Queries, actorType, actorLabel,
|
|
action string, entityID *uuid.UUID, method, path, correlationID string,
|
|
sessionID *uuid.UUID, detail map[string]any) error {
|
|
|
|
if detail == nil {
|
|
detail = map[string]any{}
|
|
}
|
|
detail["actor"] = actorLabel
|
|
detailJSON, _ := json.Marshal(detail)
|
|
|
|
var corr *string
|
|
if correlationID != "" {
|
|
corr = &correlationID
|
|
}
|
|
return q.InsertAuditEntry(ctx, sqlcgen.InsertAuditEntryParams{
|
|
ActorType: actorType,
|
|
Action: action,
|
|
EntityID: entityID,
|
|
Method: &method,
|
|
Path: &path,
|
|
Detail: detailJSON,
|
|
CorrelationID: corr,
|
|
SessionID: sessionID,
|
|
})
|
|
}
|
|
|
|
// Event emits a structured event in the caller's transaction (SG10). The
|
|
// post-commit NOTIFY trigger (migration 008) fans it out to SSE subscribers.
|
|
func Event(ctx context.Context, q *sqlcgen.Queries, eventType string,
|
|
entityID *uuid.UUID, severity, source string, correlationID string,
|
|
data map[string]any) error {
|
|
|
|
dataJSON, _ := json.Marshal(data)
|
|
if data == nil {
|
|
dataJSON = []byte("{}")
|
|
}
|
|
var corr *string
|
|
if correlationID != "" {
|
|
corr = &correlationID
|
|
}
|
|
_, err := q.InsertEvent(ctx, sqlcgen.InsertEventParams{
|
|
Type: eventType,
|
|
EntityID: entityID,
|
|
Severity: severity,
|
|
Source: source,
|
|
Data: dataJSON,
|
|
CorrelationID: corr,
|
|
})
|
|
return err
|
|
}
|