Using Semantic Indexing¶
Semantic indexing builds a local vector database of your binary's analysis artifacts. Once indexed, you can search for functions, strings, and symbols by describing what they do rather than what they are named — using either the concept() function in BNQL or the concept() function in the Sidekick Python API.
Important
Semantic indexing is disabled by default. If sidecar management is enabled, it requires llama.cpp (llama-server) to be installed on your system. If sidecar management is disabled, Sidekick uses the configured embedding and reranking service URLs directly. To enable semantic indexing, set sidekick.semantic_index.enabled to true in Edit > Preferences > Settings and restart Binary Ninja. When semantic indexing is disabled, concept() queries return empty results.
What semantic indexing enables¶
Without semantic indexing, searches are structural: you find things by name, address, or byte pattern. With semantic indexing, you can express intent in natural language:
- Find functions related to "TLS handshake" even when they are named
sub_401000. - Search for strings mentioning "credential" or "password" across the whole binary.
- Combine semantic results with structural BNQL predicates, such as "functions related to network communication that also call
send".
The index is built and maintained locally. No binary content is sent to the cloud.
How indexing works¶
When you open a binary, Sidekick registers a notification listener on the Binary View and begins a low-priority bootstrap enumeration in the background. The enumeration walks:
- Functions — decompiled source, disassembly, and all IL levels
- Strings — referenced strings and strings above a configurable minimum length
- Symbols — all symbol entries in the binary
Each object is rendered to text and passed to a locally running embedding sidecar. The sidecar uses a small language model to produce a vector representation, which is stored in a local SQLite-based vector database. After the initial bootstrap, the index stays current automatically: function additions, updates, and removals are reflected in near-real time via Binary Ninja notifications.
Note
Indexing runs at low priority to avoid interfering with Binary Ninja's analysis. The initial bootstrap on a large binary can take a few minutes.
Configuring the semantic index¶
Access the settings dialog from the Binary Ninja menu bar: Plugins > Sidekick > Configure Semantic Indexing...

