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
$ApiRequest('Save', data);
public async Task<ApiResponse> Save()
{
ApiResponse response = new ApiResponse();
response.SetElementContents("result", "<b>Saved</b>");
return response;
}
{ "o": 20, "k": "result", "p1": "<b>Saved</b>" }
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.
- BrowserGET /Contact
- Resolve classby name, in the assembly
- OnInitializeddata into placeholders
- Renderadds the page's .js and .css
- Documentfull HTML
- $ApiRequest'Save', data
- Contact/Saveform data posted
- Save()builds an ApiResponse
- JSON operationso, k, p1, p2
- $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 needshtmls/Contact.htmlMarkup with {plhd_name} style tokens the class replacesscripts/Contact.jsOne namespace, ContactJs, with the handlers the markup and server callstyles/Contact.cssStyles under one short page prefix, such as cnt-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;
}
}
}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 values
SetElementContentsAddElementContentsSetElementValue - Attributes
SetElementAttributeRemoveElementAttribute - Styles
SetElementStyleRemoveElementStyle - Structure
ReplaceElementRemoveElementReplaceText - Scripts and calls
ExecuteScriptAppendScriptServerMethod - Variable data
SetVariableData$GetVarData - Storage and cookies
StoreLocalValueSetCookie - Navigation and files
NavigateNewWindowDownloadFile
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.
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.
-
Create an empty project and add application.cfg
In Visual Studio, create an ASP.NET Core Empty project. Add an
appConfigfolder at the project root and putapplication.cfginside it. SkyNet reads it on startup. At minimum changeapp.app.name,app.app.startup(the class name of your first page) and theapp.sqldbvalues.**************************************************** ***** 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 ****************************************************
-
Install the SkyNet package
One package. Run either line from the Package Manager Console or a terminal.
Install-Package TheSkyLite.SkyNet dotnet add package TheSkyLite.SkyNet
-
Turn off hot reload
In
Properties/launchSettings.json, sethotReloadEnabledto false in each profile. Left on, it can interrupt the page while you are developing."hotReloadEnabled": false
-
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.
using SkyNet; var builder = WebApplication.CreateBuilder(args); builder.Services.AddHttpContextAccessor(); var app = builder.Build(); app.UseMiddleware<IHandler>(); app.UseStaticHttpCurrent(); app.Run();
-
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
dataneed the Exclude and Update pair, because the Web SDK already includes them.<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> -
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
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
Built on SkyNet
- Website templatesComplete sites with live demos and full source, free under MIT.
- ServiceNetField service management, from request to sign-off.
- BizJournalCRM and relationship journal with bids and projects.
- S3ShareDocument sharing on Amazon S3 by territory and access level.
Where to go next
- Project templateAn empty Visual Studio project with SkyNet already wired up.
- Developer GuideThe page model, the ApiResponse command set, the client engine and data access.
- NuGet packageTheSkyLite.SkyNet, free to use for eligible individuals and organizations, including commercially.
- SkyNet AgentBuild automation: describe a website and get it planned, written and tested.