<?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: Developer Open</title>
    <description>The latest articles on DEV Community by Developer Open (@developer_open).</description>
    <link>https://dev.to/developer_open</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%2F4139653%2F76bc5a1d-8cbf-4b34-8f71-608a9a8a1e7c.png</url>
      <title>DEV Community: Developer Open</title>
      <link>https://dev.to/developer_open</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/developer_open"/>
    <language>en</language>
    <item>
      <title>I'm interested in hearing how others approach the boundary between a VS Code extension and a separate Node.js/TypeScript runtime.</title>
      <dc:creator>Developer Open</dc:creator>
      <pubDate>Wed, 23 Sep 2026 14:54:04 +0000</pubDate>
      <link>https://dev.to/developer_open/im-interested-in-hearing-how-others-approach-the-boundary-between-a-vs-code-extension-and-a-fbc</link>
      <guid>https://dev.to/developer_open/im-interested-in-hearing-how-others-approach-the-boundary-between-a-vs-code-extension-and-a-fbc</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/aqiron-security/separating-a-vs-code-extension-from-a-typescript-core-architecture-lessons-from-aqiron-security-4mc5" class="crayons-story__hidden-navigation-link"&gt;Separating a VS Code Extension from a TypeScript Core: Architecture Lessons from Aqiron Security&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/aqiron-security"&gt;
            &lt;img alt="Aqiron Security logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F14893%2F0428b2d0-36a7-450e-a419-b394189a3ab8.png" class="crayons-logo__image" width="456" height="547"&gt;
          &lt;/a&gt;

          &lt;a href="/developer_open" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4139653%2F76bc5a1d-8cbf-4b34-8f71-608a9a8a1e7c.png" alt="developer_open profile" class="crayons-avatar__image" width="456" height="547"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/developer_open" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Developer Open
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Developer Open
                
                
              
              &lt;div id="story-author-preview-content-4726016" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/developer_open" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4139653%2F76bc5a1d-8cbf-4b34-8f71-608a9a8a1e7c.png" class="crayons-avatar__image" alt="" width="456" height="547"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Developer Open&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/aqiron-security" class="crayons-story__secondary fw-medium"&gt;Aqiron Security&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/aqiron-security/separating-a-vs-code-extension-from-a-typescript-core-architecture-lessons-from-aqiron-security-4mc5" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 23&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/aqiron-security/separating-a-vs-code-extension-from-a-typescript-core-architecture-lessons-from-aqiron-security-4mc5" id="article-link-4726016"&gt;
          Separating a VS Code Extension from a TypeScript Core: Architecture Lessons from Aqiron Security
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/vscode"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;vscode&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/node"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;node&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/aqiron-security/separating-a-vs-code-extension-from-a-typescript-core-architecture-lessons-from-aqiron-security-4mc5" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;4&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/aqiron-security/separating-a-vs-code-extension-from-a-typescript-core-architecture-lessons-from-aqiron-security-4mc5#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Separating a VS Code Extension from a TypeScript Core: Architecture Lessons from Aqiron Security</title>
      <dc:creator>Developer Open</dc:creator>
      <pubDate>Wed, 23 Sep 2026 14:51:53 +0000</pubDate>
      <link>https://dev.to/aqiron-security/separating-a-vs-code-extension-from-a-typescript-core-architecture-lessons-from-aqiron-security-4mc5</link>
      <guid>https://dev.to/aqiron-security/separating-a-vs-code-extension-from-a-typescript-core-architecture-lessons-from-aqiron-security-4mc5</guid>
      <description>&lt;p&gt;When I started building Aqiron Security, the natural approach was to keep the security logic inside the VS Code extension.&lt;/p&gt;

&lt;p&gt;That works.&lt;/p&gt;

&lt;p&gt;At least initially.&lt;/p&gt;

&lt;p&gt;But as the project started accumulating scanner orchestration, finding normalization, correlation, project intelligence, RAG, reporting, and AI operations, I started running into a deeper architectural question:&lt;/p&gt;

