<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Muhammad Ikhwan Fathulloh</title>
    <description>The latest articles on DEV Community by Muhammad Ikhwan Fathulloh (@muhammadikhwanfathulloh).</description>
    <link>https://dev.to/muhammadikhwanfathulloh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3869125%2Fdda57b91-6655-4326-803e-6700eebe3247.jpg</url>
      <title>DEV Community: Muhammad Ikhwan Fathulloh</title>
      <link>https://dev.to/muhammadikhwanfathulloh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/muhammadikhwanfathulloh"/>
    <language>en</language>
    <item>
      <title>Vector Similarity Search with DuckDB: A Practical Guide to the VSS Extension</title>
      <dc:creator>Muhammad Ikhwan Fathulloh</dc:creator>
      <pubDate>Sat, 15 Aug 2026 14:21:20 +0000</pubDate>
      <link>https://dev.to/muhammadikhwanfathulloh/vector-similarity-search-with-duckdb-a-practical-guide-to-the-vss-extension-p5c</link>
      <guid>https://dev.to/muhammadikhwanfathulloh/vector-similarity-search-with-duckdb-a-practical-guide-to-the-vss-extension-p5c</guid>
      <description>&lt;p&gt;Most people reach for a dedicated vector database — Pinecone, Qdrant, Milvus, pgvector — the moment a project needs embedding search. But if you are already using DuckDB for analytics or building a lightweight RAG pipeline, there is a good chance you do not need another moving part. DuckDB ships an official &lt;code&gt;vss&lt;/code&gt; extension that adds HNSW-based approximate nearest neighbor search directly on top of its native &lt;code&gt;ARRAY&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;This article walks through what the extension does, how to use it, and where its limits are.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the VSS Extension Actually Is
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;vss&lt;/code&gt; is an experimental core extension that adds indexing support to accelerate similarity search over DuckDB's fixed-size &lt;code&gt;ARRAY&lt;/code&gt; columns. It implements HNSW (Hierarchical Navigable Small Worlds), the same graph-based ANN algorithm used by most production vector search engines. In practice, this means you can store embeddings as a normal column, build an index on it, and run &lt;code&gt;ORDER BY ... LIMIT&lt;/code&gt; queries that DuckDB will automatically route through the index instead of a full scan.&lt;/p&gt;

&lt;p&gt;Because it is embedded, there is no separate service to run, no network hop, and no extra infrastructure to operate — the vector index lives in the same process as the rest of your SQL engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting It Up
&lt;/h2&gt;

&lt;p&gt;Installation follows DuckDB's usual extension pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;INSTALL&lt;/span&gt; &lt;span class="n"&gt;vss&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;LOAD&lt;/span&gt; &lt;span class="n"&gt;vss&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a table with a fixed-size &lt;code&gt;ARRAY&lt;/code&gt; column and build an HNSW index on it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;my_vector_table&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;my_vector_table&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;array_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ra&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;my_hnsw_index&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;my_vector_table&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;HNSW&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the dimensionality (&lt;code&gt;FLOAT[3]&lt;/code&gt; here) must be fixed at table-creation time — DuckDB's &lt;code&gt;ARRAY&lt;/code&gt; type is size-constrained, unlike the variable-length &lt;code&gt;LIST&lt;/code&gt; type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Querying with the Index
&lt;/h2&gt;

&lt;p&gt;Once the index exists, DuckDB will use it automatically whenever a query orders by a supported distance function against a constant vector and limits the result set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;my_vector_table&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;array_distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can confirm the index is actually being used by checking the query plan for an &lt;code&gt;HNSW_INDEX_SCAN&lt;/code&gt; node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;my_vector_table&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;array_distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For one-shot nearest-neighbor lookups, the overloaded &lt;code&gt;min_by(col, arg, n)&lt;/code&gt; aggregate is also index-accelerated when &lt;code&gt;arg&lt;/code&gt; matches a supported distance function, and it conveniently returns the full matched row as a struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;min_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_vector_table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;array_distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;result&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;my_vector_table&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Distance Metrics
&lt;/h2&gt;

&lt;p&gt;By default, HNSW indexes use &lt;code&gt;l2sq&lt;/code&gt; (squared Euclidean distance), matching &lt;code&gt;array_distance&lt;/code&gt;. You can choose a different metric at index-creation time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;my_hnsw_cosine_index&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;my_vector_table&lt;/span&gt;
&lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;HNSW&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;metric&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'cosine'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;l2sq&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;array_distance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Euclidean distance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cosine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;array_cosine_distance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cosine similarity distance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;array_negative_inner_product&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Negative inner product&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For most embedding models used in RAG or semantic search (OpenAI, Sentence-Transformers, etc.), cosine similarity is the natural choice. You can also build multiple indexes on the same column with different metrics, or index multiple columns independently — each &lt;code&gt;HNSW&lt;/code&gt; index applies to exactly one column.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tuning the Index
&lt;/h2&gt;

&lt;p&gt;Index quality and search speed are controlled by a handful of hyperparameters, all familiar to anyone who has tuned HNSW before:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ef_construction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;Candidate vertices considered while building the index. Higher = more accurate, slower build.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ef_search&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;Candidate vertices considered per query. Higher = more accurate, slower search.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Max neighbors per graph vertex. Higher = more accurate, slower build.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;M0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2 × &lt;code&gt;M&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Base connectivity at the zero-th graph level.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;ef_search&lt;/code&gt; can also be overridden per connection at runtime without rebuilding the index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;hnsw_ef_search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- ...run queries...&lt;/span&gt;
&lt;span class="k"&gt;RESET&lt;/span&gt; &lt;span class="n"&gt;hnsw_ef_search&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful when you want to dial accuracy up or down depending on the query, without paying the cost of a full reindex.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persistence: The Part You Need to Read Carefully
&lt;/h2&gt;

