<?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: Veys Aliyev</title>
    <description>The latest articles on DEV Community by Veys Aliyev (@mdreal32).</description>
    <link>https://dev.to/mdreal32</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3749682%2Fbf92533f-80cc-473a-b715-83f5f7f0a7e5.jpeg</url>
      <title>DEV Community: Veys Aliyev</title>
      <link>https://dev.to/mdreal32</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mdreal32"/>
    <language>en</language>
    <item>
      <title>Taking a Break — See You When I'm Ready</title>
      <dc:creator>Veys Aliyev</dc:creator>
      <pubDate>Wed, 11 Mar 2026 14:13:09 +0000</pubDate>
      <link>https://dev.to/mdreal32/taking-a-break-see-you-when-im-ready-4989</link>
      <guid>https://dev.to/mdreal32/taking-a-break-see-you-when-im-ready-4989</guid>
      <description>&lt;p&gt;This is a short one.&lt;/p&gt;

&lt;p&gt;Velnora is going on pause for an unknown amount of time. Not because something broke, not because I lost interest — the opposite, actually. I want to come back to this with fresh energy instead of grinding through tired.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Now
&lt;/h3&gt;

&lt;p&gt;A few things lined up at the same time.&lt;/p&gt;

&lt;p&gt;First — I have a project in the crypto space that needs attention. It has been waiting while I poured everything into Velnora's internals, and now it is time to give it the focus it deserves.&lt;/p&gt;

&lt;p&gt;Second — March in Azerbaijan is packed with holidays. Novruz is coming, and honestly, I want to actually enjoy it this year instead of spending it staring at TypeScript generics. Sometimes you need to step away from the screen and just live for a bit.&lt;/p&gt;

&lt;p&gt;Third — I can feel that I need a reset. The last stretch has been intense: the Unit System, BaseContext, config resolution, integration graphs, adapter loading, runtime detection. A lot of foundational work landed in a short time. The best thing I can do for Velnora right now is let all of that settle in my head before I push into the next phase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where Things Stand
&lt;/h3&gt;

&lt;p&gt;The project is in a solid place to pause:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Configuration System&lt;/strong&gt; — fully designed with 7-layer resolution, merge semantics, extends, freeze, and validation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BaseContext&lt;/strong&gt; — working expose/query mechanism, GlobalRegistry, typed registry keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations&lt;/strong&gt; — all configured, dependency graph built&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adapter loading&lt;/strong&gt; — H3 adapter's configure stage done&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime detection&lt;/strong&gt; — runtimes detected per project, but not yet implemented&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next big thing when I come back is the &lt;strong&gt;runtime implementation&lt;/strong&gt; — toolchain bootstrapping, execution plans, package manager integration. That is a meaty problem and it deserves a fresh mind, not a burned out one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not Goodbye
&lt;/h3&gt;

&lt;p&gt;This isn't the end of Velnora. The architecture is written down, the foundations are in place, and the next phase is clear. Nothing is lost — it is all sitting there, waiting.&lt;/p&gt;

&lt;p&gt;When I return, the focus shifts to runtime execution and toolchain integration. That is where the real test begins — and I want to show up for it with a clear head, not a tired one.&lt;/p&gt;

&lt;p&gt;For now, it is time to step away for a bit. Enjoy Novruz. Ship something in crypto. Let the ideas breathe.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;See you on the other side.&lt;/em&gt; 🌙&lt;/p&gt;

</description>
      <category>velnora</category>
    </item>
    <item>
      <title>Configuration System Overhaul</title>
      <dc:creator>Veys Aliyev</dc:creator>
      <pubDate>Sat, 07 Mar 2026 17:40:10 +0000</pubDate>
      <link>https://dev.to/mdreal32/configuration-system-overhaul-happy-international-womens-day-3941</link>
      <guid>https://dev.to/mdreal32/configuration-system-overhaul-happy-international-womens-day-3941</guid>
      <description>&lt;h3&gt;
  
  
  Where We Left Off
&lt;/h3&gt;

&lt;p&gt;Before we dive into the technical stuff — &lt;strong&gt;Happy March 8th!&lt;/strong&gt; 🌷 Today is International Women's Day, and I want to take a moment to celebrate the women in tech, in open source, in every field — your strength, brilliance, and contributions shape the world we build in. &lt;strong&gt;Thank you.&lt;/strong&gt; 💐&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The future is built by those who refuse to wait for permission.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alright —&lt;/p&gt;

&lt;p&gt;Last time, the BaseContext was wired up — &lt;code&gt;ctx.expose()&lt;/code&gt; and &lt;code&gt;ctx.query()&lt;/code&gt; working for real, the GlobalRegistry holding exposed APIs, and &lt;code&gt;makeRegistryObject&lt;/code&gt; keeping registry keys sane. The unit communication layer was done. But the system that feeds config into those units? That was still a rough sketch. A few notes in a spec page, no real pipeline, no contracts.&lt;/p&gt;

&lt;p&gt;That had to change before anything else could move forward.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Here is the thing about building a meta-framework — eventually every unit, every adapter, every runtime needs to ask the same question: &lt;strong&gt;"what is my config, and where did it come from?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And until this point, Velnora did not have a real answer. There was a vague idea of "find a config file and parse it" — but no defined order of precedence, no merge rules when two layers disagreed, no way to share presets between projects, and no validation at all. Every time I tried to wire up something new, the same question came back: &lt;em&gt;which config wins? What if there is a workspace config and a project config and they both set the same key?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I kept working around it. Hardcoding values here, passing things manually there. But that only works until it does not — and with integrations, adapters, and runtimes all needing config at the same time, the duct tape was running out fast.&lt;/p&gt;

&lt;p&gt;Oh, and the type system issues from the last post? 😅 To be honest, still there. Not blocking anything, so I kept moving. I will circle back and clean that mess up — but config was the fire that needed putting out first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Pipeline
&lt;/h3&gt;

&lt;p&gt;So I sat down and designed the full resolution pipeline. The goal was simple: &lt;strong&gt;config resolution should be deterministic, layered, and frozen before any unit sees it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The result is 7 layers, each overriding the previous:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Built-in defaults&lt;/strong&gt; — hardcoded in the Kernel, always present&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extended presets&lt;/strong&gt; — shared config packages resolved recursively, depth-first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspace config&lt;/strong&gt; — &lt;code&gt;velnora.config.ts&lt;/code&gt; at workspace root&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project config&lt;/strong&gt; — &lt;code&gt;velnora.config.ts&lt;/code&gt; in project root&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;package.json&lt;/code&gt; fallback&lt;/strong&gt; — &lt;code&gt;"velnora"&lt;/code&gt; key, but &lt;em&gt;only&lt;/em&gt; if no project config exists&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI flags&lt;/strong&gt; — &lt;code&gt;--log-level=verbose&lt;/code&gt; and friends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;VELNORA_*&lt;/code&gt; env vars&lt;/strong&gt; — highest priority, always wins&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One decision I spent time on: layers 4 and 5 are mutually exclusive. If a project config file exists, the &lt;code&gt;package.json&lt;/code&gt; key is completely ignored. I could have merged them, but that creates ambiguity — which one wins when they disagree? Instead, the rule is simple: if you have a config file, that is your config. No surprises.&lt;/p&gt;

&lt;h3&gt;
  
  
  Merge Rules
&lt;/h3&gt;

&lt;p&gt;When two layers provide the same key, the merge behavior depends on the type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Objects&lt;/strong&gt; → deep merge, later layer wins per key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arrays&lt;/strong&gt; → concatenated, later layer appends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primitives&lt;/strong&gt; → last-write-wins&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The decision to concatenate arrays instead of replacing them was deliberate. In a monorepo, you might declare some integrations at the workspace level and others at the project level. Replacing would force you to redeclare everything. Concatenating lets you build on what the workspace provides. It feels natural once you see it in action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Presets and &lt;code&gt;extends&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Both workspace and project configs can extend shared presets. This is the part that unlocks reuse across teams — a company can publish &lt;code&gt;@company/velnora-preset-react&lt;/code&gt; and every project just extends it instead of copying the same config.&lt;/p&gt;

&lt;p&gt;Presets resolve recursively and depth-first, merging bottom-up before the config's own values are applied. I borrowed this pattern from how TypeScript's &lt;code&gt;tsconfig.json&lt;/code&gt; extends works — familiar, predictable, no magic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Freeze and Validate
&lt;/h3&gt;

&lt;p&gt;After resolution, the entire config object is deeply frozen. No mutations. No "just tweak this one value during build." If a unit needs different config, it should be in the config file — not patched at runtime. This was a hard line to draw, but it eliminates an entire class of bugs where one unit's mutation silently affects another.&lt;/p&gt;

&lt;p&gt;Validation happens before freezing — every resolved config runs through a strict schema (via &lt;code&gt;untyped&lt;/code&gt; or &lt;code&gt;Zod&lt;/code&gt;). Unknown keys get a warning and are stripped. Errors include the source file and the key that failed. No deferred validation, no "we will check later." If the config is bad, you know immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting It to Work
&lt;/h3&gt;

&lt;p&gt;With the config pipeline in place, I moved into implementation.&lt;/p&gt;

&lt;p&gt;First — &lt;strong&gt;a Graph structure for integrations.&lt;/strong&gt; Before configuring anything, the system needs to understand the relationships between units — who depends on whom, what order things should initialize in. So I built a general-purpose graph tree that maps out those dependencies. The key decision here was designing it to be reusable — it is not tied to integrations specifically. The same graph can be used for adapters, runtimes, dependency resolution, or anything else that needs a directed relationship tree later. Build the abstraction once, use it everywhere.&lt;/p&gt;

&lt;p&gt;With the graph in place, &lt;strong&gt;all integrations are now fully configured.&lt;/strong&gt; The config resolution feeds into the integration loading pipeline, and each integration gets its resolved config through the BaseContext. That loop is closed.&lt;/p&gt;