&lt;p&gt;Should the VS Code extension actually own the application's core runtime?&lt;/p&gt;

&lt;p&gt;I decided the answer should be no.&lt;/p&gt;

&lt;p&gt;Aqiron Security is an open-source application security project. The current product is a VS Code extension, but I wanted the security/runtime layer to have a much cleaner boundary from the client.&lt;/p&gt;

&lt;p&gt;The result is a design where the extension acts as the client and starts a separate Node.js process containing the TypeScript core.&lt;/p&gt;

&lt;p&gt;This article explains why I made that separation, how the current architecture works, and some of the trade-offs I have encountered.&lt;/p&gt;

&lt;p&gt;The original problem&lt;/p&gt;

&lt;p&gt;A VS Code extension has a lot of responsibilities already.&lt;/p&gt;

&lt;p&gt;It deals with things like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;extension activation&lt;br&gt;
commands&lt;br&gt;
diagnostics&lt;br&gt;
the VS Code API&lt;br&gt;
webview communication&lt;br&gt;
editor state&lt;br&gt;
user settings&lt;br&gt;
UI lifecycle&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Security scanning introduces another class of responsibilities:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;starting external tools&lt;br&gt;
parsing scanner output&lt;br&gt;
normalizing findings&lt;br&gt;
correlating duplicate results&lt;br&gt;
generating reports&lt;br&gt;
managing long-running operations&lt;br&gt;
cancellation&lt;br&gt;
AI operations&lt;br&gt;
workspace analysis&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Putting all of that into one runtime makes the boundary blurry.&lt;/p&gt;

&lt;p&gt;The code may still work, but over time the architecture starts answering questions like:&lt;/p&gt;

&lt;p&gt;"Does this security service need VS Code?"&lt;/p&gt;

&lt;p&gt;That is a dangerous dependency to create if the answer does not actually need to be yes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The separation
&lt;/h2&gt;

&lt;p&gt;The current Aqiron architecture looks roughly like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;┌──────────────────────────────────────────────┐&lt;br&gt;
│              VS Code Extension                │&lt;br&gt;
│                                              │&lt;br&gt;
│ activation / commands / diagnostics          │&lt;br&gt;
│ React webview / settings / workspace UI      │&lt;br&gt;
│ ScanController / client-side services        │&lt;br&gt;
└──────────────────────┬───────────────────────┘&lt;br&gt;
                       │&lt;br&gt;
                       │ CoreClient&lt;br&gt;
                       │ CoreProcessManager&lt;br&gt;
                       │&lt;br&gt;
                       │ newline-delimited JSON&lt;br&gt;
                       ▼&lt;br&gt;
┌──────────────────────────────────────────────┐&lt;br&gt;
│            Aqiron Core Runtime               │&lt;br&gt;
│            Node.js + TypeScript              │&lt;br&gt;
│                                              │&lt;br&gt;
│ protocol / cancellation / adapters           │&lt;br&gt;
│ scanning / analysis / correlation            │&lt;br&gt;
│ reports / RAG / AI operations                │&lt;br&gt;
└──────────────────────┬───────────────────────┘&lt;br&gt;
                       │&lt;br&gt;
          ┌────────────┴────────────┐&lt;br&gt;
          ▼                         ▼&lt;br&gt;
   Native Aqiron rules       External scanners&lt;br&gt;
                             Trivy / Semgrep&lt;br&gt;
                             OSV-Scanner /&lt;br&gt;
                             Betterleaks / MobSF&lt;br&gt;
          │                         │&lt;br&gt;
          └────────────┬────────────┘&lt;br&gt;
                       ▼&lt;br&gt;
                Unified findings&lt;br&gt;
                       ▼&lt;br&gt;
             correlation + graph&lt;br&gt;
                       ▼&lt;br&gt;
                  reports&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is not a cloud architecture.&lt;/p&gt;

&lt;p&gt;The current project is local-first. The extension starts the core process locally and communicates with it over standard input/output. The repository currently keeps &lt;code&gt;packages/core&lt;/code&gt; private and bundles it into the extension rather than publishing it as a separate npm package.&lt;/p&gt;

&lt;p&gt;That distinction is important.&lt;/p&gt;

&lt;p&gt;The boundary exists today, but the eventual product packaging can evolve later.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why a separate process?
&lt;/h2&gt;

&lt;p&gt;There were several reasons.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Keep the core independent from VS Code
&lt;/h2&gt;

&lt;p&gt;The first reason is architectural independence.&lt;/p&gt;

&lt;p&gt;The core should not need to know that VS Code exists.&lt;/p&gt;

&lt;p&gt;Ideally, security logic should be able to operate on concepts like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;workspace&lt;br&gt;
scan&lt;br&gt;
finding&lt;br&gt;
project&lt;br&gt;
report&lt;br&gt;
AI request&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;rather than:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vscode.workspace&lt;br&gt;
vscode.window&lt;br&gt;
WebviewPanel&lt;br&gt;
TextDocument&lt;br&gt;
DiagnosticCollection&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The extension is responsible for translating between the developer environment and the core.&lt;/p&gt;

&lt;p&gt;That gives me a much cleaner dependency direction:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;VS Code client&lt;br&gt;
      ↓&lt;br&gt;
    Core&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;rather than:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;VS Code ↔ Security logic ↔ VS Code&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Process isolation gives us a real boundary
&lt;/h2&gt;

&lt;p&gt;Using a separate process also creates a runtime boundary.&lt;/p&gt;

&lt;p&gt;The extension host and the security core no longer execute as one giant logical process.&lt;/p&gt;

&lt;p&gt;That matters for long-running operations.&lt;/p&gt;

&lt;p&gt;A scan might involve:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;discover files&lt;br&gt;
      ↓&lt;br&gt;
run multiple tools&lt;br&gt;
      ↓&lt;br&gt;
parse results&lt;br&gt;
      ↓&lt;br&gt;
normalize findings&lt;br&gt;
      ↓&lt;br&gt;
correlate findings&lt;br&gt;
      ↓&lt;br&gt;
build relationships&lt;br&gt;
      ↓&lt;br&gt;
generate reports&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's a very different workload from handling an editor command or updating a sidebar.&lt;/p&gt;

&lt;p&gt;With a separate core process, the extension can treat the security engine more like a service.&lt;/p&gt;

&lt;p&gt;That makes lifecycle handling, restart behavior, and failure boundaries easier to reason about.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. IPC forces us to define a contract
&lt;/h2&gt;

&lt;p&gt;This was probably the most valuable part of the architecture.&lt;/p&gt;

&lt;p&gt;Once the extension and core became separate processes, they couldn't casually call each other's internal functions anymore.&lt;/p&gt;

&lt;p&gt;They needed a protocol.&lt;/p&gt;

&lt;p&gt;The current protocol is intentionally simple:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;stdin/stdout&lt;br&gt;
+&lt;br&gt;
newline-delimited JSON&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A request looks conceptually like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CoreRequestMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;request&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;unknown&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;And a response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CoreResponseMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;response&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;CoreProtocolError&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;There are also event messages for asynchronous pipeline updates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CoreEventMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;unknown&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 actual protocol also has a versioned handshake.&lt;/p&gt;

&lt;p&gt;For example, the current runtime exposes a protocol version and core version and can report compatibility states such as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;compatible&lt;br&gt;
protocol-mismatch&lt;br&gt;
extension-too-old&lt;br&gt;
core-too-old&lt;br&gt;
unsupported&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That gives us an explicit compatibility boundary instead of relying on both sides silently assuming they agree.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why newline-delimited JSON?
&lt;/h2&gt;

&lt;p&gt;I deliberately didn't start with something complicated.&lt;/p&gt;

&lt;p&gt;The current transport is essentially:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;message 1\n&lt;br&gt;
message 2\n&lt;br&gt;
message 3\n&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;where each line contains one JSON message.&lt;/p&gt;

&lt;p&gt;That gives us a few useful properties:&lt;/p&gt;
&lt;h2&gt;
  
  
  Easy to inspect
&lt;/h2&gt;

&lt;p&gt;You can literally look at the communication stream.&lt;/p&gt;
&lt;h2&gt;
  
  
  Easy to debug
&lt;/h2&gt;

&lt;p&gt;Malformed input can be identified and rejected.&lt;/p&gt;
&lt;h2&gt;
  
  
  No additional server required
&lt;/h2&gt;

&lt;p&gt;The extension starts the process locally and communicates through stdio.&lt;/p&gt;
&lt;h2&gt;
  
  
  Language-neutral at the protocol level
&lt;/h2&gt;

&lt;p&gt;The wire format is JSON rather than TypeScript-specific objects.&lt;/p&gt;

&lt;p&gt;That last point matters.&lt;/p&gt;

&lt;p&gt;The implementation is TypeScript, but the protocol doesn't fundamentally need to be.&lt;/p&gt;
&lt;h2&gt;
  
  
  Correlation IDs become important
&lt;/h2&gt;

&lt;p&gt;Once requests and responses cross a process boundary, we need a way to know which response belongs to which request.&lt;/p&gt;

&lt;p&gt;That's why requests carry an ID.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;request id: abc123&lt;br&gt;
method: scan.start&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The response can return:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;id: abc123&lt;br&gt;
success: true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Without this, concurrent operations become painful to reason about.&lt;/p&gt;

&lt;p&gt;The ID becomes the connection between:&lt;/p&gt;

&lt;p&gt;request&lt;br&gt;
   ↓&lt;br&gt;
core operation&lt;br&gt;
   ↓&lt;br&gt;
response&lt;/p&gt;

&lt;p&gt;and also gives us something useful for cancellation and pipeline events.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cancellation is part of the architecture
&lt;/h2&gt;

&lt;p&gt;Security operations shouldn't be treated as unstoppable functions.&lt;/p&gt;

&lt;p&gt;Imagine starting a deep scan and then closing the workspace.&lt;/p&gt;

&lt;p&gt;Or starting an AI analysis and then deciding you don't need it anymore.&lt;/p&gt;

&lt;p&gt;The architecture therefore includes explicit cancellation operations.&lt;/p&gt;

&lt;p&gt;The current core protocol exposes operations such as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;core.cancel&lt;br&gt;
scan.cancel&lt;br&gt;
ai.cancel&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and the runtime propagates cancellation through the relevant cancellation sources and scanner context.&lt;/p&gt;

&lt;p&gt;This is one of those details that seems unnecessary until you have a real long-running operation.&lt;/p&gt;

&lt;p&gt;Then it becomes essential.&lt;/p&gt;
&lt;h2&gt;
  
  
  The extension should not know how scanning works
&lt;/h2&gt;

&lt;p&gt;One of the goals of the boundary is to let the client ask for a scan without knowing the implementation details.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;coreClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startScan&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;workspaceRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deep&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;trusted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&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;The extension doesn't need to know:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;which scanners are installed&lt;br&gt;
how scanner output is parsed&lt;br&gt;
how findings are normalized&lt;br&gt;
how correlation works&lt;br&gt;
how reports are generated&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those concerns belong to the core pipeline.&lt;/p&gt;

&lt;p&gt;The current core pipeline can combine native Aqiron rules with optional external scanners, normalize results into &lt;code&gt;UnifiedFinding[]&lt;/code&gt;, then pass them through correlation/graph processing and report generation.&lt;/p&gt;

&lt;p&gt;That separation is the main reason I like this architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The finding model becomes the shared language
&lt;/h2&gt;

&lt;p&gt;A scanner may produce one format.&lt;/p&gt;

&lt;p&gt;Another scanner produces something completely different.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Scanner A&lt;br&gt;
severity = HIGH&lt;br&gt;
file = foo.dart&lt;br&gt;
line = 41&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;while another might report:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Scanner B&lt;br&gt;
level = error&lt;br&gt;
path = foo.dart&lt;br&gt;
startLine = 41&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The core shouldn't force the rest of the system to understand every scanner's native format.&lt;/p&gt;

&lt;p&gt;Instead, scanner-specific parsers convert the output into a common model.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;external scanner&lt;br&gt;
       ↓&lt;br&gt;
scanner-specific parser&lt;br&gt;
       ↓&lt;br&gt;
UnifiedFinding&lt;br&gt;
       ↓&lt;br&gt;
correlation&lt;br&gt;
       ↓&lt;br&gt;
report&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is one of the biggest advantages of having an application-level core rather than scattering scanner logic throughout the VS Code extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture isn't completely finished
&lt;/h2&gt;

&lt;p&gt;This is important because architecture diagrams can easily make an early project look more mature than it actually is.&lt;/p&gt;

&lt;p&gt;Aqiron is currently version 0.0.1 and under active development.&lt;/p&gt;

&lt;p&gt;There are still intentional limitations.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;workspace operations currently require a Flutter workspace&lt;br&gt;
external scanners are optional&lt;br&gt;
the core is still bundled into the extension&lt;br&gt;
quick file scans use a separate direct extension path&lt;br&gt;
the project does not yet have independently published Core, CLI, or Desktop packages&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the current architecture is not:&lt;/p&gt;

&lt;p&gt;`Aqiron Core npm package&lt;br&gt;
        ↓&lt;br&gt;
VS Code&lt;br&gt;
CLI&lt;br&gt;
Desktop&lt;/p&gt;

&lt;p&gt;Not yet.&lt;/p&gt;

&lt;p&gt;It's closer to:&lt;/p&gt;

&lt;p&gt;VS Code&lt;br&gt;
   ↓&lt;br&gt;
internal Core process&lt;br&gt;
   ↓&lt;br&gt;
bundled runtime`&lt;/p&gt;

&lt;p&gt;That is an important distinction.&lt;/p&gt;

&lt;p&gt;The architecture is being prepared for broader reuse without prematurely creating a bunch of packages that don't yet need to exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why I didn't immediately split everything into repositories&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was another deliberate decision.&lt;/p&gt;

&lt;p&gt;It would be easy to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;aqiron-core&lt;br&gt;
aqiron-security-vscode&lt;br&gt;
aqiron-security-cli&lt;br&gt;
aqiron-security-desktop&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and create four repositories immediately.&lt;/p&gt;

&lt;p&gt;But that would add operational complexity before the products existed.&lt;/p&gt;

&lt;p&gt;You would now have to manage:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;package publishing&lt;br&gt;
version coordination&lt;br&gt;
cross-repository changes&lt;br&gt;
release synchronization&lt;br&gt;
dependency management&lt;br&gt;
contributor workflow across multiple repositories&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The current repository gives me a cleaner intermediate step:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/&lt;br&gt;
packages/core/&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;with an explicit runtime boundary.&lt;/p&gt;

&lt;p&gt;When multiple clients become real products, the repository structure can change.&lt;/p&gt;

&lt;p&gt;Until then, the architecture can evolve without forcing the project to pay the cost of premature distribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I like about this architecture
&lt;/h2&gt;

&lt;p&gt;The biggest win isn't actually "using IPC."&lt;/p&gt;

&lt;p&gt;The bigger win is making &lt;strong&gt;the boundary explicit.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The VS Code extension owns the developer environment.&lt;/p&gt;

&lt;p&gt;The core owns security operations.&lt;/p&gt;

&lt;p&gt;The protocol connects them.&lt;/p&gt;

&lt;p&gt;That gives us a mental model like:&lt;/p&gt;