&lt;p&gt;This is the biggest practical caveat. By default, &lt;code&gt;HNSW&lt;/code&gt; indexes can only be created on &lt;strong&gt;in-memory&lt;/strong&gt; databases. If you want the index to persist in a disk-backed &lt;code&gt;.duckdb&lt;/code&gt; file, you must explicitly opt in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;hnsw_enable_experimental_persistence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is locked behind this flag because WAL (write-ahead log) recovery is not yet fully implemented for custom extension indexes. If DuckDB crashes or is killed while there are uncommitted changes to an HNSW-indexed table, the index can end up corrupted or lose data. The docs are explicit that this is not recommended for production use.&lt;/p&gt;

&lt;p&gt;If you do enable it and hit an unexpected shutdown, recovery is possible by starting DuckDB separately, loading &lt;code&gt;vss&lt;/code&gt;, and then &lt;code&gt;ATTACH&lt;/code&gt;ing the database file before letting WAL replay run — this makes the HNSW functionality available during recovery.&lt;/p&gt;

&lt;p&gt;When persistence is enabled, the entire index is serialized to disk on every checkpoint (no incremental updates) and deserialized back into memory on the next access after restart — which is still generally faster than dropping and rebuilding it from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For a local RAG prototype or an in-memory analytical session, this is a non-issue.&lt;/strong&gt; For anything that needs durable, crash-safe vector storage in production, treat this as a hard constraint and plan accordingly — or keep the source-of-truth embeddings elsewhere and rebuild the index on startup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inserts, Updates, Deletes
&lt;/h2&gt;

&lt;p&gt;The index supports mutation after creation, with two practical notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is faster to build the index &lt;strong&gt;after&lt;/strong&gt; bulk-loading data, since the initial build parallelizes better than incremental inserts.&lt;/li&gt;
&lt;li&gt;Deletes are lazy: rows are marked deleted rather than removed from the graph immediately, which causes gradual quality and performance degradation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To reclaim this, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;PRAGMA&lt;/span&gt; &lt;span class="n"&gt;hnsw_compact_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'my_hnsw_index'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or periodically drop and recreate the index if the table sees heavy churn.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Fuzzy Joins with &lt;code&gt;vss_join&lt;/code&gt; and &lt;code&gt;vss_match&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Beyond single-query nearest-neighbor search, the extension ships two table macros for matching two sets of vectors against each other — useful for deduplication, entity resolution, or batch retrieval:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;haystack&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;needle&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search_vec&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;haystack&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;row_number&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;array_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ra&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;needle&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vss_join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;search_vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;vss_match&lt;/code&gt; offers the same brute-force k-NN matching but as a lateral join, grouping results per left-table row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vss_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;search_vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important: &lt;strong&gt;neither macro uses the HNSW index&lt;/strong&gt; — they perform brute-force search. They are convenience utilities for correctness, not performance, though the docs note they may become index-accelerated in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations to Keep in Mind
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Only 32-bit &lt;code&gt;FLOAT&lt;/code&gt; vectors are supported today — no &lt;code&gt;DOUBLE&lt;/code&gt;, no quantized/int8 vectors.&lt;/li&gt;
&lt;li&gt;The index is not buffer-managed and must fit entirely in RAM.&lt;/li&gt;
&lt;li&gt;Index memory does not count against DuckDB's &lt;code&gt;memory_limit&lt;/code&gt; setting, so it is easy to overshoot available memory without warning.&lt;/li&gt;
&lt;li&gt;Persistent indexes require the experimental flag discussed above.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vss_join&lt;/code&gt; / &lt;code&gt;vss_match&lt;/code&gt; never use the index, regardless of whether one exists on the underlying columns.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When This Makes Sense
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;vss&lt;/code&gt; is a strong fit when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are building a local-first or embedded RAG system (for example, a FastAPI service backed by DuckDB and a local GGUF model) and do not want to run a separate vector database.&lt;/li&gt;
&lt;li&gt;Your embedding volume is modest enough to fit in memory, and you are comfortable with in-memory or experimental-persistence trade-offs.&lt;/li&gt;
&lt;li&gt;You want vector search to live in the same SQL surface as your relational and analytical queries — joining structured metadata filters with &lt;code&gt;ORDER BY array_cosine_distance(...)&lt;/code&gt; in a single statement, without shipping data to another system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is a weaker fit if you need durable, crash-safe indexes at scale in a multi-writer production environment — for that, a dedicated vector database or an extension like pgvector on a durable RDBMS is currently the safer choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;vss&lt;/code&gt; extension turns DuckDB into a capable, embedded ANN engine: &lt;code&gt;INSTALL&lt;/code&gt;/&lt;code&gt;LOAD&lt;/code&gt; the extension, store embeddings in a fixed-size &lt;code&gt;ARRAY&lt;/code&gt; column, &lt;code&gt;CREATE INDEX ... USING HNSW&lt;/code&gt;, and query with &lt;code&gt;ORDER BY array_distance(...) LIMIT k&lt;/code&gt;. It supports L2, cosine, and inner-product metrics, exposes the usual HNSW tuning knobs, and even offers brute-force join macros for batch matching. The one thing to plan around carefully is persistence — it is opt-in and explicitly experimental, so treat durability as something you design for rather than assume.&lt;/p&gt;

</description>
      <category>database</category>
      <category>rag</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Secure File and Photo Upload Validation in PHP: Beyond Checking the Extension</title>
      <dc:creator>Muhammad Ikhwan Fathulloh</dc:creator>
      <pubDate>Mon, 10 Aug 2026 12:57:45 +0000</pubDate>
      <link>https://dev.to/muhammadikhwanfathulloh/secure-file-and-photo-upload-validation-in-php-beyond-checking-the-extension-4eie</link>
      <guid>https://dev.to/muhammadikhwanfathulloh/secure-file-and-photo-upload-validation-in-php-beyond-checking-the-extension-4eie</guid>
      <description>&lt;p&gt;File upload is a feature present in almost every web application, from document uploads and profile photos to report attachments. Unfortunately, it's also one of the most common entry points for attacks: web shells disguised as images, HTML files containing malicious scripts, or files deliberately oversized to overload the server.&lt;/p&gt;

&lt;p&gt;This article covers a more thorough approach to validating file and photo uploads in PHP, drawn from experience building an upload helper in a Laravel project. The code examples here are simplified to avoid depending on any specific storage service, so they can be adapted to local disk, S3, or any other storage backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Extension Validation Alone Isn't Enough
&lt;/h2&gt;

