Oqtane Event System — Hooking into the Framework

Oqtane Event System — Hooking into the Framework

OK, I’m still blocked from GitHub Copilot, so I still have more things to write about.
In this article, the topic that we’re going to see is the event system of Oqtane.For example, usually in most systems you want to hook up something when the application starts.
In XAF from Developer Express, which is my specialty (I mean, that’s the framework I really know well),
you have the DB Updater, which you can use to set up some initial data.
In Oqtane, you have the Module Manager, but there are also other types of events that you might need —
for example, when the user is created or when the user signs in for the first time.

So again, using the method that I explained in my previous article — the “OK, I have a doubt” method —
I basically let the guide of Copilot hike over my installation folder or even the Oqtane source code itself, and try to figure out how to do it.
That’s how I ended up using event subscribers.

In one of my prototypes, what I needed to do was detect when the user is created and then create some records in a different system
using that user’s information. So I’ll show an example of that type of subscriber, and I’ll actually share the
Oqtane Event Handling Guide here, which explains how you can hook up to system events.

I’m sure there are more events available, but this is what I’ve found so far and what I’ve tested.
I guess I’ll make a video about all these articles at some point, but right now, I’m kind of vibing with other systems.
Whenever I get blocked, I write something about my research with Oqtane.


Oqtane Event Handling Guide

Comprehensive guide to capturing and responding to system events in Oqtane

This guide explains how to handle events in Oqtane, particularly focusing on user authentication events (login, logout, creation)
and other system events. Learn to build modules that respond to framework events and create custom event-driven functionality.

Version: 1.0.0
Last Updated: October 3, 2025
Oqtane Version: 6.0+
Framework: .NET 9.0


1. Overview of Oqtane Event System

Oqtane uses a centralized event system based on the SyncManager that broadcasts events throughout the application when entities change.
This enables loose coupling between components and allows modules to respond to framework events without tight integration.

Key Components

  • SyncManager — Central event hub that broadcasts entity changes
  • SyncEvent — Event data containing entity information and action type
  • IEventSubscriber — Interface for objects that want to receive events
  • EventDistributorHostedService — Background service that distributes events to subscribers
Entity Changes → SyncManager → EventDistributorHostedService → IEventSubscriber Implementations
                     ↓
               SyncEvent Created → Distributed to All Event Subscribers
  

2. Event Types and Actions

SyncEvent Model


public class SyncEvent : EventArgs
{
    public int TenantId { get; set; }
    public int SiteId { get; set; }
    public string EntityName { get; set; }
    public int EntityId { get; set; }
    public string Action { get; set; }
    public DateTime ModifiedOn { get; set; }
}
  

Available Actions


public class SyncEventActions
{
    public const string Refresh = "Refresh";
    public const string Reload = "Reload";
    public const string Create = "Create";
    public const string Update = "Update";
    public const string Delete = "Delete";
}
  

Common Entity Names


public class EntityNames
{
    public const string User = "User";
    public const string Site = "Site";
    public const string Page = "Page";
    public const string Module = "Module";
    public const string File = "File";
    public const string Folder = "Folder";
    public const string Notification = "Notification";
}
  

3. Creating Event Subscribers

To handle events, implement IEventSubscriber and filter for the entities and actions you care about.
Subscribers are automatically discovered by Oqtane and injected with dependencies.


public class UserActivityEventSubscriber : IEventSubscriber
{
    private readonly ILogger<UserActivityEventSubscriber> _logger;

    public UserActivityEventSubscriber(ILogger<UserActivityEventSubscriber> logger)
    {
        _logger = logger;
    }

    public void EntityChanged(SyncEvent syncEvent)
    {
        if (syncEvent.EntityName != EntityNames.User)
            return;

        switch (syncEvent.Action)
        {
            case SyncEventActions.Create:
                _logger.LogInformation("User created: {UserId}", syncEvent.EntityId);
                break;
            case "Login":
                _logger.LogInformation("User logged in: {UserId}", syncEvent.EntityId);
                break;
        }
    }
}
  

4. User Authentication Events

Login, logout, and registration trigger SyncEvent notifications that you can capture to send notifications,
track user activity, or integrate with external systems.


public class LoginActivityTracker : IEventSubscriber
{
    private readonly ILogger<LoginActivityTracker> _logger;

    public LoginActivityTracker(ILogger<LoginActivityTracker> logger)
    {
        _logger = logger;
    }

    public void EntityChanged(SyncEvent syncEvent)
    {
        if (syncEvent.EntityName == EntityNames.User && syncEvent.Action == "Login")
        {
            _logger.LogInformation("User {UserId} logged in at {Time}", syncEvent.EntityId, syncEvent.ModifiedOn);
        }
    }
}
  

5. System Entity Events