&lt;p&gt;`Client responsibilities&lt;br&gt;
    ↓&lt;br&gt;
UI&lt;br&gt;
VS Code&lt;br&gt;
commands&lt;br&gt;
diagnostics&lt;br&gt;
workspace interaction&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      │
      │ protocol
      ▼
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Core responsibilities&lt;br&gt;
    ↓&lt;br&gt;
scanning&lt;br&gt;
normalization&lt;br&gt;
correlation&lt;br&gt;
RAG&lt;br&gt;
AI operations&lt;br&gt;
reports&lt;br&gt;
`&lt;/p&gt;

&lt;p&gt;That's much easier to reason about than a single giant extension runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I still need to watch
&lt;/h2&gt;

&lt;p&gt;The architecture also introduces new problems.&lt;/p&gt;

&lt;p&gt;A process boundary is not free.&lt;/p&gt;

&lt;p&gt;Now we have to care about:&lt;/p&gt;

&lt;p&gt;process startup time&lt;/p&gt;

&lt;p&gt;restart behavior&lt;/p&gt;

&lt;p&gt;malformed messages&lt;/p&gt;

&lt;p&gt;protocol compatibility&lt;/p&gt;

&lt;p&gt;stderr/stdout handling&lt;/p&gt;

&lt;p&gt;partial failures&lt;/p&gt;

&lt;p&gt;cancellation&lt;/p&gt;

&lt;p&gt;shutdown&lt;/p&gt;

&lt;p&gt;concurrent requests&lt;/p&gt;

&lt;p&gt;serialization overhead&lt;/p&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We traded code coupling for process-boundary complexity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I think that's a reasonable trade for Aqiron, but it isn't automatically the right choice for every VS Code extension.&lt;/p&gt;

&lt;p&gt;If your project is a small command-based extension with a few hundred lines of logic, this architecture would probably be overkill.&lt;/p&gt;

&lt;p&gt;For a growing security platform with multiple subsystems and long-running operations, the boundary becomes much more interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bigger goal
&lt;/h2&gt;

&lt;p&gt;The long-term idea is not "make a complicated VS Code extension."&lt;/p&gt;

&lt;p&gt;It's to make the security core reusable.&lt;/p&gt;

&lt;p&gt;The future might eventually look like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                Aqiron Core
               /     |      \
              /      |       \
             ↓       ↓        ↓

         VS Code    CLI     Desktop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;But I don't need to build all three clients today.&lt;/p&gt;

&lt;p&gt;Right now, I'm using the VS Code extension as the first real client and using the Core boundary to keep the architecture ready for future evolution.&lt;/p&gt;

&lt;p&gt;That's the part I'm most interested in getting right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;The biggest lesson I've taken from this project is that architecture isn't about drawing the biggest possible diagram.&lt;/p&gt;

&lt;p&gt;It's about deciding where responsibilities should stop.&lt;/p&gt;

&lt;p&gt;For Aqiron, the important boundary became:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;VS Code is the client.&lt;br&gt;
The TypeScript runtime is the security engine.&lt;br&gt;
IPC is the contract between them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That doesn't mean the architecture is finished.&lt;/p&gt;

&lt;p&gt;It means there is now a clear place to evolve it.&lt;/p&gt;

&lt;p&gt;I'm still working through the trade-offs, so I'd be interested in hearing from people who have built:&lt;/p&gt;

&lt;p&gt;VS Code extensions with external processes&lt;/p&gt;

&lt;p&gt;TypeScript/Node developer tools&lt;/p&gt;

&lt;p&gt;language-server-style architectures&lt;/p&gt;

&lt;p&gt;security scanners&lt;/p&gt;

&lt;p&gt;CLI + GUI products sharing a common runtime&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How would you design this boundary differently?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Aqiron Security is open source and currently under active development.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>vscode</category>
      <category>opensource</category>
      <category>node</category>
    </item>
  </channel>
</rss>