&lt;p&gt;A common pattern looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getClientOriginalExtension&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$extension&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'pdf'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// reject&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that &lt;code&gt;getClientOriginalExtension()&lt;/code&gt; reads the filename sent by the user's browser. This filename can be manipulated entirely. A file named &lt;code&gt;shell.jpg&lt;/code&gt; could actually contain PHP code. The extension is just a label, not proof of the file's actual content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layers of Validation to Combine
&lt;/h2&gt;

&lt;p&gt;Secure upload validation shouldn't rely on a single check. It works best as several complementary layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Validate MIME Type from File Content, Not from the Client
&lt;/h3&gt;

&lt;p&gt;Use a function that reads the file's opening bytes (magic numbers), not the header sent by the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getRealMimeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$finfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;finfo_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FILEINFO_MIME_TYPE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$mime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;finfo_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$finfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;finfo_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$finfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$mime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$allowedMimes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'image/jpeg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'image/png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'application/pdf'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getRealMimeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPathname&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="nv"&gt;$allowedMimes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// reject&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;finfo&lt;/code&gt;, file type detection is based on actual content, so a file with a swapped extension alone won't pass.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Limit File Size Up Front
&lt;/h3&gt;

&lt;p&gt;Besides saving resources, a size limit also prevents denial-of-service attacks through oversized files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$maxFileSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 2 MB&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getSize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$maxFileSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// reject, and state the maximum limit in the error message&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ideally this limit should also be enforced at the web server level (e.g. &lt;code&gt;client_max_body_size&lt;/code&gt; in Nginx or &lt;code&gt;upload_max_filesize&lt;/code&gt; in php.ini), not just in application code.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Scan Content for Dangerous Patterns
&lt;/h3&gt;

&lt;p&gt;For files like images that shouldn't contain any executable code, a simple check for suspicious patterns can serve as an additional layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPathname&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nv"&gt;$dangerousPatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;?php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;script'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;html'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dangerousPatterns&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;stripos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// reject&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this is not a substitute for antivirus or malware scanning, only a cheap additional filter for the most common cases. For production needs with higher stakes, integrating ClamAV or a third-party scanning service is recommended.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Never Trust the Original Filename
&lt;/h3&gt;

&lt;p&gt;A filename from the user can contain path traversal characters like &lt;code&gt;../../etc/passwd&lt;/code&gt; or characters that cause problems on certain filesystems. Always generate a new filename on the server under controlled conditions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;generateSafeFilename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$originalName&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pathinfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$originalName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;PATHINFO_EXTENSION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$safeName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/[^a-zA-Z0-9_-]/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'_'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;pathinfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$originalName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;PATHINFO_FILENAME&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'_'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;strtolower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$safeName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;strtolower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$extension&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final filename should be built from a combination of a timestamp, a random identifier, or a user ID, never copied raw from user input.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Store Outside the Web Root, Serve Through Application Code
&lt;/h3&gt;

&lt;p&gt;Uploaded files shouldn't be stored directly in a publicly accessible directory. Store them outside &lt;code&gt;public/&lt;/code&gt;, then create a dedicated endpoint that verifies access rights before serving the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/files/{id}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;findOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;abort_unless&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;can&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'view'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/private/'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach prevents a file that passed validation from still being directly executable by the web server, since the server never serves it as a static path.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Use a Whitelist, Not a Blacklist
&lt;/h3&gt;

&lt;p&gt;Instead of rejecting a list of known-dangerous file types (which can easily be incomplete), explicitly define which types are allowed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$allowedExtensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'jpeg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'pdf'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$allowedMimes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'image/jpeg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'image/png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'application/pdf'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A whitelist approach is far safer because it defaults to rejecting anything unrecognized, rather than only rejecting what's already known to be harmful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting the Layers Together
&lt;/h2&gt;

&lt;p&gt;Here's a validation function that combines all the points above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validateUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;strtolower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getClientOriginalExtension&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$extension&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'allowed_extensions'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'File extension is not allowed.'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$realMime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getRealMimeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPathname&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$realMime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'allowed_mimes'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'File type does not match its actual content.'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getSize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'max_size'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'File size exceeds the maximum limit.'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPathname&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'dangerous_patterns'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;stripos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'File contains disallowed content.'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'filename'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;generateSafeFilename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getClientOriginalName&lt;/span&gt;&lt;span class="p"&gt;())];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Secure upload validation isn't about adding a single, "smartest" check. It's about layering several simple safeguards that cover each other's blind spots: MIME type from actual content, size limits, content scanning, controlled filename generation, and storage outside public access. No single layer is perfect on its own, but the combination makes the attack surface far harder to exploit.&lt;/p&gt;

&lt;p&gt;For applications with higher security requirements, also consider adding external malware scanning and audit logging for every upload activity, so every incoming file can be traced back to its origin and history.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>laravel</category>
      <category>php</category>
      <category>security</category>
    </item>
    <item>
      <title>T-Guard: An Integrated Open-Source SOC Solution for Your Organization</title>
      <dc:creator>Muhammad Ikhwan Fathulloh</dc:creator>
      <pubDate>Fri, 12 Jun 2026 15:44:45 +0000</pubDate>
      <link>https://dev.to/muhammadikhwanfathulloh/t-guard-an-integrated-open-source-soc-solution-for-your-organization-1cob</link>
      <guid>https://dev.to/muhammadikhwanfathulloh/t-guard-an-integrated-open-source-soc-solution-for-your-organization-1cob</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In an increasingly complex digital era, cyber threats continue to evolve at a rapid pace. Organizations of all sizes need security systems that are both robust and efficient. Enter &lt;strong&gt;T-Guard&lt;/strong&gt; an innovative Security Operations Center (SOC) solution that harnesses the power of leading open-source tools into a single, cohesive platform.&lt;/p&gt;