&lt;p&gt;Next — &lt;strong&gt;adapter loading.&lt;/strong&gt; Adapters now load through the system properly, resolved from config, classified, and handed to the Kernel. The &lt;strong&gt;H3 adapter's configure stage is done&lt;/strong&gt; — it receives its config, sets up its context, and is ready for the dev and build lifecycle hooks.&lt;/p&gt;

&lt;p&gt;Then I went through each project and &lt;strong&gt;detected the runtime for each one.&lt;/strong&gt; The Kernel now knows which runtime belongs to which project. But here is the honest part — &lt;strong&gt;the runtimes do nothing yet.&lt;/strong&gt; 😄 They are detected, assigned, sitting in the right slots. But the actual runtime implementation — toolchain bootstrapping, execution plans, package manager integration — is still the open question from the last post. The runtimes are placeholders waiting for the research to finish.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Next
&lt;/h3&gt;

&lt;p&gt;The config system is done. Integration configuration is done. Adapter loading and the H3 configure stage are done. Runtime detection is done. What is &lt;em&gt;not&lt;/em&gt; done is making the runtimes actually do something — which means the next step is the same one I paused on last time: designing the runtime implementation. How toolchains boot, how execution plans get built and dispatched, and how the package manager API connects. That is the next wall to break through.&lt;/p&gt;

</description>
      <category>velnora</category>
      <category>typescript</category>
    </item>
    <item>
      <title>One Context, One Registry, and Knowing When to Stop</title>
      <dc:creator>Veys Aliyev</dc:creator>
      <pubDate>Wed, 04 Mar 2026 14:28:33 +0000</pubDate>
      <link>https://dev.to/mdreal32/one-context-one-registry-and-knowing-when-to-stop-4jgm</link>
      <guid>https://dev.to/mdreal32/one-context-one-registry-and-knowing-when-to-stop-4jgm</guid>
      <description>&lt;p&gt;Last post, the first runtime and adapter were alive. The type system had been fighting back — declaration merging across packages, build tool swaps — but honestly? I kind of forgot about that problem. It is still there, just not blocking anything right now. Sometimes you move forward and the old fight waits. 😄 The units existed. They could register. But the plumbing between "units declared in a config file" and "units actually running" did not exist yet.&lt;/p&gt;

&lt;p&gt;That changed. This round of work is quieter than the last, but it touches everything: &lt;strong&gt;config resolution, a shared base context, a working expose-and-query system, and a few small utilities to keep the registry sane.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  First Things First: Loading the Units
&lt;/h3&gt;

&lt;p&gt;Before anything else can work, the Kernel needs to know what units it is dealing with. That is what &lt;code&gt;config.resolve.units&lt;/code&gt; does — it takes the flat list of units from your config file and resolves them into something the Kernel can actually use. The units go in as references — strings, factory functions, imported objects — and they come out as loaded, validated, ready-to-boot definitions that the Kernel knows how to classify and initialize.&lt;/p&gt;

&lt;p&gt;This is the bridge between "I declared units in my config" and "the Kernel knows what to do with them." Without it, the unit array is just data sitting in a file. With it, the Kernel can classify units by kind, topological-sort them by dependencies, and start calling lifecycle hooks in the right order.&lt;/p&gt;

&lt;p&gt;It is not glamorous work. But it is the plumbing that makes everything else possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  One Context to Share Them All
&lt;/h3&gt;

&lt;p&gt;With units resolved, the next question is: what do they get to work with? Every unit kind in Velnora gets a context object — the thing passed into lifecycle hooks like &lt;code&gt;configure()&lt;/code&gt; and &lt;code&gt;build()&lt;/code&gt;. The context is how a unit talks to the rest of the system: exposing APIs, querying other units, reading config.&lt;/p&gt;

&lt;p&gt;Until now, each context was defined independently. &lt;code&gt;IntegrationConfigureContext&lt;/code&gt; had its own &lt;code&gt;ctx.query()&lt;/code&gt;. &lt;code&gt;IntegrationBuildContext&lt;/code&gt; had its own. &lt;code&gt;AdapterDevContext&lt;/code&gt; — same thing, redeclared from scratch. That meant duplicated surface area everywhere, and any change to the shared behavior had to be mirrored across all of them. It was manageable with three unit kinds. It would not be manageable at ten.&lt;/p&gt;

&lt;p&gt;The fix is straightforward — create a &lt;code&gt;BaseContext&lt;/code&gt; that carries everything shared, and let the specific contexts extend from it. Integration contexts inherit the base and add integration-specific capabilities on top. Adapter contexts do the same. Runtime contexts do the same. The shared parts live in one place. The specialized parts stay where they belong.&lt;/p&gt;

&lt;p&gt;This is not a clever idea. It is just the first time it is actually built.&lt;/p&gt;

&lt;h3&gt;
  
  
  Expose, Query, Done
&lt;/h3&gt;

&lt;p&gt;The BaseContext now fully implements the exposing and querying mechanism — the two operations that every unit needs regardless of kind. This is &lt;code&gt;ctx.expose()&lt;/code&gt; and &lt;code&gt;ctx.query()&lt;/code&gt; actually working, not just typed, but wired up end-to-end. A unit can expose an API under a key during &lt;code&gt;configure()&lt;/code&gt;, and any other unit that declared a dependency on it can query that key and get the real implementation back. No casting, no &lt;code&gt;any&lt;/code&gt;, no manual lookups. The foundation that the entire Unit System was designed around is now real.&lt;/p&gt;

&lt;p&gt;Backing the BaseContext is the &lt;strong&gt;&lt;code&gt;GlobalRegistry&lt;/code&gt;&lt;/strong&gt; — a central registry object that holds all exposed APIs at runtime. When a unit calls &lt;code&gt;ctx.expose("docker", dockerApi)&lt;/code&gt;, it writes to the GlobalRegistry. When another unit calls &lt;code&gt;ctx.query("docker")&lt;/code&gt;, it reads from the same place. One source of truth for the entire unit graph.&lt;/p&gt;

&lt;p&gt;Every registry entry is keyed, and the keys are properly typed and enforced. The same key that TypeScript checks at compile time is the one used to look things up at runtime. If the types say &lt;code&gt;"docker"&lt;/code&gt; exists, the runtime registry has a slot for &lt;code&gt;"docker"&lt;/code&gt;. If they do not, the slot does not exist and the query fails loudly.&lt;/p&gt;

&lt;p&gt;With these in place, the BaseContext is not just a shared interface anymore. It is the working layer that integrations, adapters, and runtimes all build on top of. The thing that was just types two posts ago is now running code.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Helper for the Lazy
&lt;/h3&gt;

&lt;p&gt;One small helper that came out of this work — &lt;code&gt;makeRegistryObject&lt;/code&gt;. It is a convenience for lazy people. You give it a name and a shape, and it builds you a nested object where every level is also a usable string key. So instead of writing &lt;code&gt;"kernel:config:units"&lt;/code&gt; by hand and hoping you did not typo it, you just do &lt;code&gt;result.config.units&lt;/code&gt; and it gives you that exact string. You can use it as a lookup key &lt;em&gt;and&lt;/em&gt; dot into it for child keys — both at the same time.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;makeRegistryObject("kernel", { config: { units: "units" } })&lt;/code&gt; gives you a value where &lt;code&gt;result&lt;/code&gt; is &lt;code&gt;"kernel"&lt;/code&gt;, &lt;code&gt;result.config&lt;/code&gt; is &lt;code&gt;"kernel:config"&lt;/code&gt;, and &lt;code&gt;result.config.units&lt;/code&gt; is &lt;code&gt;"kernel:config:units"&lt;/code&gt;. Each level works as a registry key. No manual string building, no typos, full autocomplete.&lt;/p&gt;

&lt;p&gt;It is a tiny thing. But in a monorepo with a growing number of registry keys, it saves a lot of stupid mistakes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pause
&lt;/h3&gt;

&lt;p&gt;After wiring up the base context and the config resolution, I started pushing into the runtime implementation — the actual guts of how a runtime like Node or Bun boots its toolchain, resolves packages, and executes code. And then I stopped.&lt;/p&gt;

&lt;p&gt;Not because something broke. Because I realized I was about to make decisions that would be very expensive to undo. The runtime layer touches everything: how units get their toolchains, how execution plans are built, how the package manager abstraction plugs in, how thread-mode and process-mode execution diverge. One wrong abstraction here and every adapter, every integration, every future runtime inherits the mistake.&lt;/p&gt;

&lt;p&gt;So I went back to research. Reading through the earlier design notes, sketching alternatives, revisiting the Toolchain API and the Adapter Protocol to see if the boundaries still made sense after everything that changed in the last few rounds. Sometimes the most productive thing you can do is stop coding and think. This is one of those times.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Next
&lt;/h3&gt;

&lt;p&gt;Once the runtime design solidifies, the next step is implementing it: a real Node runtime that boots its toolchain through the BaseContext, resolves packages through the registry, and produces execution plans that adapters can consume without knowing which runtime is behind them. That is the test that proves the whole stack works end-to-end.&lt;/p&gt;

</description>
      <category>velnora</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Filling the Unit System: IntegrationUnit, AdapterUnit, and the Full Boot Sequence</title>
      <dc:creator>Veys Aliyev</dc:creator>
      <pubDate>Sat, 28 Feb 2026 14:39:19 +0000</pubDate>
      <link>https://dev.to/mdreal32/filling-the-unit-system-integrationunit-adapterunit-and-the-full-boot-sequence-35hh</link>
      <guid>https://dev.to/mdreal32/filling-the-unit-system-integrationunit-adapterunit-and-the-full-boot-sequence-35hh</guid>
      <description>&lt;p&gt;Last post, the Unit System had its first real types — &lt;code&gt;RuntimeUnit&lt;/code&gt;, &lt;code&gt;PackageManager&lt;/code&gt;, and &lt;code&gt;defineRuntime()&lt;/code&gt;. The runtime layer was done. But a runtime by itself does nothing. It knows how to run things, but nothing is asking it to run anything yet. The system needed the other two unit kinds — integrations and adapters — before any of it would actually connect.&lt;/p&gt;

&lt;h3&gt;
  
  
  IntegrationUnit
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;IntegrationUnit&lt;/code&gt; is the interface for framework-specific plugins — React, NestJS, Angular, Docker as an orchestrator, and anything else that wires into the Velnora lifecycle. If runtimes are the foundation and adapters are the build tools, integrations are the things that actually give a workspace its identity.&lt;/p&gt;

&lt;p&gt;The key design decision here was making all lifecycle hooks &lt;strong&gt;optional&lt;/strong&gt;. An integration only implements what it needs. If it just needs to expose an API during init, it implements &lt;code&gt;configure()&lt;/code&gt;. If it also needs to do something at build time, it adds &lt;code&gt;build()&lt;/code&gt;. If it needs neither, it can still exist as a unit that just declares capabilities for others to depend on.&lt;/p&gt;

&lt;p&gt;This is Interface Segregation in practice. The Kernel duck-types before calling — &lt;code&gt;'configure' in unit&lt;/code&gt; — so there is no dead code, no empty method stubs, no forced implementations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two Phases, Two Contexts
&lt;/h3&gt;

&lt;p&gt;Every unit kind now gets &lt;strong&gt;phased contexts&lt;/strong&gt;. The &lt;code&gt;configure()&lt;/code&gt; hook receives an &lt;code&gt;IntegrationConfigureContext&lt;/code&gt; — full access to &lt;code&gt;ctx.expose()&lt;/code&gt; and &lt;code&gt;ctx.query()&lt;/code&gt;. The &lt;code&gt;build()&lt;/code&gt; hook receives an &lt;code&gt;IntegrationBuildContext&lt;/code&gt; — query-only, because by the time you are building, registration is closed. No new APIs can be exposed during a production build.&lt;/p&gt;

&lt;p&gt;Right now all three unit kinds share the same idea, but the context types are not fully separated yet. Runtimes, adapters, and integrations each have different phase-specific needs, and the concrete context interfaces still need to be split out per kind. That is a task for the next round.&lt;/p&gt;

&lt;p&gt;This separation is not just for safety. It makes the intent clear at the type level. If you are inside &lt;code&gt;build()&lt;/code&gt;, TypeScript will not even let you call &lt;code&gt;expose()&lt;/code&gt;. You know exactly what phase you are in by looking at the context type.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;defineIntegration&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Just like &lt;code&gt;defineRuntime()&lt;/code&gt;, the factory is thin. You pass your integration definition, it injects &lt;code&gt;kind: 'integration'&lt;/code&gt; and returns a &lt;code&gt;VelnoraUnit&lt;/code&gt;. Same pattern, same type inference, same generics capturing literal strings from &lt;code&gt;requires&lt;/code&gt; and &lt;code&gt;capabilities&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All three define helpers — &lt;code&gt;defineRuntime()&lt;/code&gt;, &lt;code&gt;defineAdapter()&lt;/code&gt;, and &lt;code&gt;defineIntegration()&lt;/code&gt; — support a second calling convention: a &lt;strong&gt;factory function&lt;/strong&gt;. Instead of passing a plain object, you can pass a function that receives &lt;code&gt;ConfigEnv&lt;/code&gt; (command, mode, etc.) and returns the config. This is useful when you need to change behavior based on whether you are running &lt;code&gt;dev&lt;/code&gt; or &lt;code&gt;build&lt;/code&gt;. The factory wraps your function and injects &lt;code&gt;kind&lt;/code&gt; at call-time.&lt;/p&gt;

&lt;p&gt;Both conventions produce the same thing. Static for simple cases, factory for environment-aware ones. The pattern is identical across all three unit kinds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Init Order
&lt;/h3&gt;

&lt;p&gt;In theory, integrations should initialize &lt;strong&gt;last&lt;/strong&gt; — after runtimes, after adapters. The idea is that by the time an integration runs its &lt;code&gt;configure()&lt;/code&gt; hook, every runtime is booted and every adapter is ready, so the integration can safely query anything it depends on.&lt;/p&gt;

&lt;p&gt;Within the integration tier, a topological sort by &lt;code&gt;requires&lt;/code&gt; and &lt;code&gt;optionalRequires&lt;/code&gt; would determine exact ordering. So if your integration depends on &lt;code&gt;'docker'&lt;/code&gt;, the Docker integration would configure first. This is the design — the actual resolver is not wired up yet.&lt;/p&gt;

&lt;p&gt;One open question: what happens when A requires B and B requires A? A circular dependency. The honest answer is I have not decided yet. The resolver could reject it at startup, or it could try to break the cycle with a warning. For now the design assumes no cycles — if you create one, you get what you deserve. This will need a real answer before the resolver ships.&lt;/p&gt;

&lt;h3&gt;
  
  
  AdapterUnit — The Middle Layer
&lt;/h3&gt;

&lt;p&gt;With integrations done, the adapter was next. &lt;code&gt;AdapterUnit&lt;/code&gt; represents build-tool orchestrators — Vite, Webpack, Turbopack, esbuild, and similar. Unlike integrations, adapters have &lt;strong&gt;required&lt;/strong&gt; hooks: &lt;code&gt;dev(project, ctx)&lt;/code&gt; and &lt;code&gt;build(project, ctx)&lt;/code&gt;. Both take a &lt;code&gt;Project&lt;/code&gt; — the specific project directory the adapter is operating on — plus a typed context. An adapter must handle both modes. There is no optional here — if you are an adapter, you know how to start a dev server and you know how to produce a production build. And &lt;code&gt;dev()&lt;/code&gt; returns a &lt;code&gt;DevServerResult&lt;/code&gt; — connection info that the Host uses to wire up the dev server.&lt;/p&gt;

&lt;p&gt;But the biggest design shift from earlier iterations of the spec was removing the &lt;code&gt;mode&lt;/code&gt; field. I originally had &lt;code&gt;mode: 'thread' | 'process'&lt;/code&gt; on the adapter itself. That was wrong. Whether something runs in-thread or as a child process is not the adapter's decision — it is the &lt;strong&gt;runtime's&lt;/strong&gt; decision. A Vite adapter should not care if it runs inside Node's process or gets spawned separately. It just says &lt;em&gt;what&lt;/em&gt; to run. The runtime says &lt;em&gt;how&lt;/em&gt;. Velnora executes the plan.&lt;/p&gt;

&lt;p&gt;So the adapter queries the runtime via &lt;code&gt;ctx.query()&lt;/code&gt;, calls something like &lt;code&gt;execute()&lt;/code&gt;, and the runtime returns a declarative &lt;code&gt;ExecutionPlan&lt;/code&gt; — a discriminated union. In thread mode, it carries a &lt;code&gt;run&lt;/code&gt; function that Velnora calls in-process. In process mode, it carries the binary, args, and cwd for Velnora to spawn as a child process. Either way, the adapter does not care which one comes back. Same adapter code works with Node, Bun, or any other runtime. The adapter never changes.&lt;/p&gt;

&lt;p&gt;Like integrations, adapters get phased contexts — &lt;code&gt;AdapterDevContext&lt;/code&gt; for &lt;code&gt;dev()&lt;/code&gt; and &lt;code&gt;AdapterBuildContext&lt;/code&gt; for &lt;code&gt;build()&lt;/code&gt;. And &lt;code&gt;defineAdapter()&lt;/code&gt; follows the same pattern: static object or factory function, &lt;code&gt;kind&lt;/code&gt; injected automatically, full type inference on dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Full Boot Sequence (In Theory)
&lt;/h3&gt;

&lt;p&gt;The init order is not implemented yet, but in theory this is how it should work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Runtimes&lt;/strong&gt; — language runtimes boot first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adapters&lt;/strong&gt; — build tools next&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations&lt;/strong&gt; — frameworks last&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Within each tier, topological sort by &lt;code&gt;requires&lt;/code&gt; and &lt;code&gt;optionalRequires&lt;/code&gt; would determine exact ordering. You drop units into one array and the Kernel figures out the rest — that is the design. What actually happens in practice is a different story, but this is the contract the system is built around.&lt;/p&gt;

&lt;p&gt;All three unit kinds are now implemented. The runtime layer has &lt;code&gt;RuntimeUnit&lt;/code&gt;, &lt;code&gt;PackageManager&lt;/code&gt;, &lt;code&gt;Toolchain&lt;/code&gt;, and &lt;code&gt;defineRuntime()&lt;/code&gt;. The integration layer has &lt;code&gt;IntegrationUnit&lt;/code&gt;, phased contexts, and &lt;code&gt;defineIntegration()&lt;/code&gt;. The adapter layer has &lt;code&gt;AdapterUnit&lt;/code&gt;, the execution model split, and &lt;code&gt;defineAdapter()&lt;/code&gt;. The Unit System is complete — every unit kind has its interface, its factory, and its place in the boot sequence.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Note on AI and How I Work
&lt;/h3&gt;

&lt;p&gt;I use AI for generating tests and JSDoc. In rare cases, code. And even when it generates code, I review it multiple times before applying it to the project and committing. The code architecture is mine. But the way it gets &lt;em&gt;seen&lt;/em&gt; — which packages should exist, how the spec should read, how the design should be documented — that is where I use Notion AI. Each time I work on a feature, it takes almost a couple of hours: understand the design, consult with AI, analyze it again, and then document it properly. The thinking is mine. The documentation process is a collaboration.&lt;/p&gt;

&lt;p&gt;But right now I hit the limits. Almost every code assistant out there has usage caps — except Notion AI — and I have burned through all of them. That means tests and JSDoc generation are temporarily gone until I get a local assistant running. I have been setting one up, but it is not fully ready yet — right now I am working on creating a connection between my PC and my Mac so I can run and use it remotely, distance independent.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Next
&lt;/h3&gt;

&lt;p&gt;The Unit System is complete — every unit kind has its interface, its factory, and its place in the boot sequence. The next step is wiring them into the Kernel's actual resolver and making the first real unit packages. That means real &lt;code&gt;defineRuntime()&lt;/code&gt; calls for Node and Bun, a real &lt;code&gt;defineAdapter()&lt;/code&gt; for Vite, and a real &lt;code&gt;defineIntegration()&lt;/code&gt; for React. Interfaces are done. Now it is time to fill them.&lt;/p&gt;

</description>
      <category>velnora</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Filling the Unit System: IntegrationUnit, AdapterUnit, and the Full Boot Sequence</title>
      <dc:creator>Veys Aliyev</dc:creator>
      <pubDate>Wed, 25 Feb 2026 18:52:17 +0000</pubDate>
      <link>https://dev.to/mdreal32/filling-the-unit-system-integrationunit-adapterunit-and-the-full-boot-sequence-25hd</link>
      <guid>https://dev.to/mdreal32/filling-the-unit-system-integrationunit-adapterunit-and-the-full-boot-sequence-25hd</guid>
      <description>&lt;p&gt;Last post, the Unit System had its first real types — &lt;code&gt;RuntimeUnit&lt;/code&gt;, &lt;code&gt;PackageManager&lt;/code&gt;, and &lt;code&gt;defineRuntime()&lt;/code&gt;. The runtime layer was done. But a runtime by itself does nothing. It knows how to run things, but nothing is asking it to run anything yet. The system needed the other two unit kinds — integrations and adapters — before any of it would actually connect.&lt;/p&gt;

&lt;h3&gt;
  
  
  IntegrationUnit
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;IntegrationUnit&lt;/code&gt; is the interface for framework-specific plugins — React, NestJS, Angular, Docker as an orchestrator, and anything else that wires into the Velnora lifecycle. If runtimes are the foundation and adapters are the build tools, integrations are the things that actually give a workspace its identity.&lt;/p&gt;

&lt;p&gt;The key design decision here was making all lifecycle hooks &lt;strong&gt;optional&lt;/strong&gt;. An integration only implements what it needs. If it just needs to expose an API during init, it implements &lt;code&gt;configure()&lt;/code&gt;. If it also needs to do something at build time, it adds &lt;code&gt;build()&lt;/code&gt;. If it needs neither, it can still exist as a unit that just declares capabilities for others to depend on.&lt;/p&gt;

&lt;p&gt;This is Interface Segregation in practice. The Kernel duck-types before calling — &lt;code&gt;'configure' in unit&lt;/code&gt; — so there is no dead code, no empty method stubs, no forced implementations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two Phases, Two Contexts
&lt;/h3&gt;

&lt;p&gt;Every unit kind now gets &lt;strong&gt;phased contexts&lt;/strong&gt;. The &lt;code&gt;configure()&lt;/code&gt; hook receives an &lt;code&gt;IntegrationConfigureContext&lt;/code&gt; — full access to &lt;code&gt;ctx.expose()&lt;/code&gt; and &lt;code&gt;ctx.query()&lt;/code&gt;. The &lt;code&gt;build()&lt;/code&gt; hook receives an &lt;code&gt;IntegrationBuildContext&lt;/code&gt; — query-only, because by the time you are building, registration is closed. No new APIs can be exposed during a production build.&lt;/p&gt;

&lt;p&gt;Right now all three unit kinds share the same idea, but the context types are not fully separated yet. Runtimes, adapters, and integrations each have different phase-specific needs, and the concrete context interfaces still need to be split out per kind. That is a task for the next round.&lt;/p&gt;

&lt;p&gt;This separation is not just for safety. It makes the intent clear at the type level. If you are inside &lt;code&gt;build()&lt;/code&gt;, TypeScript will not even let you call &lt;code&gt;expose()&lt;/code&gt;. You know exactly what phase you are in by looking at the context type.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;defineIntegration&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Just like &lt;code&gt;defineRuntime()&lt;/code&gt;, the factory is thin. You pass your integration definition, it injects &lt;code&gt;kind: 'integration'&lt;/code&gt; and returns a &lt;code&gt;VelnoraUnit&lt;/code&gt;. Same pattern, same type inference, same generics capturing literal strings from &lt;code&gt;requires&lt;/code&gt; and &lt;code&gt;capabilities&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All three define helpers — &lt;code&gt;defineRuntime()&lt;/code&gt;, &lt;code&gt;defineAdapter()&lt;/code&gt;, and &lt;code&gt;defineIntegration()&lt;/code&gt; — support a second calling convention: a &lt;strong&gt;factory function&lt;/strong&gt;. Instead of passing a plain object, you can pass a function that receives &lt;code&gt;ConfigEnv&lt;/code&gt; (command, mode, etc.) and returns the config. This is useful when you need to change behavior based on whether you are running &lt;code&gt;dev&lt;/code&gt; or &lt;code&gt;build&lt;/code&gt;. The factory wraps your function and injects &lt;code&gt;kind&lt;/code&gt; at call-time.&lt;/p&gt;

&lt;p&gt;Both conventions produce the same thing. Static for simple cases, factory for environment-aware ones. The pattern is identical across all three unit kinds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Init Order
&lt;/h3&gt;

&lt;p&gt;In theory, integrations should initialize &lt;strong&gt;last&lt;/strong&gt; — after runtimes, after adapters. The idea is that by the time an integration runs its &lt;code&gt;configure()&lt;/code&gt; hook, every runtime is booted and every adapter is ready, so the integration can safely query anything it depends on.&lt;/p&gt;

&lt;p&gt;Within the integration tier, a topological sort by &lt;code&gt;requires&lt;/code&gt; and &lt;code&gt;optionalRequires&lt;/code&gt; would determine exact ordering. So if your integration depends on &lt;code&gt;'docker'&lt;/code&gt;, the Docker integration would configure first. This is the design — the actual resolver is not wired up yet.&lt;/p&gt;

&lt;p&gt;One open question: what happens when A requires B and B requires A? A circular dependency. The honest answer is I have not decided yet. The resolver could reject it at startup, or it could try to break the cycle with a warning. For now the design assumes no cycles — if you create one, you get what you deserve. This will need a real answer before the resolver ships.&lt;/p&gt;

&lt;h3&gt;
  
  
  AdapterUnit — The Middle Layer
&lt;/h3&gt;

&lt;p&gt;With integrations done, the adapter was next. &lt;code&gt;AdapterUnit&lt;/code&gt; represents build-tool orchestrators — Vite, Webpack, Turbopack, esbuild, and similar. Unlike integrations, adapters have &lt;strong&gt;required&lt;/strong&gt; hooks: &lt;code&gt;dev(project, ctx)&lt;/code&gt; and &lt;code&gt;build(project, ctx)&lt;/code&gt;. Both take a &lt;code&gt;Project&lt;/code&gt; — the specific project directory the adapter is operating on — plus a typed context. An adapter must handle both modes. There is no optional here — if you are an adapter, you know how to start a dev server and you know how to produce a production build. And &lt;code&gt;dev()&lt;/code&gt; returns a &lt;code&gt;DevServerResult&lt;/code&gt; — connection info that the Host uses to wire up the dev server.&lt;/p&gt;

&lt;p&gt;But the biggest design shift from earlier iterations of the spec was removing the &lt;code&gt;mode&lt;/code&gt; field. I originally had &lt;code&gt;mode: 'thread' | 'process'&lt;/code&gt; on the adapter itself. That was wrong. Whether something runs in-thread or as a child process is not the adapter's decision — it is the &lt;strong&gt;runtime's&lt;/strong&gt; decision. A Vite adapter should not care if it runs inside Node's process or gets spawned separately. It just says &lt;em&gt;what&lt;/em&gt; to run. The runtime says &lt;em&gt;how&lt;/em&gt;. Velnora executes the plan.&lt;/p&gt;

&lt;p&gt;So the adapter queries the runtime via &lt;code&gt;ctx.query()&lt;/code&gt;, calls something like &lt;code&gt;execute()&lt;/code&gt;, and the runtime returns a declarative &lt;code&gt;ExecutionPlan&lt;/code&gt; — a discriminated union. In thread mode, it carries a &lt;code&gt;run&lt;/code&gt; function that Velnora calls in-process. In process mode, it carries the binary, args, and cwd for Velnora to spawn as a child process. Either way, the adapter does not care which one comes back. Same adapter code works with Node, Bun, or any other runtime. The adapter never changes.&lt;/p&gt;

&lt;p&gt;Like integrations, adapters get phased contexts — &lt;code&gt;AdapterDevContext&lt;/code&gt; for &lt;code&gt;dev()&lt;/code&gt; and &lt;code&gt;AdapterBuildContext&lt;/code&gt; for &lt;code&gt;build()&lt;/code&gt;. And &lt;code&gt;defineAdapter()&lt;/code&gt; follows the same pattern: static object or factory function, &lt;code&gt;kind&lt;/code&gt; injected automatically, full type inference on dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Full Boot Sequence (In Theory)
&lt;/h3&gt;

&lt;p&gt;The init order is not implemented yet, but in theory this is how it should work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Runtimes&lt;/strong&gt; — language runtimes boot first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adapters&lt;/strong&gt; — build tools next&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations&lt;/strong&gt; — frameworks last&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Within each tier, topological sort by &lt;code&gt;requires&lt;/code&gt; and &lt;code&gt;optionalRequires&lt;/code&gt; would determine exact ordering. You drop units into one array and the Kernel figures out the rest — that is the design. What actually happens in practice is a different story, but this is the contract the system is built around.&lt;/p&gt;