Besides user events, you can track changes in entities like Pages, Files, and Modules.


public class PageAuditTracker : IEventSubscriber
{
    private readonly ILogger<PageAuditTracker> _logger;

    public PageAuditTracker(ILogger<PageAuditTracker> logger)
    {
        _logger = logger;
    }

    public void EntityChanged(SyncEvent syncEvent)
    {
        if (syncEvent.EntityName == EntityNames.Page && syncEvent.Action == SyncEventActions.Create)
        {
            _logger.LogInformation("Page created: {PageId}", syncEvent.EntityId);
        }
    }
}
  

6. Custom Module Events

You can create custom events in your own modules using ISyncManager.


public class BlogManager
{
    private readonly ISyncManager _syncManager;

    public BlogManager(ISyncManager syncManager)
    {
        _syncManager = syncManager;
    }

    public void PublishBlog(int blogId)
    {
        _syncManager.AddSyncEvent(
            new Alias { TenantId = 1, SiteId = 1 },
            "Blog",
            blogId,
            "Published"
        );
    }
}
  

7. Best Practices

  • Filter early — Always check the entity and action before processing.
  • Handle exceptions — Never throw unhandled exceptions inside EntityChanged.
  • Log properly — Use structured logging with context placeholders.
  • Keep it simple — Extract complex logic to testable services.