&lt;p&gt;T-Guard is not just a collection of security tools. It is a complete ecosystem designed to provide comprehensive protection for your digital assets. Developed with support from &lt;strong&gt;Universitas Indonesia - Japan International Cooperation Agency (UI-JICA Project)&lt;/strong&gt; , this project demonstrates how collaboration between academia and security practitioners can produce truly impactful solutions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why T-Guard?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajuo7q5iy6362ooou95h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajuo7q5iy6362ooou95h.png" alt="T-Guard Source: https://tguard.org/" width="799" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;T-Guard addresses a major challenge organizations face when building a SOC: &lt;strong&gt;integration&lt;/strong&gt;. Instead of hiring a large team to manage multiple disparate platforms, T-Guard unifies four key pillars of cybersecurity under one roof:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;License&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Wazuh&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time monitoring, threat detection, and compliance&lt;/td&gt;
&lt;td&gt;GPL v2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DFIR-IRIS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Incident management and digital forensics&lt;/td&gt;
&lt;td&gt;LGPL v3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shuffle&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Security workflow automation (SOAR)&lt;/td&gt;
&lt;td&gt;AGPL v3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MISP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open-source threat intelligence platform&lt;/td&gt;
&lt;td&gt;AGPL v3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;By combining these four tools, T-Guard delivers a complete defense lifecycle: &lt;strong&gt;detection&lt;/strong&gt; (Wazuh), &lt;strong&gt;intelligence&lt;/strong&gt; (MISP), &lt;strong&gt;automated response&lt;/strong&gt; (Shuffle), and &lt;strong&gt;incident management&lt;/strong&gt; (IRIS).&lt;/p&gt;




&lt;h2&gt;
  
  
  Simple Installation
&lt;/h2&gt;

&lt;p&gt;One of T-Guard's greatest strengths is its ease of installation. Within minutes, you can have a fully functional SOC up and running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OS:&lt;/strong&gt; Ubuntu 24.04 LTS (fresh machine recommended)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection:&lt;/strong&gt; Broadband internet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Minimum requirements (trial deployments):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU: 8 cores&lt;/li&gt;
&lt;li&gt;RAM: 16 GB (swap memory required)&lt;/li&gt;
&lt;li&gt;Storage: 100 GB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Standard requirements (production environments):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU: 8 cores&lt;/li&gt;
&lt;li&gt;RAM: 32 GB (no swap needed)&lt;/li&gt;
&lt;li&gt;Storage: 250 GB&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installation Steps
&lt;/h3&gt;

