Skip to content

Repository files navigation

Nous

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.


Stack

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 IFileStorageFileSystemStorage (dev) or S3Storage (S3/MinIO)
Tests xUnit + WebApplicationFactory
Ops docker-compose (Postgres + MinIO + API + Web), GitHub Actions CI

Repository layout

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

Running the API

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

Prod-like stack (Postgres + MinIO)

docker compose up -d postgres minio minio-init   # dependencies only
docker compose up -d --build                     # everything, API on :8080

Switching 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

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 Migrations

NousDbContextFactory supplies a design-time Postgres context, so no running server is needed to generate a migration.

Tests

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.

Cleaning test databases

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* -Force

Running the MAUI client

Requires 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.0

Nous.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.


Web frontend (Nous.Web)

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.

Run locally

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:BaseUrl in src/Nous.Web/appsettings.json (or set Api__BaseUrl=http://... as an environment variable).

Share on a LAN (Docker)

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.

MVP scope

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.

Categories

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

Feature module contract

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.


Security model — McCumber Cube note (stub)

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).

About

Nous - open education/discovery platform (Blazor Server + ASP.NET Minimal API)

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages