LocalOrigin 0.4.1

dotnet add package LocalOrigin --version 0.4.1
                    
NuGet\Install-Package LocalOrigin -Version 0.4.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LocalOrigin" Version="0.4.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LocalOrigin" Version="0.4.1" />
                    
Directory.Packages.props
<PackageReference Include="LocalOrigin" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LocalOrigin --version 0.4.1
                    
#r "nuget: LocalOrigin, 0.4.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package LocalOrigin@0.4.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LocalOrigin&version=0.4.1
                    
Install as a Cake Addin
#tool nuget:?package=LocalOrigin&version=0.4.1
                    
Install as a Cake Tool

local-origin

Give a local web page its own origin — serve it, keep what it saves, and try it out before it is trusted.

Status: 0.4.1 published on NuGet (LocalOrigin, LocalOrigin.AspNetCore). This document fixes purpose, scope and the principles the implementation must honor. While the version is 0.x, a minor release may change the public surface.


Why

Desktop applications increasingly hold web pages that nobody deployed: a page a model just wrote, an HTML file a person dropped in, a small tool saved next to the data it works on. These pages need to run — and the moment they run, three questions have to be answered by the application that holds them:

  1. Where does the page live? Opened from file://, a page gets an opaque or shared origin: its storage collides with every other local file, fetch of its own data fails, and nothing isolates it from the next page. Served from the application's own HTTP endpoint, it becomes same-origin with that application's API. The browser isolates at scheme + host + port and nowhere else, so a page needs an origin of its own.
  2. Where does what it saves go? A page's writes — localStorage, a file it puts back — have to survive a crash, a reload and an update of the page itself, and they have to land somewhere the application can find, back up and move.
  3. Does it work before anyone relies on it? A page that has just changed should be opened once against a copy of the real state, with its writes kept away from the real data and its errors collected, before it replaces the version people use.

Every application that holds local pages ends up writing this layer, and the details — port stability, header sets, durable write paths, cross-site refusal — are exactly where a hand-rolled version goes wrong. local-origin is that layer, once.

What local-origin is

  • An origin allocator. Each page (or group of pages the host treats as one scope) gets a stable origin. Two strategies, chosen by the host:

    • subdomain per scope on one port — http://<id>.localhost:<port>/, cheap and numerous, for hosts whose browser engine resolves *.localhost itself;
    • port per scope — http://127.0.0.1:<port>/, for hosts that must be reachable from any client or resolver, or later from another machine.

    An origin is remembered across restarts, because a page's stored data is keyed by its origin: an origin that moves silently loses the page's data.

  • A static server with a security profile. Serves a scope's files with a header set decided once: a Content Security Policy chosen by the host, a Connection-Allowlist when the host turns it on (WebRTC is outside the policy), Cross-Origin-Resource-Policy: same-origin, refusal of cross-site requests (Sec-Fetch-Site), frame-ancestors, and no referrer. Binds to loopback unless the host deliberately opens it.

  • A durable write sink. One store under each scope, written through a journal and atomic file replacement, so an acknowledged write is on disk and a crash never leaves a half-written file. Pages reach it through one or both of two write channels, enabled per scope:

    • file channel — the page PUTs a file to its own address, confined to its own scope;
    • storage channel — an opt-in script mirrors localStorage to the store, for pages that were never written for any host and only know localStorage.
  • Preview origins. A throwaway origin for a candidate version of a page: it reads the current data, its writes are acknowledged and discarded, and its errors and policy violations are collected into a report the host reads afterwards. How the preview is driven — a headless browser, an off-screen view — is the host's choice; local-origin supplies the origin and the report.

What local-origin is not

  • Not a sandbox for untrusted code inside a host page. Pages run as ordinary pages on their own origin. Rendering generated UI inside the host's own window, behind a capability bridge, is vivarium's job.
  • Not a change lifecycle. Proposing, approving, applying and rolling back versions of an application belongs to the host, or to vivarium-stage. A preview origin is a place such a lifecycle can run its simulation; it decides nothing.
  • Not an authoring tool. It never calls a model and never edits a page.
  • Not a web framework. It hosts pages it is pointed at; routing, templating and application logic stay with the page or the host.

Fixed principles

These are the anchors. An implementation that violates one of them is not local-origin.

  1. The page is not modified on disk. Whatever a host chooses to inject is added to the served copy only; the stored bytes are the page's own.
  2. One origin, one scope. No route of the host application and no other scope's files are reachable from a page's origin.
  3. An acknowledged write is durable. A write is acknowledged only after it is on disk; a write resent after a lost acknowledgement is applied once.
  4. Data outlives the page. Replacing or reverting a page's code never discards the data it wrote; data written by a version that is left behind is kept aside, not merged and not deleted. A page still running replaced code cannot write into the data the new code owns: its late writes are refused and handed to the host once, never silently dropped — and the page is told (409 { "retired": true }, a cancelable localoriginretired event; by default it reloads) instead of retrying a refusal forever.
  5. Loopback by default. Reaching an origin from beyond the machine is an explicit host decision, never a default.
  6. No knowledge of its consumers. Nothing here names or special-cases an application that uses it. A need that only one consumer has stays in that consumer.

