Memoria Web is not published to NuGet. It is an ASP.NET Core application in the repository, and you build it, publish it, and host it yourself.
Read Security before deciding where to put it. There is no authentication or authorization of any kind in this release, and anyone who can reach
/settingscan upload an assembly this process will load and execute. Both are coming in the next release; everything on this page describes the tool as it stands.
git clone https://github.com/lucabriguglia/Memoria.git
cd Memoria
dotnet run --project src/Memoria.Web
That uses the project’s launch profile: the Development environment, on http://localhost:5159
(https://localhost:7197 under the https profile). Point it at your store first — see
Configuration — or at the sample one, which
Try it with sample data fills for you.
Use the launch profile, or set
ASPNETCORE_ENVIRONMENTyourself. Started with--no-launch-profile, the environment is Production and the development static-asset handler looks for a bundle that only exists in publish output. The scoped-CSS bundle then answers 500 and the application renders unstyled. The same applies to anything run out ofbin/rather than out ofdotnet publishoutput.
dotnet publish src/Memoria.Web --configuration Release --output ./web
Run the result with the ASP.NET Core 10 runtime:
cd web
ASPNETCORE_URLS=http://localhost:5000 \
ConnectionStrings__Memoria="Host=db;Port=5432;Database=memoria;Username=reader;Password=…" \
dotnet Memoria.Web.dll
Publish output serves its static assets correctly in any environment. Build output does not — see the note above.
| Requirement | Why |
|---|---|
| ASP.NET Core 10 runtime | The published application is framework-dependent |
| Network to the store | The only external dependency there is |
| A writable content root | Uploaded archives and assemblies are written under it unless Extensions:Directory moves them elsewhere |
Nothing else. There is no cache, no message broker, no background worker and no scheduled job.
Every page renders statically — all of their state travels in the query string — so no component declares an interactive render mode and no Blazor circuit is opened. Ordinary HTTP proxying is enough; nothing here needs a WebSocket today.
The pipeline calls UseHttpsRedirection always, and UseHsts outside Development. Terminating TLS at
a reverse proxy is the usual arrangement; configure
forwarded headers on the
proxy side so the application sees the original scheme, or the redirect will fight the proxy.
Everything in the extensions directory is read again at start-up, so uploads survive a restart as long as that directory does. In a container, mount it:
docker run -p 8080:8080 \
-e ASPNETCORE_URLS=http://+:8080 \
-e ConnectionStrings__Memoria="Host=db;Port=5432;Database=memoria;Username=reader;Password=…" \
-e Extensions__Directory=/data/extensions \
-v memoria-web-extensions:/data/extensions \
memoria-web:latest
Without a volume, every deployment starts with nothing installed and everyone has to upload again.
Type registration lives in the process. Two instances behind a load balancer each register their own uploads from their own extensions directory, so the request after an upload can land on an instance that has never seen the assembly. Run one instance unless you have a reason not to; if you must run more, share the extensions directory between them and accept that an instance only picks up another’s upload when it is restarted or someone refreshes the types on it.
There is no horizontal-scale case to make here. The tool is read-mostly, its queries are the store’s, and the load it puts on a host is one operator at a time.
None ships with the repository. This is the standard shape, if you want one:
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish src/Memoria.Web -c Release -o /app
FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY --from=build /app .
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "Memoria.Web.dll"]
The application has none in this release, so the proxy has to be the whole of it. Whatever you use —
an identity-aware proxy, OAuth2 Proxy, a Kubernetes ingress with an auth annotation, basic auth on
nginx — the requirement is the same: no request reaches the application unauthenticated,
including POST /settings/upload. Protecting the pages and leaving the form posts open protects
nothing.
Grant access to the people you would give shell access on that host to, because an uploaded assembly runs with the application’s own privileges.
The next release adds authentication and authorization to the application itself, which will make this section a choice rather than the only option. A proxy in front of it stays perfectly valid either way — and until then it is the only thing standing between the internet and an upload form that runs code.
Perfectly reasonable, with two precautions:
SELECT on the store’s tables (or read access to the Cosmos container); Update additionally
needs to write the aggregate and projection rows.The tool creates nothing and deletes nothing. The only write it can make is refreshing a snapshot — see the one thing it writes.