Skip to content

SkyNet, a server-driven framework for ASP.NET Core

You write pages in C#. The first request returns a complete HTML document, and every interaction after that returns a short list of DOM operations that the browser applies in place. The front end stays plain HTML, CSS and JavaScript, with no npm, bundler or SPA.

SkyNet is the successor of SkyLite, the technology base it grew from, rebuilt for ASP.NET Core on .NET 10.

  • ASP.NET Core, .NET 10
  • C# on the server
  • One NuGet package
  • Windows, Windows Server
Browser
$ApiRequest('Save', data);
Server, in C#
public async Task<ApiResponse> Save()
{
    ApiResponse response = new ApiResponse();
    response.SetElementContents("result", "<b>Saved</b>");
    return response;
}
Sent back
{ "o": 20, "k": "result", "p1": "<b>Saved</b>" }
The server decides what changes. The browser applies it. Nothing else to write.

Two paths, and only two

A GET builds and returns a whole document. Every interaction after that is a POST that returns DOM operations, and the page stays loaded while only the targeted elements change.

GET, the first request
  1. BrowserGET /Contact
  2. Resolve classby name, in the assembly
  3. OnInitializeddata into placeholders
  4. Renderadds the page's .js and .css
  5. Documentfull HTML
POST, every interaction after
  1. $ApiRequest'Save', data
  2. Contact/Saveform data posted
  3. Save()builds an ApiResponse
  4. JSON operationso, k, p1, p2
  5. $WebActpatches the DOM

Runs on the server What the browser ends up with

What it is not

Not an SPA framework
No client-side router, virtual DOM, component tree or client state store.
Not a templating engine
No Razor. HTML lives in .html files with {placeholder} tokens that the page class replaces.
Not an ORM
You write SQL. SQLData parameterizes and runs it, and maps rows onto plain classes when you want typed lists.
Not MVC routing
Pages are found by class name in the assembly, not by a route table or attributes.

A page is a class and three files beside it

Request a page by name and SkyNet finds the class, loads its .html, and adds its .js and .css automatically. No registration, no route entry. Keeping each page self-contained, with its own CSS prefix and JavaScript namespace, is the recommended house style.

codes/Contact.csThe page class: : WebPage, its server methods and any DTOs it needs
htmls/Contact.htmlMarkup with {plhd_name} style tokens the class replaces
scripts/Contact.jsOne namespace, ContactJs, with the handlers the markup and server call
styles/Contact.cssStyles under one short page prefix, such as cnt-
codes/Contact.cs
using SkyNet;

namespace MyApp.codes
{
    public class Contact : WebPage
    {
        public override async Task OnInitialized()
        {
            HtmlDoc.SetTitle("Contact");
            HtmlDoc.HtmlBodyText = HtmlDoc.HtmlBodyText
                .Replace("{plhd_name}", HtmlEncode("Sample name"));
        }

        public async Task<ApiResponse> Save()
        {
            string name = GetDataValue("name");
            ApiResponse response = new ApiResponse();
            response.SetElementContents("cnt-msg", "Saved " + HtmlEncode(name));
            response.ExecuteScript("ContactJs.onSaved();");
            return response;
        }
    }
}
scripts/Contact.js
var ContactJs = (function () {
    function val(id) {
        var e = document.getElementById(id);
        return e ? e.value : '';
    }

    function save() {
        $WaitOn();
        $ApiRequest('Save', JSON.stringify([
            { key: 'name', vlu: val('cnt-name') }
        ]));
    }

    function onSaved() {
        $WaitOff();
    }

    return {
        save: function () { save(); },
        onSaved: function () { onSaved(); }
    };
})();

ApiResponse carries instructions, not data

There is no render step on the client. If the browser should show something different, the server says exactly which element changes and to what. Most methods also come in ByName and ByDataId forms.

  • Contents and valuesSetElementContentsAddElementContentsSetElementValue
  • AttributesSetElementAttributeRemoveElementAttribute
  • StylesSetElementStyleRemoveElementStyle
  • StructureReplaceElementRemoveElementReplaceText
  • Scripts and callsExecuteScriptAppendScriptServerMethod
  • Variable dataSetVariableData$GetVarData
  • Storage and cookiesStoreLocalValueSetCookie
  • Navigation and filesNavigateNewWindowDownloadFile

Data access

SQLData runs parameterized SQL against SQL Server and returns tables or typed lists. You write the SQL, so you always know what runs.

Why it suits AI-assisted development

SkyNet was shaped by a workflow where the developer owns the architecture and the database schema, and an AI assistant writes most of the implementation.

Everything in one place

A page is four files sharing one name. An assistant given one page has the complete context for that page.

Duplication over abstraction

Copying a block into a second page is preferred to a shared helper, so a change to one page cannot break another.

No front-end build

No bundler config, transpile target or module resolution to get wrong. Generated C# compiles or it does not.

Naming is the contract

Method names, element ids, CSS prefixes and placeholder tokens follow conventions stated in plain words.

A flat client API

Every client global starts with $. There is no import to resolve and no namespace to guess.

Copy, retarget, adapt

"Build the Invoice page the way the Contact page works" is a complete instruction, because pages do not share code.

The trade-off is real. Strings crossing the C# and JavaScript boundary are not checked by a compiler, and a bug fixed in one page stays in its copies. Both are manageable with a verification step before each delivery.

Get started

An empty ASP.NET Core project, one NuGet package and six short steps. After that you write C# for the logic and let your AI assistant write the pages.

