Nous — Explore, learn, and build. An education / discovery social platform for curious people AND the people who make software: anyone can post things worth knowing (articles, links, explainers, how-tos), organised by category and tag, surfaced through a feed, an explore page and search, with voting, saving, reporting and moderation.
This repository contains the foundation skeleton. Feature teams fill in the modules (see Feature module contract) without touching each other's files.
| Layer | Technology |
|---|---|
| Client | .NET MAUI (Windows-first, net10.0-windows10.0.19041.0) |
| Web | Blazor Web App (net10.0, Server interactivity) — Win98-styled frontend |
| API | ASP.NET Core Minimal APIs (net10.0), JWT bearer auth, Swagger |
| Data | EF Core 10 — PostgreSQL 16 in production, SQLite for local dev |
| Storage | IFileStorage → FileSystemStorage (dev) or S3Storage (S3/MinIO) |
| Tests | xUnit + WebApplicationFactory |
| Ops | docker-compose (Postgres + MinIO + API + Web), GitHub Actions CI |
Nous.sln all six projects (Windows dev: builds everything)
Nous.Backend.slnf solution filter without the MAUI client (used by Linux CI)
Directory.Build.props common LangVersion/Nullable + shared package versions
global.json pins SDK 10.0.203
docker-compose.yml postgres + minio + minio-init + api + web
.github/workflows/ci.yml restore / build / test on ubuntu-latest
src/
Nous.Api/ ASP.NET Core host (Program.cs, Common/, Features/, Dockerfile)
Nous.Contracts/ shared request/response records (the wire contract)
Nous.Domain/ EF Core POCO entities only
Nous.Infrastructure/ NousDbContext, Migrations/, Storage/, Extensions/
Nous.Maui/ thin Windows client shell (no backend references)
Nous.Web/ Blazor Web App frontend (Components/, Services/, Dockerfile)
tests/
Nous.Api.Tests/ xUnit smoke tests against the real host
No Docker, no PostgreSQL required — Development defaults to SQLite (nous.db, created via
EnsureCreated) and local disk storage (./uploads).
dotnet run --project src/Nous.Api- Swagger UI: http://localhost:5080/swagger
- Health: http://localhost:5080/api/v1/health
docker compose up -d postgres minio minio-init # dependencies only
docker compose up -d --build # everything, API on :8080Switching providers is pure configuration:
| Key | Values | Notes |
|---|---|---|
Database:Provider |
Sqlite | Postgres |
SQLite → EnsureCreated; Postgres → Migrate |
Database:AutoInitialize |
true | false |
Set false to skip DB bootstrap (tests do this) |
Storage:Provider |
FileSystem | S3 |
S3 mode targets MinIO via Storage:ServiceUrl |
Jwt:Key / Jwt:Issuer / Jwt:Audience |
Jwt:Key is required outside Development |
|
Security:MasterKey |
base64, 32+ bytes | encrypts TOTP secrets at rest |
Dev-safe values live in src/Nous.Api/appsettings.Development.json. Never reuse them
outside Development — override with environment variables (Jwt__Key=...) or user-secrets.
Migrations exist for PostgreSQL only (SQLite dev uses EnsureCreated).
dotnet tool restore
dotnet ef migrations add <Name> --project src/Nous.Infrastructure --startup-project src/Nous.Infrastructure --output-dir MigrationsNousDbContextFactory supplies a design-time Postgres context, so no running server is needed
to generate a migration.
dotnet test Nous.Backend.slnf # backend only — what CI runs (cross-platform, no MAUI workload needed)Nous.sln includes the MAUI client project (net10.0-windows10.0.19041.0), which only builds on
Windows with the MAUI workload installed. Only use dotnet test Nous.sln if you have that
setup; Nous.Backend.slnf is the backend-only solution filter used by CI and works everywhere.
Tests write a SQLite file (nous-*.db) into the test bin output. To reset to a clean state
between runs:
Remove-Item tests/Nous.Api.Tests/bin\*\net10.0\nous-*.db* -ForceRequires the MAUI workload:
dotnet workload install maui
dotnet build src/Nous.Maui/Nous.Maui.csproj
dotnet run --project src/Nous.Maui -f net10.0-windows10.0.19041.0Nous.Maui is part of Nous.sln, but Linux CI builds Nous.Backend.slnf instead, since
net10.0-windows10.0.19041.0 cannot be built without Windows + the workload.
The client is a thin shell: it references no backend project and talks HTTP only.
src/Nous.Web is a Blazor Web App (net10.0, Server interactivity) styled as a Windows 98
desktop. It is a pure HTTP client of the API — it references only Nous.Contracts and never
touches the database.
The API must be running first (Development defaults to http://localhost:5080):
dotnet run --project src/Nous.Api # terminal 1
dotnet run --project src/Nous.Web # terminal 2- Web UI: http://localhost:5100 (HTTPS: https://localhost:7100)
- If your API is on a different address, change
Api:BaseUrlinsrc/Nous.Web/appsettings.json(or setApi__BaseUrl=http://...as an environment variable).
docker compose up -d --build # postgres + minio + api (:8080) + web (:5100)Then open http://<host-ip>:5100 from any other machine on the network (allow the port through
the host firewall). Inside the compose network the web container reaches the API at
http://api:8080 — this is set by Api__BaseUrl on the web service; localhost would point
at the web container itself. The container listens on 8081 internally and is published as
5100:8081.
CORS is not needed: Blazor Server renders on the server, so API calls originate from the web container's server process, not from the browser.
In scope:
- Feed — paged list of posts, vote / save from the list.
- Post detail — full post view with vote / save.
- Auth — register, login, TOTP two-factor challenge, logout.
- Anonymous visitors get a view-only experience; voting and saving require sign-in.
Posts are organised by category. The seeded set spans broad general education plus a Software & Making track for coders and builders:
General education (the largest slice): Science, History, Math, Technology, Nature, Language, Art, Geography, Physics, Chemistry, Biology, Astronomy, Philosophy, Literature, Music, Psychology, Economics, Health & Medicine, Design, Business, Pop Culture, Food & Cooking, Sports.
Software & Making: Programming, Software Engineering, Web Development, DevOps & Cloud, AI & Machine Learning, Databases, Cybersecurity, Open Source.
New categories are seeded idempotently (add-missing-by-slug), so both fresh and existing
databases pick them up on the next request to /api/v1/categories.
Explicit non-goals (deferred, not bugs):
- Create/edit post UI (posting is API-only for now)
- Search and explore pages
- Moderation / admin console
- Comments and threaded discussion
Program.cs is already wired and should not need changes. Each feature team owns exactly one
file and implements two extension methods:
| Module | File | Extensions | Route prefix |
|---|---|---|---|
| Auth | src/Nous.Api/Features/Auth/AuthModule.cs |
builder.AddAuth() / app.MapAuth() |
/api/v1/auth |
| Content | src/Nous.Api/Features/Content/ContentModule.cs |
builder.AddContent() / app.MapContent() |
/api/v1/posts, /feed, /explore, /search, /categories, /media |
| Moderation | src/Nous.Api/Features/Moderation/ModerationModule.cs |
builder.AddModeration() / app.MapModeration() |
/api/v1/reports, /api/v1/admin |
Route prefixes are constants in src/Nous.Api/Common/Result.cs (ApiRoutes). Request/response
shapes are records in Nous.Contracts — extend that project rather than inventing per-feature DTOs.
Nous is designed against the McCumber Cube: every control is placed at the intersection of an information state, a security goal and a safeguard. Fill in per-feature detail as it lands.
| Information state | Confidentiality | Integrity | Availability |
|---|---|---|---|
| Storage (at rest) | password hashes only, TOTP secrets encrypted with Security:MasterKey, secrets out of source control |
EF Core constraints + unique indexes (Email, Slug, (UserId,PostId)), AuditLog append-only trail |
Postgres volume + backup story (TODO) |
| Transmission (in transit) | HTTPS/HSTS outside Development, JWT bearer over TLS | signed JWTs (HS256), issuer/audience/lifetime validated | rate limiting on auth endpoints (TODO) |
| Processing (in use) | role-based authorization (User / Admin), least-privilege endpoints |
server-side validation of every DTO, moderation workflow for flagged content | health endpoint /api/v1/health, graceful error handling via ProblemDetails |
Safeguard axis: technology (the table above), policy & practice (secret rotation, code review, moderation SOP — TODO), people (2FA enrollment, admin onboarding — TODO).