public void EntityChanged(SyncEvent syncEvent)
{
    try
    {
        if (syncEvent.EntityName == EntityNames.User && syncEvent.Action == "Login")
        {
            _logger.LogInformation("User {UserId} logged in", syncEvent.EntityId);
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error processing event {Action}", syncEvent.Action);
    }
}
  

8. Summary

Oqtane’s event system provides a clean, decoupled way to respond to system changes.
It’s perfect for audit logs, notifications, custom workflows, and integrations.

  • Automatic discovery of subscribers
  • Centralized event distribution
  • Supports custom and system events
  • Integrates naturally with dependency injection
Oqtane Silent Installation Guide

Oqtane Silent Installation Guide

OK, I’ve been wanting to write this article for a few days now, but I’ve been vibing a lot — writing tons of prototypes and working on my Oqtane research. This morning I got blocked by GitHub Copilot because I hit the rate limit, so I can’t use it for a few hours. I figured that’s a sign to take a break and write some articles instead.

Actually, I’m not really “writing” — I’m using the Windows dictation feature (Windows key + H). So right now, I’m just having coffee and talking to my computer. I’m still in El Salvador with my family, and it’s like 5:00 AM here. My mom probably thinks I’ve gone crazy because I’ve been talking to my computer a lot lately. Even when I’m coding, I use dictation instead of typing, because sometimes it’s just easier to express yourself when you talk. When you type, you tend to shorten things, but when you talk, you can go on forever, right?

Anyway, this article is about Oqtane, specifically something that’s been super useful for me — how to set up a silent installation. Usually, when you download the Oqtane source or use the templates to create a new project or solution, and then run the server project, you’ll see the setup wizard first. That’s where you configure the database, email, host password, default theme, and all that.

Since I’ve been doing tons of prototypes, I’ve seen that setup screen thousands of times per day. So I downloaded the Oqtane source and started digging through it — using Copilot to generate guides whenever I got stuck. Honestly, the best way to learn is always by looking at the source code. I learned that the hard way years ago with XAF from DevExpress — there was no documentation back then, so I had to figure everything out manually and even assemble the projects myself because they weren’t in one solution. With Oqtane, it’s way simpler: everything’s in one place, just a few main projects.

Now, when I run into a problem, I just open the source code and tell Copilot, “OK, this is what I want to do. Help me figure it out.” Sometimes it goes completely wrong (as all AI tools do), but sometimes it nails it and produces a really good guide.

So the guide below was generated with Copilot, and it’s been super useful. I’ve been using it a lot lately, and I think it’ll save you a ton of time if you’re doing automated deployment with Oqtane.

I don’t want to take more of your time, so here it goes — I hope it helps you as much as it helped me.


Oqtane Installation Configuration Guide

This guide explains the configuration options available in the appsettings.json file under the Installation section for automated installation and default site settings.

Overview

The Installation section in appsettings.json controls the automated installation process and default settings for new sites in Oqtane. These settings are particularly useful for:

  • Automated installations – Deploy Oqtane without manual configuration
  • Development environments – Quickly spin up new instances
  • Multi-tenant deployments – Standardize new site creation
  • CI/CD pipelines – Automate deployment processes

Configuration Structure

{
  "Installation": {
    "DefaultAlias": "",
    "HostPassword": "",
    "HostEmail": "",
    "SiteTemplate": "",
    "DefaultTheme": "",
    "DefaultContainer": ""
  }
}
  
Key Purpose Required
DefaultAlias Initial site URL(s)
HostPassword Super admin password
HostEmail Super admin email
SiteTemplate Initial site structure Optional
DefaultTheme Site appearance Optional
DefaultContainer Module wrapper style Optional

SiteTemplate

A Site Template defines the initial structure and content of a new site, including pages, modules, folders, and navigation.

"SiteTemplate": "Oqtane.Infrastructure.SiteTemplates.DefaultSiteTemplate, Oqtane.Server"

Default options:

  • DefaultSiteTemplate – Home, Privacy, example content
  • EmptySiteTemplate – Minimal, clean slate
  • AdminSiteTemplate – Internal use

If empty, Oqtane uses the default template automatically.


DefaultTheme

A Theme controls the visual appearance and layout of your site (page structure, navigation, header/footer, and styling).

"DefaultTheme": "Oqtane.Themes.OqtaneTheme.Default, Oqtane.Client"

Built-in themes:

  • Oqtane Theme (default) – clean and responsive
  • Blazor Theme – Blazor-branded styling
  • Bootswatch variants – Cerulean, Cosmo, Darkly, Flatly, Lux, etc.
  • Corporate Theme – business layout

If left blank, it defaults to the Oqtane Theme.


DefaultContainer

A Container is the wrapper around each module, controlling how titles, buttons, and borders look.

"DefaultContainer": "Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client"

Common containers:

  • OqtaneTheme.Container – standard and responsive
  • AdminContainer – management modules
  • Theme-specific containers – match the chosen theme

Defaults automatically if left empty.


Example Configurations

Minimal Configuration

{
  "Installation": {
    "DefaultAlias": "localhost",
    "HostPassword": "YourSecurePassword123!",
    "HostEmail": "admin@example.com"
  }
}
  

Custom Theme and Container

{
  "Installation": {
    "DefaultAlias": "localhost",
    "HostPassword": "YourSecurePassword123!",
    "HostEmail": "admin@example.com",
    "SiteTemplate": "Oqtane.Infrastructure.SiteTemplates.DefaultSiteTemplate, Oqtane.Server",
    "DefaultTheme": "Oqtane.Theme.Bootswatch.Flatly.Default, Oqtane.Theme.Bootswatch.Oqtane",
    "DefaultContainer": "Oqtane.Theme.Bootswatch.Flatly.Container, Oqtane.Theme.Bootswatch.Oqtane"
  }
}
  

Troubleshooting

  • Settings ignored during installation: Ensure all required fields are filled (DefaultAlias, HostPassword, HostEmail).
  • Theme not found: Check assembly reference and type name.
  • Container displays incorrectly: Use a container matching your theme.
  • Site template creates no pages: Ensure your template returns valid page definitions.

Logs can be found in Logs/oqtane-log-YYYYMMDD.txt.


Best Practices

  • Match your theme and container.
  • Leave defaults empty unless customization is needed.
  • Test in development first.
  • Document any custom templates or themes.
  • Use environment-specific appsettings (e.g. appsettings.Development.json).

Summary

The Installation configuration in appsettings.json lets you fully automate your Oqtane setup.

  • SiteTemplate: defines structure
  • DefaultTheme: defines appearance
  • DefaultContainer: defines module layout

Empty values use defaults, and you can override them for automation, branding, or custom scenarios.

My Journey Exploring the Oqtane Framework

My Journey Exploring the Oqtane Framework

Mental notes on architecture, learning by reading source, and what’s next.

OK — so it’s time for a new article. Lately, I’ve been diving deep into the Oqtane framework, and it’s been a beautiful journey. It reminds me of my early days with XAF from Developer Express—when I learned to think in software architecture and modern design patterns by simply reading the code.Back then, documentation was scarce. The advice was: “Look at the code.” I did—and that shaped a big part of my software education. It taught me that good source code is often self-explanatory.

Even though XAF is still our main tool at the office (Xari & BIT Frameworks), we’re expanding. We’re researching new divisions for Flutter and React, since some projects already use those fronts with an XAF backend. I also wanted to explore building client-server apps with a single .NET codebase that includes mobile—another reason Oqtane caught my eye.

Why Oqtane Caught My Attention

The Oqtane team is very responsive on GitHub. You can open a discussion and get thoughtful replies quickly. The source code is clean and educational—perfect for learning by reading. There are plenty of talks and videos on architecture and module development; some are a bit dated, but if you cross-check with the code, you’ll be fine.

I’ve learned there are two steps to mastering a framework: (1) immerse yourself in material (videos, code, docs), and (2) explain it to someone else. These notes do both—part research, part knowledge sharing.

Oqtane Video References

A Missing Clip Worth Finding

There’s one clip I couldn’t locate where Shaun Walker explains that .NET already provides the pieces for modern, multi-platform, server-and-client applications—but the ecosystem is fragmented. Oqtane unifies those pieces into a single .NET codebase. If I find it, I’ll make a highlight and share it.

On Learning and Time

I’m trying to publish as much as I can now because I’m about to start a new chapter: I’ll be joining the University of St. Petersburg to learn Russian as my second language. It’s a tough language—very different from Spanish or Italian—so I’ll likely have less time to write for a while. Better to document these experiments now than let them sit in my notes for months.

That’s it for today. I hope these clips and notes help you understand Oqtane the way they helped me. Stay tuned—and happy coding!

 

Setting Up Hostnames for Multi-Tenant Sites in Oqtane

Setting Up Hostnames for Multi-Tenant Sites in Oqtane

OK, so it’s time for a new article about Oqtane!

In my previous post (or “mental note,” as I like to call them), I covered how to set up
multi-tenancy in Oqtane. Today, I got a really nice surprise —
Shaun Walker just posted an excellent video explaining how multi-tenancy works,
along with its advantages and possible drawbacks.

From my point of view, the advantages clearly outweigh the disadvantages,
although it depends on your specific scenario.


Extending the Previous Example

I wanted to improve my previous example a bit. So, I created a new GitHub repository using the same base code,
but this time I added hostnames for each tenant.

A hostname is basically the domain that points to one of your tenants in Oqtane.
In a typical setup, you use DNS records for this.
The simplest case is an A record that points to a specific IP address.

When a request arrives, the server reads the hostname from the request and routes it to the correct tenant.

This part isn’t specific to Oqtane — it’s how web servers work in general.
The concept exists in IIS, Apache, and NGINX,
and it’s part of basic networking theory. If you want to learn more,
there are countless articles about how DNS works.


A Small Story from the Past

This actually takes me back — one of the first things I learned as a teenager was how to configure DNS
and run my own Apache web server.

I even started offering web hosting from my home using an old 486 computer (yes, really).
Eventually, my internet provider noticed what I was doing, blocked my connection, and called my parents.
Let’s just say… that Christmas was canceled for me. 😅
Anyway, that’s a story for another time.


Setting Up Local Domains for Tenants

For today’s example, I’m using the same structure as before:

  • One host site
  • Two tenant sites: MyCompany1 and MyCompany2

I want to show you how to assign domain names to each of them.

If you’re running everything locally (for example, through Visual Studio or VS Code),
you can’t use real domain names — but you can simulate them using the Windows hosts file.

If you’ve ever wondered how your computer resolves localhost to 127.0.0.1,
the answer lies in that file. It’s located inside the Windows system folder,
and it maps domain names to IP addresses.

Here’s the cool part: you can add your own domains there, pointing them to any IP you like.
It’s a great trick for local testing.

Below, you’ll see a screenshot of my hosts file.
I’ve mapped my fake domains to my local IP address,
so when I open them in the browser, the requests go straight to my
Kestrel server, which then routes them to the correct tenant.


How to Edit the Windows Hosts File

Editing the hosts file in Windows is simple, but you need administrative permissions.
Here’s how you can do it safely:

  1. Press Start, type Notepad, then right-click it and select
    Run as administrator.
  2. Once Notepad opens, go to File → Open and browse to:
    C:\Windows\System32\drivers\etc\hosts
  3. In the open dialog, change the filter from “Text Documents (*.txt)” to “All Files (*.*)”
    so you can see the hosts file.
  4. Add your entries at the bottom of the file. For example:
    127.0.0.1   mycompany1.xyz
    127.0.0.1   mycompany2.xyz
    

    Each line maps an IP address to a domain name.

  5. Save the file and close Notepad.
  6. Open your browser and visit http://mycompany1.xyz:44398
    (or the port your Oqtane app is running on).
    You should see the tenant corresponding to that domain.

⚠️ Important: If you edit the file without admin rights,
you won’t be able to save it. Also, be careful — if you modify or delete system entries by accident,
your network resolution might stop working.

Here is how my host file actually looks at the moment

 

Set siteURL for :Company 1

Set siteURL for :Company 2

 

Testing with Real Domains

Of course, this same logic applies to real domains too — as long as your Oqtane instance is publicly accessible.

In one of the next parts (maybe part 3 or 4), I’ll show how to configure it using a web server like
Apache. I know that NGINX is more popular these days,
but I’ve used Apache since my teenage years, so I’m more comfortable with it.

Still, I’ll probably demonstrate both.

Most developers today use cloud providers like AWS or Azure,
but honestly, I still prefer spinning up a simple Ubuntu server and doing everything manually.
The best tool is the one you know best — and for me, that’s Apache on Ubuntu.


Demo

As you can see there is a little bit of a different behavior if is a default site or not If it’s a default site it will redirect to that URL if not it’s going to redirect to the default site URL

 

Resources

  • 🧩 GitHub Repository — This project is based on the previous example
    but adds hostname configuration and uses SQLite for simplicity.

egarim/DomainNames

  • 🎥 Shaun Walker’s Video on Multi-Tenancy
    Highly recommended if you want a solid overview of how it all works:


 

Understanding Multi-Tenancy in Oqtane (and How to Set Up Sites)

Understanding Multi-Tenancy in Oqtane (and How to Set Up Sites)

OK — it’s time for today’s Oqtane blog post!

Yesterday, I wrote about changing the runtime mode in Oqtane and how that allows you to switch between
Blazor Server and Blazor WebAssembly functionality.

Today, I decided to explore how multi-tenancy works — specifically, how Oqtane manages multiple sites within the same
installation.

Originally, I wanted to cover the entire administrative panel and all of its modules in one post, but that would’ve been too big.
So, I’m breaking it down into smaller topics. This post focuses only on site functionality and how
multi-tenancy works from the administrative side — basically, how to set up tenants in Oqtane.


Setting Up a Multi-Tenant Oqtane Installation

To make these experiments easy to replicate, I decided to use SQLite as my database.

I created a new .NET Oqtane application using the official templates and added it to a
GitHub repository.

Here’s what I did:

  1. Set up the host configuration directly in appsettings.json.
  2. Ran the app, went to the admin panel, and created two additional sites.

You can see the screenshots below showing the settings for each site.

At first, it was a bit confusing — I thought I could simply use different ports for each site (like 8081,
8082, etc.), but that’s not how Oqtane works. Everything runs in the same process, so all tenants
share the same port.

Instead of changing ports, you configure different URL paths or folders. For example:

  • http://localhost:8080/ → the main host
  • http://localhost:8080/mycompany1 → first tenant
  • http://localhost:8080/mycompany2 → second tenant

Site Managment

Site MyCompany1

 

Site MyCompany2

 

Each tenant can:

  • Use a separate database or share the same one as the host
  • Have its own theme
  • Maintain independent site settings

 

In the short GIF animation I attached to the repository, you can see how each site has its
own unique visual theme — it’s really neat to watch.

When you add a new site, its connection string is also stored automatically in the application settings. So, if you download
the repository and run it locally, you’ll be able to access all the sites and see how the URLs and configurations work.

Here is the repository egarim/OqtaneMultiTenant: an example of how to use multi tenant in oqtane


Why I’m Doing These Posts

These blog entries are like my personal research notes — documenting what I discover while working with Oqtane.
I’m keeping each experiment small and reproducible so I can:

  • Share them with others easily
  • Download them later and reproduce the same setup, including data and configuration

What’s Next

In the next post, I’ll cover virtual hosting — how to use domain names that forward to specific
URLs or tenants. I’ve already done some research on that, but I don’t want to overload this post with too many topics.

For now, I’ll just attach the screenshots showing the different site configurations and URLs, along with a link to the GitHub
repository so you can try it yourself.

If you have any questions, feel free to reach out! I’ll keep documenting everything as I go.

One of the great things about Oqtane is that it’s open source — you can always dive into the code, or if you’re
stuck, open a GitHub issue. Shaun Walker and the community are incredibly helpful, so don’t hesitate to ask.

Thanks again to the Oqtane team for building such an amazing framework.

 

Oqtane Notes: Understanding Site Settings vs. App Settings for Hosting Models and Render Modes

Oqtane Notes: Understanding Site Settings vs. App Settings for Hosting Models and Render Modes

Oqtane Notes: Understanding Site Settings vs. App Settings

OK — it’s time for another blog post (or maybe just a mental note) about Oqtane.
I’ve been doing what feels like a million installations of it lately. Honestly, if the Oqtane team gets a notification every time I spin up a new instance, they’re probably tired of seeing my name by now. I’ve been spending nearly every free minute exploring the framework — I love diving into new technologies, digging into the source code, and figuring out how things really work.

One of the most beautiful parts about Oqtane is that it’s open source. You can simply go into the repository and inspect the source code yourself. Some parts might not be obvious at first glance, but the project’s creator, Shaun Walker, is incredibly responsive and helpful to the community. I think I’ve only posted a couple of issues over the years, but every single time I’ve woken up the next morning with a thoughtful response waiting — even though I’m usually several time zones ahead in Europe. He really knows Oqtane inside and out.


Hosting Models and Render Modes

As you probably know, one of Oqtane’s biggest strengths is its flexibility with Blazor hosting models. It can run as Server or WebAssembly, and you can switch between them with a simple configuration change.
On top of that, Oqtane supports different render modes for components: Interactive or Static. In simple terms, you can choose to render content on the server (similar to how PHP works) or make it fully interactive like a standard Blazor app where the state refreshes dynamically.

You can toggle these behaviors with just a few clicks in the admin backend — which is awesome once you understand how the settings are actually applied.


My Confusion (and the Lesson Learned)

This post was originally meant to be a follow-up to the previous one about database configuration, but I ran into an interesting issue while testing API controllers. I wanted to confirm that when I ran the application in WebAssembly mode, it would hit the API controllers correctly.

It didn’t — at least not at first.

I spent quite a while trying to figure out why. Oqtane has both app-level settings (in appsettings.json) and site-level settings (in the admin panel), and it wasn’t immediately clear which ones took priority. I initially thought I could just change the render and runtime options in appsettings.json, restart the app, and see the effect. But it didn’t work that way.

After some trial and error — and a helpful reply from Shaun — I realized my mistake. When you first spin up a new site, Oqtane uses the values defined in appsettings.json. But once that site exists, it maintains its own configuration separately. From that point forward, any runtime or render mode changes must be made in the site settings from the admin panel, not in the original configuration file.

 

Server Runtime

WebAssembly Runtime

 


The Takeaway

If you edit appsettings.json after your first site is already created, it won’t affect the existing site — those values only apply when a new site is initialized.

So, to summarize:

  • Before the first run → Configure defaults in appsettings.json.
  • After the site is running → Change settings from the admin backend.

That was the source of my confusion. Hopefully, this note saves someone else a few hours of head-scratching.


Thanks again to Shaun and the entire Oqtane team for keeping this project alive and so well supported.
These posts are just my personal notes, but I hope they help someone who’s following the same learning path.

Setting Up Your Oqtane Database: First Run and Configuration

Setting Up Your Oqtane Database: First Run and Configuration

In this article, I’ll show you what to do after you’ve obtained and opened an Oqtane solution. Specifically, we’ll go through two different ways to set up your database for the first time.

  1. Using the setup wizard — this option appears automatically the first time you run the application.
  2. Configuring it manually — by directly editing the appsettings.json file to skip the wizard.

Both methods achieve the same result. The only difference is that, if you configure the database manually, you won’t see the setup wizard during startup.


Step 1: Running the Application for the First Time

Once your solution is open in Visual Studio, set the Server project as the startup project. Then run it just as you would with any ASP.NET Core application.

You’ll notice several run options — I recommend using the HTTPS version instead of IIS Express (I stopped using IIS Express because it doesn’t work well on ARM-based computers).

When you run the application for the first time and your settings file is still empty, you’ll see the Database Setup Wizard. As shown in the image, the wizard allows you to select a database provider and configure it through a form.

There’s also an option to paste your connection string directly. Make sure it’s a valid Entity Framework Core connection string.

After that, fill in the admin user’s details — username, email, and password — and you’re done. Once this process completes, you’ll have a working Oqtane installation.


Step 2: Setting Up the Database Manually

If you prefer to skip the wizard, you can configure the database manually. To do this, open the appsettings.json file and add the following parameters:

{
"DefaultDBType": "Oqtane.Database.Sqlite.SqliteDatabase, Oqtane.Server",
"ConnectionStrings": {
  "DefaultConnection": "Data Source=Oqtane-202510052045.db;"
},
"Installation": {
  "DefaultAlias": "https://localhost:44388",
  "HostPassword": "MyPasswor25!",
  "HostEmail": "joche@myemail.com",
  "SiteTemplate": "",
  "DefaultTheme": "",
  "DefaultContainer": ""
}
}

Here you need to specify:

  • The database provider type (e.g., SQLite, SQL Server, PostgreSQL, etc.)
  • The connection string
  • The admin email and password for the first user — known as the host user (essentially the root or super admin).

This is the method I usually use now since I’ve set up Oqtane so many times recently that I’ve grown tired of the wizard. However, if you’re new to Oqtane, the wizard is a great way to get started.


Wrapping Up

That’s it for this setup guide! By now, you should have a running Oqtane installation configured either through the setup wizard or manually via the configuration file. Both methods give you a solid foundation to start exploring what Oqtane can do.

In the next article, we’ll dive into the Oqtane backend, exploring how the framework handles modules, data, and the underlying architecture that makes it flexible and powerful. Stay tuned — things are about to get interesting!

Getting Started with Oqtane 6.2.x

Getting Started with Oqtane 6.2.x

I’m returning to work with Oqtane, and to be honest, we’re exploring new frameworks at the office that will help us expand our market reach. We specialize in a specific type of development and don’t want to move too far away from .NET in general. We’ve used Oqtane in the past—I’ve even installed it on Linux—but I want to conduct fresh research: try it again, get a feel for it, and potentially move some production-sized projects to it.

There’s a lot to understand about Oqtane, especially because it’s a truly layered development framework where you need to work across different layers. This will be the first article in a series that serves both as a guide and as personal mental notes of things I don’t want to forget the next time I develop prototypes or production sites with Oqtane.

The first topic we’ll review is how to install Oqtane and start it up. Here are the steps I usually follow.

Choosing Your Development Approach

The first question you should ask yourself is: How do you want to develop? There are a few ways to do it, and I’ve watched every video about Oqtane over the last two weeks. Here are your three main options:

Option 1: Clone or Download from GitHub (Main Branch)

The old way was to simply go to GitHub and clone or download the source from the main branch, which is actually the development branch. This approach allows you to see all the ins and outs of the framework—how each module is created and registered, how they use repositories, and so on.

However, this may be overwhelming at the beginning. To be honest, since Oqtane has several layers, I got confused many times and wrote code in the wrong layer more than once.

Option 2: Download a Specific Version from Releases

You can also download just the version you want from the releases section on GitHub. This gives you a stable, versioned codebase to work with.

Option 3: Use .NET Templates (Recommended for Beginners)

Now they have .NET new templates available. If you’re a beginner, I strongly suggest installing the .NET templates and using dotnet new because the structure is much more simplified.

However, if you want to understand the ins and outs of the application framework and how it works, I strongly recommend taking a look at the source code.

Installation Resources

Source Code Repository

You can get the source code at this GitHub repository:

https://github.com/oqtane/oqtane.framework

In the README of that repository, you’ll find instructions for installing the templates.

Using the Templates

Here’s an example of how to install and use the templates:

dotnet new install Oqtane.Application.Template
dotnet new oqtane-app -o MyCompany.MyProject 
cd MyCompany.MyProject
dotnet build
cd Server
dotnet run

Release Versions

In the releases section, you can find the source code for each specific version:

https://github.com/oqtane/oqtane.framework/releases

Solution Structure

The solution structure differs depending on which installation method you choose:

Solution structure when created with the templates:

MyTests/
├── Client/                    # Blazor WebAssembly Client Project
├── Server/                    # ASP.NET Core Server Project
├── Shared/                    # Shared Models and Contracts
├── MyTests.sln               # Solution File
└── README.md                 # Solution Documentation

Solution structure when using the downloaded source:

OqtaneFramework/
├── .github/                      # GitHub configuration and issue templates
├── Oqtane.Application/           # .NET Template for creating new Oqtane apps
├── Oqtane.Client/                # Core Blazor WebAssembly client framework
├── Oqtane.Server/                # Core ASP.NET server framework
├── Oqtane.Shared/                # Shared contracts, models, and interfaces
├── Oqtane.Maui/                  # MAUI mobile application support
├── Oqtane.Package/               # NuGet packaging and distribution tools
├── Oqtane.Updater/               # Framework update/upgrade utilities
├── screenshots/                  # Marketing and documentation images
├── Oqtane.sln                    # Main solution file
├── Oqtane.Maui.sln              # MAUI solution file
├── Oqtane.Updater.sln           # Updater solution file
└── Configuration Files           # Build, deployment, and Git configs

In the source you will also find the source of around 27+ admin modules, which is a great way to learn how to create your own modules.

  • Dashboard
  • Files (file management)
  • Jobs (background job scheduler)
  • Languages (localization)
  • Login/Register/Reset (authentication)
  • Logs (system logging viewer)
  • ModuleDefinitions (module registry)
  • Modules (module instance management)
  • Pages (page/route management)
  • Profiles (user profiles)
  • RecycleBin (soft delete management)
  • Roles (role-based access)
  • Search/SearchResults
  • Settings (system configuration)
  • Site/Sites (multi-tenancy)
  • SQL (database query tool)
  • SystemInfo (diagnostics)
  • Themes (theme management)
  • Upgrade (version upgrades)
  • UrlMappings (URL rewriting)
  • Users (user management)
  • Visitors (analytics)

Wrapping Up

That’s it for this first article! We’ve covered the basics of getting Oqtane installed and understanding the different approaches you can take depending on your experience level and project needs.

In the next article, we’ll dive into starting up the application and picking a database provider. This is where things get interesting, as Oqtane supports multiple database options, and understanding how to configure them properly is crucial for getting your project off the ground.

Stay tuned, and happy coding!


This is part 1 of a series documenting my journey with Oqtane. Check back soon for the next installment.

The Dangers (and Joys) of Vibe Coding

The Dangers (and Joys) of Vibe Coding

It’s Sunday — so maybe it’s time to write an article to break the flow I’ve been in lately. I’ve been deep into researching design patterns for Oqtane, the web application framework created by Shaun Walker.

Today I woke up really early, around 4:30 a.m. I went downstairs, made coffee, and decided to play around with some applications I had on my list. One of them was HotKey Typer by James Montemagno.

I ran it for the first time and instantly loved it. It’s super simple and useful — but I had a problem. I started using glasses a few years ago, and I generally have trouble with small UI elements on the computer. I usually work at 150% scaling. Unfortunately, James’s app has a fixed window size, so everything looked cut off.

Since I’ve been coding a lot lately, I figured it would be an easy fix. I tweaked it — and it worked! Everything looked better, but a bit too large, so I adjusted it again… and again… and again. Before I knew it, I had turned it into a totally different application.

I was vibe coding for four or five hours straight. In the end, I added a lot of new functionality because I genuinely loved the app and the idea behind it. I added sets (or collections) — basically groups of snippets you can assign to keys 1–9. Then I added autosave, a settings screen, and a reset option for the collections. Every time I finished one feature, I said, “Just one more thing.” Five minutes turned into five hours.

When I was done, I recorded a demo video. It was a lot of fun — and the result was genuinely useful. I even want to create an installer for myself so I can easily reinstall it if I ever reformat my computer. (I used to be that guy who formatted his PC every month. Not anymore… but you never know.)

Lessons From Vibe Coding

I learned a lot from this little experiment. I’ve been vibe coding nonstop for about three months now — I’ve even used up all my Copilot credits before the 25th of the month more than once! Vibe coding is a lot of fun, but it can easily spiral out of control and take you in the wrong direction.

Next week, I want to change my approach a bit — maybe follow a more structured pattern.

Another thing this reminded me of is how important it is to work in a team. My business partner, José Javier Columbie, has always helped me with that. We’ve been working together for about 10 years now. I’m the kind of developer who keeps rewriting, refactoring, optimizing, making things faster, reusable, turning them into plugins or frameworks — and sometimes the original task was actually quite small.

That’s where Javier comes in. He’s the one who says, “José, it’s done. This is what they asked for, and this is what we’re delivering.” He keeps me grounded. Every developer needs that — or at least needs to learn how to set that boundary for themselves.

Final Thoughts

So that’s my takeaway from today’s vibe coding session: have fun, but know when to stop.

I’ll include below the links to:

James Montemagno’s original HotKey Typer repository

My fork with the modifications

egarim/app-hotkeytyper

A video demo of what I built

Be careful when you’re vibe coding — it’s a great place to find flow, but it’s also easy to lose track of direction.

See you in the next article.

From Weasel to Sequel to “Speckified”: How Developers Twist Acronyms

From Weasel to Sequel to “Speckified”: How Developers Twist Acronyms

If you hang out around developers long enough, you’ll notice we don’t just use tools — we nickname them, mispronounce them, and sometimes turn them into full-blown mascots. Here are three favorites: WSL, SQL, and GitHub Copilot’s Spec Kit.


WSL → “Weasel”

English reality: WSL stands for Windows Subsystem for Linux.

Nickname: Said quickly as “double-u S L,” it echoes weasel, so the meme stuck.

Spanish (El Salvador / Latin America): In El Salvador and many Latin American countries, the letter W is read as “doble be” (not doble u). So WSL is pronounced “doble be, ese, ele.”

SQL → “Sequel”

English reality: SQL stands for Structured Query Language.

Pronunciation: Both “S-Q-L” and “sequel” are used in English.

Spanish (LatAm): Most developers say it letter by letter: “ese cu e ele.” Bilingual teams sometimes mix in “sequel.”

Spec Kit → “Speckified” (Spooky Spell)

English reality: GitHub Copilot’s Spec Kit helps scaffold code from specs.

Community fun: Projects get “speckified,” a word that mischievously echoes “spookified.” Our playful mascot idea is a wizard enchanting a codebase: You have been Speckified!

Spanish (LatAm): Phonetically, SPEC is “ese, pe, e, ce.” In casual talk many devs just say “espec” (es-pek) to keep the pun alive.

Quick Reference (Latin American / El Salvador Spanish)

Acronym English Pronunciation Spanish (LatAm / El Salvador) Phonetics Nickname / Mascot
WSL “double-u S L” (sounds like weasel) “doble be, ese, ele” Weasel
SQL “S-Q-L” or “sequel” “ese cu e ele” Sequel Robot
SPEC “spec” → “speckified” “ese, pe, e, ce” (or “espec”) Spec Wizard (spell)

Why This Matters

These playful twists — weasel, sequel robot, speckified wizard — show how dev culture works:

  • Acronyms turn into characters.
  • English vs. Spanish pronunciations add layers of humor.
  • Memes make otherwise dry tools easier to talk about.

Next time someone says their project is fully speckified on WSL with SQL, you might be hearing about a weasel, a robot, and a wizard casting spooky spec spells.

Related Links

VS Code – Let it Cook – Introducing Spec Kit for Spec-Driven Development! – Episode 13