Give your AI the framework first.

Point it at the SkyNet Developer Guide before it writes a line. To automate the whole build, from plan to tested pages, use the SkyNet Agent.

  1. Create an empty project and add application.cfg

    In Visual Studio, create an ASP.NET Core Empty project. Add an appConfig folder at the project root and put application.cfg inside it. SkyNet reads it on startup. At minimum change app.app.name, app.app.startup (the class name of your first page) and the app.sqldb values.

    appConfig/application.cfg
    ****************************************************
    ***** 1.System Configuration: These values can be changed but Do Not Delete
    ***** 2. app.app.runningmode : 0.Demo, 1.Production
    ****************************************************
    app.app.name | MyApp
    app.app.version | 1.0
    app.app.releasedate | 10/22/2026
    app.app.runmode | 0
    app.app.startup | Home
    app.app.regno | 00000000-0000-0000-0000-000000000000
    app.Settings.encryptkey | %SYSTEM_PASSWORD%
    app.Settings.isocode | en-US
    app.Settings.pagetimeout | 300
    app.Settings.waitimage | ani_wait.gif
    app.folders.code | codes
    app.folders.html | htmls
    app.folders.script | scripts
    app.folders.style | styles
    app.folders.image | images
    app.folders.temp | temp
    app.folders.data | data
    app.folders.log | logs
    app.folders.bin | bin
    app.mail.server | smtp.office365.com
    app.mail.port | 587
    app.mail.addr | noreply@email.com
    app.mail.id | noreply@email.com
    app.mail.password | 1234
    app.mail.title | Notification_Center
    app.mail.credentials | 0
    app.sqldb.source | (local)
    app.sqldb.catalog | MyApp
    app.sqldb.id | sa
    app.sqldb.password | 1111
    app.sqldb.timeout | 3600
    ****************************************************
  2. Install the SkyNet package

    One package. Run either line from the Package Manager Console or a terminal.

    NuGet
    Install-Package TheSkyLite.SkyNet
    dotnet add package TheSkyLite.SkyNet
  3. Turn off hot reload

    In Properties/launchSettings.json, set hotReloadEnabled to false in each profile. Left on, it can interrupt the page while you are developing.

    launchSettings.json, in each profile
    "hotReloadEnabled": false
  4. Wire up Program.cs

    Three lines turn a bare ASP.NET Core app into a SkyNet app: the HttpContext service, the SkyNet handler as middleware, and the static HTTP accessor.

    Program.cs
    using SkyNet;
    
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddHttpContextAccessor();
    
    var app = builder.Build();
    app.UseMiddleware<IHandler>();
    app.UseStaticHttpCurrent();
    
    app.Run();
  5. Copy the content folders on build

    SkyNet serves assets from root folders rather than wwwroot, so the project file carries them to the output and publish folders. JSON files in data need the Exclude and Update pair, because the Web SDK already includes them.

    YourProject.csproj
    <Project Sdk="Microsoft.NET.Sdk.Web">
    
      <PropertyGroup>
        <TargetFramework>net10.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <SatelliteResourceLanguages>es</SatelliteResourceLanguages>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="TheSkyLite.SkyNet" Version="1.0.8" />
      </ItemGroup>
    
      <ItemGroup>
        <Content Include="appConfig\**">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          <CopyToPublishDirectory>Always</CopyToPublishDirectory>
        </Content>
        <Content Include="htmls\**">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          <CopyToPublishDirectory>Always</CopyToPublishDirectory>
        </Content>
        <Content Include="images\**">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          <CopyToPublishDirectory>Always</CopyToPublishDirectory>
        </Content>
        <Content Include="logs\**">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          <CopyToPublishDirectory>Always</CopyToPublishDirectory>
        </Content>
        <Content Include="scripts\**">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          <CopyToPublishDirectory>Always</CopyToPublishDirectory>
        </Content>
        <Content Include="styles\**">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          <CopyToPublishDirectory>Always</CopyToPublishDirectory>
        </Content>
        <Content Include="temp\**">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          <CopyToPublishDirectory>Always</CopyToPublishDirectory>
        </Content>
        <Content Include="data\**" Exclude="data\**\*.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          <CopyToPublishDirectory>Always</CopyToPublishDirectory>
        </Content>
        <Content Update="data\**\*.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          <CopyToPublishDirectory>Always</CopyToPublishDirectory>
        </Content>
      </ItemGroup>
    
    </Project>
  6. Add anything else you need

    SkyNet does not restrict what else you reference. Add a mail library, a payment SDK or anything else the project calls for, then press F5.

What the project looks like

Page classes in codes/, their companions in htmls/, scripts/ and styles/. The class name is the URL, so folders under codes/ only organize files.

  • No npmNo package manifest to maintain
  • No bundlerNo webpack, Vite or rollup config
  • No front-end buildThe C# compiles like any ASP.NET Core app; HTML, CSS and JavaScript are copied as they are
  • No SPANo React, Vue or Angular to migrate
Project root
MyApp/
├── codes/                 page classes in C#
│   ├── Home.cs
│   ├── Contact.cs
│   └── Admin/
│       ├── Admin_Users.cs
│       └── Models/
│           └── AdminModel.cs
├── htmls/                 page markup
├── scripts/               page JavaScript
├── styles/                page CSS
├── images/                pictures and icons
├── data/                  data files
├── appConfig/
│   └── application.cfg    application settings
├── logs/
├── temp/
├── MyApp.csproj
└── Program.cs

License

Loading...