Packages

Package Contents
LocalOrigin Core with no dependencies: origin allocation and persistence, the durable store, the preview report model.
LocalOrigin.AspNetCore Hosting on ASP.NET Core: the security header profile, document injection, the write channels, preview origins.

Target: .NET 10. Nullable reference types and warnings-as-errors are on.

What exists so far

LocalOrigin

  • Origins — IOriginStrategy (a scope's origin; the scope a request names, from its host name and local port), SubdomainOrigins, PortOrigins (Bind/Unbind a scope to the port its listener got), ScopeName (DNS-label names), IPortMemory / PortMemoryFile, RememberedPort.StartAsync (start on the remembered port, retry briefly, otherwise move and report the move).
  • Storage — KeyValueStore (journal flushed before acknowledgement, atomic snapshots, previous snapshots kept, unreadable files set aside and reported; snapshot format identifier is a host option; the journal is folded into the snapshot after CheckpointEvery operations or once it is past CheckpointJournalBytes and CheckpointJournalGrowth times the snapshot — a page that rewrites one large value per change stays a few times its data on disk — and again on a clean close and on opening a journal that has already outgrown it; folds the store makes on its own keep one previous snapshot per KeepRecentVersionsApart, so a burst of writes does not push older states out of versions/ — versions made by CheckpointAsync/RestoreAsync stay), DurableFile (atomic replace, shared reads, set aside).
  • Previews — PreviewOrigins<T> (throwaway scope names, a lifetime that is fixed or renewed on use, bound), PreviewReport (errors and refusals in arrival order as typed entries — numbered and stamped with when they arrived, an error with whether the page was still loading, where it came from and where in the code — readable whole or Since what a reader has seen, so findings can be read while a page is being driven; bounded by the host, with the overflow counted).
  • Files — ScopeFolder (a scope's folder and the one way a request path becomes a file in it: dot segments, backslashes, colons, trailing dots and spaces, device names and links on the way all name nothing).

LocalOrigin.AspNetCore

  • ScopeOrigins.UseScopeOrigins(strategy, handle) — scope traffic is terminal: a request naming a scope is answered by the host's scope handler only (never by the host's own routes), with the profile on the response and cross-site requests refused; a request on a port the strategy owns that names no scope (a rebound name) gets 421. IOriginStrategy.OwnsPort says which ports those are (PortOrigins: the bound ones). Given a ScopeActivity, it counts each request the handler answers as in flight for its scope: how many are pending and when that last changed — «nothing pending since T», for telling when a page has settled instead of waiting a fixed time (an open event stream counts as pending; ScopeActivityState.IsSettled takes how many to allow).
  • OriginSecurityProfile — the header set of every response, and refusal of requests other sites (sibling origins included) make, navigation excepted.
  • DocumentInjector — host markup after the doctype of the served copy; stored bytes and declared charset kept.
  • Storage.StorageChannel / ChannelSessions — the storage channel: the opt-in script, sessions and tabs, writes applied once, writes from revoked pages refused and reported, writes discarded for previews. Wire names and the global the host reads before closing a page are options.
  • Previews.ProblemReports — a script that reports the page's errors (thrown while loading, thrown afterwards, or reported with console.error) and refused requests, and the endpoint that takes them in. An error says where it came from (ErrorSource: uncaught, rejection, console, or a file from another host that could not load) and, when the browser gives it, where in the code (SourcePosition: line and column in the page's own file — markup placed in front of it not counted — or in another file of its origin, by path).
  • Files.FileChannel — the file channel: PUT to the page's own address, confined to its folder, limited to the paths the host allows, atomic, size-capped; writes discarded for previews; the host told after each write.
  • Files.ScopeFiles — static serving of a scope's folder: GET/HEAD only, confined through ScopeFolder, the host deciding which files are served and which render as pages (others: sandbox + attachment), host markup injected into pages on the served copy, validators and ranges for the rest, no listings, a plain 404 for closed and missing alike. It applies the security profile itself, because it overrides the policy for files that are not pages; the write channels leave the profile to the host's pipeline.
  • Origins.ScopeListeners — a listener per scope for PortOrigins, opened and closed while one server runs (each an endpoint in the Kestrel:Endpoints configuration it supplies); open only once the server reports it, on the remembered port when it can, otherwise the first free port of an optional range or one the system picks.
  • OriginRequests.ScopeOf(HttpRequest).

Deliberately undecided

  • How scopes are registered and looked up — today the host resolves a scope and hands the library its store and documents; whether the library should own a registry is open.
  • The on-disk layout of a scope's store, and how the file channel and the key-value store share it.
  • Whether the storage-channel script also ships as an npm package.
  • Authentication for an origin opened beyond loopback.

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on LocalOrigin:

Package Downloads
LocalOrigin.AspNetCore

Hosting for local-origin on ASP.NET Core: serving a scope's pages with a fixed security profile, the write channels and preview origins.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.4.1 55 10/17/2026
0.4.0 97 10/15/2026
0.3.0 120 10/7/2026
0.2.0 112 10/5/2026
0.1.0 122 10/1/2026