The dialog has three tabs:
Semantic Index tab¶
- Enable Semantic Indexing (Experimental) — Master toggle (off by default). Enabling it starts the embedding sidecar and background enumeration process. Requires
llama-serveron yourPATH. Requires a Binary Ninja restart to take effect. - Binary View Indexing — Indexes functions, strings, and symbols from open binary views.
- Chat Message Indexing — Indexes conversation history from the Sidekick chat panel.
The Activity panel shows live processing state:
- Status —
Idlewhen no work is queued;Busywhile items are being processed. - Pending — number of items currently waiting in the queue.
- Re-queue (dirty) — items that have been modified and are waiting to be re-indexed.
- Workers — active/total worker threads (e.g.,
2/4 alive). - Processed OK — cumulative count of items indexed successfully.
- Processed Error — cumulative count of items that failed to index.
The Storage panel shows the on-disk and in-memory state:
- Total Embeddings — number of searchable embedding vectors stored in the database.
- Database Size — size of the SQLite vector database on disk (in MB).
- Vector Integrity — reports
OKwhen embedding records and vectors are consistent; shows a mismatch description if orphaned or missing vectors are detected. - LRU Cache Entries — number of in-memory cached embeddings and the cache capacity (e.g.,
128 / 1024).
Embedding Service tab¶
Controls the service that computes embeddings. The default model is ggml-org/embeddinggemma-300M-GGUF (approximately 330 MB). Sidekick uses http://<host>:<port> for embedding requests. The Manage Sidecar toggle controls whether Sidekick launches and stops the local llama.cpp process for that endpoint; disable it when another process owns the configured URL, even if the host is localhost.
Note
llama-server (from the llama.cpp project) must be installed on your system only for services where Manage Sidecar is enabled. If the binary is not on your PATH, those managed sidecars will fail to start.
Reranking Service tab¶
Controls the reranking service, which improves result quality by re-scoring the top candidates from the initial vector search using a cross-encoder model. The default model is minhtd14/jina-reranker-v2-base-multilingual-Q8_0-GGUF. Like embeddings, Manage Sidecar controls whether Sidekick owns the local process; the host alone does not imply ownership.
Tip
Managed sidecars start automatically when Sidekick initializes. If a sidecar fails to start, check the Embedding Service and Reranking Service tabs for error messages and verify that the configured host and port are available.
Adjusting string admission filters¶
Sidekick filters short or low-quality strings before indexing them to keep the database compact and search results relevant. You can tune the thresholds in Binary Ninja's Settings panel under the Sidekick group:
| Setting | Default | Description |
|---|---|---|
sidekick.semantic_index.adapters.binaryview.strings.filter.enabled |
true |
Enable or disable string admission filtering. |
sidekick.semantic_index.adapters.binaryview.strings.filter.min_length |
3 |
Minimum trimmed length for any referenced string. |
sidekick.semantic_index.adapters.binaryview.strings.filter.unreferenced_min_length |
8 |
Minimum trimmed length for unreferenced strings. |
sidekick.semantic_index.adapters.binaryview.strings.filter.min_alpha_chars |
3 |
Minimum alphabetic character count for unreferenced strings. |
sidekick.semantic_index.adapters.binaryview.strings.filter.max_punctuation_percent |
60 |
Maximum punctuation percentage for unreferenced strings. |
Changes to filter settings take effect the next time a string is encountered by the adapter.
Searching with concept() in BNQL¶
The concept() function performs a semantic search against the index. You can use it as a BNQL starting point or inside a predicate.
Signature:
text_or_obj— A text description or a Binary Ninja object (function, string, etc.) to use as the query anchor.similarity— Minimum cosine similarity score, from0.0to1.0. Lower values return more results; higher values restrict to closer matches. The default0.45is a balanced starting point.limit— Maximum number of results to return. Defaults to20.
Searching by description¶
Run a semantic search from the BNQL query bar in the Sidekick chat panel or any tool that accepts BNQL:
This returns up to 20 functions, strings, and symbols whose embeddings are semantically close to "file encryption".
To restrict results to a specific object type, combine concept() with a traversal step:
Using concept() in a predicate¶
Filter a structural result set with a semantic condition:
This returns only functions that:
- Appear in the binary as functions (structural step), and
- Are semantically related to "TLS handshake" (semantic predicate).
Combining semantic and structural queries¶
Returns functions related to "network communication" that also call send.
Returns semantically relevant results while excluding functions whose name contains "log".
Note
concept() requires the semantic index to be enabled and running. If the index is disabled (the default) or the embedding sidecar is not yet ready, the function returns an empty set and logs a warning to the Binary Ninja log.
To save concept() results for later review or to feed them into further analysis, add them to an Index.
Searching with concept() in the Python API¶
The sidekick.api module exposes a concept() function for use in Binary Ninja scripts.
Signature:
concept(
target,
subject,
object_type=None,
similarity=0.45,
limit=10,
) -> list[tuple[Any, float, float]]
target— ABinaryVieworBinaryViewSetthat has an active Sidekick session.subject— A text string or a Binary Ninja object to use as the query anchor.object_type— An optional filter such as"function","string", or"symbol". PassNoneto search all indexed types.similarity— Minimum cosine similarity score (0.0—1.0). Default0.45.limit— Maximum number of results to return. Default10.
Returns a list of (object, score, similarity_score) tuples sorted by relevance. score is the final ranking score after reranking; similarity_score is the raw cosine similarity from the vector search.
Basic example¶
import sidekick.api as api
results = api.concept(bv, "file encryption")
for obj, score, similarity in results:
print(f"{obj} score={score:.3f} similarity={similarity:.3f}")
Filtering by object type¶
# Return only functions
func_results = api.concept(bv, "TLS handshake", object_type="function")
for func, score, _ in func_results:
print(f"{func.name} at {func.start:#x} score={score:.3f}")
Using a Binary Ninja object as the query anchor¶
You can pass any indexed object as subject. Sidekick looks up or computes the embeddings for that object and uses them as the query vector. This lets you find objects that are semantically similar to a known one:
# Find functions similar to a known function
current_func = bv.get_function_at(0x401000)
similar = api.concept(bv, current_func, object_type="function", limit=5)
Headless scripts¶
Semantic indexing is disabled by default in headless sessions. Enable it explicitly:
with api.sidekick("/tmp/sidekick-headless", semantic_index=True, cleanup_app_dir=True) as sk:
with sk.session("/tmp/sample.bndb") as sess:
sess.wait_for_semantic_index_ready(timeout_secs=120.0)
results = api.concept(sess.bv, "credential handling", object_type="function")
for func, score, _ in results:
print(func.name, score)
Set cleanup_app_dir=True when the headless app directory is only scratch state. Leave it at the default False if you want to preserve workspace and semantic-index databases across runs.
Warning
In headless mode, call sess.wait_for_semantic_index_ready(...) before relying on concept() results. The search API remains non-blocking and may return an empty list until bootstrap, debounced events, and pending semantic-index jobs have drained.
What gets indexed¶
| Object type | Indexed content |
|---|---|
function |
Decompiled HLIL source; disassembly; function name and signature |
string |
String value; address; reference context |
symbol |
Symbol name; type; address |
The index is scoped to the binary view. If you have multiple binaries open in a project, each binary view is indexed separately, and concept() queries return results from all indexed views in the active session.
Note
Basic blocks, IL instructions, data variables, types, sections, and segments are supported by the adapter's object model but are not included in the default bootstrap enumeration. They may be indexed as a result of notification-driven updates when Binary Ninja analysis produces or modifies them.
Checking index status¶
To verify that indexing is active and progressing:
- Open Plugins > Sidekick > Configure Semantic Indexing....
- Select the Semantic Index tab.
- The Activity panel shows the current queue depth and worker status.
When the Status field reads Idle and the Pending count is 0, the index is current.
You can also query index statistics from a script:
from sidekick.api import get_session_for_view
session = get_session_for_view(bv)
if session.semantic_index:
stats = session.semantic_index.stats()
print(stats)
Related pages¶
- BNQL Reference — Full BNQL syntax and function reference.
- Sidekick API Guide — Using
execute_bnqland theconcept()API in scripts.