Add tsk_notes for comments, AI answers and summaries - #41
Closed
jayaramcs wants to merge 1 commit into
Closed
Conversation
- Add tsk_notes and tsk_note_types at schema 9.9, for text that belongs to an object in the case, has no score, and can change after it is written - Add a root-level CASE object so case-wide notes have something to point at, with its id recorded in tsk_db_info_extended - Add NoteManager, Note, NoteType and NoteRequest, with batch reads and writes and append-only revisions rather than in-place edits - Add the TSK_NOTE_ID attribute so an analysis result can point back at the note holding its reasoning - Add note added, updated and deleted events, fired once per batch after commit - Add NoteTest covering threading, revisions, deletes, batch parity and the 9.8 to 9.9 upgrade
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the "Notes in The Sleuth Kit" design proposal (revision 2).
One new table for text that belongs to an object in the case, has no score, and can change after it is written. Comments, AI enrichment, remediation advice and host or case summaries all come from it. Analysis results stay immutable, which is why this is a new table rather than more columns on an existing one.
Schema 9.9 (from 9.8)
tsk_note_types— open types, seeded with the built-ins on every case open, extended by consumers at runtime.tsk_notes— append-only. An edit inserts a new row sharing the sameoriginal_note_idand clearsis_currenton the row it replaces, so earlier text survives and outside references stay correct.tsk_notes_current_revision_index, a partial UNIQUE on(original_note_id) WHERE is_current = 1. It is on both engines: SQLite has supported partial indexes since 3.8.0, and this is what settles the revision flip rather than leaving it to every call site.updateFromSchema9dot8toSchema9dot9(), modelled on the 9.7-to-9.8 method.The CASE object
ObjectType.CASE(10), one per case database,par_obj_idNULL, its id recorded intsk_db_info_extendedunderCASE_OBJECT_ID. It is a sibling of the data sources, not their parent.A new object type is deliberately more backward compatible here than reusing
ARTIFACT:ObjectType.valueOf(short)returnsUNSUPPORTEDfor unknown values,getRootObjects()skipsUNSUPPORTED, andgetContentById()falls through toUnsupportedContent. All of that shipped in 9.0, andisCompatible()checks the major version only, so every build that can open a 9.x database has the escape hatch. A root-level artifact would throw in every build from 9.0 to 9.8.case CASE: break;ingetRootObjects()is not optional — once the enum knows the value it stops mapping toUNSUPPORTEDand would hit the throwingdefault:.No
Caseclass implementingContent: that would add a method to the publicContentVisitorandSleuthkitItemVisitorinterfaces and force a coordinated release with every implementor. It is exposed as a plainlongfromSleuthkitCase.getCaseObjectId().API
NoteManagerviaskCase.getNoteManager(), followingHostManager. Reads do not filter — superseded revisions and soft-deleted rows come back with everything else and the caller decides what to render — withgetCurrentNotes(),getCurrentRevision()andgetCurrentNoteCounts()as the opt-in narrow reads.TSK_NOTE_ID(160, LONG)lets an analysis result point back at the note holding its reasoning. The value isoriginal_note_id, never a revision id, so the attribute is written once and stays correct across edits.Judgement calls worth a reviewer's attention
addNote()delegates toaddNotes(). The proposal asks for batch/single parity on the derived columns; making them one code path is stronger than testing for it.PG_NOTES_CHUNK_SIZE = 4000, notPG_CHUNK_SIZE = 9000. 13 bound columns x 9000 = 117k parameters, over PostgreSQL's 65,535 ceiling. Sized the wayPG_FILES_CHUNK_SIZEis.parent_note_idalone is not enough. Soft delete works on the lineage too, so a consumer holding the stableoriginal_note_idretracts the note a reader can see rather than a superseded draft.getCurrentNoteCounts()besidegetNoteCounts(). The broad-read contract meansgetNoteCounts()counts every revision, which is the wrong number for the badge it exists for. It counts exactly whatgetCurrentNotes()returns, retracted notes included — whether a retraction is shown is a product ruling, and it has to be the same ruling for the badge and the list behind it.SleuthkitCase.init(), rather than also inCaseDatabaseFactoryand the upgrade method.newCase()always opens the case and the upgrade runs insideinit(), so all three entry points in the proposal are covered by one idempotent implementation. It is race-safe on the primary key oftsk_db_info_extended.name, sinceacquireSingleUserCaseWriteLock()is a JVM lock and does nothing across PostgreSQL clients.Testing
NoteTest, added toDataModelTestSuite. 67 tests pass, no regressions in the existing suite.Covers each item on the proposal's test list: cascade of a deleted thread (including one whose reply was revised), the unique index rejecting a second current revision rather than the manager racing, batch-versus-single parity on
root_note_idandoriginal_note_id, case-object get-or-create on a fresh and an upgraded case, andreviseNote()rejecting a different author.upgradeFromSchema9dot8Testtakes a 9.9 case back apart to 9.8 and reopens it, so the upgrade method is exercised rather than assumed.The multi-row INSERT only runs on PostgreSQL in production and would otherwise have no coverage. Its SQL is engine-neutral, so there is a package-private
addNotes(requests, trans, batched)seam and a test that runs it against SQLite and checks it against the row-at-a-time path.Not verified
ON CONFLICT DO NOTHING, the partial indexes, and the batched insert underreWriteBatchedInserts=true. No PostgreSQL server was available.UNSUPPORTEDpath and was checked by reading this repository, but it is still an argument about how other software behaves.Left alone deliberately
tsk/auto/db_sqlite.cppholds a stale copy of the schema and onlytsk_loaddbuses that path; both real consumers go throughSleuthkitCase.newCase().bindings/java/doxygen/schema/is stale at 9.4 and the last four schema bumps did not touch it.Open product decisions (unchanged by this PR)
Whether the delete button does a hard or soft delete, whether reports and exports show retracted or superseded notes, chat policy, and whether Autopsy surfaces notes in this release. The schema and API answer either way.
🤖 Generated with Claude Code