Cases and importing¶
A case is one investigation: a single file holding a copy of every row you import and everything you do with them — tags, notes, dashboards. This page covers the home screen where cases live, what a case file is, and every way to get evidence into one. Importing never changes the evidence file; Winnow reads it and copies the rows into the case.
The home screen¶
The home screen is what Winnow shows when no case is open. ⚙ Settings and ⏻ Shut down sit beside the Winnow wordmark at the top. Below them is this machine's list of cases, in the same frame as the Tables manager: sections down the left, the chosen one on the right. Along the bottom are Open existing case file…, Quick look at files… and + New case.
| Section | What it lists |
|---|---|
| Recent | Cases opened in the last 30 days, and cases never opened yet, most recently opened first. Each row shows its group as a badge. |
| All cases | Every case, under a heading for each group with its case count, total rows and when it was last opened. Its side entry says how many case files are missing. |
| each group | That group's cases. Groups are listed by when one of their cases was last opened. |
| No group | Cases with no group (shown once there is at least one group). |
Each row is one case: its name, with its notes (or file not found) beside it, then Tables, Rows, Tagged (rows with at least one tag) and Opened (37 min ago, yesterday, 3 days ago, Sep 5). Hover over the name for the case file's path, and over the opened time for the exact time. Click a column heading to sort by it. Click a row to open the case; with the keyboard, ↓ from the filter box walks the names and Enter opens one.
Filter cases, groups and notes… filters the section you are in. When the case you are after is in another section (an older case, while you are in Recent), a button under the list says how many more match and takes you to All cases. Escape clears the filter. A case whose file has gone missing shows ⚠ and file not found, and can't be opened until you fix or forget it.
Inside a case, the brand button at the top left shows the case's name and takes you back here, to the section you were on. The case stays open in the server until you open another one.
Creating and opening cases¶
+ New case asks for a Name, an optional Group (an engagement
name, say; it gets its own section on the home screen and is offered again
as you type), the Case file path and a Profile. The path is filled
in from the name — ACME Breach becomes
<cases folder>/acme-breach.db-winnow — until you edit it; Browse… picks another folder and New folder… makes one. A
profile turns on its plugins and dashboards as the case opens, and asks for
any case variables it needs (required ones are marked *); see
Dashboards. Create case writes the file and opens it.
Open existing case file… takes a path (typed, or Browse…) and adds
the case to this machine's list as it opens it — for a case someone handed
you, or one you removed from the list. Older cases saved as .db open the
same way. A file that is not a Winnow case, such as a browser History
database, is refused; open a case and import it instead. A path with no
file at it is refused too (No case file at …) rather than creating an
empty case there; + New case makes new cases.
Each row's ⋯ button, or right-clicking the row, opens its menu:
| Menu item | What it does |
|---|---|
| Open | Opens the case. |
| Edit name, group and notes… | Changes the name, group and notes on this machine's list. The file keeps its name on disk. |
| Copy case file path | Copies the full path of the case file. |
| Remove from list… | Forgets the case on this machine; the file is left untouched. |
| Delete case file… | Permanently deletes the case file and everything in it. Going back to the case list leaves the last case open, so deleting that one closes it first; the confirmation says so. Refused for a case open in another Winnow. |
The cases folder¶
On first run Winnow asks where to keep case files. Choose a folder…
picks one; Use the default uses the cases folder inside the Winnow
folder, beside server.py and the launchers, wherever Winnow is started
from; the prompt shows its full path. New cases are proposed
there and quick looks go in its quicklook subfolder. No setting changes it
later; the WINNOW_CASES_DIR environment variable overrides it. See
Installation for where everything else lives.
Case files¶
A case file ends in .db-winnow and is an ordinary SQLite database. It
holds a copy of every imported row and its search index; tags, row notes
and the Notes page; merges, derived columns and table folders;
the watchlist, dashboards, SQL tabs and sessions; and the case's own
settings, such as which plugins are on for it. Saved filters, header-set
names, folder-import profiles, analysis profiles, the case list and your
appearance and keyboard settings stay on the machine instead.
Because the case holds its own copy of the data, it grows with every import. Removing a table does not shrink it; Case ▾ → Compact case file… does. On a large case that takes several minutes, the app is unresponsive meanwhile, and it needs as much free disk space as the case file uses.
The evidence is never modified. Files are opened read-only and their
rows copied. Nothing is written beside a SQLite database either: one with an
un-checkpointed -wal file is copied, with it, to a private temporary
folder and read there, so those rows are included. The only import that
writes near the evidence is an archive, which expands into a new folder
beside itself (see Archives).
One Winnow per case. Opening a case another Winnow has open shows who
has it (user, host, since when, last seen) and asks: Open anyway or
Don't open. Two servers on one case don't see each other's tags, notes or
imports until they reload, and a long write in one starts failing the other.
On the command line the open is refused unless you pass --force. A Winnow
that crashed stops counting as a holder about two and a half minutes after
its last heartbeat.
Keep case files on a local disk. SQLite's locking does not work over a network share (SMB, NFS) and the file can be corrupted there. Two analysts work in separate case files and exchange session files — see Sessions and export.
Two cases at once means two Winnows. A Winnow has one case open, and every window on it shows that case. To work on a second case side by side, start Winnow again: the second one takes the next free port (8778, 8779, …) and opens its own window. Opening another case from Home switches every window on that Winnow instead, and any other window on it says This window is out of date and saves nothing more until it is reloaded.
Checking a case for misplaced tags¶
Before this version, two Winnows on one computer could mix up their cases. A second Winnow started while the first was running opened its window on the first Winnow's case. And a window left showing one case after another window opened a different one went on saving: its tags, notes and imports went into the case that was open then, onto the rows at the same row numbers. If an analyst did either, check the cases they had open then.
Winnow keeps no record of which window made a tag, so it cannot pick the misplaced ones out for you. What can:
- A session saved before the mix-up. In Case ▾ → Sessions…, click that session's name to compare it (From · A) with Current work (live) (To · B). B only counts every row tagged since, and clicking the count opens those rows. Untag the ones that don't belong, or Load… the session from its ⋯ menu to put the whole case back as it was then (save the current work first if some of the later tags are real; see Sessions and export).
- Tags you know you applied then. Filter the case by those tags and check each tagged row is one you meant. A misplaced tag sits on the row with the same Line number (the row number in the gutter) as the row it was meant for in the other case, usually in the table imported in the same order.
- Tags, tables and notes that don't belong. A tag name, an imported table or a note that belongs to the other case came from a window that was showing that case. Delete them here and add them to the right case.
Quick-look cases¶
A quick look is a temporary case for looking at a file without filing it. Quick look at files… on the home screen makes one and opens the Import window to add files to it. Dropping files anywhere on the home screen except a case row makes one too (dropping them on a case's row imports them into that case), and so does opening a file through an OS file association (Windows and Linux; Settings → File associations, see Settings and shortcuts).
An associated file imports with no questions: every readable table of a
SQLite database, with likely Chrome timestamps converted, and every sheet of
a workbook that has data. Files opened together share one quick look. A
Winnow on the home screen with no case open takes the file; otherwise a new
Winnow starts in its own window, so a case you have open is never switched
under you. Double-clicking a .db-winnow file opens that case.
A quick look never appears in the case list. Its banner reads Quick look — this case is temporary and isn't saved anywhere yet.:
| Button | What it does |
|---|---|
| Save as a case… | Opens the same dialog as + New case (Name, Group, Case file path, Profile), titled Save as a case. Save case moves the quick look's file to that path, lists it on the home screen and applies the profile. The path starts in the cases folder. Refused while an import is running, and while a download or a search started before your latest changes is still reading the case: save again when it finishes. |
| Add tables to an existing case… | Copies every table, with its tags and notes, into a case you pick. The dialog lists your cases the way the home screen does (Recent, All cases and your groups down the left); click one, then Copy N tables into case. Double-clicking a case copies straight away. |
| Discard… | Deletes the quick look. |
The brand button asks the same question before leaving, since there is no way back from the home screen. At startup Winnow deletes quick looks older than seven days that are not open and hold no tags, notes or other work.
Ways to import¶
| Where | What happens |
|---|---|
| Case ▾ → Import…, or Import files… in an empty case | Opens the Import window. |
| Drag files onto an open case | Queues them in the Import window. |
| Drag files onto a case row on the home screen | Opens that case and queues them there. |
| Drag files onto the rest of the home screen | Makes a quick look and queues them there. |
| Import a whole folder… in the Import window | Folder import. |
| Double-click a file with an association | A quick look, imported straight away. |
--open on the command line |
Imports delimited files at startup. |
| Import files… on a format in Settings → Plugins | Forces files through that plugin's parser. See Plugins. |
Drag-and-drop takes files; for a folder use Import a whole folder…. If the pointer turns into a no-entry sign instead, see Troubleshooting.
The Import window¶
The Import window is a queue in two panes, like Import a folder and the Tables manager: the queued files down the left, the chosen file's settings and preview on the right, and the buttons along the foot. Add any mix of files, set the ones that need it, then Import N files. The window closes and the imports run in the background while you work (see Import jobs).
- Add from this machine… browses the disk Winnow runs on and imports in place, with no copy — the fast route for big files. Type a path or click down to it; Filter this folder… narrows a big folder, and the Name, Modified and Size headings sort it (click one again to turn it round). Down the left are places to start from: Home, the Cases folder, This case’s folder and the folder you Last used, with ☆ to pin any folder you keep going back to. Tick the files, then Add N files. It only works when the browser runs on the same machine as Winnow.
- Upload from browser… is the browser's own picker. The file is copied to the server's temporary folder first, which needs the space and takes longer; a file over 1 GB gets a reminder to add it by path.
- Import a whole folder… is folder import.
Under each file's name the list says how it will be read: Delimited · auto-detect, JSON · nested kept as JSON, SQLite · 2 tables, Text · one line per row and so on. Filter queued files… narrows a long queue, and ✕ takes a file off it. The chosen file's pane, from the top:
| Part | What it holds |
|---|---|
| File | Its name, the folder it is in, its size, and read in place (added from this machine) or uploaded when it imports. |
| Read as | Only for a file that can be read more than one way: Delimited, One line per row or Key=value for delimited and other text; JSON or One line per row; IIS log, Delimited or One line per row; Key=value, Delimited or One line per row for a key=value log; a plugin's format or One line per row for an uploaded file a plugin claims. |
| Settings | The reader's own: Delimiter and Header, JSON's Nested objects, an IIS log's Format, a plugin format's options. Plaso files, key=value logs and archives have none. |
| Preview | The first 50 rows as they will import, a type picker in each column's heading where the type can be set, and a line under it saying what was detected. A SQLite file or workbook lists its Tables or Sheets here instead, to tick. |
A setting is kept as you change it, and choosing another file keeps it, so there is nothing to confirm; a file whose pane you never open imports with the detected settings. A SQLite file or workbook can't import until its tables or sheets are ticked: the list says SQLite · pick tables in the accent colour until then, and Import N files takes you to it.
An uploaded file previews from its first 1 MB, which is more than the preview reads. JSON, SQLite and Excel previews need the whole file, so over 32 MB the pane asks first with Load preview (uploads …). A SQLite file's tables or a workbook's sheets are read once: choosing the file again shows the same list without sending it again. A file added from this machine previews in place at any size.
A file headed for the raw-text reader that looks binary (a NUL in its first
8 KB; a UTF-16 byte-order mark is exempt) is dropped from the queue with
Skipped N binary files (not text), so an .exe or .evtx picked by
mistake is never uploaded. Added by path, it fails when its import starts.
From the command line¶
python server.py --case acme.db-winnow --open Security.csv System.csv
--case opens the case, creating it if it doesn't exist and adding it to the
home screen. --open imports each file before the window opens and prints
row counts and warnings to the terminal; without --case it creates
case.db-winnow in the current folder. --open reads every file as
delimited text, whatever its extension — use the Import window for JSON,
SQLite, Excel, Plaso and the rest. Other options are in
Installation.
Supported formats¶
| File | Extensions | You get |
|---|---|---|
| Delimited text | .csv .tsv .tab .psv .txt |
One table, columns from the header row. |
| JSON | .json .jsonl .ndjson |
One table, one row per record. |
| SQLite | .db .sqlite .sqlite3 |
One table per table you pick. |
| Excel | .xlsx .xlsm |
One table per sheet you pick. |
| Plaso | .plaso |
One timeline table. |
| IIS web server logs | named u_ex…, ex…, nc…, in….log |
One table, columns from the log's own #Fields: lines. |
| FortiGate and other key=value logs | .log, .txt, no extension…, recognised by the first line |
One table, a column per key. |
| Archives | .zip .tar .tgz .gz .tbz2 .txz .bz2 .xz |
A folder, then folder import. |
| Any other text | .log, .1, no extension… |
One row per line. |
| Plugin formats | raw $MFT and $J, ESXi and UAC logs |
Whatever the plugin parses. |
Legacy Excel .xls and binary formats such as .evtx are refused as
binary. Run EVTX through EvtxECmd first; save an .xls as .xlsx in Excel.
Every column is stored as text. Winnow guesses a type for each from its first 500 rows — text, number or datetime: number columns sort numerically, and datetime columns are the ones the Timeframe filter, Histogram and Timeline use. The type is fixed at import (the delimited-text preview can change it); to get a real datetime out of an epoch or other encoded value, add a derived column.
CSV, TSV and other delimited text¶
Delimiter. The extension is believed first — .csv comma, .tsv and
.tab tab, .psv pipe — if the file agrees. For a tab that means the tab
splits at least half of the first 20 rows, so short rows, a title line or a
cell running over several lines don't put a .tsv off; a .tsv with no
tab in it is sniffed instead. For the others it means that character on
every sampled line, the same number of times on at least 90% of them.
Otherwise, and for .txt and extensionless files, the content is sniffed:
tab first, if it is on every sampled line, then comma, semicolon or pipe.
The line under the file's preview says what was found
(Comma-delimited (detected) · 12 columns · first 50 rows), and
Delimiter overrides it (Auto-detect, Comma, Tab,
Semicolon, Pipe). Read as → One line per row reads the file as
raw text instead. KAPE writes some delimited
output as .txt, so .txt comes here by default; a .txt log wants
One line per row, unless it is a
key=value log, which is recognised by
its first line.
Header. First row is headers is on by default, and the first
non-blank line is the header. Off, columns are named col_1, col_2… and
the first line is data. A blank header becomes col_N, a repeated one gets
_1, _2, and rid, rank or rowid get _1 because those names are
taken. Each column's heading in the preview has a text / number / datetime
picker, and what you pick is kept as you pick it.
Encodings. Winnow decides from the first 1 MB:
| File | Read as |
|---|---|
| UTF-8, with or without a byte-order mark | UTF-8. |
UTF-16 with a byte-order mark (Windows PowerShell 5.1's Out-File and >) |
UTF-16. |
| Not UTF-8 and holding no UTF-8 at all (Excel "CSV" on Windows, older tools) | Windows-1252, said in the preview, the finished toast and the table's hover text. |
| UTF-8 with a few bad bytes | UTF-8; each bad byte is read as its Windows-1252 character rather than lost, and counted. |
UTF-16 without a byte-order mark is not detected; save it as UTF-8 first.
Ragged rows. The header sets the width. Short rows are padded with blanks; long rows are cut to fit and their extra fields lost. No row is dropped, and the toast counts them (N ragged rows padded/trimmed). If the header itself is short, every longer row is trimmed to it — that count is the only sign.
Stray quotes. An unbalanced " folds the following lines into one field
until the next quote, and those rows vanish without an error. Rows that
swallowed ten or more lines are counted: N rows with very long multi-line
fields — check for a stray quote if the row count looks low. Properly
quoted multi-line fields, such as EVTX XML payloads, stay under that.
Large fields and bad lines. One field can hold up to 64 Mi (67,108,864) characters, so script blocks and base64 blobs import whole. A field past that, or any line the reader can't parse, stops the import with an error naming the line, and the rows before it are kept: Line 1,010,002: … — import stopped there; the 1,010,000 rows before it were kept. If no row was read, no table is left behind.
JSON and JSON Lines¶
.jsonl and .ndjson hold one JSON value per line and stream. A .json
file is one document — an array of records, a single object (one row), or a
bare value — and is loaded whole, so memory grows with its size. A value
that isn't an object lands in a column called value. The import reads the
file twice, once to find every column, so it is slower than a CSV of the
same size.
Nested objects, in the file's pane, chooses how nested objects become columns:
| Option | {"user": {"name": "a", "id": 1}, "tags": ["x"]} becomes |
|---|---|
| Keep as JSON (default) | user = {"name": "a", "id": 1}, tags = ["x"] |
| Flatten | user.name = a, user.id = 1, tags = ["x"] |
| Flatten to depth N | Objects unfolded N levels; anything deeper stays JSON text. |
Arrays are never split, at any depth — their length varies record to record — so they stay as JSON text in one column. Columns appear in the order keys were first seen, and a record without a key gets a blank cell. To lift a field out of a JSON cell later, use derived columns.
- Numbers are kept as written:
1.10stays1.10. - Two values for one column — a repeated key, or a literal
"process.name"beside a nestedprocess.name— are both kept; the second becomesprocess.name_1, and the preview and toast count the records affected. - Past 1,000 columns, the first 999 keys stay columns and the rest go, per
record, into one
Other fields (JSON)column. - A broken line in a
.jsonlis skipped and counted (N unreadable records skipped); a truncated.jsondocument fails the import. - The preview reads the whole file, so an uploaded
.jsonis uploaded in full just to preview it, and past 32 MB the pane asks first. Add big ones with Add from this machine….
SQLite databases¶
A SQLite file's pane lists every table with its row count where the preview
would be. Tick the ones you want (none are ticked at first; All and
None tick or clear the lot). Each becomes its own table, named
<file name>.<table> such as History.urls, and all of them import as one
job.
Chromium browsers store times as microseconds since 1601. A column whose
name contains time, utc or date and whose values look like that gets a
button under its table, after Convert to dates:, already pressed. Pressed
columns import as ISO 8601 UTC (2024-05-01T13:07:00.123Z), and a value
that doesn't convert keeps its number; unpress a button to keep the raw
number throughout.
The database is opened read-only (see Case files for WAL
databases). BLOBs import as <N bytes>. Text that isn't valid UTF-8 gets
� in place of the bad bytes, and the toast counts the values. A table this
SQLite can't read, such as a virtual table whose module it lacks, is listed
with can't be read: … and can't be ticked; the others still import.
Excel workbooks¶
A workbook's pane works the same way: tick sheets, and each becomes a table
(named <file name>.<sheet>). Its row counts are estimates from the sheet's
declared size; the import reads every row there is. .xlsm imports the same way, and macros never run.
- The header is the first non-empty row, so title rows above it are skipped.
Data wider than the header lands in
col_Ncolumns. - Date-formatted cells become text:
2024-05-01 13:07:00, or2024-05-01at exactly midnight, or13:07:00for a time of day. - Numbers lose float noise (
42, not42.0); booleans readTRUEandFALSE. - A formula imports as its last saved result — empty for a workbook written by a tool and never saved in Excel.
- Short rows are normal in Excel and pad silently; only rows wider than the table count as ragged.
Folder import skips SQLite files and workbooks, since which tables or sheets to take is a choice per file.
Plaso storage files¶
A .plaso file from log2timeline imports as one table, with no options and
no Plaso install. Old and new storage formats both work. The columns are
Datetime, Timestamp desc, Data type, Parser, Source file, Host,
User and Attributes (JSON). Datetime is UTC to the microsecond and the
rows are in time order, so the unsorted table reads as a timeline.
Everything else an event carries is in Attributes (JSON); pull fields out
with derived columns. An event that can't be decoded
still gets a row, with an error note in Attributes (JSON).
IIS web server logs¶
IIS names a site's logs after their format and how often they roll over, and
Winnow recognises them by that name, wherever they sit (usually
inetpub\logs\LogFiles\W3SVC<n>\). No other .log is read this way.
| Name | Format |
|---|---|
u_ex240501.log, ex240501.log |
W3C extended, one file a day. Hourly is u_ex24050113.log, monthly u_ex2405.log, weekly u_ex240502.log (the week of the month); u_extend1.log rolls over by size or never. |
u_nc240501.log, nc…, ncsa1.log |
NCSA common log format. |
u_in240501.log, in…, inetsv1.log |
IIS native format. |
u_ means IIS wrote the file as UTF-8; without it the file is in the server's
code page, which reads as Windows-1252 and is said so.
W3C columns come from the log itself. Which fields a site logs is its own
setting, so each #Fields: line names the columns for the lines under it, and
the table has every field any of them names. IIS writes a new header whenever
the site restarts, and a changed setting changes the fields from that point
on: each line's values land under the field its own header names, and a field
its header doesn't have is blank on that line. The first column,
Timestamp (UTC), joins date and time (always UTC in a W3C log) so the
Timeframe filter, Histogram and Timeline work straight away; date and time
stay too. Values are kept exactly as logged, including - for an empty field
and + for a space in cs(User-Agent).
A line with more values than its header names, or one before any header,
keeps the extras in an Unnamed values column, which only appears when a file
needs it; a short line is padded. Both count as ragged rows in the import
toast.
NCSA lines become Timestamp (UTC) (the logged time with its offset
applied), Remote host, Remote log name, User name,
Timestamp (as logged), Request, Status and Bytes. IIS native lines
become Timestamp (local) and the format's fifteen fields, Client IP address
to Parameters; native format logs the server's local time with no offset,
so that timestamp is not converted. In both, a line that isn't in the format
is kept whole in an Unparsed line column.
An IIS log's pane previews the first rows as they will import, from the
start of the file; for W3C the line under the preview says when a #Fields:
line further in can still add columns. Format picks W3C extended,
NCSA common or IIS native: the one the name says is picked, and the
other two are there for a log that was renamed or isn't what its name says.
Read as → Delimited or One line per row reads the file as any
other text file. Each column's type can be changed; a type set here sticks
to that column by name, so a column a later header adds keeps its detected
type.
A server that loses power mid-write leaves a run of zero bytes in its log, and IIS carries on after them when it comes back. The text either side of the gap imports as separate rows, and Case ▾ → Log… notes that the gap was there. A log IIS is still writing can be imported in place: the lines it adds during the import are left for next time.
Default-configured W3C tables, and NCSA and native ones, get a one-line summary on the Timeline: the URL, method, status and client address.
FortiGate and other Fortinet logs¶
A FortiGate writes its logs as key=value pairs, one event to a line:
date=2026-10-23 time=05:39:00 devname=Main-Firewall type=traffic srcip=192.168.1.50 srcintf="internal" dstip=8.8.8.8 dstport=53 action=accept
Downloaded from the GUI or collected by a syslog server, these files have no
name of their own, so Winnow recognises them by their first line: it
starts with date=, then time=, then another pair. A syslog <189> and a
syslog header before the pairs are fine, as are quoted dates and FortiOS's
comma-separated form (set format csv). Other Fortinet products' logs that
start the same way, such as FortiMail's, are read the same way. Only a file no
other reader claims by its name is looked at: a .log, a .txt, an
extensionless file. A .csv is always delimited and an IIS log is always an
IIS log. The queue says Key=value log · a column per key under its name.
Every key is a column, in the order keys first appear in the file. A
traffic line and an event line carry different keys, so the table has every
key any line has, and a key a line doesn't have is blank on that line. Keys
are matched whatever their case, and a key that appears twice in one line
gets a second column, key_1. Quotes come off a value: msg="Admin login
successful" is one value, and so is a quoted value holding spaces, commas or
=.
date and time are joined into a timestamp, the first column, so the
Timeframe filter, Histogram and Timeline work straight away; date and
time stay too. If every line carries a tz offset (FortiOS 6.2 and later
write tz="+0100"), the times are converted and the column is
Timestamp (UTC). If any line doesn't, the column is Timestamp (local):
the firewall's own clock, as logged, not converted. eventtime, an epoch
count, is kept as logged; a Unix epoch derived column
turns it into a datetime.
Text before the first pair (a syslog header) is kept in a Line prefix
column, and words in a line that aren't pairs, or a line with no pairs at
all, in Unparsed text. Either appears only when a file needs it, and a line
that needed it counts as a ragged row in the import toast. A file with more
than 1,000 different keys keeps the rest of each line's pairs in an
Other fields (JSON) column.
The pane previews the first rows as they will import; the line under it says when a key further into the file can still add columns. Each column's type can be changed, and sticks to that column by name. Read as → Delimited or One line per row reads the file as any other text file, and Read as → Key=value reads any delimited or text file this way, for a key=value log whose first line didn't say so. As with an IIS log, a run of zero bytes from a power cut is a gap between rows, and a log still being written imports in place.
Plain text and log files¶
A file no other reader claims — hostd.log, auth.log.1, an extensionless
dump — imports as raw text: one row per line, the whole line in a single
Message column, no header, nothing split. The row number is the line
number: blank lines are kept as empty rows, and only a newline ends a line,
so a carriage return inside a logged value can't fake an extra row. Binary
files are refused. Read as → One line per row, in the pane of any
file that has a Read as, forces that file into this reader.
Archives¶
.zip, .tar, .tgz/.tar.gz, .tbz2/.tar.bz2 and .txz/.tar.xz
archives and single gzipped files (hostd.0.gz) expand, then import. A
.bz2 or .xz that is a single compressed file, not a tarball, is not
supported.
Import N files expands the archive into a new folder named after it
(bundle.tgz → bundle-extracted) — beside the archive when added from
this machine, beside the case file when uploaded — and opens
folder import on it so you choose what to import.
Archives inside are expanded in place, up to six levels deep, and a gzipped
rotated log becomes the plain file. The original is never touched. With
several archives queued, the folder window opens on the first and a toast
names the others' folders.
The toast names the first skipped and first renamed file; Case ▾ → Log… lists them all.
- Members whose path would escape the folder, links and devices in a tar, and encrypted zip members are skipped.
- A damaged member is skipped and the rest expand; a truncated archive keeps what came before the damage. Large zips made by Windows Explorer can use Deflate64, which Winnow can't read — extract those with Explorer or 7-Zip and import the folder.
- Two members with one name are both kept; the later becomes
name (2).ext. - Expansion stops, leaving a partial tree, at 32 GB or 200,000 files.
ESXi support bundles and UAC collections are mostly .log files, and
folder import's default file types leave .log out. Turn the esxi_logs
plugin on first: creating the case with the ESXi / UAC triage profile
does it and adds that profile's dashboard, or switch it on in
Settings → Plugins. It claims .log and the ESXi log names, parses them
into one schema and adds a .log chip to folder import. Without it, turn on
other text files to bring the logs in as raw text.
Folders and KAPE output¶
Import a whole folder… imports every matching file under a directory — a KAPE triage output, an expanded archive. The window lists your profiles down the left and shows the folder, its settings and what the scan found on the right. It stays the same size whatever you pick or the scan finds.
- Browse… to the folder.
- Pick a profile on the left, or stay on Custom (not saved) to set things up yourself. A profile fills in everything below it.
- Pick File types.
.csv,.tsv,.tab,.txt,.psv,.json,.jsonl,.ndjson,.plaso, IIS logs and any extension an enabled plugin claims are on; other text files (off) adds every other non-binary file as raw text. IIS logs picks up IIS web server logs by name, and a file named like one is read as one however it got in. A FortiGate log the file types let in is read as one: a.txt, or a.logonce other text files, or a plugin that claims.log, is on. Include subfolders (on) walks the tree. - Narrow with Patterns: Include and Exclude, one glob per line,
case-insensitive. A pattern containing
/matches the path from the chosen folder (RegistryHives/*); one without matches the file name anywhere (*_Amcache_UnassociatedFileEntries.csv). Include patterns only narrow what the file types let through. - Check the Preview, which updates as you type. Will import lists
the matches with a box per file; Excluded lists the rest with each
reason (
extension,IIS logs not selected,no include pattern matched,excluded by pattern: …,binary,unreadable,not a regular file). A profile saved before IIS logs existed leaves them out until you tick it and Update profile. The line under the list counts them. - Import checked (N).
Each table is named after its file. Files in subfolders are filed in table folders mirroring the tree and arrive closed — open them from the sidebar — so a hundred files don't become a hundred tabs; files at the top level open as tabs. A file already imported from that path shows already in case and starts unticked. Plugin-claimed files use the plugin's default options. SQLite files and workbooks are never picked up and archives in the folder are not expanded; import those on their own. The scan stops at 5,000 files and says so.
Profiles. Save as profile… stores the file types, patterns and subfolder choice under a name, for every case on this machine, and lists it under Profiles. With a profile chosen, Update profile saves your changes to it and Delete profile removes it; what it held stays on screen under Custom (not saved).
Finding a profile. Type in the Search… box at the top of the list to
narrow it. A profile is found by its name or by one of its patterns or file
types, and then says which one under its name: unassociated finds a
profile that excludes *_Amcache_UnassociatedFileEntries.csv and shows
excludes *_Amcache_UnassociatedFileEntries.csv. The arrow keys move through the
list, and Enter in the search box picks the first match. Esc clears the
search; with the search empty, it closes the window.
Pinning favourites. Point at a profile and click the ☆ beside it to pin it to a Pinned section at the top of the list. Click its ✕ to put it back. Pins stay in this browser, like the ones in the derived column list.
Plugin formats¶
Two bundled plugins add formats; both ship switched off (Settings →
Plugins). mft_usn reads a raw NTFS $MFT and $UsnJrnl:$J, recognised
by name ($MFT, $J, *$UsnJrnl*…), without EZTools. esxi_logs reads
ESXi support-bundle and UAC Linux host logs. A claimed file shows the
format's name in the queue, and its options, if it has any, in the file's
pane. Built-in extensions always win, so no plugin can take over .csv or
.json, and neither do built-in names: esxi_logs claims .log, but an
IIS log is still read as one, and so is a
FortiGate log. Plugin
imports don't run as background jobs: they show an Importing … notice, not
a progress row, and can't be cancelled. See Plugins.
Recognised tool output¶
Winnow knows the header sets common forensic tools write, and recognises a table by its column names whatever the file was called (order and letter case don't matter):
| Tool | Header sets |
|---|---|
| EvtxECmd | Event logs (EvtxECmd) |
| AmcacheParser | Amcache — file entries, device containers, device PnPs, driver binaries, driver packages, program entries, shortcuts |
| AppCompatCacheParser | Shimcache (AppCompatCache) |
| PECmd | Prefetch (PECmd), Prefetch timeline (PECmd) |
| RBCmd | Recycle Bin (RBCmd) |
| WxTCmd | Windows Timeline — activity, operations, package IDs |
| JLECmd | Jump lists — automatic, custom |
| LECmd | LNK files (LECmd) |
| SBECmd | Shellbags (SBECmd) |
| MFTECmd | NTFS $Boot, NTFS $MFT, USN journal $J, NTFS $SDS |
| RECmd (batch) | Registry (RECmd batch) |
| SrumECmd | SRUM — app resource use, network usage |
| esxi_logs plugin | ESXi / Linux host logs |
Recognition gets you:
- A name in place of a raw column list, in the Filters menu and in Merge tables….
- Shipped triage filters that light up when a matching table opens:
twenty for EvtxECmd (logons, RDP, Defender tampering, services, scheduled
tasks…), five for RECmd batch output, three for MFTECmd
$MFT. See Filtering and searching. - Timeline summaries: a tagged row reads as one line ("Special privileges assigned svc_backup · WKSTN-4471 · id 4672") instead of the whole row. See Timeline, Search all and the Watchlist.
- Shipped dashboards, such as the KAPE triage profile's, which find their tables by header set. See Dashboards.
You can name your own header sets in the table menu's Name & merge section, under These N columns.
Import jobs¶
Every import runs in the background, tracked in the panel at the bottom right — the same panel that shows search-index builds.
| Row | Shows | ✕ |
|---|---|---|
| uploading | A browser upload's transfer, in MB. | Cancels the upload. |
| importing | Progress and rows so far; a multi-table job reads name — 2/5: table. |
Cancels the import. |
| N queued | Imports waiting. Two run at once. | Cancels all of them. |
| done, error, cancelled | The result. Finished rows clear after a few seconds; errors stay. | Dismisses the row. |
| indexing | A search index being built. | — |
Clear all dismisses every finished row and leaves running work alone. Reloading the page mid-import picks the jobs back up. Progress counts bytes for delimited and raw text, records for JSON, and rows for SQLite, Excel and Plaso.
Cancelling drops the table entirely, so a half-imported table never sits in the list looking complete; a SQLite or Excel job keeps the tables it had finished. An import that fails with an error keeps what it read before the problem. Opening another case or shutting Winnow down cancels running imports.
A finished import's toast says name: N rows imported, plus any of:
| Warning | Means |
|---|---|
| N ragged rows padded/trimmed | Rows with the wrong number of fields. |
| N rows with very long multi-line fields — check for a stray quote… | A possible unbalanced quote. |
| N unreadable records skipped | Broken JSON Lines. |
| N records with two values for one key — the later kept as <key>_1 | JSON key clashes. |
| not UTF-8 — read as Windows-1252 | The whole file was Windows-1252. |
| N bytes not valid UTF-8, read as Windows-1252 | Stray bytes in a UTF-8 file. |
| N text values were not valid UTF-8 (shown with �) | SQLite text with bad bytes. |
Case ▾ → Log… keeps every import, failure and warning for the session. New tables join the tab strip without moving you; turn on Settings → Imports → Open new tables when an import finishes to jump to the first table of each batch. A column that looks like an encoded timestamp brings a toast offering Add datetime column (Derived columns), and each new table is scanned against the case's watchlist (Timeline, Search all and the Watchlist).
The search index¶
After each import Winnow builds a full-text index of the table in the background: an indexing row in the panel, then Search index ready for …. The table is usable at once; until the index lands, search scans the table — the same results, slower. The index is what makes Contains and Advanced search and Search all fast (Filtering and searching), and it takes space in the case file. Case ▾ → Tables… shows which tables have one.
The build works in short steps, so tagging, undo and grouping stay quick while it runs; on a large event log it can take a while. Closing the case before it finishes keeps what is done: the next build, on the first search or from Build in the Tables manager, carries on from where it stopped.
--no-fts skips the index for files imported with --open; the first
Contains or Advanced search, or Search all, on such a table builds it anyway.
If the SQLite library in your Python is older than 3.45, no index is built
and every search scans.
Importing the same file again¶
Importing a file again makes a second, independent table with the same name. Tags and notes belong to the table they were made on, so the copy starts clean, and only folder import flags the repeat. To replace a table, import the new copy and remove the old one in Case ▾ → Tables…; renaming, folders, removing and merging tables are covered in Working with tables.
Winnow records how each file was read — reader, delimiter, header, flatten mode, table or sheet — so loading a session file into a case that lacks a table can re-import it the same way, if the file is still at the path it was imported from. That works for files added from this machine, not uploads: an upload records only the file's name, so the load says so and you import the file yourself. See Sessions and export.