Architecture is a topic the MagnusMinds engineering team writes about from project work: how it is set up, where it breaks, and what we changed in production. This tag page lists all 7 articles tagged Architecture.
Start with “Building a RAG-Powered Enterprise Knowledge Assistant with ASP.NET Core, Gemini, and PostgreSQL”, “How to Setup Azure VM”, or “Web Components: The Future of Modular UI Design”. Open a post for the full write-up, or follow another tag or category on the card to a related topic.
documents flow into a semantic retrieval layer that supplies context to a generative model, backed by an ASP.NET Core API and PostgreSQL with pgvector. Why RAG matters for enterprise knowledge Most organizations already hold the answers to their employees’ questions. They sit in policy documents, wikis, handbooks and runbooks. The hard part is putting the right paragraph in front of the person asking, in the words they used to ask. Three approaches handle this differently: Keyword search matches terms. It works when the user knows the document’s vocabulary, and struggles when they ask about “probation” and the policy says “trial period”. A plain generative API call sends the question straight to a model. The answer reads fluently, but the model only knows its training data and the prompt. It has never seen your leave policy, so it will decline or guess. Retrieval-Augmented Generation (RAG) retrieves relevant passages first and gives them to the model together with the question. The original RAG paper describes combining a model’s parametric memory with a non-parametric memory: a dense vector index reached through a retriever. RAG adds a retrieval step between the question and the model. It can improve access to relevant information; it does not guarantee accuracy. Not every application needs RAG. If answers don’t depend on private or changing documents, a direct model call is simpler. RAG earns its place when answers must come from a specific body of documents. The proof of concept To understand the pipeline end to end, I built a small proof of concept called the Enterprise AI Knowledge Assistant. It uses ASP.NET Core for the API, Google’s Gemini API for embeddings and answer generation, and PostgreSQL with the pgvector extension, hosted on Supabase, as the vector store. The knowledge base is deliberately tiny: a text file of five company-policy statements covering annual leave, remote work, password rotation, probation and deployment approval. That is enough to see every stage of RAG working on questions like “How many days of paid leave do employees get?”. It is a learning project, so this article makes no claims about real users, accuracy or performance. What it does show is the workflow, which is the same whether the knowledge base holds five sentences or five thousand pages. The RAG workflow at a glance Two workflows share one retrieval store. Every RAG system has two phases that meet in a vector store. Phase 1, knowledge preparation (done ahead of time). Read the documents, split them into chunks, turn each chunk into a vector with an embedding model, and store the text, vector and source name. Phase 2, question answering (done per question). Turn the question into a vector with the same model, find the stored chunks closest to it, place those chunks in a prompt, and ask the generative model to answer from them. The rest of this article follows those steps in order. Step 1: Chunking, deciding what the model gets to see Embedding a whole handbook as one vector would blur dozens of topics together, and long passages inflate prompt size and cost. Documents are split into chunks small enough to be about one thing and large enough to make sense alone. The usual choices: Fixed-size splitting is simple and predictable but can cut through sentences. Paragraph-based splitting follows the author’s structure but produces uneven sizes. Overlap repeats a little text across chunk boundaries so a fact that straddles two chunks survives intact in at least one. Metadata, such as the source filename, lets you trace a chunk back to its document. The proof of concept splits on paragraphs and groups whole paragraphs into a chunk until a character limit is reached, keeping each statement intact. The demo file becomes three chunks. Each chunk is stored with its source filename. For longer, messier documents, overlap and token-aware sizing are the natural refinements. Step 2: Embeddings, turning text into vectors An embedding model maps text to a list of numbers so that texts with similar meaning end up near each other. That is what lets “how long is probation?” find “new employees have a probation period of six months” without sharing many words. An embedding does not answer anything. It only helps find candidates, and the nearest vectors can still be irrelevant. The proof of concept calls Gemini’s gemini-embedding-001 model and requests 768-dimensional vectors. According to the Gemini embeddings documentation, this model outputs 3072 dimensions by default and supports smaller sizes such as 768, and the docs also describe retrieval task types (RETRIEVAL_DOCUMENT for stored text, RETRIEVAL_QUERY for questions) that suit RAG. The same page names a newer model, gemini-embedding-2, which is worth evaluating. One rule matters above the rest: documents and questions must be embedded with the same model and dimensionality. Vectors from different models are not comparable. Step 3: Storing and searching vectors in PostgreSQL PostgreSQL with pgvector keeps chunks, metadata and embeddings in one familiar database. Each row in a document_chunks table holds the chunk text, its source file, and its embedding in a vector(768) column. Retrieval is a nearest-neighbor query. In pgvector, <=> is cosine distance, and cosine similarity is 1 - distance according to the pgvector reference. The proof of concept orders chunks by cosine distance to the question vector and keeps the top K: .Select(x => new { x.Id, x.Content, x.SourceFile, Distance = x.Embedding.CosineDistance(queryVector) }) .OrderBy(x => x.Distance) .Take(topK) Choosing K is a trade-off. Too small and the answer may miss a relevant chunk; too large and the prompt fills with noise. The demo uses a small K, which suits its small knowledge base. Without an index, pgvector performs exact nearest-neighbor search by scanning the table, which gives perfect recall (same reference). That is ideal at demo scale. For large collections, Supabase’s vector index guide generally recommends HNSW, and EDB’s pgvector documentation describes it as faster with better recall than IVFFlat, at the cost of memory and build time. That is a decision to make when the data grows, not before. Step 4: Building the augmented prompt and generating the answer This is the step that makes the pipeline RAG. The retrieved chunks are formatted into labelled blocks and placed in the prompt ahead of the question: Context: [Source 1] File: mock.txt Employees are entitled to 12 days of annual paid leave every year. Question: How many days of paid leave do employees get? A system instruction tells Gemini to answer using only the Context, not to fill gaps from general knowledge, and to reply with a fixed “not found” sentence when the context lacks the answer. Compare this with the plain call, where only the question is sent and the model answers from its training data. With retrieval, the model can answer from content it never saw in training, such as a company’s own leave policy. Changing the knowledge means updating the stored chunks, not retraining a model. The API response also returns the retrieved passages with their source file, so a reader can see what the answer was based on. How far can you trust a RAG answer? RAG improves the odds of a relevant answer. It does not guarantee a correct one. Grounding by prompt is an instruction, not a guarantee. A model told to use only the context can still misread it or lean on memory. Retrieval quality sets the ceiling. Top-K returns the closest chunks even when none is truly relevant, which is why production systems often add a similarity threshold or a “no good match” path. Returned sources are not verified citations. They show which passages the model was given, not which passage supports which claim. Evaluating a RAG system means testing retrieval and generation separately: did the right chunk come back, and did the answer stay faithful to it? Taking RAG beyond a proof of concept A few concerns move to the front when real documents and real users arrive: Permission-aware retrieval. Access control has to be applied in the retrieval query, before private content reaches the model. OWASP’s Vector and Embedding Weaknesses entry warns about weak access controls and context leakage between groups in shared vector stores. Untrusted documents. Retrieved text is input, and a document can contain instructions. OWASP’s prompt injection guidance notes that RAG does not fully prevent prompt injection and suggests constraining model behavior and clearly separating untrusted content. More sources and formats. PDFs, Word files, wikis and uploads need text extraction and a way to update or remove documents. Measurement. A set of real questions with known answers, used to tune chunking and K. Where this could help When an organization’s answers live in documents, RAG may reduce the friction of finding them: people ask in their own words and get an answer tied to passages in specific files. Those are potential benefits, not measured results. Building this proof of concept showed me that the model call is the smallest part of the system. Useful AI applications depend on how knowledge is chunked and retrieved, who is allowed to see what, and how answers are checked. How could your team make organizational knowledge easier to reach without sacrificing security or reliability? References Lewis et al., Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks, arXiv:2005.11401. Google, Embeddings, Gemini API, Google AI for Developers. Supabase, Vector indexes, Supabase Docs. pgvector 0.8.2 reference, pgvector documentation (PDF), hosted by Crunchy Data. EnterpriseDB, Using pgvector, EDB Docs. OWASP, LLM08:2025 Vector and Embedding Weaknesses, OWASP Gen AI Security Project. OWASP, LLM01:2025 Prompt Injection, OWASP Gen AI Security Project.
What is Azure VM? Azure virtual machines (VMs) are a cloud-based computing service that allows users to run applications on the Microsoft Azure platform. VMs are a type of on-demand, scalable computing resource that offers a number of benefits, including: Security: Azure VMs offer a secure way to run applications. Affordability: Users can pay for extra VMs when needed and shut them down when not. Flexibility: Users can choose from various operating systems, including Windows and Linux. Scalability: Users can scale up to thousands of VMs based on demand or schedules. Performance: Users can enhance network and storage performance with custom hardware. Azure virtual machines (VMs) can be created through the Azure portal. This method provides a browser-based user interface to create VMs and their associated resources. In this blog, I'll show you how to use the Azure portal to deploy a virtual machine (VM) in Azure. Sign in to the Azure portal. Create Virtual Machine Enter Virtual Machine in the search. Under Services, select Virtual machines. In the Virtual Machines page, select Create and then Azure virtual machine. The Create a Virtual Machine page opens. Under Project details, select the resource group. Under the instance details enter the Virtual machine name and choose "Windows Server 2022 Datacenter: Azure Edition - x64 Gen 2" for the Image. Leave the other defaults. Also use can choose image based on your requirement. Under Administrator account, provide a username, such as azureuser, and a password. Under Inbound port rules, choose Allow selected ports and then select RDP (3389) and HTTP (80) from the drop-down. Leave the remaining defaults and then select the Review + create button at the bottom of the page. After validation runs, select the Create button at the bottom of the page. After deployment is complete, select Go to resource. Connect to Virtual Machine On the overview page for your virtual machine, select Connect. Download the RDP file. Open the downloaded RDP file and click Connect when prompted. Click on more choise and enter your username and password that you have added while creating the VM and click on OK. You can see your VM is running now. Here are some other things to know about Azure VMs: Maintenance: Users still need to maintain the VM by configuring, patching, and installing software. Cost: The cost of an Azure VM depends on the size and type of VM, as well as other services used with it. Security: Users should take steps to ensure the security of their data and applications, such as identity management, encryption, and network protection. Virtual machine selector: Users can use the virtual machine selector to find the right VMs for their needs and budget. Conclusion: An Azure virtual machine gives you the flexibility of virtualization without buying and maintaining the physical hardware that runs it. However, you still need to maintain the virtual machine by performing tasks, such as configuring, patching, and installing the software that runs on it.
Have you ever wanted to create reusable JavaScript building blocks that work in any project, no matter what framework others are using? If so, then Web Components might be the solution you're looking for! This Guide is intended to dive into custom components. Web Components are a collection of tools that expand the web platform. This allows you to define new HTML elements with specific functionality and behavior. There’s a lot to think about, and writing a component can require a lot of boilerplate code. Fortunately, some great libraries can make creating custom elements more straightforward, and save you a lot of time and effort. However, if you’re writing lots and lots of custom elements, using a library can make your code simpler and cleaner, and your workflow more efficient. What are the Web Components? That allows developers to create custom, reusable, encapsulated HTML elements. Custom Elements: Enables the creation of new HTML tags. Shadow DOM: Provides encapsulation for custom elements, ensuring that their internal structure and styles do not interfere with the rest of the document. HTML Templates: Allows the definition of HTML templates that can be reused and instantiated in custom elements. Note: The name of a Web Component needs to contain a dash (-). This naming convention is put into place to enable the HTML parser to distinguish custom from regular elements and also avoid creating your own components that could be added as part of future HTML standards. <mycard></mycard>, <card></card> or <CardComponent></CardComponent> are all invalid names, while <my-card></my-card> is allowed. Why Web Components? Web components allow us to take our frontend widgets off this cycle of getting rebuilt in the newest framework flavor Reuse is King: Ever build the same button style ten times? With Web Components, you define a button component once and use it everywhere! Framework Freedom: No more framework lock-in! Web Components work seamlessly with vanilla JavaScript, React, Angular, or any framework you choose. Encapsulation Power: Components keep their styles and functionality isolated, preventing conflicts and promoting cleaner code. To define a new custom element using the v1 implementation, you simply create a new class that extends HTMLElement using ES6 syntax and register it with the browser: class MyElement extends HTMLElement {...} window.customElements.define('my-element, MyElement); //example usage in your app: <my-element></my-element> NOTE: Only Chrome V67 and up supports customized built-in elements! Let's Jump over the example, Building with LitElement. Certainly! Below is an example of a toggle switch web component using Lit. This example includes the essential parts: defining the component, its styles, and its template. Code Snippets for User Template: // component's template render() { return html` <label class="switch"> <input type="checkbox" .checked="${this.checked}" @change="${this._toggle}"> <span class="slider"></span> </label> `; } // Method handler. // The toggle handler public _toggle(event): void { this.checked = event.target.checked; this.dispatchEvent(new CustomEvent('toggle', { detail: this.checked })); } Explanation: We import LitElement and the html function for templating. We define a AppToggle class that extends LitElement. We set the name property to accept a string value. The render method defines how the component looks using LitElement's HTML-like syntax. Finally, we register the app-toggle custom element. Now, you can use your app-toggle component anywhere in your HTML: <div style="display: block; padding:200px"> <app-toggle name="toggle" checked="true"></app-toggle> <app-toggle name="World"></app-toggle> </div> The Future is Modular! Web Components offer a powerful and versatile approach to building user interfaces. With Reusability, framework independence, and clear separation of concerns, they are poised to be a significant force in the future of web development. So, start building your UI block party with Web Components today! I have included a screenshot below from WebComponents.org that shows the current browser support - a really nice community guide worth checking out and adding to your Bookmarks: Conclusion In this article, you took your first step into the world of web components. Web components have no third-party dependencies, so using them won't have a big impact on your bundle size. But for more complex components, you may want to reach for a library like Svelte or Lit.
Hosting an Angular application on IIS involves a few straightforward steps. Follow this step-by-step guide to seamlessly deploy your Angular project on IIS. Step 1: Open Your Angular Project in Visual Studio Code Review the build command in the package.json file. By default, it's usually set to ng build. Step 2: Run the Build Command Execute the ng build command in the terminal to compile your Angular application. This command creates a 'dist' folder, typically located at the specified output path in the angular.json file. Step 3: Install IIS Ensure that IIS is installed on your machine. You can install it through the "Turn Windows features on or off" option in the Control Panel. Step 4: Create a New Site in IIS Open the IIS Manager. In the Connections pane, right-click on the "Sites" node and select "Add Website." Fill in the required information, such as the Site name, Physical path to the folder , and choose a port. Step 5: Configure URL Rewrite (Optional) If your Angular application uses routing, consider configuring URL Rewrite for proper routing. Create a 'web.config' file in your 'dist' folder with the appropriate configurations. Here's a simple example of a web.config file for an Angular application with routing.This file helps configure how the server handles URL requests. ---------------------------------------------------------------------------------------------------------------- <?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <rewrite> <rules> <rule name="Angular Routes" stopProcessing="true"> <match url=".*" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> </conditions> <action type="Rewrite" url="/" /> </rule> </rules> </rewrite> <staticContent> <remove fileExtension=".json" /> <mimeMap fileExtension=".json" mimeType="application/json" /> </staticContent> </system.webServer> </configuration> ---------------------------------------------------------------------------------------------------------------- Step 6: Restart IIS After making these changes, restart IIS to apply the configurations. Step 7: Access Your Angular Application Open a web browser and navigate to http://localhost:yourport (replace 'yourport' with the specified port from Step 4). Now, your Angular application is hosted on IIS. Access it through the specified port. If any issues arise, check the IIS logs for more information. Customize these instructions based on your specific requirements and environment. Thanks!
In the dynamic landscape of data processing, Apache Kafka stands out as a robust and scalable distributed event streaming platform. This blog post aims to demystify Kafka, guiding you through its installation process step by step, and unraveling the concepts of topics, producers, and consumers. Understanding Kafka: 1. What is Kafka? Apache Kafka is an open-source distributed streaming platform that excels in handling real-time data feeds. Originally developed by LinkedIn, Kafka has evolved into a powerful solution for building scalable and fault-tolerant data pipelines. Installing Kafka: 2. Step-by-Step Installation Guide: Let's dive into the installation process for Kafka: Prerequisites: Before installing Kafka, ensure you have Java installed on your machine, as Kafka is built on Java. Download Kafka: Visit the official Apache Kafka website (https://kafka.apache.org/) and download the latest stable release. Unzip the downloaded file to your preferred installation directory. Start Zookeeper: Kafka relies on Zookeeper for distributed coordination. Navigate to the Kafka installation directory and start Zookeeper: bin/zookeeper-server-start.sh config/zookeeper.properties Start Kafka Broker: Open a new terminal window and start the Kafka broker: bin/kafka-server-start.sh config/server.properties Congratulations! You now have Kafka up and running on your machine. Kafka Concepts: 3. Topics: Definition: In Kafka, a topic is a category or feed name to which messages are published by producers and from which messages are consumed by consumers. Creation: Create a topic using the following command: kafka-topics.bat --create --topic MyTopics --bootstrap-server localhost:9092 --partitions 3 --replication-factor 1 List out Topics : To see all the topics will use following command : \bin\windows\kafka-topics.bat --list --bootstrap-server localhost:9092 4. Producers and Consumers: Producers: Producers are responsible for publishing messages to Kafka topics. You can create a simple producer using the following command: bin/kafka-console-producer.sh --topic myTopic --bootstrap-server localhost:9092 Consumers: Consumers subscribe to Kafka topics and process the messages. Create a consumer with: bin/kafka-console-consumer.sh --topic myTopic --bootstrap-server localhost:9092 --from-beginning Conclusion: Apache Kafka is a game-changer in the world of real-time data processing. By following this step-by-step guide, you've successfully installed Kafka and gained insights into key concepts like topics, producers, and consumers. Stay tuned for more in-depth Kafka tutorials as you explore the vast possibilities of this powerful streaming platform.
Here, I will explain how to restrict users from using expired tokens in a .NET Core application. Token expiration checks are crucial for ensuring the security of your application. Here's a general outline of how you can achieve this: 1. Configure Token Expiration: When generating a token, such as a JWT, set an expiration time for the token. This is typically done during token creation. For example, when using JWTs, you can specify the expiration claim: var tokenDescriptor = new SecurityTokenDescriptor { Expires = DateTime.Now.AddMinutes(30) // Set expiration time }; 2. Token Validation Middleware: Create middleware in your application to validate the token on each request. This middleware should verify the token's expiration time. You can configure this middleware in the startup or program file on the .NET side. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseMiddleware<TokenExpirationMiddleware>(); } 3. Token Expiration Middleware: Develop middleware to validate the token's expiration time. Take note of the following points: ValidateIssuerSigningKey: Set to true, indicating that the system should validate the issuer signing key. IssuerSigningKey: The byte array represents the secret key used for both signing and verifying the JWT token. ValidateIssuer and ValidateAudience: Set to false, indicating that validation of the issuer and audience is skipped. By setting ClockSkew to TimeSpan.Zero, you specify no tolerance for clock differences. If the current time on the server or client is not precisely within the token's validity period, the token is considered expired. public class TokenExpirationMiddleware { private readonly RequestDelegate _next; public TokenExpirationMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { // Check if the request has a valid token var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last(); if (token != null) { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes("YourSecretKey"); // Replace with your actual secret key of Issuer var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = false, ValidateAudience = false, ClockSkew = TimeSpan.Zero }; try { // Validate the token var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var securityToken); // Check if the token is expired if (securityToken is JwtSecurityToken jwtSecurityToken) { if (jwtSecurityToken.ValidTo < DateTime.Now) { // Token is expired context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; } } } catch (SecurityTokenException) { // Token validation failed context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; } } await _next(context); } } Working fine with proper token time. Here is an example: I am providing an expired token, and it will result in a 401 Unauthorized status. You can also check the token in https://jwt.io/ for time expired (exp) . By following these steps, you can effectively implement checks to ensure that users are not able to use expired tokens within your .NET Core application.
What Is a Docker? Let’s Say you created an application, and that’s working fine in your machine.??????? Figure 1: App Working Fine But in production it doesn’t work properly, developers experience it a lot. Figure 2: Not Working in Production That is when the developer’s famous words are spoken Client: Your application is not working Developer: It works on my machine Figure 3: Client Developer The Reason could be due to: Dependencies Libraries and versions Framework OS Level features Microservices That the developer’s machine has but not there in the production environment. We need a standardized way to package the application with its dependencies and deploy it on any environment. Docker is a tool designed to make it easier to create, deploy, and run applications by using containers. Figure 4: Docker Icon How does docker work? Docker packages an application and all its dependencies in a virtual container that can run on any server. Figure 5: Container Each container runs as an isolated process in the user space and take up less space than regular VMs due to their layered architecture. Figure 6: Architecture So, it will always work the same regardless of its environment. Credit Goes to @codechips