&lt;p&gt;All three unit kinds are now implemented. The runtime layer has &lt;code&gt;RuntimeUnit&lt;/code&gt;, &lt;code&gt;PackageManager&lt;/code&gt;, &lt;code&gt;Toolchain&lt;/code&gt;, and &lt;code&gt;defineRuntime()&lt;/code&gt;. The integration layer has &lt;code&gt;IntegrationUnit&lt;/code&gt;, phased contexts, and &lt;code&gt;defineIntegration()&lt;/code&gt;. The adapter layer has &lt;code&gt;AdapterUnit&lt;/code&gt;, the execution model split, and &lt;code&gt;defineAdapter()&lt;/code&gt;. The Unit System is complete — every unit kind has its interface, its factory, and its place in the boot sequence.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Note on AI and How I Work
&lt;/h3&gt;

&lt;p&gt;I use AI for generating tests and JSDoc. In rare cases, code. And even when it generates code, I review it multiple times before applying it to the project and committing. The code architecture is mine. But the way it gets &lt;em&gt;seen&lt;/em&gt; — which packages should exist, how the spec should read, how the design should be documented — that is where I use Notion AI. Each time I work on a feature, it takes almost a couple of hours: understand the design, consult with AI, analyze it again, and then document it properly. The thinking is mine. The documentation process is a collaboration.&lt;/p&gt;

&lt;p&gt;But right now I hit the limits. Almost every code assistant out there has usage caps — except Notion AI — and I have burned through all of them. That means tests and JSDoc generation are temporarily gone until I get a local assistant running. I have been setting one up, but it is not fully ready yet — right now I am working on creating a connection between my PC and my Mac so I can run and use it remotely, distance independent.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Next
&lt;/h3&gt;

&lt;p&gt;The Unit System is complete — every unit kind has its interface, its factory, and its place in the boot sequence. The next step is wiring them into the Kernel's actual resolver and making the first real unit packages. That means real &lt;code&gt;defineRuntime()&lt;/code&gt; calls for Node and Bun, a real &lt;code&gt;defineAdapter()&lt;/code&gt; for Vite, and a real &lt;code&gt;defineIntegration()&lt;/code&gt; for React. Interfaces are done. Now it is time to fill them.&lt;/p&gt;

</description>
      <category>velnora</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>One Array to Rule Them All: Designing Velnora's Unit System</title>
      <dc:creator>Veys Aliyev</dc:creator>
      <pubDate>Sat, 21 Feb 2026 08:48:08 +0000</pubDate>
      <link>https://dev.to/mdreal32/one-array-to-rule-them-all-designing-velnoras-unit-system-3m0p</link>
      <guid>https://dev.to/mdreal32/one-array-to-rule-them-all-designing-velnoras-unit-system-3m0p</guid>
      <description>&lt;p&gt;v0.1 is tagged. The skeleton stands. And the original plan said v0.2 would be graphs — dependency graphs, project graphs, the whole resolution layer.&lt;/p&gt;

&lt;p&gt;But after researching it, I realized graphs are useless at this stage. There is no real workspace to resolve yet. No integrations to connect. The graph would just be a piece of code that does nothing because it has nothing to operate on. It would exist purely to satisfy the roadmap, not to solve an actual problem.&lt;/p&gt;

&lt;p&gt;So I skipped it. Graphs will come later, in a future release, when there are enough moving parts to make resolution meaningful. For now, the real next step is integrations — the things that actually &lt;em&gt;fill&lt;/em&gt; the workspace. That became the new v0.2 target.&lt;/p&gt;

&lt;p&gt;And the first question was: how do integrations even enter the system?&lt;/p&gt;

&lt;p&gt;The answer started with a problem I kept hitting in earlier rewrites — three separate registration paths. Integrations had one way in, adapters had another, and runtimes were their own thing. Every time I added something, I had to remember which path it belonged to. It was manageable at the scale of v0.1, but I could already feel it becoming a mess.&lt;/p&gt;

&lt;p&gt;So I asked: what if everything entered the same way?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Unit System
&lt;/h3&gt;

&lt;p&gt;That is the core idea behind what I am calling the Unit System. One array in the config. One resolver in the Kernel. Three typed helpers — &lt;code&gt;defineIntegration()&lt;/code&gt;, &lt;code&gt;defineAdapter()&lt;/code&gt;, &lt;code&gt;defineRuntime()&lt;/code&gt; — for type safety. But underneath, they all produce the same thing: a &lt;code&gt;VelnoraUnit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The Kernel does not care what kind of unit you are. It collects them all, resolves dependencies, and initializes in the correct order. Kind only matters for &lt;em&gt;when&lt;/em&gt; you init — runtimes first, then adapters, then integrations — and for the specific hooks you expose.&lt;/p&gt;

&lt;p&gt;This felt right immediately. The config went from three separate concepts to one flat array. And the Kernel went from three separate loaders to one resolver.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies Without Imports
&lt;/h3&gt;

&lt;p&gt;The part that took the most thinking was inter-unit communication. Units should never import each other directly — that would create hard coupling and break the whole plugin model. But they &lt;em&gt;need&lt;/em&gt; to talk to each other. A Kubernetes integration needs Docker. A Spring Boot integration needs a JVM runtime.&lt;/p&gt;

&lt;p&gt;The solution is a capability-based system. Each unit declares what it &lt;em&gt;provides&lt;/em&gt; (capabilities) and what it &lt;em&gt;requires&lt;/em&gt;. The resolver matches them by string. If you require &lt;code&gt;'docker'&lt;/code&gt;, any unit that has &lt;code&gt;'docker'&lt;/code&gt; in its capabilities array satisfies it.&lt;/p&gt;

&lt;p&gt;But here is where it gets interesting — the type safety.&lt;/p&gt;

&lt;p&gt;I built a global type registry using TypeScript declaration merging. When you install &lt;code&gt;@velnora/integrations-docker&lt;/code&gt;, its type declarations automatically register &lt;code&gt;DockerApi&lt;/code&gt; into &lt;code&gt;Velnora.UnitRegistry&lt;/code&gt;. Then when you call &lt;code&gt;ctx.query('docker')&lt;/code&gt; inside another unit, TypeScript knows the return type is &lt;code&gt;DockerApi&lt;/code&gt;. No imports. No type casting. Just install the package and the types flow.&lt;/p&gt;

&lt;p&gt;Hard dependencies (&lt;code&gt;requires&lt;/code&gt;) return the API directly. Soft dependencies (&lt;code&gt;optionalRequires&lt;/code&gt;) return &lt;code&gt;T | undefined&lt;/code&gt;. The generics on the define helpers infer the literal strings from the arrays, so TypeScript knows exactly which queries are safe and which might be undefined.&lt;/p&gt;

&lt;p&gt;This is the kind of thing that makes me love TypeScript. The type system is doing real architectural work here — it is enforcing the dependency contract at compile time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Composite Units
&lt;/h3&gt;

&lt;p&gt;One design decision I am particularly happy with: composite units. A single unit can bundle child units inside itself using a &lt;code&gt;units[]&lt;/code&gt; array. The user adds one thing to the config, and the Kernel unpacks everything.&lt;/p&gt;

&lt;p&gt;Docker is the perfect example. As a user, you call &lt;code&gt;docker()&lt;/code&gt; and you get both the integration (build images, push, create networks) &lt;em&gt;and&lt;/em&gt; the runtime (Docker Engine). One function call. The Kernel flattens the tree, resolves dependencies across all of them, and inits in order.&lt;/p&gt;

&lt;p&gt;This keeps the config clean. The user does not need to know the internal structure of what they are adding. They just add &lt;code&gt;docker()&lt;/code&gt; and everything works.&lt;/p&gt;

&lt;h3&gt;
  
  
  First Interfaces: RuntimeUnit &amp;amp; PackageManager
&lt;/h3&gt;

&lt;p&gt;With the design in place, it was time to write actual types. The first two interfaces I implemented were &lt;code&gt;RuntimeUnit&lt;/code&gt; and &lt;code&gt;PackageManager&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;RuntimeUnit&lt;/code&gt; defines what a language runtime looks like to the Kernel — its toolchain, its supported package managers, and a &lt;code&gt;resolvePackageManager()&lt;/code&gt; method that figures out which one to use for a given project directory. The &lt;code&gt;PackageManager&lt;/code&gt; interface captures the basics: install, add, remove, and how to resolve the lock file. Nothing fancy — just enough contract for the Kernel to work with any runtime without knowing its internals.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;defineRuntime&lt;/code&gt; — The First Helper
&lt;/h3&gt;

&lt;p&gt;With the interfaces in place, the next step was &lt;code&gt;defineRuntime()&lt;/code&gt; — the first of the three typed factory helpers.&lt;/p&gt;

&lt;p&gt;The reason this one came first is simple: runtimes are the bottom of the stack. Everything else depends on them. An adapter needs to know what runtime it is orchestrating. An integration needs to know what language capabilities are available. If the runtime layer is not solid, nothing above it can be trusted.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;defineRuntime()&lt;/code&gt; is a thin function. You pass it your runtime definition — name, version, capabilities, toolchain, package managers — and it gives you back a &lt;code&gt;VelnoraUnit&lt;/code&gt;. That is it. No magic. No hidden state. The function exists purely for type inference — so TypeScript can capture the exact literal strings you declared in &lt;code&gt;requires&lt;/code&gt; and &lt;code&gt;capabilities&lt;/code&gt;, and carry that information all the way through to &lt;code&gt;ctx.query()&lt;/code&gt; later.&lt;/p&gt;

