Documentation
¶
Index ¶
- Constants
- func CaseStatusIDFor(label string) int
- func CaseStatusLabelFor(statusID int) string
- func CaseStatuses() []string
- func DefaultRoleFor(memberType string) string
- func IsBriefingNote(n Note) bool
- type ActiveCases
- type AuditEntry
- type Briefing
- type Case
- type CaseCounts
- type CaseIndicator
- type CaseMember
- type CaseTicket
- type Enrichment
- type Event
- type EventFilter
- type Finding
- func (f Finding) AttackTechniques() []string
- func (f Finding) Attacks() []ocsf.Attack
- func (f Finding) ClassName() string
- func (f Finding) Evidences() []ocsf.Evidence
- func (f Finding) IsOpen() bool
- func (f Finding) RelatedEvents() []ocsf.RelatedEvent
- func (f Finding) StatusName() string
- func (f Finding) VerdictName() string
- type FindingFilter
- type FindingSort
- type Hypothesis
- type NextAction
- type Note
- type Observable
- type OpenFindings
- type SavedRecord
- type Store
- func (s *Store) AcceptSummary(ctx context.Context, caseID, author, summary string) error
- func (s *Store) AddAuditEntry(ctx context.Context, entry AuditEntry) error
- func (s *Store) AddCaseMember(ctx context.Context, m CaseMember) error
- func (s *Store) AddCaseMembers(ctx context.Context, members []CaseMember) error
- func (s *Store) AddCaseTicket(ctx context.Context, t CaseTicket) error
- func (s *Store) AddHypothesis(ctx context.Context, caseID, author string, h Hypothesis) error
- func (s *Store) AddNextAction(ctx context.Context, caseID, author string, a NextAction) error
- func (s *Store) AddNote(ctx context.Context, note Note) (string, error)
- func (s *Store) ApplyEnrichment(ctx context.Context, eventID string, enrichment Enrichment) error
- func (s *Store) AssignEventToCase(ctx context.Context, eventID, caseID string) error
- func (s *Store) AssignFindingToCase(ctx context.Context, findingID, caseID string) error
- func (s *Store) Close() error
- func (s *Store) CountActiveCases(ctx context.Context) (ActiveCases, error)
- func (s *Store) CountCaseMembers(ctx context.Context, caseID string) (CaseCounts, error)
- func (s *Store) CountEvents(ctx context.Context, f EventFilter) (int, error)
- func (s *Store) CountEventsByObservable(ctx context.Context, typeID int, value string) (int, error)
- func (s *Store) CountEventsFiltered(ctx context.Context, caseID string, start, end time.Time, severities []string, ...) (int, error)deprecated
- func (s *Store) CountEventsToday(ctx context.Context, now time.Time) (int, error)
- func (s *Store) CountFindings(ctx context.Context, f FindingFilter) (int, error)
- func (s *Store) CountFindingsByObservable(ctx context.Context, typeID int, value string) (int, error)
- func (s *Store) CountObservables(ctx context.Context) (int, error)
- func (s *Store) CountOpenFindings(ctx context.Context) (OpenFindings, error)
- func (s *Store) CreateOrUpdateCase(ctx context.Context, case_ Case) (string, error)
- func (s *Store) DeleteCaseAndUnassign(ctx context.Context, caseID string) error
- func (s *Store) DeleteEvents(ctx context.Context, ids []string) error
- func (s *Store) DeleteFindings(ctx context.Context, ids []string) error
- func (s *Store) DeleteNote(ctx context.Context, noteID string) error
- func (s *Store) FindEventsByObservable(ctx context.Context, typeID int, value string, limit int) ([]Event, error)
- func (s *Store) FindFindingsByObservable(ctx context.Context, typeID int, value string, limit int) ([]Finding, error)
- func (s *Store) GetAllEvents(ctx context.Context, limit int) ([]Event, error)
- func (s *Store) GetAuditEntries(ctx context.Context, caseID string, limit int) ([]AuditEntry, error)
- func (s *Store) GetBriefing(ctx context.Context, caseID string) (Briefing, error)
- func (s *Store) GetCase(ctx context.Context, caseID string) (*Case, error)
- func (s *Store) GetCaseEventMembers(ctx context.Context, caseID string) ([]Event, error)
- func (s *Store) GetCaseFindings(ctx context.Context, caseID string) ([]Finding, error)
- func (s *Store) GetCaseIndicators(ctx context.Context, caseID string) ([]CaseIndicator, error)
- func (s *Store) GetCaseMembers(ctx context.Context, caseID string) ([]CaseMember, error)
- func (s *Store) GetCaseTickets(ctx context.Context, caseID string) ([]CaseTicket, error)
- func (s *Store) GetCasesForMember(ctx context.Context, memberType, memberID string) ([]Case, error)
- func (s *Store) GetEnrichmentsByEvent(ctx context.Context, eventID string) ([]Enrichment, error)
- func (s *Store) GetEvents(ctx context.Context, f EventFilter) ([]Event, error)
- func (s *Store) GetEventsByCase(ctx context.Context, caseID string) ([]Event, error)
- func (s *Store) GetEventsByTimeRange(ctx context.Context, caseID string, start, end time.Time, limit int) ([]Event, error)
- func (s *Store) GetEventsFiltered(ctx context.Context, caseID string, start, end time.Time, severities []string, ...) ([]Event, error)deprecated
- func (s *Store) GetFindingByUID(ctx context.Context, findingUID string) (*Finding, error)
- func (s *Store) GetFindings(ctx context.Context, f FindingFilter) ([]Finding, error)
- func (s *Store) GetLastEvent(ctx context.Context) (time.Time, bool, error)
- func (s *Store) GetNotes(ctx context.Context, caseID string) ([]Note, error)
- func (s *Store) GetObservablesByEvent(ctx context.Context, eventID string) ([]Observable, error)
- func (s *Store) GetObservablesByFinding(ctx context.Context, findingID string) ([]Observable, error)
- func (s *Store) GetObservablesForEvents(ctx context.Context, eventIDs []string) (map[string][]Observable, error)
- func (s *Store) GetPinnedMemberIDs(ctx context.Context, caseID, memberType string) (map[string]bool, error)
- func (s *Store) GetPriorityQueue(ctx context.Context, limit int) ([]Finding, error)
- func (s *Store) GetRecentCases(ctx context.Context, limit int) ([]Case, error)
- func (s *Store) ListCases(ctx context.Context) ([]Case, error)
- func (s *Store) LogCaseAction(ctx context.Context, caseID, action, actor string, ...) error
- func (s *Store) LogCopilotQuery(ctx context.Context, caseID, actor, query, response string, tokens int, ...) error
- func (s *Store) LogEventAction(ctx context.Context, caseID, eventID, action, actor string, ...) error
- func (s *Store) OnEnrichment(fn func(eventID string))
- func (s *Store) RefreshCaseCounts(ctx context.Context, caseID string) error
- func (s *Store) RemoveCaseMember(ctx context.Context, caseID, memberType, memberID string) error
- func (s *Store) SaveEvent(ctx context.Context, ocsfEvent *ocsf.Event) (string, error)
- func (s *Store) SaveFinding(ctx context.Context, f *ocsf.Finding) (string, error)
- func (s *Store) SaveRecord(ctx context.Context, rec ingestRecord) (SavedRecord, error)
- func (s *Store) SearchEvents(ctx context.Context, query string, limit int) ([]Event, error)
- func (s *Store) SetHypotheses(ctx context.Context, caseID, author string, hs []Hypothesis) error
- func (s *Store) SetMemberPinned(ctx context.Context, caseID, memberType, memberID string, pinned bool) error
- func (s *Store) SetNextActions(ctx context.Context, caseID, author string, actions []NextAction) error
- func (s *Store) SetStatement(ctx context.Context, caseID, author, text string) error
- func (s *Store) SetupAuditTables() error
- func (s *Store) UpdateCaseEventCount(ctx context.Context, caseID string) error
- func (s *Store) UpdateCaseStatus(ctx context.Context, caseID string, statusID int) error
- func (s *Store) UpdateCaseTriage(ctx context.Context, caseID string, priorityID, impactID int, ...) error
- func (s *Store) UpdateCaseVerdict(ctx context.Context, caseID string, verdictID int) error
- func (s *Store) UpdateFindingStatus(ctx context.Context, findingID string, statusID int) error
- func (s *Store) UpdateFindingVerdict(ctx context.Context, findingID string, verdictID int) error
Constants ¶
const ( NoteTypeStatement = "statement" NoteTypeHypothesis = "hypothesis" NoteTypeNextAction = "next_action" NoteTypeSummary = "summary" )
Briefing note types. These occupy Note.LinkedType, alongside the existing "summary" and the "event"/"ioc" links a note can carry.
const ( ConfidenceConfirmed = "confirmed" ConfidenceLikely = "likely" ConfidenceOpen = "open" )
Confidence levels a hypothesis can carry.
const ( MemberTypeFinding = "finding" MemberTypeEvent = "event" // RoleMember is what the case is about — normally its findings. RoleMember = "member" // RoleEvidence is what supports it — normally raw events pulled in during // investigation. RoleEvidence = "evidence" )
Case member kinds and roles.
The role distinction is the point of this table. A case is *about* its findings (members) and *supported by* its events (evidence). Collapsing both into one undifferentiated pile makes a case with 4 detections and 300 corroborating log lines look like 304 equally important things.
const ( CaseStatusOpen = "open" CaseStatusInvestigating = "investigating" CaseStatusContained = "contained" CaseStatusResolved = "resolved" CaseStatusClosed = "closed" )
Case status labels. These are what the UI shows and what the status column stores; status_id is the OCSF Incident Finding status the case projects onto.
"contained" is not an OCSF status — it maps onto On Hold, which is the closest OCSF equivalent for "action taken, not yet finished". "resolved" is new: OCSF distinguishes Resolved (dealt with) from Closed (filed away), and the app previously had no way to say the former.
const ( ObservableSourceAsserted = "asserted" ObservableSourceDerived = "derived" )
Observable provenance. OCSF observables are assertions made by the producer; anything Console-IR works out for itself is recorded separately so an analyst can tell a vouched-for indicator from an inferred one.
Variables ¶
This section is empty.
Functions ¶
func CaseStatusIDFor ¶ added in v0.2.0
CaseStatusIDFor resolves a status label to its OCSF status_id, defaulting to New for anything unrecognised.
func CaseStatusLabelFor ¶ added in v0.2.0
CaseStatusLabelFor resolves an OCSF status_id to the app's label.
func CaseStatuses ¶ added in v0.2.0
func CaseStatuses() []string
CaseStatuses lists the selectable case statuses in lifecycle order.
func DefaultRoleFor ¶ added in v0.2.0
DefaultRoleFor returns the role a member type takes unless overridden.
func IsBriefingNote ¶ added in v0.2.0
IsBriefingNote reports whether a note holds briefing content rather than analyst prose. The notes tab must exclude these, or the decision log fills with fragments of the briefing.
Types ¶
type ActiveCases ¶ added in v0.2.0
type ActiveCases struct {
Total int
// Investigating counts cases in progress, which is the number the card
// shows: "3 active, 1 investigating" tells an analyst where the work is.
Investigating int
// OldestOpened is when the longest-running active case was created. Zero
// when there are no active cases.
OldestOpened time.Time
}
ActiveCases is the active-cases metric card.
type AuditEntry ¶
type AuditEntry struct {
ID string `json:"id"`
CaseID string `json:"case_id"`
EventID string `json:"event_id,omitempty"`
Action string `json:"action"` // "create_case", "assign_event", "copilot_query", "note_added", etc.
Actor string `json:"actor"` // user or system identifier
Details map[string]interface{} `json:"details"` // action-specific data
Metadata map[string]string `json:"metadata"` // tokens, cost, etc.
Timestamp time.Time `json:"timestamp"`
CreatedAt time.Time `json:"created_at"`
}
AuditEntry represents an audit log entry
type Briefing ¶ added in v0.2.0
type Briefing struct {
Statement string
Hypotheses []Hypothesis
NextActions []NextAction
// Summary is generated text. It is kept apart from the rest so nothing can
// mistake it for something an analyst wrote.
Summary string
SummaryAt time.Time
HasSummary bool
}
Briefing is a case's narrative content.
type Case ¶
type Case struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Severity string `json:"severity"`
Status string `json:"status"`
AssignedTo string `json:"assigned_to,omitempty"`
// OCSF incident-profile fields. StatusID is the authoritative lifecycle
// state; Status is its display label.
StatusID int `json:"status_id,omitempty"`
VerdictID int `json:"verdict_id,omitempty"`
PriorityID int `json:"priority_id,omitempty"`
ImpactID int `json:"impact_id,omitempty"`
IsSuspectedBreach bool `json:"is_suspected_breach,omitempty"`
AssigneeGroup string `json:"assignee_group,omitempty"`
// EventCount is supporting evidence; FindingCount is what the case is about.
EventCount int `json:"event_count"`
FindingCount int `json:"finding_count"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Case represents an incident case.
A Case is an application concept — OCSF has no Case object. Its projection into the schema is an Incident Finding (class_uid 2005), which is why it carries the incident-profile fields below.
func (Case) VerdictName ¶ added in v0.2.0
VerdictName resolves the case verdict caption, empty when none is set.
type CaseCounts ¶ added in v0.2.0
CaseCounts summarizes what a case holds.
type CaseIndicator ¶ added in v0.2.0
type CaseIndicator struct {
TypeID int
Type string
Value string
// Source is "asserted" when any sighting came from the producer, and
// "derived" only when every sighting was inferred here. An analyst defends
// those two differently, so the distinction survives aggregation.
Source string
Sightings int
FirstSeen time.Time
LastSeen time.Time
}
CaseIndicator is one observable aggregated across everything a case holds.
type CaseMember ¶ added in v0.2.0
type CaseMember struct {
CaseID string `json:"case_id"`
MemberType string `json:"member_type"`
MemberID string `json:"member_id"`
Role string `json:"role"`
AddedBy string `json:"added_by,omitempty"`
AddedAt time.Time `json:"added_at"`
}
CaseMember links a case to a finding or an event.
type CaseTicket ¶ added in v0.2.0
type CaseTicket struct {
ID string `json:"id"`
CaseID string `json:"case_id"`
UID string `json:"uid"`
Title string `json:"title,omitempty"`
Type string `json:"type,omitempty"`
SrcURL string `json:"src_url,omitempty"`
Status string `json:"status,omitempty"`
}
CaseTicket links a case to an external tracking system, mirroring the OCSF `tickets` attribute on the incident profile.
type Enrichment ¶
type Enrichment struct {
ID string `json:"id"`
EventID string `json:"event_id"`
Source string `json:"source"`
Type string `json:"type"`
Data map[string]string `json:"data"`
CreatedAt time.Time `json:"created_at"`
}
Enrichment represents event enrichment data
type Event ¶
type Event struct {
ID string `json:"id"`
CaseID string `json:"case_id,omitempty"`
// OCSF identity. class_uid is authoritative; EventType is the coarse
// category grouping derived from it for display and filtering.
ClassUID int `json:"class_uid,omitempty"`
CategoryUID int `json:"category_uid,omitempty"`
ActivityID int `json:"activity_id,omitempty"`
TypeUID int `json:"type_uid,omitempty"`
SeverityID int `json:"severity_id,omitempty"`
MetadataUID string `json:"metadata_uid,omitempty"`
Timestamp time.Time `json:"timestamp"`
EventType string `json:"event_type"`
Severity string `json:"severity"`
Message string `json:"message"`
Host string `json:"host,omitempty"`
SrcIP string `json:"src_ip,omitempty"`
DstIP string `json:"dst_ip,omitempty"`
SrcPort int `json:"src_port,omitempty"`
DstPort int `json:"dst_port,omitempty"`
ProcessName string `json:"process_name,omitempty"`
FileName string `json:"file_name,omitempty"`
FileHash string `json:"file_hash,omitempty"`
UserName string `json:"user_name,omitempty"`
RawJSON string `json:"raw_json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Event represents a stored event
func (Event) CategoryName ¶ added in v0.2.0
CategoryName returns the OCSF caption for the event's category.
type EventFilter ¶ added in v0.2.0
type EventFilter struct {
CaseID string
Start time.Time
End time.Time
// Severities matches events.severity (case-insensitive).
Severities []string
// Categories matches events.event_type, the coarse OCSF category slug.
Categories []string
// Classes matches events.class_uid exactly, e.g. 2004 for Detection Findings.
Classes []int
Limit int
Offset int
}
EventFilter describes the optional constraints applied to an events query. The zero value matches everything.
type Finding ¶ added in v0.2.0
type Finding struct {
ID string `json:"id"`
FindingUID string `json:"finding_uid"`
CaseID string `json:"case_id,omitempty"`
ClassUID int `json:"class_uid"`
CategoryUID int `json:"category_uid"`
ActivityID int `json:"activity_id,omitempty"`
TypeUID int `json:"type_uid,omitempty"`
Title string `json:"title"`
Message string `json:"message,omitempty"`
AnalyticName string `json:"analytic_name,omitempty"`
AnalyticUID string `json:"analytic_uid,omitempty"`
Status string `json:"status,omitempty"`
StatusID int `json:"status_id"`
Verdict string `json:"verdict,omitempty"`
VerdictID int `json:"verdict_id,omitempty"`
Severity string `json:"severity,omitempty"`
SeverityID int `json:"severity_id,omitempty"`
ConfidenceID int `json:"confidence_id,omitempty"`
RiskLevelID int `json:"risk_level_id,omitempty"`
RiskScore int `json:"risk_score,omitempty"`
ImpactID int `json:"impact_id,omitempty"`
PriorityID int `json:"priority_id,omitempty"`
IsAlert bool `json:"is_alert,omitempty"`
IsSuspectedBreach bool `json:"is_suspected_breach,omitempty"`
Assignee string `json:"assignee,omitempty"`
MetadataUID string `json:"metadata_uid,omitempty"`
FirstSeen time.Time `json:"first_seen"`
LastSeen time.Time `json:"last_seen"`
CreatedTime time.Time `json:"created_time,omitempty"`
AttacksJSON string `json:"attacks_json,omitempty"`
EvidencesJSON string `json:"evidences_json,omitempty"`
RelatedEventsJSON string `json:"related_events_json,omitempty"`
FindingInfoListJSON string `json:"finding_info_list_json,omitempty"`
RawJSON string `json:"raw_json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Finding is a stored OCSF Findings-category record — an analytic's conclusion, with a lifecycle. Unlike an event, a finding is mutable: it is keyed on FindingUID and updated in place as the producer revises it.
func (Finding) AttackTechniques ¶ added in v0.2.0
AttackTechniques returns the ATT&CK technique identifiers on the finding, for compact display (e.g. "T1059.001"). Sub-techniques win over their parent.
func (Finding) ClassName ¶ added in v0.2.0
ClassName returns the OCSF caption for the finding's class.
func (Finding) IsOpen ¶ added in v0.2.0
IsOpen reports whether the finding still needs analyst attention.
func (Finding) RelatedEvents ¶ added in v0.2.0
func (f Finding) RelatedEvents() []ocsf.RelatedEvent
RelatedEvents decodes the events the analytic examined — the documented route from a finding back to the telemetry that produced it.
func (Finding) StatusName ¶ added in v0.2.0
StatusName resolves the lifecycle status caption for the finding's class.
func (Finding) VerdictName ¶ added in v0.2.0
VerdictName resolves the analyst verdict caption, empty when none is set.
type FindingFilter ¶ added in v0.2.0
type FindingFilter struct {
CaseID string
// Statuses matches status_id.
Statuses []int
// Severities matches the severity label (case-insensitive).
Severities []string
// Classes matches class_uid, e.g. 2004 for Detection Findings.
Classes []int
// OpenOnly restricts to findings still needing attention.
OpenOnly bool
Search string
// MinSeverityID keeps findings at or above a severity. Fatal counts as
// critical; Other (99) is a sentinel and never satisfies a minimum.
MinSeverityID int
// SeenAfter and SeenBefore bound last_seen. SeenAfter backs "last 24h";
// SeenBefore backs "stale", which is the same field read the other way.
SeenAfter time.Time
SeenBefore time.Time
// Assignee matches the owner exactly. Empty matches every owner.
Assignee string
// HasObservables keeps only findings carrying at least one indicator.
HasObservables bool
// Sort selects the ordering. The zero value keeps the historical
// most-recently-seen-first, so existing callers are unaffected.
Sort FindingSort
Limit int
Offset int
}
FindingFilter describes optional constraints on a findings query. The zero value matches everything.
type FindingSort ¶ added in v0.2.0
type FindingSort int
FindingSort names an ordering for a findings query.
const ( // SortRecent is most recently seen first. SortRecent FindingSort = iota // SortPriority is the triage ordering: the same sequence the Analyst Home // priority queue applies, so the queue and its full version agree about // which finding matters most. SortPriority )
type Hypothesis ¶ added in v0.2.0
Hypothesis is a belief about the incident and how sure the analyst is.
type NextAction ¶ added in v0.2.0
NextAction is one item on the case's checklist.
type Note ¶
type Note struct {
ID string `json:"id"`
CaseID string `json:"case_id"`
Content string `json:"content"`
Author string `json:"author"`
Color string `json:"color,omitempty"` // hex color e.g. "#f1c40f"
LinkedType string `json:"linked_type,omitempty"` // "event", "ioc", or ""
LinkedID string `json:"linked_id,omitempty"` // event_id or ioc unique value
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Note represents a case note (enhanced for sticky notes and linking)
type Observable ¶ added in v0.2.0
type Observable struct {
ID string `json:"id"`
EventID string `json:"event_id,omitempty"`
FindingID string `json:"finding_id,omitempty"`
TypeID int `json:"type_id"`
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Value string `json:"value"`
Source string `json:"source"`
Reputation *ocsf.Reputation `json:"reputation,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
Observable is a stored OCSF observable — the pivot element that makes "have I seen this indicator before?" an indexed lookup rather than a scan.
func (Observable) IsAsserted ¶ added in v0.2.0
func (o Observable) IsAsserted() bool
IsAsserted reports whether the producer supplied this observable, as opposed to Console-IR deriving it from the event's fields.
type OpenFindings ¶ added in v0.2.0
OpenFindings is the open-findings metric card.
type SavedRecord ¶ added in v0.2.0
SavedRecord reports what SaveRecord persisted.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store represents the SQLite storage implementation
func (*Store) AcceptSummary ¶ added in v0.2.0
AcceptSummary promotes a generated summary into an ordinary authored note.
This is the only route from generated text into the case record, and it takes a deliberate action. The summary itself stays where it was: accepting it is the analyst saying "this is now my words", not a move.
func (*Store) AddAuditEntry ¶
func (s *Store) AddAuditEntry(ctx context.Context, entry AuditEntry) error
AddAuditEntry adds an audit entry to the database
func (*Store) AddCaseMember ¶ added in v0.2.0
func (s *Store) AddCaseMember(ctx context.Context, m CaseMember) error
AddCaseMember links one finding or event to a case.
func (*Store) AddCaseMembers ¶ added in v0.2.0
func (s *Store) AddCaseMembers(ctx context.Context, members []CaseMember) error
AddCaseMembers links several items to a case in one transaction.
The legacy single-case column is written alongside for compatibility with readers that have not moved over yet. It can only hold one case, so for an item that belongs to several it records the first — the membership table is the source of truth.
func (*Store) AddCaseTicket ¶ added in v0.2.0
func (s *Store) AddCaseTicket(ctx context.Context, t CaseTicket) error
AddCaseTicket links a case to an external tracker.
func (*Store) AddHypothesis ¶ added in v0.2.0
AddHypothesis records a belief and its confidence.
func (*Store) AddNextAction ¶ added in v0.2.0
AddNextAction records a checklist item.
func (*Store) ApplyEnrichment ¶
ApplyEnrichment applies enrichment data to an event
func (*Store) AssignEventToCase ¶
AssignEventToCase attaches an event to a case as supporting evidence.
Membership lives in case_members, so an event can belong to more than one case; the events.case_id column is kept in step for readers that have not migrated yet.
func (*Store) AssignFindingToCase ¶ added in v0.2.0
AssignFindingToCase makes a finding one of the things a case is about.
Findings join as members rather than evidence: a case is *about* its detections and merely *supported by* the raw events pulled in around them.
func (*Store) CountActiveCases ¶ added in v0.2.0
func (s *Store) CountActiveCases(ctx context.Context) (ActiveCases, error)
CountActiveCases returns the active case count, how many are being investigated, and when the oldest was opened.
func (*Store) CountCaseMembers ¶ added in v0.2.0
CountCaseMembers reports how many findings and events a case holds.
func (*Store) CountEvents ¶ added in v0.2.0
CountEvents returns the number of events matching the filter, ignoring Limit/Offset.
func (*Store) CountEventsByObservable ¶ added in v0.2.0
CountEventsByObservable reports how many events carry an indicator, for the "have I seen this before?" answer without loading the events.
func (*Store) CountEventsFiltered
deprecated
func (s *Store) CountEventsFiltered( ctx context.Context, caseID string, start, end time.Time, severities []string, types []string, ) (int, error)
CountEventsFiltered returns the total count of events matching the same filters as GetEventsFiltered.
Deprecated: use CountEvents with an EventFilter, which also supports class_uid.
func (*Store) CountEventsToday ¶ added in v0.2.0
CountEventsToday returns the number of events since local midnight.
Local midnight, not UTC: "today" on a dashboard means the analyst's today.
func (*Store) CountFindings ¶ added in v0.2.0
CountFindings reports how many findings match the filter.
func (*Store) CountFindingsByObservable ¶ added in v0.2.0
func (s *Store) CountFindingsByObservable(ctx context.Context, typeID int, value string) (int, error)
CountFindingsByObservable reports how many findings carry an indicator.
func (*Store) CountObservables ¶ added in v0.2.0
CountObservables returns the number of distinct indicators, counted by identity — `(type_id, value)` — not by sighting. The same IP seen in four hundred events is one indicator.
func (*Store) CountOpenFindings ¶ added in v0.2.0
func (s *Store) CountOpenFindings(ctx context.Context) (OpenFindings, error)
CountOpenFindings returns the number of findings still needing attention, broken down by severity.
One query rather than six: a card that shows a total and a breakdown taken at different instants can show a breakdown that does not sum to its own total.
func (*Store) CreateOrUpdateCase ¶
CreateOrUpdateCase creates a new case or updates an existing one
func (*Store) DeleteCaseAndUnassign ¶
DeleteCaseAndUnassign deletes a case and unassigns all its events (sets events.case_id=NULL). This keeps events accessible under ALL EVENTS after the case is removed.
func (*Store) DeleteEvents ¶
DeleteEvents deletes events by IDs along with their enrichments, then updates event_count for any affected cases. Deletion is executed in a single transaction. Note: enrichments table has a FK to events without ON DELETE CASCADE, so we must delete enrichments explicitly before deleting events.
func (*Store) DeleteFindings ¶ added in v0.2.0
DeleteFindings removes findings by ID.
func (*Store) DeleteNote ¶
DeleteNote deletes a note
func (*Store) FindEventsByObservable ¶ added in v0.2.0
func (s *Store) FindEventsByObservable(ctx context.Context, typeID int, value string, limit int) ([]Event, error)
FindEventsByObservable returns every event carrying the given indicator. This is the indicator-pivot primitive: with the (type_id, value) index it is a single indexed lookup rather than a scan over every event's text.
A typeID of ocsf.ObservableTypeUnknown matches on value alone.
func (*Store) FindFindingsByObservable ¶ added in v0.2.0
func (s *Store) FindFindingsByObservable(ctx context.Context, typeID int, value string, limit int) ([]Finding, error)
FindFindingsByObservable returns every finding carrying the given indicator.
func (*Store) GetAllEvents ¶
GetAllEvents returns all events ordered by timestamp
func (*Store) GetAuditEntries ¶
func (s *Store) GetAuditEntries(ctx context.Context, caseID string, limit int) ([]AuditEntry, error)
GetAuditEntries retrieves audit entries for a case
func (*Store) GetBriefing ¶ added in v0.2.0
GetBriefing assembles a case's briefing from its notes.
Malformed entries are skipped rather than failing the whole briefing: one bad row written by an older build must not blank the screen.
func (*Store) GetCase ¶ added in v0.2.0
GetCase returns a single case by ID, or nil when it does not exist.
func (*Store) GetCaseEventMembers ¶ added in v0.2.0
GetCaseEventMembers returns the events attached to a case as evidence.
func (*Store) GetCaseFindings ¶ added in v0.2.0
GetCaseFindings returns the findings a case is about.
func (*Store) GetCaseIndicators ¶ added in v0.2.0
GetCaseIndicators aggregates a case's observables by identity.
One query over the (type_id, value) index rather than a text scan of the records: the same address seen forty times is one indicator with forty sightings, and finding that out should not mean reading forty rows.
MIN(source) picks "asserted" over "derived" alphabetically, which is the answer we want: if any sighting came from the producer, the indicator is asserted.
func (*Store) GetCaseMembers ¶ added in v0.2.0
GetCaseMembers returns every membership row for a case.
func (*Store) GetCaseTickets ¶ added in v0.2.0
GetCaseTickets returns the external trackers linked to a case.
func (*Store) GetCasesForMember ¶ added in v0.2.0
GetCasesForMember returns every case containing the given item.
This is what one-to-one membership could not express: an alert routinely belongs to both the incident it triggered and a longer-running campaign case.
func (*Store) GetEnrichmentsByEvent ¶
GetEnrichmentsByEvent returns all enrichments associated with an event (newest first)
func (*Store) GetEvents ¶ added in v0.2.0
GetEvents returns events matching the filter, ordered by timestamp DESC. When Limit is 0, all matching rows are returned.
func (*Store) GetEventsByCase ¶
GetEventsByCase returns the events attached to a case as evidence.
func (*Store) GetEventsByTimeRange ¶
func (s *Store) GetEventsByTimeRange(ctx context.Context, caseID string, start, end time.Time, limit int) ([]Event, error)
GetEventsByTimeRange returns events filtered by optional case and time range
func (*Store) GetEventsFiltered
deprecated
func (s *Store) GetEventsFiltered( ctx context.Context, caseID string, start, end time.Time, severities []string, types []string, limit, offset int, ) ([]Event, error)
GetEventsFiltered returns events filtered by optional case, time range, severity list, type list, with pagination via limit/offset. Results are ordered by timestamp DESC. When limit is 0, all matching rows are returned (no LIMIT/OFFSET).
Deprecated: use GetEvents with an EventFilter, which also supports class_uid.
func (*Store) GetFindingByUID ¶ added in v0.2.0
GetFindingByUID looks a finding up by its OCSF finding_info.uid.
func (*Store) GetFindings ¶ added in v0.2.0
GetFindings returns findings matching the filter, most recently seen first.
func (*Store) GetLastEvent ¶ added in v0.2.0
GetLastEvent returns the timestamp of the most recent event, and false when the database holds none.
This is the freshness signal in the header. "No events yet" and "last event 4 hours ago" are different problems, and both are different from a stalled watcher, so the caller needs to tell them apart.
func (*Store) GetObservablesByEvent ¶ added in v0.2.0
GetObservablesByEvent returns every observable recorded for an event.
func (*Store) GetObservablesByFinding ¶ added in v0.2.0
func (s *Store) GetObservablesByFinding(ctx context.Context, findingID string) ([]Observable, error)
GetObservablesByFinding returns every observable recorded for a finding.
func (*Store) GetObservablesForEvents ¶ added in v0.2.0
func (s *Store) GetObservablesForEvents(ctx context.Context, eventIDs []string) (map[string][]Observable, error)
GetObservablesForEvents returns observables for many events in one query, keyed by event ID. The IOC view needs the whole case at once; issuing a query per event would make it O(n) round trips.
func (*Store) GetPinnedMemberIDs ¶ added in v0.2.0
func (s *Store) GetPinnedMemberIDs(ctx context.Context, caseID, memberType string) (map[string]bool, error)
GetPinnedMemberIDs returns the pinned member ids of one type for a case.
Ids rather than rows: the caller already holds the members, and the briefing needs only to know which of them carry a star.
func (*Store) GetPriorityQueue ¶ added in v0.2.0
GetPriorityQueue returns the highest-priority open findings, most urgent first.
func (*Store) GetRecentCases ¶ added in v0.2.0
GetRecentCases returns the most recently updated cases.
By update rather than by creation, which is what ListCases orders on: the case an analyst touched ten minutes ago is the one they are resuming, not the one they opened first.
func (*Store) LogCaseAction ¶
func (s *Store) LogCaseAction(ctx context.Context, caseID, action, actor string, details map[string]interface{}) error
LogCaseAction logs a case-related action
func (*Store) LogCopilotQuery ¶
func (s *Store) LogCopilotQuery(ctx context.Context, caseID, actor, query, response string, tokens int, cost float64) error
LogCopilotQuery logs a Copilot query with token/cost information
func (*Store) LogEventAction ¶
func (s *Store) LogEventAction(ctx context.Context, caseID, eventID, action, actor string, details map[string]interface{}) error
LogEventAction logs an event-related action
func (*Store) OnEnrichment ¶ added in v0.2.0
OnEnrichment registers fn to run after an enrichment is applied to an event.
Enrichment is asynchronous and, in standalone mode, the bus is a no-op — so there is nothing for a view to subscribe to. This is the in-process alternative. Callbacks run on the goroutine that applied the enrichment, usually an enrichment worker, so fn must not block: hand off and return.
func (*Store) RefreshCaseCounts ¶ added in v0.2.0
RefreshCaseCounts syncs the denormalized counts on the case row.
func (*Store) RemoveCaseMember ¶ added in v0.2.0
RemoveCaseMember unlinks an item from a case.
func (*Store) SaveFinding ¶ added in v0.2.0
SaveFinding writes a finding, updating in place when one with the same finding_info.uid already exists.
This is what makes activity_id meaningful: a finding arrives repeatedly as Create, then Update, then Close. Appending each arrival would turn a single alert into a queue full of near-duplicates.
func (*Store) SaveRecord ¶ added in v0.2.0
func (s *Store) SaveRecord(ctx context.Context, rec ingestRecord) (SavedRecord, error)
SaveRecord persists a parsed OCSF record, routing it to the events table, the findings table, or both. Ingest paths call this rather than SaveEvent so the routing decision lives in one place.
func (*Store) SearchEvents ¶
SearchEvents performs full-text search on events (falls back to LIKE if FTS unavailable)
func (*Store) SetHypotheses ¶ added in v0.2.0
SetHypotheses replaces the whole set, for the same reason as SetNextActions.
func (*Store) SetMemberPinned ¶ added in v0.2.0
func (s *Store) SetMemberPinned(ctx context.Context, caseID, memberType, memberID string, pinned bool) error
SetMemberPinned marks or unmarks a case member as pinned.
func (*Store) SetNextActions ¶ added in v0.2.0
func (s *Store) SetNextActions(ctx context.Context, caseID, author string, actions []NextAction) error
SetNextActions replaces the whole checklist, which is what ticking an item amounts to when the items have no ids of their own.
func (*Store) SetStatement ¶ added in v0.2.0
SetStatement records the incident statement, replacing any previous one.
func (*Store) SetupAuditTables ¶
SetupAuditTables creates the audit and notes tables if they don't exist
func (*Store) UpdateCaseEventCount ¶
UpdateCaseEventCount recalculates and persists the event_count for the given case. UpdateCaseEventCount syncs a case's denormalized counts from its membership.
func (*Store) UpdateCaseStatus ¶ added in v0.2.0
UpdateCaseStatus records a lifecycle transition, keeping the label and the OCSF status_id in step.
func (*Store) UpdateCaseTriage ¶ added in v0.2.0
func (s *Store) UpdateCaseTriage(ctx context.Context, caseID string, priorityID, impactID int, suspectedBreach bool) error
UpdateCaseTriage sets the incident-profile triage fields together.
func (*Store) UpdateCaseVerdict ¶ added in v0.2.0
UpdateCaseVerdict records the analyst's conclusion about a case.
func (*Store) UpdateFindingStatus ¶ added in v0.2.0
UpdateFindingStatus records an analyst's triage decision.