&lt;p&gt;Installation is performed through an interactive command-line script. Here's the overview:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clone the repository:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/sguresearcher/nusantara.git
&lt;span class="nb"&gt;cd &lt;/span&gt;nusantara
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x setup.sh
./setup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Main menu options:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt; → Update system &amp;amp; install dependencies (Docker, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2&lt;/code&gt; → Install T-Guard SOC package (choose environment: local VM or cloud)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3&lt;/code&gt; → Module integration (API keys, webhooks)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;4&lt;/code&gt; → Use case simulation menu&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The installation process runs automatically. Upon completion, you will receive a table with dashboard access information for each module:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;th&gt;Username&lt;/th&gt;
&lt;th&gt;Password&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wazuh&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://&amp;lt;ip&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;admin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SecretPassword&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IRIS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://&amp;lt;ip&amp;gt;:8443&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;administrator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MySuperAdminPassword!&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shuffle&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://&amp;lt;ip&amp;gt;:3001&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Create your own&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Create your own&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MISP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://&amp;lt;ip&amp;gt;:1443&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;admin@admin.test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;admin&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; SSL warnings in your browser are normal due to self-signed certificates. For production, installing official certificates is recommended.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Use Cases: Proving T-Guard's Capabilities
&lt;/h2&gt;

&lt;p&gt;The T-Guard documentation provides three simulated attack scenarios to demonstrate how all components work together automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Brute-Force Detection
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Simulated repeated login attempts against SSH.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wazuh detects the brute-force attempts&lt;/li&gt;
&lt;li&gt;Alerts are sent to Shuffle&lt;/li&gt;
&lt;li&gt;Shuffle automatically creates a new incident ticket in IRIS&lt;/li&gt;
&lt;li&gt;The SOC team can immediately review attack details and begin investigation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Malware Detection with Auto-Response
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; A malicious file (malware) is uploaded to the system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wazuh detects the file&lt;/li&gt;
&lt;li&gt;Integration with &lt;strong&gt;VirusTotal&lt;/strong&gt; (API key required) confirms the file as malware&lt;/li&gt;
&lt;li&gt;Wazuh automatically executes a response (e.g., deleting the file)&lt;/li&gt;
&lt;li&gt;Alerts and automated responses are recorded in the Wazuh dashboard&lt;/li&gt;
&lt;li&gt;A new incident ticket is created in IRIS&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Web Defacement Detection
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; A sample website page is defaced.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wazuh's Integrity Monitoring module detects file content changes&lt;/li&gt;
&lt;li&gt;Alert with rule ID 550 (&lt;em&gt;Integrity Checksum Changed&lt;/em&gt;) is triggered&lt;/li&gt;
&lt;li&gt;The SOC team is immediately notified of unauthorized changes to web assets&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Threat Intelligence Integration with MISP
&lt;/h2&gt;

&lt;p&gt;One of the most powerful features is the integration with &lt;strong&gt;MISP&lt;/strong&gt; (&lt;em&gt;Malware Information Sharing Platform&lt;/em&gt;). After installation, administrators can import over 100 threat feeds from MISP's &lt;code&gt;defaults.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;code&gt;https://github.com/MISP/MISP/blob/2.4/app/files/feed-metadata/defaults.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy the entire JSON content&lt;/li&gt;
&lt;li&gt;In MISP Dashboard: &lt;strong&gt;Sync Actions&lt;/strong&gt; → &lt;strong&gt;Feeds&lt;/strong&gt; → &lt;strong&gt;Import Feeds from JSON&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select all feeds → &lt;strong&gt;Enable Selected&lt;/strong&gt; → &lt;strong&gt;Fetch and Store All Feed Data&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The threat intelligence from MISP is then used by Wazuh to enrich detection and perform IOC (&lt;em&gt;Indicators of Compromise&lt;/em&gt;) matching.&lt;/p&gt;




&lt;h2&gt;
  
  
  Automation Workflow: Connecting the Pieces
&lt;/h2&gt;

&lt;p&gt;A key highlight of T-Guard is how Shuffle orchestrates actions between components. For example, when Wazuh detects an alert:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A webhook trigger in Shuffle receives the alert&lt;/li&gt;
&lt;li&gt;Shuffle extracts relevant data (source IP, rule description, log details)&lt;/li&gt;
&lt;li&gt;The workflow calls the IRIS module to create a new case with all alert information&lt;/li&gt;
&lt;li&gt;The SOC team sees a complete, auto-populated ticket ready for investigation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This automation eliminates manual hand-offs and accelerates response times dramatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;T-Guard&lt;/strong&gt; is concrete proof that enterprise-grade SOC solutions can be built from well-integrated open-source components. This project is ideally suited for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Organizations looking to build an internal SOC with limited budgets&lt;/li&gt;
&lt;li&gt;Security teams wanting workflow automation without building from scratch&lt;/li&gt;
&lt;li&gt;Educational institutions and researchers studying modern SOC architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With clear installation guides, available testing scenarios, and community support, T-Guard offers a fast path toward maturing your organization's cybersecurity capabilities.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Official T-Guard Website&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://tguard.org/" rel="noopener noreferrer"&gt;https://tguard.org/&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Installation Documentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.tguard.org/installation" rel="noopener noreferrer"&gt;https://docs.tguard.org/installation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitHub Repository&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/sguresearcher/nusantara" rel="noopener noreferrer"&gt;https://github.com/sguresearcher/nusantara&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;License Information&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Available on GitHub (mix of GPL, Apache, AGPL, LGPL)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Closing Thought:&lt;/strong&gt; Cybersecurity is not about the most expensive tools  it's about the most integrated systems that are ready to respond. T-Guard proves that open source can be a solid foundation for building the digital fortresses of tomorrow.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
    </item>
    <item>
      <title>Secure Your Microservices: Meet Halimun, the High-Performance Encrypted Proxy</title>
      <dc:creator>Muhammad Ikhwan Fathulloh</dc:creator>
      <pubDate>Fri, 29 May 2026 15:44:21 +0000</pubDate>
      <link>https://dev.to/muhammadikhwanfathulloh/secure-your-microservices-meet-halimun-the-high-performance-encrypted-proxy-32mm</link>
      <guid>https://dev.to/muhammadikhwanfathulloh/secure-your-microservices-meet-halimun-the-high-performance-encrypted-proxy-32mm</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fed4hekpliy9fram7s8sc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fed4hekpliy9fram7s8sc.png" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;Halimun Proxy&lt;/strong&gt; a high-performance, ultra-low latency proxy tunnel system &lt;strong&gt;built from the ground up in Rust.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Rust?
&lt;/h2&gt;

&lt;p&gt;By leveraging &lt;strong&gt;Rust&lt;/strong&gt;, Halimun achieves extreme efficiency. Using the &lt;code&gt;Axum&lt;/code&gt; web framework and &lt;code&gt;Tokio&lt;/code&gt; for non-blocking asynchronous I/O, it manages to maintain a tiny footprint—running on as little as &lt;strong&gt;~15MB of RAM&lt;/strong&gt;. It’s designed to be fast, memory-safe, and incredibly stable under load.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core Security Features
&lt;/h2&gt;

&lt;p&gt;Halimun isn't just a proxy; it’s a security layer. It enforces strict request validation to ensure your internal services are never exposed to malicious actors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AES-256-CBC Encryption:&lt;/strong&gt; End-to-end payload masking. Even if your traffic is intercepted, the actual API endpoint and data remain indecipherable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HMAC-SHA256 Integrity:&lt;/strong&gt; Validates that data hasn't been tampered with in transit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replay Attack Prevention:&lt;/strong&gt; Uses &lt;code&gt;Nonce&lt;/code&gt; and timestamp verification in-memory (via &lt;code&gt;DashMap&lt;/code&gt;) to reject duplicate spoofed requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSRF Protection:&lt;/strong&gt; Built-in mechanisms to prevent attackers from targeting your internal network infrastructure (e.g., &lt;code&gt;127.0.0.1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Camouflage Routing:&lt;/strong&gt; It hides your actual API structure behind random, dummy URL segments, making traffic profiling by WAFs or human analysts nearly impossible.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Start (Docker)
&lt;/h2&gt;

&lt;p&gt;Halimun is "Docker-ready," making it easy to drop into any existing infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Configuration
&lt;/h3&gt;

&lt;p&gt;First, generate your encryption keys using the built-in generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate keys and save to .env&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; halimun-proxy &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; halimun-proxy ./halimun-proxy &lt;span class="nt"&gt;--keygen&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;env&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Deployment
&lt;/h3&gt;

&lt;p&gt;Configure your &lt;code&gt;config.yaml&lt;/code&gt; to map your backend services, then launch your cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your production proxy is now live, listening securely on port 80 while your backend services remain completely secluded within a private Docker network.&lt;/p&gt;




&lt;h2&gt;
  
  
  Under the Hood: Request Lifecycle
&lt;/h2&gt;

&lt;p&gt;Halimun uses an encrypted tunnel approach. A typical request follows this structure:&lt;br&gt;
&lt;code&gt;POST /proxy/1/SEGMENT1/SEGMENT2/SEGMENT3/SEGMENT4/SEGMENT5&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The actual data is sent via &lt;code&gt;x-www-form-urlencoded&lt;/code&gt; with the body encrypted as a Base32 string. Once received, Halimun decrypts the payload, validates the HMAC, checks the Nonce for replays, and only then forwards the request to your microservice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring &amp;amp; Management
&lt;/h2&gt;

&lt;p&gt;Security doesn't have to be a "black box." Halimun includes a sleek, &lt;strong&gt;Glassmorphism-styled Admin UI&lt;/strong&gt; accessible via your dashboard. It provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Traffic Logs:&lt;/strong&gt; See exactly what’s happening in real-time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registry Hub:&lt;/strong&gt; Overview of all your active backend mappings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Management:&lt;/strong&gt; Rotate credentials remotely to maintain high security.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Join the Community
&lt;/h2&gt;

&lt;p&gt;Whether you are building a microservices mesh or simply want to add a hardened security layer to your existing API, Halimun is a fantastic, open-source choice.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/Muhammad-Ikhwan-Fathulloh/Halimun-Proxy" rel="noopener noreferrer"&gt;Muhammad-Ikhwan-Fathulloh/Halimun-Proxy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Hub:&lt;/strong&gt; &lt;a href="https://hub.docker.com/r/ikhwan17/halimun-proxy" rel="noopener noreferrer"&gt;ikhwan17/halimun-proxy&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Have you experimented with Rust-based proxies yet?&lt;/strong&gt; Let us know how Halimun fits into your stack in the comments below!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>performance</category>
      <category>rust</category>
      <category>security</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Professional Inverse Kinematics on Arduino: Deep Dive into NocKinematics</title>
      <dc:creator>Muhammad Ikhwan Fathulloh</dc:creator>
      <pubDate>Tue, 21 Apr 2026 08:04:28 +0000</pubDate>
      <link>https://dev.to/muhammadikhwanfathulloh/professional-inverse-kinematics-on-arduino-deep-dive-into-nockinematics-7hi</link>
      <guid>https://dev.to/muhammadikhwanfathulloh/professional-inverse-kinematics-on-arduino-deep-dive-into-nockinematics-7hi</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1b5bg4n6uqtegwfkwbwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1b5bg4n6uqtegwfkwbwh.png" alt=" " width="800" height="453"&gt;&lt;/a&gt;While professional robotics often use the &lt;strong&gt;Jacobian Inverse&lt;/strong&gt; or &lt;strong&gt;Cyclic Coordinate Descent (CCD)&lt;/strong&gt;, these methods are either too "heavy" for an Arduino or produce jerky, unnatural movements.&lt;/p&gt;

&lt;p&gt;Today, we are exploring &lt;strong&gt;NocKinematics&lt;/strong&gt;, a lightweight C++ library that brings the industry-standard &lt;strong&gt;FABRIK&lt;/strong&gt; algorithm to the world of microcontrollers.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔬 The Science: Why FABRIK?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NocKinematics&lt;/strong&gt; is built upon the foundational research of &lt;strong&gt;Andreas Aristidou&lt;/strong&gt; and &lt;strong&gt;Joan Lasenby&lt;/strong&gt;. If you are looking for the academic rigor behind this library, you should refer to the original paper:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.andreasaristidou.com/publications/papers/FABRIK.pdf" rel="noopener noreferrer"&gt;&amp;gt; &lt;strong&gt;Aristidou, A., &amp;amp; Lasenby, J. (2011).&lt;/strong&gt; &lt;em&gt;"FABRIK: A fast, iterative solver for the Inverse Kinematics problem."&lt;/em&gt; Graphical Models, 73(5), 243-260.&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Why this matters for Embedded Systems:
&lt;/h3&gt;

&lt;p&gt;Unlike traditional methods that rely on expensive trigonometric functions or matrix inversions, &lt;strong&gt;FABRIK&lt;/strong&gt; (Forward And Backward Reaching Inverse Kinematics) uses an iterative approach based on finding points on a line. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Low Computational Cost:&lt;/strong&gt; Perfect for 8-bit AVR (Arduino Uno) and 32-bit ESP32 alike.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast Convergence:&lt;/strong&gt; It usually finds a solution in just a few iterations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle Constraints:&lt;/strong&gt; It gracefully handles "unreachable" targets by stretching the arm to its maximum toward the point.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  🛠️ Key Features of NocKinematics
&lt;/h2&gt;

&lt;p&gt;Developed by &lt;strong&gt;Muhammad Ikhwan Fathulloh&lt;/strong&gt; and the &lt;strong&gt;Nocturnailed Community&lt;/strong&gt;, this library bridges the gap between game engine math and hardware.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;N-Joint Support:&lt;/strong&gt; Solve kinematics for 2, 3, or even 20+ joints (ideal for snake robots or tentacles).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Optimized:&lt;/strong&gt; It avoids &lt;code&gt;std::vector&lt;/code&gt; to prevent heap fragmentation. It allocates memory exactly once during initialization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform Agnostic:&lt;/strong&gt; Runs on &lt;strong&gt;Arduino Uno, Nano, Mega, ESP8266, and ESP32.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🚀 Step-by-Step Implementation
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Define Your Robot's Anatomy
&lt;/h3&gt;

&lt;p&gt;Think of your robot as a collection of "joints" connected by "bones." If you have 4 joints, you have 3 bones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;NocKinematics.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;NUM_JOINTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Length of each bone segment in your chosen unit (cm, mm, etc.)&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;boneLengths&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NUM_JOINTS&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; 

&lt;span class="c1"&gt;// Initialize the solver on the Heap&lt;/span&gt;
&lt;span class="n"&gt;NocKinematics&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FABRIK&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;armSolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;NocKinematics&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FABRIK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;boneLengths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NUM_JOINTS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Set the Anchor
&lt;/h3&gt;

&lt;p&gt;The "Base" is the stationary part of your robot (e.g., the shoulder).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;armSolver&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setBasePosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NocCore&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Reach the Target
&lt;/h3&gt;

&lt;p&gt;Instruct the arm to calculate the positions needed to touch a point in space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;NocCore&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector3&lt;/span&gt; &lt;span class="nf"&gt;target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;12.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;armSolver&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Mapping to Hardware (Servos)
&lt;/h3&gt;

&lt;p&gt;The library gives you &lt;code&gt;Vector3&lt;/code&gt; coordinates for each joint. To move a real servo, you simply use &lt;code&gt;atan2&lt;/code&gt; to find the angle between these points.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;NUM_JOINTS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;NocCore&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector3&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;armSolver&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getJointPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Joint "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;": "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x_val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y_val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📂 Real-World Examples Included
&lt;/h2&gt;

&lt;p&gt;The library ships with several built-in examples (&lt;code&gt;File &amp;gt; Examples &amp;gt; NocKinematics&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BasicArm.ino&lt;/code&gt;&lt;/strong&gt;: The "Hello World" of kinematics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;DynamicTarget.ino&lt;/code&gt;&lt;/strong&gt;: Watch the arm follow a moving coordinate in real-time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MultiJointSnake.ino&lt;/code&gt;&lt;/strong&gt;: A stress test showing 10+ joints moving smoothly on an Arduino.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ServoArm4DOF/5DOF&lt;/code&gt;&lt;/strong&gt;: The most practical scripts. They show how to translate $X, Y, Z$ math into actual &lt;code&gt;servo.write(angles)&lt;/code&gt; commands.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📦 Installation
&lt;/h2&gt;

&lt;p&gt;Get started today by downloading the library through your preferred channel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Arduino Library Manager:&lt;/strong&gt; Search for &lt;code&gt;NocKinematics&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Nocturnailed-Community/NocKinematics" rel="noopener noreferrer"&gt;Nocturnailed-Community/NocKinematics&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation:&lt;/strong&gt; &lt;a href="https://www.arduinolibraries.info/libraries/noc-kinematics" rel="noopener noreferrer"&gt;Arduino Libraries Info&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Inverse Kinematics doesn't have to be a "math wall" that stops your project. With &lt;strong&gt;NocKinematics&lt;/strong&gt;, you can focus on building the hardware and the behavior, while the FABRIK algorithm handles the heavy lifting of spatial geometry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;License:&lt;/strong&gt; Released under the &lt;strong&gt;MIT License&lt;/strong&gt;.&lt;br&gt;
&lt;strong&gt;Maintained by:&lt;/strong&gt; Nocturnailed Community.&lt;/p&gt;

&lt;p&gt;#Robotics #Arduino #InverseKinematics #FABRIK #ESP32 #OpenSource #NocLab #Mathematics&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>iot</category>
      <category>cpp</category>
      <category>kinematik</category>
    </item>
    <item>
      <title>Bringing Generative AI to Microcontrollers: Introducing NocLLM</title>
      <dc:creator>Muhammad Ikhwan Fathulloh</dc:creator>
      <pubDate>Tue, 21 Apr 2026 05:30:05 +0000</pubDate>
      <link>https://dev.to/muhammadikhwanfathulloh/bringing-generative-ai-to-microcontrollers-introducing-nocllm-2ii7</link>
      <guid>https://dev.to/muhammadikhwanfathulloh/bringing-generative-ai-to-microcontrollers-introducing-nocllm-2ii7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4u21k0xtavskwp1zzbn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4u21k0xtavskwp1zzbn.png" alt=" " width="800" height="454"&gt;&lt;/a&gt;The barrier between resource-constrained hardware and Large Language Models (LLMs) has finally been broken. While microcontrollers lack the VRAM to run a 70B parameter model locally, they can now act as intelligent gateways to the world's most powerful AI engines.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;NocLLM&lt;/strong&gt;, an optimized integration and inference library designed specifically for Arduino and embedded systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is NocLLM?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NocLLM&lt;/strong&gt; is a high-performance C++ library that allows microcontrollers to communicate with LLM providers (OpenAI, Gemini, Groq, DeepSeek) or local LLM servers (Ollama, LMStudio) using a &lt;strong&gt;non-blocking, stream-oriented architecture&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unlike traditional HTTP clients that hang while waiting for a full JSON response, NocLLM parses incoming data chunks in real-time. This means your Arduino can keep reading sensors or driving motors while the AI is "typing" its response.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Strengths:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Overhead Streaming:&lt;/strong&gt; Uses background TCP polling to prevent CPU stalls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Provider Support:&lt;/strong&gt; One unified syntax for various AI infrastructures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Parsing:&lt;/strong&gt; Automatically adapts its internal configuration based on the target URL (e.g., switching between Gemini and OpenAI protocols).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge-First Design:&lt;/strong&gt; Optimized for memory efficiency, preventing "Out of Memory" crashes during long AI conversations.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5 Ways to Power Your Hardware with LLMs
&lt;/h2&gt;

&lt;p&gt;NocLLM ships with five comprehensive examples (found in &lt;code&gt;File -&amp;gt; Examples -&amp;gt; NocLLM&lt;/code&gt;) to get you started:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;01_Sumopod:&lt;/strong&gt; DeepSeek-V3 integration via Sumopod Cloud.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;02_OpenAI:&lt;/strong&gt; The industry standard—perfect for GPT-4o or GPT-3.5 Turbo.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;03_Gemini_Native:&lt;/strong&gt; Harness Google’s &lt;code&gt;gemini-3-flash&lt;/code&gt;. NocLLM handles the specific Google GenAI headers and parsing logic automatically.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;04_Groq:&lt;/strong&gt; Experience ultra-low latency with &lt;code&gt;llama3-70b&lt;/code&gt;. Ideal for voice assistants or real-time robotics.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;05_Local_LMStudio:&lt;/strong&gt; The privacy-focused choice. Connect to Ollama or LMStudio on your local network. It uses bare TCP streams with &lt;strong&gt;0 SSL overhead&lt;/strong&gt;, providing blazing-fast speeds for local AI setups.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Technical Spotlight: Non-Blocking Execution
&lt;/h2&gt;

&lt;p&gt;The most powerful feature of NocLLM is its ability to multitask. Here is how simple it is to implement a streaming AI response without freezing your microcontroller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"NocLLM.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// Initialize with your Key, Endpoint, and Model&lt;/span&gt;
&lt;span class="n"&gt;NocAI&lt;/span&gt; &lt;span class="nf"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"YOUR_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"https://api.openai.com/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gpt-3.5-turbo"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Callback function triggered as each word/chunk arrives&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;115200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ... [Insert your WiFi connection logic here] ...&lt;/span&gt;

    &lt;span class="c1"&gt;// Attach the listener and trigger a prompt&lt;/span&gt;
    &lt;span class="n"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;beginStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Write a 1-sentence poem about a robot."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This gently pulls the data from the network in the background&lt;/span&gt;
    &lt;span class="n"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Your main logic stays alive!&lt;/span&gt;
    &lt;span class="c1"&gt;// Example: Blink an LED or read a DHT22 sensor here.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why Use NocLLM for Your Next Project?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Robotics:&lt;/strong&gt; Give your robot a "brain" that can understand complex natural language commands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Home Hubs:&lt;/strong&gt; Build a private voice assistant that processes logic via a local Ollama server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intelligent Data Analysis:&lt;/strong&gt; Send sensor logs to an LLM to receive a human-readable summary of system health.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation &amp;amp; Resources
&lt;/h2&gt;

&lt;p&gt;Ready to build the next generation of smart hardware?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Arduino Library Manager:&lt;/strong&gt; Search for &lt;strong&gt;"NocLLM"&lt;/strong&gt; and click install.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/Nocturnailed-Community/NocLLM" rel="noopener noreferrer"&gt;Nocturnailed-Community/NocLLM&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registry Details:&lt;/strong&gt; &lt;a href="https://www.arduinolibraries.info/libraries/noc-llm" rel="noopener noreferrer"&gt;NocLLM on Arduino Libraries&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NocLLM is part of the &lt;strong&gt;Noc Lab&lt;/strong&gt; ecosystem, dedicated to pushing the boundaries of what is possible on the edge. &lt;/p&gt;

&lt;p&gt;#Arduino #LLM #GenerativeAI #IoT #EdgeAI #NocLab #OpenSource #Programming&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>iot</category>
      <category>cpp</category>
      <category>llm</category>
    </item>
    <item>
      <title>Bringing Intelligence to the Edge: Introduction to NocML for Arduino</title>
      <dc:creator>Muhammad Ikhwan Fathulloh</dc:creator>
      <pubDate>Tue, 21 Apr 2026 05:22:10 +0000</pubDate>
      <link>https://dev.to/muhammadikhwanfathulloh/bringing-intelligence-to-the-edge-introduction-to-nocml-for-arduino-4fmd</link>
      <guid>https://dev.to/muhammadikhwanfathulloh/bringing-intelligence-to-the-edge-introduction-to-nocml-for-arduino-4fmd</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42c0h2xr6pqtxecpwm6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42c0h2xr6pqtxecpwm6g.png" alt=" " width="800" height="453"&gt;&lt;/a&gt;Edge AI is often seen as a field reserved for powerful single-board computers, but what if you could run machine learning logic directly on a standard Arduino?&lt;/p&gt;

&lt;p&gt;In this article, we will explore &lt;strong&gt;NocML&lt;/strong&gt;, an efficient machine learning library specifically designed for resource-constrained microcontrollers.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is NocML?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NocML&lt;/strong&gt; is a lightweight C++ library built to bridge the gap between complex ML logic and the limited processing power of microcontrollers like the ESP32, Arduino Uno, or Nano. Inspired by the Scikit-Learn API, it offers a familiar workflow for developers coming from a Python background.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low Memory Footprint:&lt;/strong&gt; Optimized to run within the tight SRAM limits of common microcontrollers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Preprocessing:&lt;/strong&gt; Includes tools like &lt;code&gt;MinMaxScaler&lt;/code&gt; for data normalization on-device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versatile Algorithms:&lt;/strong&gt; Supports Classification (&lt;strong&gt;KNN, Naive Bayes&lt;/strong&gt;), Clustering (&lt;strong&gt;K-Means&lt;/strong&gt;), and Regression (&lt;strong&gt;Linear Regression&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Latency:&lt;/strong&gt; Perform inference locally on the device, ensuring privacy and real-time response without cloud dependency.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Use Case: K-Nearest Neighbors (KNN) Classification
&lt;/h2&gt;

&lt;p&gt;One of the strongest features of NocML is its ability to perform sensor classification directly on the "edge." Imagine building a device that classifies activity types based on accelerometer data.&lt;/p&gt;

&lt;p&gt;Here is a practical example of how to implement a &lt;strong&gt;KNN&lt;/strong&gt; algorithm using NocML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;NocML.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// Define training data (Features)&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;X_train&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// Category A&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.8&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// Category A&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// Category B&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;7.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;// Category B&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Labels for training data&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize KNN with k=3&lt;/span&gt;
&lt;span class="n"&gt;KNN&lt;/span&gt; &lt;span class="nf"&gt;knn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;115200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Local training (Fit)&lt;/span&gt;
  &lt;span class="n"&gt;knn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"KNN Model Ready!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// New sensor data to classify&lt;/span&gt;
  &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.1&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Perform prediction&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;prediction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;knn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Classification Result: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prediction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"Category A"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Category B"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why NocML?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Smart IoT:&lt;/strong&gt; Transform passive sensors into intelligent nodes that make decisions without a server.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Predictive Maintenance:&lt;/strong&gt; Detect anomalous vibration patterns in industrial motors before failure occurs.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Human-Machine Interaction:&lt;/strong&gt; Recognize gestures or simple audio patterns in real-time.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;You can easily integrate NocML into your project via the Arduino Library Manager or by visiting the official repositories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; Open your &lt;strong&gt;Arduino IDE&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Go to &lt;strong&gt;Sketch&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Include Library&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Manage Libraries...&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; Search for &lt;strong&gt;"NocML"&lt;/strong&gt; and click install.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alternatively, explore the source code and documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://www.google.com/search?q=https://github.com/Nocturnailed-Community/NocML" rel="noopener noreferrer"&gt;Nocturnailed-Community/NocML&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arduino Library Registry:&lt;/strong&gt; &lt;a href="https://www.arduinolibraries.info/libraries/noc-ml" rel="noopener noreferrer"&gt;NocML on Arduino Libraries&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;NocML is part of the &lt;strong&gt;TinyML&lt;/strong&gt; movement, making artificial intelligence accessible on devices costing only a few dollars. By bringing logic closer to the data source, we create faster, more reliable, and smarter IoT systems.&lt;/p&gt;

&lt;p&gt;Are you working on an Edge AI project? Give &lt;strong&gt;NocML&lt;/strong&gt; a try and share your results!&lt;/p&gt;

&lt;p&gt;#Arduino #MachineLearning #TinyML #IoT #OpenSource #NocLab #ArtificialIntelligence&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>iot</category>
      <category>cpp</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