&lt;p&gt;This is the pattern all three helpers will follow. &lt;code&gt;defineAdapter()&lt;/code&gt; and &lt;code&gt;defineIntegration()&lt;/code&gt; will look almost identical in shape. The only difference is the kind-specific fields each one adds. But the contract is the same: you define what you are, what you provide, and what you need. The Kernel handles the rest.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Still Open
&lt;/h3&gt;

&lt;p&gt;There are questions I have not answered yet. Should capabilities support versions — like &lt;code&gt;'jvm@21'&lt;/code&gt;? How should the resolver pick between multiple providers of the same capability when both Node and Bun provide &lt;code&gt;'javascript'&lt;/code&gt;? Should there be lifecycle hooks for cross-unit events?&lt;/p&gt;

&lt;p&gt;These are real design questions, not implementation details. They will shape how the Unit System scales. But the core is solid: one array, capability-based resolution, type-safe queries, composite unpacking, and now the first real types and factory in place. The runtime layer has a shape — the rest of the unit kinds will follow the same pattern.&lt;/p&gt;

</description>
      <category>velnora</category>
      <category>typescript</category>
      <category>runtime</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Static Serving, H3, and the Last Piece of v0.1</title>
      <dc:creator>Veys Aliyev</dc:creator>
      <pubDate>Wed, 18 Feb 2026 06:20:45 +0000</pubDate>
      <link>https://dev.to/mdreal32/static-serving-h3-and-the-last-piece-of-v01-3iea</link>
      <guid>https://dev.to/mdreal32/static-serving-h3-and-the-last-piece-of-v01-3iea</guid>
      <description>&lt;p&gt;Last post, the Kernel was booting, the Host was listening, and projects were being discovered and routed. The skeleton was alive — but it had no skin. The Host could tell you &lt;em&gt;about&lt;/em&gt; a project, but it could not actually &lt;em&gt;serve&lt;/em&gt; one. That is what changed this week.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static Serving from &lt;code&gt;src/&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The Host now serves files from &lt;code&gt;src/&lt;/code&gt; — just serving. No fallback logic, no integration detection. I did not even try to find any integration today because they do not exist yet at all. The entry point is &lt;code&gt;src/index.html&lt;/code&gt;. Simple.&lt;/p&gt;

&lt;p&gt;For later, this will be moved to the fallback section — once integrations are real, static serving becomes the baseline when nothing else claims the request. But for now, it is the only thing running.&lt;/p&gt;

&lt;p&gt;I also added a simple JSON endpoint — &lt;code&gt;$path/__json&lt;/code&gt; — to see a small JSON about the app itself. Just for debug, or maybe later for the app. It depends. It may be needed or it will be removed. Future releases will show.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Real Story: H3
&lt;/h3&gt;

&lt;p&gt;But the real story here is &lt;a href="https://h3.unjs.io/" rel="noopener noreferrer"&gt;H3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;H3 is the HTTP framework I chose for Velnora's Host early on, but this was the first time I actually went deep with it. And the experience was something different.&lt;/p&gt;

&lt;p&gt;If you come from Express — &lt;code&gt;req&lt;/code&gt;, &lt;code&gt;res&lt;/code&gt;, &lt;code&gt;next()&lt;/code&gt; — H3 will feel weird at first. There is no &lt;code&gt;req&lt;/code&gt; and &lt;code&gt;res&lt;/code&gt;. You get an &lt;code&gt;event&lt;/code&gt; object. You do not call &lt;code&gt;res.send()&lt;/code&gt;. You &lt;strong&gt;return&lt;/strong&gt; the response. If you return nothing, the next handler runs. That is it.&lt;/p&gt;

&lt;p&gt;Middleware is not a stack you push onto. It is a set of event handlers composed together. The framework decides what runs. You just define handlers and compose them.&lt;/p&gt;

&lt;p&gt;For the first few hours, my muscle memory kept fighting it. I kept reaching for Express patterns that do not exist here. The documentation is good but minimal — the &lt;a href="https://unjs.io/" rel="noopener noreferrer"&gt;UnJS&lt;/a&gt; ecosystem assumes you read source code. That is fine, but it is a different experience.&lt;/p&gt;

&lt;p&gt;Once I stopped mapping H3 onto Express and started thinking in its own terms, things clicked. The code got shorter. The composition got cleaner. Static serving slotted in naturally alongside the Vite middleware.&lt;/p&gt;

&lt;p&gt;Static serving is now the baseline. Every project in a Velnora workspace gets it for free. No config needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Last Piece: &lt;code&gt;velnora init&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;With serving working, there was one thing left for v0.1 — a way to &lt;em&gt;start&lt;/em&gt; a workspace from nothing.&lt;/p&gt;

&lt;p&gt;I built the &lt;code&gt;velnora init &amp;lt;directory&amp;gt;&lt;/code&gt; command. The directory is required for now. Later, when the questionary is implemented — including questions from integrations — it will be optional or changed. But the decision here was deliberate: keep it minimal, keep it honest. The init command does not pretend to know what your workspace looks like yet. It just gives you the two files that make a valid Velnora workspace — a config and a &lt;code&gt;package.json&lt;/code&gt; — and gets out of your way.&lt;/p&gt;

&lt;p&gt;The rest is manual for now, alas. This is where the questionary will live later. But for v0.1, scaffolding an empty workspace is enough to close the loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  v0.1 — The Skeleton Is Complete
&lt;/h3&gt;

&lt;p&gt;And with this, v0.1 is done. There is nothing flashy to show — no UI, no dashboard, no demo. But the pipeline is real. The Kernel boots, the Host serves, discovery works, and &lt;code&gt;velnora init&lt;/code&gt; scaffolds a workspace. That is the whole point of v0.1: prove the pipeline exists.&lt;/p&gt;

&lt;p&gt;I will tag it as v0.1, but I will not publish yet. There is no rush. The code is there, the &lt;a href="https://github.com/Velnora/velnora/releases" rel="noopener noreferrer"&gt;tag&lt;/a&gt; marks the milestone, and that is enough for now.&lt;/p&gt;

&lt;p&gt;Next: I am going to analyze the v0.2 release scope to decide which task comes first. The skeleton is standing — now it needs muscle.&lt;/p&gt;

</description>
      <category>velnora</category>
      <category>h3</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a Type-Safe Runtime from Scratch: Kernel, Host &amp; Inferred CLI Types in Velnora</title>
      <dc:creator>Veys Aliyev</dc:creator>
      <pubDate>Sat, 14 Feb 2026 20:18:59 +0000</pubDate>
      <link>https://dev.to/mdreal32/building-a-type-safe-runtime-from-scratch-kernel-host-inferred-cli-types-in-velnora-12b</link>
      <guid>https://dev.to/mdreal32/building-a-type-safe-runtime-from-scratch-kernel-host-inferred-cli-types-in-velnora-12b</guid>
      <description>&lt;h3&gt;
  
  
  Starting the Schemas
&lt;/h3&gt;

&lt;p&gt;So, discovery is working. Now we need to define &lt;strong&gt;what&lt;/strong&gt; we are discovering.&lt;/p&gt;

&lt;p&gt;This is where the schemas come in.&lt;/p&gt;

&lt;p&gt;The goal is to have a strict contract for every part of the system. If Velnora detects a package, it needs to know exactly what that package is capable of.&lt;/p&gt;

&lt;p&gt;The first question was: what does a discovered project actually look like at runtime? I needed a &lt;code&gt;Project&lt;/code&gt; interface — a strict contract that captures everything the system needs to know about a package.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;package.json&lt;/code&gt; alone isn't enough. It gives you the name, version, and dependencies — but nothing Velnora-specific. Where does the adapter config go? The framework hints? The build targets? Polluting &lt;code&gt;package.json&lt;/code&gt; with custom fields felt wrong.&lt;/p&gt;

