Skip to content

Digital Forensics Investigation

This document describes how to use Sec-Gemini for performing a digital forensics investigation based on logs.

This assumes your logs are managed by Chronicle. If this is not the case, take a look at the Advanced Usage section below. In this setting, Sec-Gemini will query the logs in your Chronicle instance via your local network connection.

Authenticate with Google Cloud by running the following command. This is a one-time step and will be reused in future sessions:

Terminal window
gcloud auth application-default login

Run the following command to launch the Sec-Gemini TUI with the Chronicle backend:

Terminal window
sec-gemini dfir --chronicle=CUSTOMER_ID,PROJECT_ID,REGION

For example:

Terminal window
sec-gemini dfir --chronicle=12345678-9abc-def0-1234-567890abcdef,chronicle-project,us

Once the TUI is ready and a new session is established, prompt Sec-Gemini to perform a forensics investigation.

If you want Sec-Gemini to perform an unhinted investigation (i.e., operate in threat hunting mode), your prompt can be as simple as:

Perform a forensics investigation on the available logs.

When a starting point is available, or if you want to focus the agent’s attention on a specific part, you may provide the information in the prompt itself. Here are good examples of additional information to provide:

  • an (approximate) incident time, e.g., 2026-05-06 13:00
  • a hostname
  • an account or username
  • a specific filename

For instance:

Perform a forensics investigation on the available logs.
An alert was triggered at 2026-05-06 13:00 on hostname "machine1234".

If your logs are not managed by Chronicle, there are three alternative ways to provide logs to Sec-Gemini.

If your logs are not too large (e.g., a few GBs), you can store them in a local SQLite3 database file, which Sec-Gemini repeatedly queries during the investigation. The SQLite3 database file must contain two tables with the following schemas:

Column NameData TypeDescriptionExample values
record_idTEXTUnique record identifierE6vV41eMYw
log_typeTEXTLog source or type classificationsyslog:line, fs:stat
timestamp_microsINTEGEREpoch timestamp in microseconds1645056112000000
timestamp_descTEXTHuman-readable timestamp descriptionContent Modification Time, Start Time
messageTEXTMain log message payload[sshd, pid: 31357] Received disconnect from 218.157.73.75: 11: Bye Bye [preauth]
enrichmentTEXTExtra enrichment details or metadata (optional)tags: [is_tor_ip]

Notice that log records do not need to be parsed or otherwise structured beyond providing a timestamp. You may use arbitrary values for the log_type field, or even a single value for all records, as long as each value has a corresponding entry in the log_descriptions table below.

Column NameData TypeDescriptionExample values
log_typeTEXTReference classification identifiersyslog:line, fs:stat
descriptionTEXTSummary details of corresponding log recordsSyslog line event data., File system stat event data.

To run the Sec-Gemini TUI for digital forensics using logs stored in an SQLite DB file, use the following command:

Terminal window
sec-gemini dfir --sqlite=PATH_TO_FILE.DB

Alternatively, if you have multiple SQLite database files, each corresponding to exactly one investigation, you can name them following the TICKET_ID.db convention (where ticket IDs are numerical, e.g., 12345.db) and place them in the same directory.

You can launch the TUI by providing the path to the directory instead of a single file:

Terminal window
sec-gemini dfir --sqlite=PATH_TO_DIRECTORY_OF_DB_FILES

If there are multiple database files in the directory, you must specify the target investigation ID in the prompt so Sec-Gemini can load the corresponding file:

Perform a forensics investigation on the logs with ticket ID 12345.
Section titled “Option 2: Custom Python script (describe & search)”

If your logs are stored in an external database, queryable via an API, or require custom preprocessing, you can write a custom Python script that implements the LogStore interface.

Sec-Gemini will dynamically load your class and use your custom implementation to query logs during the investigation.

To do this, create a Python file (e.g., my_logstore.py) that inherits from LogStore (from sec_gemini.logs_mcp.common.logstore) and implements the describe_logs and search_logs asynchronous methods.

Here is a template demonstrating how to structure your custom log store:

import datetime
import sys
from sec_gemini.logs_mcp.common import logstore as ls
class MyCustomLogStore(ls.LogStore):
def __init__(self):
# You can access command line arguments passed to the script via sys.argv.
# This is useful for passing API tokens, file paths, or custom query configs.
self.args = sys.argv[1:]
async def describe_logs(self) -> ls.LogDescriptions:
"""Describe the available log sources, event counts, and sample records."""
return ls.LogDescriptions(
status=ls.ResultStatus.SUCCESS,
descriptions=[
ls.LogDescription(
log_type="custom:app_logs",
description="Application event logs retrieved from custom source.",
per_day_counts=[("2026-07-06", 120)],
examples=[
ls.LogRecordResult(
record_id="ex-1",
log_type="custom:app_logs",
timestamp=datetime.datetime.now(datetime.timezone.utc),
timestamp_desc="Event Time",
message="User login succeeded for user@example.com",
enrichment="ip_address=192.168.1.100",
)
],
)
],
)
async def search_logs(
self,
log_type: str | list[str] | None,
limit: int,
at_or_after: datetime.datetime | None,
at_or_before: datetime.datetime | None,
contains_at_least_one_of: list[str] | None,
must_contain_all_of: list[str] | None,
must_not_contain_any_of: list[str] | None,
order_by: ls.Order,
exclude_log_type: str | list[str] | None = None,
) -> ls.SearchResult:
"""Search and filter logs using the search parameters specified by the agent."""
# Replace this with your custom query/API call logic using the parameters.
records = [
ls.LogRecordResult(
record_id="rec-123",
log_type="custom:app_logs",
timestamp=datetime.datetime.now(datetime.timezone.utc),
timestamp_desc="Event Time",
message="Database connection established",
enrichment=None,
)
]
return ls.SearchResult(
status=ls.ResultStatus.SUCCESS,
results=records,
)

To run the Sec-Gemini TUI for digital forensics using your custom backend implementation, use the following command:

Terminal window
sec-gemini dfir --custom="path/to/custom_logstore.py"

If your custom script requires command-line arguments (e.g., database credentials or environment flags), you can pass them within the same string option:

Terminal window
sec-gemini dfir --custom="./my_logstore.py --db_host localhost --port 5432"

If your logs are managed in a Timesketch server, you can perform autonomous investigations directly against your sketches. In this setup, you run a stateless Timesketch BYOT Client container alongside your Timesketch server to establish an outbound reverse tunnel, allowing the cloud agent to query logs in-place. For setup and deployment steps, see the official Timesketch Investigation View Setup Guide.

Split Execution (Local Log Server + Web UI / SDK)

Section titled “Split Execution (Local Log Server + Web UI / SDK)”

To perform an investigation where the logs server runs locally on your workstation while the investigation itself is managed via the Web UI or Python SDK, you can perform a split execution.

The forensics capability of Sec-Gemini is designed to operate on logs that are both massive in quantity (multiple TBs) and sensitive. To give Sec-Gemini the ability to access the logs under investigation, you run a local logs server. Note that when using the sec-gemini dfir command, this step is automated. The local logs server is built into the sec-gemini-byot binary. There are currently three supported log sources for the local logs server:

  • Chronicle (Google SecOps):
    Terminal window
    sec-gemini-byot --no-base-tools --dfir-chronicle=CUSTOMER_ID,PROJECT_ID,REGION
  • Local SQLite3 database file:
    Terminal window
    sec-gemini-byot --no-base-tools --dfir-sqlite=PATH_TO_FILE.DB
  • Custom Python script:
    Terminal window
    sec-gemini-byot --no-base-tools --dfir-custom="path/to/custom_logstore.py"

[!NOTE] The custom script option also supports arguments:

Terminal window
sec-gemini-byot --no-base-tools --dfir-custom="./my_logstore.py --arg val"

Once the local logs server is running in the background, choose one of the following methods to run the investigation:

Use the SDK to create a session, enable the byot tool tunnel (which routes queries back to your local logs server), and prompt the agent in dfir mode:

import asyncio
from sec_gemini import SecGemini
async def main():
async with SecGemini(api_key="YOUR_API_KEY") as client:
session = await client.sessions.create()
# Ensure the BYOT tool tunnel is active for this session
await session.mcps.set(["byot"])
# Enable the digital forensics engine by setting the mode to dfir
await session.prompt(
"Perform a forensics investigation on the given logs.",
meta={"config.mode": "dfir"}
)
async for msg in session.messages.stream():
msg_type = msg.get("message_type", "")
content = msg.get("content", "")
if msg_type == "MESSAGE_TYPE_RESPONSE":
print(f"Agent: {content}")
elif msg_type == "MESSAGE_TYPE_TOOL_CALL":
print(f" [tool] {msg.get('title', '')}")
asyncio.run(main())

You can also run the investigation directly through the browser Web UI by manually configuring the session parameters:

  1. Open the Sec-Gemini Web UI and select New Session.
  2. Toggle on DFIR Mode in the session options.
  3. Type your prompt (e.g., “Perform a forensics investigation on the available logs”) and submit.