&lt;p&gt;So the &lt;code&gt;Project&lt;/code&gt; interface pulls from &lt;strong&gt;two sources of truth&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;packageJson&lt;/code&gt;&lt;/strong&gt; — the raw &lt;code&gt;package.json&lt;/code&gt;, stored as-is so adapters and plugins can inspect dependencies without re-reading the file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;config&lt;/code&gt;&lt;/strong&gt; — the resolved &lt;code&gt;VelnoraAppConfig&lt;/code&gt; from &lt;code&gt;velnora.config.ts&lt;/code&gt; (or an empty object if none exists). This is where all Velnora-specific metadata lives.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of that, each project gets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A stable, path-based &lt;code&gt;name&lt;/code&gt; derived from the workspace root (e.g., &lt;code&gt;packages/app-one&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;A human-readable &lt;code&gt;displayName&lt;/code&gt; from &lt;code&gt;package.json&lt;/code&gt; (e.g., &lt;code&gt;@example/app-one&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The absolute &lt;code&gt;root&lt;/code&gt; path and a routable &lt;code&gt;path&lt;/code&gt; for the Host.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Two Levels of Configuration
&lt;/h3&gt;

&lt;p&gt;The configuration itself works at two distinct levels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;VelnoraConfig&lt;/code&gt; (Global):&lt;/strong&gt; Lives in the workspace root. Defines the rules for the entire repo — plugins, shared defaults, and global overrides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;VelnoraAppConfig&lt;/code&gt; (App-Specific):&lt;/strong&gt; Lives inside each project folder. Defines the specific framework, build targets, and unique needs of that application. This is what ends up in &lt;code&gt;Project.config&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By separating them, we get strict typing for each context while keeping the overall system cohesive.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pivot to Execution
&lt;/h3&gt;

&lt;p&gt;But as satisfying as it was to define these interfaces, I realized something.&lt;/p&gt;

&lt;p&gt;It is okay to have perfect types, but right now, &lt;strong&gt;running the app is the first priority.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is no point in building a complex config parser if the core runtime isn't spinning up the application yet. So I decided to simplify.&lt;/p&gt;

&lt;p&gt;I am shifting focus:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;First:&lt;/strong&gt; Make the app run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Then:&lt;/strong&gt; Parse &lt;code&gt;velnora.config.ts&lt;/code&gt; and apply the fine-grained configs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The schemas are defined, but the implementation of their full power can wait. Execution comes first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kernel &amp;amp; Host Implementation
&lt;/h3&gt;

&lt;p&gt;So that is exactly what I did. I went ahead and implemented the &lt;strong&gt;Kernel&lt;/strong&gt; and &lt;strong&gt;Host&lt;/strong&gt;—the two core runtime pieces that boot up Velnora. The Kernel orchestrates everything: loads the config, resolves projects, and hands off to the Host. The Host spins up the HTTP server and mounts adapter middleware.&lt;/p&gt;

&lt;p&gt;But as soon as the runtime started taking shape, I hit a wall. &lt;strong&gt;Typing issues.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;dev&lt;/code&gt; command needs options like &lt;code&gt;--host&lt;/code&gt;, &lt;code&gt;--port&lt;/code&gt;, &lt;code&gt;--mode&lt;/code&gt;, &lt;code&gt;--root&lt;/code&gt;. These options are parsed by the CLI, but they are also needed deeper in the system—by the Host when it starts listening, and by the Kernel when it calls &lt;code&gt;bootHost()&lt;/code&gt;. So the question becomes: where do the types live?&lt;/p&gt;

&lt;p&gt;The obvious answer would be to use something like Commander.js and just pass the parsed result around. But here is the problem: libraries like Commander have insane adoption—almost every Node.js CLI uses it—yet the typing story is practically nonexistent. You define commands, options, arguments… and then you get back &lt;code&gt;any&lt;/code&gt; or loosely typed objects. You lose all the safety the moment you try to pass parsed options to another part of your system.&lt;/p&gt;

&lt;h3&gt;
  
  
  @velnora/cli-helper &amp;amp; The Lazy Developer's Split
&lt;/h3&gt;

&lt;p&gt;I wanted both: &lt;strong&gt;great runtime ergonomics&lt;/strong&gt; (chainable API, descriptions, defaults) &lt;em&gt;and&lt;/em&gt; &lt;strong&gt;full type inference&lt;/strong&gt; (so that when I define &lt;code&gt;--port &amp;lt;number&amp;gt;&lt;/code&gt;, the result type knows &lt;code&gt;port&lt;/code&gt; is a &lt;code&gt;number&lt;/code&gt;, not &lt;code&gt;string | undefined&lt;/code&gt;). That is why I built &lt;code&gt;@velnora/cli-helper&lt;/code&gt;—a thin, Commander-like API that infers the full option type from the definition using &lt;code&gt;inferCommandType&amp;lt;typeof command&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that in place, the lazy developer in me took it one step further: &lt;strong&gt;split the CLI into two separate packages.&lt;/strong&gt; Same practice from previous versions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@velnora/commands&lt;/code&gt;&lt;/strong&gt; (the helper package) — exports the command definitions &lt;em&gt;and&lt;/em&gt; their inferred types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@velnora/cli&lt;/code&gt;&lt;/strong&gt; — imports those commands and executes them. Thin wrapper. No business logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;commands&lt;/code&gt; package defines each command and exports both the command object and its inferred type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;devCommand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Start the development server in watch mode.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--host &amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Host to run the development server on&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--port &amp;lt;number&amp;gt;, -p&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Port to run the development server on&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--watch, -w&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enable watch mode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&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="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--mode &amp;lt;development|production&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Set the mode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--root &amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Root directory of the project&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DevCommandOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inferCommandType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;devCommand&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/Velnora/velnora/blob/master/libs/helper/commands/src/main.ts#L9C1-L18C69" rel="noopener noreferrer"&gt;View the full source on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By exporting &lt;code&gt;DevCommandOptions&lt;/code&gt; from the commands package, I use it as the typing for the &lt;strong&gt;Host&lt;/strong&gt; application and the &lt;strong&gt;Kernel's &lt;code&gt;bootHost()&lt;/code&gt;&lt;/strong&gt; functionality. No duplication. The types are inferred directly from the command definition—if I add a new flag, the entire chain updates automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@velnora/commands  →  defines commands + exports types (DevCommandOptions)
       ↓
@velnora/kernel   →  imports DevCommandOptions for bootHost()
       ↓
@velnora/host     →  imports DevCommandOptions for server startup
       ↓
@velnora/cli      →  imports commands, executes them, passes options down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the kind of lazy that pays off. One source of truth for the command shape, and TypeScript does the rest.&lt;/p&gt;

&lt;h3&gt;
  
  
  v0.0.1 — It Runs 😄
&lt;/h3&gt;

&lt;p&gt;Not ready for publish (don't get excited), but &lt;strong&gt;it runs.&lt;/strong&gt; And that matters.&lt;/p&gt;

&lt;p&gt;Here is what &lt;code&gt;velnora dev&lt;/code&gt; gives you today. A request to the root &lt;code&gt;/&lt;/code&gt; returns a JSON overview of the entire workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"velnora"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"packages/app-one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@example/app-one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/@example/app-one"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"libs/lib-one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@example/lib-one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/@example/lib-one"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is temporary—eventually the root will serve the Velnora dashboard. But right now, it proves the pipeline works: CLI → Kernel → Host → discovered projects → HTTP response.&lt;/p&gt;

&lt;p&gt;And for each project path, the Host returns a small status object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"libs/lib-one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@example/lib-one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;project-root&amp;gt;/libs/lib-one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"registered"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Discovery works. The Kernel boots. The Host listens. Projects are registered and routed. It is not much—but it is &lt;strong&gt;real.&lt;/strong&gt; The skeleton is alive.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>cli</category>
      <category>javascript</category>
      <category>velnora</category>
    </item>
    <item>
      <title>Building v0.1: The 4th Gen &amp; Notion Shift</title>
      <dc:creator>Veys Aliyev</dc:creator>
      <pubDate>Wed, 11 Feb 2026 19:00:19 +0000</pubDate>
      <link>https://dev.to/mdreal32/building-v01-the-4th-gen-notion-shift-56jg</link>
      <guid>https://dev.to/mdreal32/building-v01-the-4th-gen-notion-shift-56jg</guid>
      <description>&lt;p&gt;I am currently working on Release 0.1. Recently, I completed the project discovery phase. I wouldn't say I hit "hard walls," but a significant amount of time went into refactoring. In fact, I completely closed the GitHub project board and have fully moved to Notion for task management.&lt;/p&gt;

&lt;p&gt;The bulk of the work has been a massive refactor. You can see the start of it in &lt;a href="https://github.com/Velnora/velnora/commit/abe3028f4277ced2d6f1b18ed35954cc50092ff5" rel="noopener noreferrer"&gt;this commit&lt;/a&gt;. I re-created the &lt;code&gt;utils&lt;/code&gt;, &lt;code&gt;types&lt;/code&gt;, and &lt;code&gt;cli&lt;/code&gt; packages with a new structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why rewrite it?
&lt;/h3&gt;

&lt;p&gt;You might ask: &lt;em&gt;Why are you rewriting it?&lt;/em&gt; To be honest, this is the 4th time I've rewritten it.&lt;/p&gt;

&lt;p&gt;I found hard issues in the previous build. But the real problem was that I was stuck without a proper task management system. I was coding without a map. The current build targets Notion as the source of truth. The new rule is simple: &lt;strong&gt;Document first, then code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before I wrote a single line of code for this refactor, I spent almost a week just documenting what it would be. It makes you think: &lt;em&gt;What is actually going on? Why didn't I start this way earlier?&lt;/em&gt; The truth is, I just wasn't that type of person. I wanted to build, not plan. But now, alongside the project, I am growing too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fighting Gravity
&lt;/h3&gt;

&lt;p&gt;I am also fighting with &lt;strong&gt;Antigravity&lt;/strong&gt;. It is powerful (accessing code and web simultaneously), but it struggles with context.&lt;/p&gt;

&lt;p&gt;In my session, I set the context for "project discovery", but after a few prompts, it loses the plot and jumps forward to future tasks like Adapters or &lt;code&gt;tsconfig&lt;/code&gt; syncers. It runs forward to be fast, even when I just need to review the previous step.&lt;/p&gt;

&lt;p&gt;I found a workaround: &lt;strong&gt;Resetting.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I create a new session and feed it the artifacts from the old session as data. This forces it to stop running forward and just &lt;em&gt;think&lt;/em&gt; on the provided data.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Flip Side (Notion AI)
&lt;/h3&gt;

&lt;p&gt;But here is the contrast. The same Gemini models, running inside Notion?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Voila.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It is working so well. The context holds. The logic flows. It feels like a completely different tool. I have to say it: I love &lt;a href="https://x.com/NotionHQ" rel="noopener noreferrer"&gt;&lt;strong&gt;@NotionHQ&lt;/strong&gt;&lt;/a&gt; for the product they are building. Thanks to them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Discovery &amp;amp; Detection (Core Working)
&lt;/h3&gt;

&lt;p&gt;And just like that, the core discovery logic is functioning.&lt;/p&gt;

&lt;p&gt;The core rule is simple: we look for the &lt;code&gt;workspaces&lt;/code&gt; field in &lt;code&gt;package.json&lt;/code&gt;. That is the entry point for working with monorepos.&lt;/p&gt;

&lt;p&gt;The logic follows two steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Find the root:&lt;/strong&gt; Locate the root &lt;code&gt;package.json&lt;/code&gt; with the workspaces definition. Its directory becomes the &lt;code&gt;workspaceRoot&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parse and Detect:&lt;/strong&gt; Scan the packages, parse their configs, and map the structure.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@example/app-one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.../packages/app-one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@example/app-one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0-dev.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;configFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.../packages/app-one/package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@example/lib-one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.../libs/lib-one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@example/lib-one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0-dev.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;configFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.../libs/lib-one/package.json&lt;/span&gt;&lt;span class="dl"&gt;'&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;It’s real. It’s detecting. It’s working.&lt;/p&gt;

&lt;p&gt;This is just the &lt;strong&gt;basic detection&lt;/strong&gt; logic. In later releases, I will work on making it truly &lt;em&gt;understand&lt;/em&gt; the project, not just find it. But for now, it's a start.&lt;/p&gt;

</description>
      <category>velnora</category>
      <category>devops</category>
      <category>metaframework</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Why Velnora Exists — The Silence Before the Obsession</title>
      <dc:creator>Veys Aliyev</dc:creator>
      <pubDate>Sat, 07 Feb 2026 09:31:00 +0000</pubDate>
      <link>https://dev.to/mdreal32/why-velnora-exists-the-silence-before-the-obsession-3297</link>
      <guid>https://dev.to/mdreal32/why-velnora-exists-the-silence-before-the-obsession-3297</guid>
      <description>&lt;p&gt;For a long time, my writing focused on Docker, Kubernetes, and CI—the tools that promised to tame complexity.&lt;/p&gt;

&lt;p&gt;And they did—at least at first.&lt;/p&gt;

&lt;p&gt;Deployment pipelines replaced fragile scripts. Containers replaced snowflake servers. Infrastructure became reproducible.&lt;/p&gt;

&lt;p&gt;But the deeper I went, the more something felt off.&lt;/p&gt;

&lt;p&gt;We weren’t eliminating complexity.&lt;/p&gt;

&lt;p&gt;We were moving it upward.&lt;/p&gt;

&lt;p&gt;In frontend development, raw HTML/CSS/JS gave way to frameworks like React, Angular, and Vue. Codebases became cleaner. But applications grew heavier—more tooling, more configuration, more layers.&lt;/p&gt;

&lt;p&gt;The same pattern showed up in infrastructure.&lt;/p&gt;

&lt;p&gt;Every layer I mastered—containers, clusters, pipelines—shifted the chaos somewhere else:&lt;/p&gt;

&lt;p&gt;Fix deployment → integration breaks&lt;/p&gt;

&lt;p&gt;Fix containers → config explodes&lt;/p&gt;

&lt;p&gt;Fix configuration → observability becomes painful&lt;/p&gt;

&lt;p&gt;That was when the silence started.&lt;/p&gt;

&lt;p&gt;I wasn’t stuck.&lt;/p&gt;

&lt;p&gt;I was investigating.&lt;/p&gt;

&lt;p&gt;The Rejected Project That Started It All&lt;/p&gt;

&lt;p&gt;I’ve never been satisfied with “it works.”&lt;/p&gt;

&lt;p&gt;When I wrote about Docker, I wasn’t just using it—I was digging into how it actually worked. That instinct pulled me away from blogging for almost two years.&lt;/p&gt;

&lt;p&gt;The simple answers stopped being enough.&lt;/p&gt;

&lt;p&gt;Why did building software feel heavier every year… even though our tools kept improving?&lt;/p&gt;

&lt;p&gt;It didn’t start as a big architectural vision.&lt;/p&gt;

&lt;p&gt;It started as a rejected diploma project.&lt;/p&gt;

&lt;p&gt;During my master’s degree, I proposed building a custom bundler. My professors didn’t accept it—maybe it was too ambitious, maybe too broad.&lt;/p&gt;

&lt;p&gt;So I kept going on my own.&lt;/p&gt;

&lt;p&gt;I tried to rebuild Vite just to understand how modern tooling really works.&lt;/p&gt;

&lt;p&gt;That’s when reality hit:&lt;/p&gt;

&lt;p&gt;Bundlers aren’t just complex.&lt;/p&gt;

&lt;p&gt;They’re bottomless.&lt;/p&gt;

&lt;p&gt;So I reframed the problem.&lt;/p&gt;

&lt;p&gt;Instead of replacing Vite, I asked:&lt;/p&gt;

&lt;p&gt;What if I built above it?&lt;/p&gt;

&lt;p&gt;What if Angular and NestJS—two frameworks that normally live in separate worlds—could share a single build pipeline?&lt;/p&gt;

&lt;p&gt;I stopped trying to be the engine.&lt;/p&gt;

&lt;p&gt;I started building the glue.&lt;/p&gt;

&lt;p&gt;Walking Away—and Coming Back Different&lt;/p&gt;

&lt;p&gt;Obsession is rarely linear.&lt;/p&gt;

&lt;p&gt;There were months when the glue didn’t stick. I convinced myself I had solved the problem—until I saw how much complexity still sat underneath.&lt;/p&gt;

&lt;p&gt;Tooling fighting tooling.&lt;br&gt;
Layers stacked on layers.&lt;/p&gt;

&lt;p&gt;I stepped away.&lt;/p&gt;

&lt;p&gt;Went back to writing safer DevOps posts.&lt;/p&gt;

&lt;p&gt;Tried to convince myself that the industry stack was good enough.&lt;/p&gt;

&lt;p&gt;But the satisfaction was gone.&lt;/p&gt;

&lt;p&gt;The feedback felt quiet.&lt;/p&gt;

&lt;p&gt;Like I was contributing noise instead of signal.&lt;/p&gt;

&lt;p&gt;That’s when I realized something:&lt;/p&gt;

&lt;p&gt;If I didn’t build this, I would spend my career managing complexity instead of removing it.&lt;/p&gt;

&lt;p&gt;So I closed the blog.&lt;/p&gt;

&lt;p&gt;Stopped chasing validation.&lt;/p&gt;

&lt;p&gt;Went back to code.&lt;/p&gt;

&lt;p&gt;This time, with a different question:&lt;/p&gt;

&lt;p&gt;Why is DevOps a separate phase at all?&lt;/p&gt;

&lt;p&gt;Why does every project require developers to leave business logic behind just to configure Dockerfiles, pipelines, manifests?&lt;/p&gt;

&lt;p&gt;Every time we do that, we’re not solving a user problem.&lt;/p&gt;

&lt;p&gt;We’re solving the computer’s.&lt;/p&gt;

&lt;p&gt;That was the pivot.&lt;/p&gt;

&lt;p&gt;What if infrastructure became a side effect of writing software?&lt;/p&gt;

&lt;p&gt;What if a system understood your code deeply enough to generate containers, configs, and CI pipelines automatically?&lt;/p&gt;

&lt;p&gt;In December 2024, I pushed the first commit.&lt;/p&gt;

&lt;p&gt;That was the real beginning.&lt;/p&gt;

&lt;p&gt;What Is Velnora?&lt;/p&gt;

&lt;p&gt;Before diving into architecture—one confession.&lt;/p&gt;

&lt;p&gt;The project wasn’t always called Velnora.&lt;/p&gt;

&lt;p&gt;Originally, it was Fluxora.&lt;/p&gt;

&lt;p&gt;It sounded… fine.&lt;/p&gt;

&lt;p&gt;Too fine.&lt;/p&gt;

&lt;p&gt;Like another state management library.&lt;/p&gt;

&lt;p&gt;This wasn’t about state.&lt;/p&gt;

&lt;p&gt;It was about orchestration.&lt;br&gt;
Velocity.&lt;br&gt;
Systems thinking.&lt;/p&gt;

&lt;p&gt;So Velnora was born.&lt;/p&gt;

&lt;p&gt;Velnora is a meta-framework for systems.&lt;/p&gt;

&lt;p&gt;If React manages the lifecycle of a component—mount, update, unmount—&lt;/p&gt;

&lt;p&gt;Velnora manages the lifecycle of an application:&lt;/p&gt;

&lt;p&gt;Resolve → Build → Deploy → Run&lt;/p&gt;

&lt;p&gt;It sits above frameworks and below infrastructure.&lt;/p&gt;

&lt;p&gt;React. Angular. NestJS.&lt;br&gt;
Docker. Kubernetes.&lt;/p&gt;

&lt;p&gt;Velnora is the missing layer between them.&lt;/p&gt;

&lt;p&gt;At its core is a layered plugin architecture. The kernel understands graphs and lifecycles. Everything else—TypeScript compilation, dev servers, cloud targets—is implemented through adapters.&lt;/p&gt;

&lt;p&gt;Today it speaks Vite.&lt;/p&gt;

&lt;p&gt;Tomorrow? Something else.&lt;/p&gt;

&lt;p&gt;Velnora resolves monorepos into dependency trees, unifies builds across ecosystems, and generates infrastructure automatically.&lt;/p&gt;

&lt;p&gt;Once that layer exists, we stop writing glue code.&lt;/p&gt;

&lt;p&gt;We start building platforms.&lt;/p&gt;

&lt;p&gt;This is where the origin story ends.&lt;/p&gt;

&lt;p&gt;Next comes the real work: deep dives into architecture, orchestration engines, and the core runtime.&lt;/p&gt;

&lt;p&gt;Let’s make building software lighter instead of heavier.&lt;br&gt;
This post is part of an ongoing series documenting the design and evolution of&lt;/p&gt;

&lt;p&gt;• GitHub: &lt;a href="https://github.com/Velnora/velnora" rel="noopener noreferrer"&gt;https://github.com/Velnora/velnora&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;• Notion Space (roadmaps, design notes, community):&lt;br&gt;
&lt;a href="https://www.notion.so/velnora/invite/476be70c8d26fad67beda510c00fe0b61e4512a5" rel="noopener noreferrer"&gt;https://www.notion.so/velnora/invite/476be70c8d26fad67beda510c00fe0b61e4512a5&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to see how Velnora is taking shape—from architecture experiments to production-grade tooling—you’re welcome to follow the journey or get involved.&lt;/p&gt;

</description>
      <category>velnora</category>
      <category>devops</category>
      <category>metaframework</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
