Skip to content

forge-trust/AppSurface

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

306 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AppSurface

Build Package Gate Package Artifacts Code Quality CodeQL Codecov NuGet Packages Dependabot Security Policy License

⚠️ Under Construction: This library is actively being developed and is not intended for production use yet. Monorepo for the ForgeTrust.AppSurface projects

ForgeTrust.AppSurface is a collection of .NET libraries designed to provide a lightweight, modular startup pipeline for both console and web applications.

If you are deciding which package to install first, start with the AppSurface v0.1 package chooser.

Vision

The primary vision of AppSurface is to simplify application bootstrapping by encouraging composition through small, focused modules. Instead of monolithic startup classes or scattered configuration logic, AppSurface allows developers to encapsulate features into reusable modules that handle:

  • Dependency Injection (DI) registration
  • Host configuration
  • Application-specific startup logic

This approach aims to:

  • Share cross-cutting concerns between different application types (e.g., sharing logging or database setup between a Web API and a background Console worker).
  • Keep applications minimal, with infrastructure heavily decoupled from business logic.
  • Provide consistency in how applications are initialized and configured, regardless of whether they are web or console apps.

Key Design Goals

  1. Modularity: Everything should be a module that does one thing well. Take what you need and don't get burdened by what you don't.
  2. Consistency: A unified AppSurfaceStartup pipeline for different project types.
  3. Flexibility: Open for integration with external libraries (Autofac, OpenApi, etc.) and stick to framework provided abstractions where possible.
  4. Performance: Designed to have minimal overhead on the application startup and execution.
  5. Ease of Use: Simple APIs and clear patterns to make getting started frictionless.
  6. Convention over Configuration: Sensible defaults are provided so only minimal configuration is required.
  7. Secure By Default: Security best practices are applied automatically where appropriate.

Caching Conventions

  • Use IMemo for application and service-layer caching (for example, web modules and domain services).
  • Use direct IMemoryCache only inside caching infrastructure (the ForgeTrust.AppSurface.Caching package) or framework integration points where IMemo cannot be injected.
  • If a module depends on AppSurfaceCachingModule, do not call AddMemoryCache() again in that module.
  • Prefer one cache boundary per data snapshot. In AppSurface Docs, DocAggregator owns both docs aggregation and search-index payload caching so downstream controllers consume one shared snapshot.

Project Structure

  • ForgeTrust.AppSurface.Core – Core abstractions for defining modules, starting an application via AppSurfaceStartup and StartupContext, and running AppSurface-owned process workflows through a CliWrap-backed policy surface.
  • ForgeTrust.AppSurface.Auth – Surface-neutral auth vocabulary for AppSurface modules, including user/session/context contracts, auth outcome results, durable external-subject to app-user-id mapping contracts, passive login/logout prompts, passive audit event descriptions, and no runtime request or identity-provider behavior.
  • ForgeTrust.AppSurface.Auth.AspNetCore – ASP.NET Core adapter that maps existing host request auth context and named policies into AppSurface auth results without owning schemes, middleware, challenges, forbids, redirects, or identity-provider setup. Run the Auth Web/RazorWire proof to see one host policy drive both API and rendered UI state.
  • ForgeTrust.AppSurface.Intelligence - Product-intelligence event contracts, lifecycle metadata, privacy validation, and host-owned sink hooks for forwarding sanitized AppSurface product events to systems such as PostHog without taking a vendor dependency.
  • ForgeTrust.AppSurface.Flow – Typed long-running process contracts, generated-case authoring, graph validation, definition registry, and an in-memory runner for local tests and hello-world flows.
  • ForgeTrust.AppSurface.Flow.DurableTask – Durable Task adapter boundary with runner/client services, resume-event authorization, timeout, late-event and retry behavior, and context serialization validation.
  • ForgeTrust.AppSurface.Console – Helpers for building command line apps with CliFx, source-generated command descriptors, a CriticalService-based command runner, and helpers for configuring services.
  • ForgeTrust.AppSurface.Web – Bootstraps ASP.NET Core apps, lets modules register pre-routing middleware, endpoint-aware middleware, and endpoints, and includes conventional browser status pages plus opt-in production 500 pages.
  • ForgeTrust.AppSurface.Web.OpenApi – Optional module that adds OpenAPI generation with development-only endpoint exposure by default.
  • ForgeTrust.RazorWire – Adds reactive Razor-based streaming, islands, and CDN-default export tooling for server-rendered web apps.
  • ForgeTrust.AppSurface.Docs – Reusable Razor Class Library package that serves harvested source docs with section-first landing, sidebar, search, built-in trust plus contributor-provenance details, and optional published-version archive surfaces.
  • ForgeTrust.AppSurface.Docs.Standalone – Thin export host for exporting or serving AppSurface Docs as an application.
  • ForgeTrust.AppSurface.Web.Scalar – Optional module that serves the Scalar API reference UI when both Scalar and OpenAPI exposure gates allow it.
  • ForgeTrust.AppSurface.Cli – Public appsurface command-line tool, including appsurface docs preview/export workflows, appsurface coverage run private test orchestration, appsurface coverage merge Cobertura fan-in, and appsurface coverage gate local threshold enforcement.
  • ForgeTrust.AppSurface.Aspire – Local .NET Aspire AppHost composition with AppSurface modules, CLI-selectable profiles, and reusable Aspire components.

These packages are designed to work together so that features can be shared across different application types while maintaining a consistent startup approach.

Getting started

If you want to see value first, run the web hello world:

dotnet run --project examples/web-app -- --port 5055

Then, from another terminal, prove the running endpoint:

curl http://127.0.0.1:5055

Expected response:

Hello World from the root!

That example is the smallest concrete path through ForgeTrust.AppSurface.Web: a root module, one mapped endpoint, and the AppSurface startup pipeline doing the hosting work.

To verify the browser/API error-page contract instead, run the focused proof:

bash examples/web-error-pages/verify.sh

That proof starts a local production-mode web app, checks conventional browser 401, 403, 404, and 500 pages, and verifies API requests do not receive surprise browser HTML.

If you are evaluating packages from your own app project rather than running this repo, use the package-first path to create a fresh ASP.NET Core app, install ForgeTrust.AppSurface.Web, and verify the first route. Run the package command from the app project directory, or pass the project path explicitly. The generated package chooser in packages/README.md is the install map for picking optional modules after that first proof.

dotnet package add ForgeTrust.AppSurface.Web

Add optional modules only when the generated chooser points you to them.

If you need a composed product-shaped proof instead of a single package slice, run the product-readiness lab:

dotnet run --project examples/product-readiness-lab/ProductReadinessLab.csproj -- --report

That fast command emits a no-infrastructure readiness report. To exercise every row the local lab can prove, including Postgres-backed product-state persistence, run the AppHost verifier:

aspire run --non-interactive --apphost examples/product-readiness-lab-apphost/ProductReadinessLabAppHost.csproj -- verify

The lab emits a readiness report with proven-locally, host-owned, deferred, unsafe-to-copy, and blocked rows. Its in-process host proof shows where IDurableTaskFlowRunner<TContext> and IDurableTaskFlowClient<TContext> fit, while keeping Durable Task worker/client hosting and storage provider setup explicitly host-owned.

For contributor verification, build the solution:

dotnet build
dotnet test --no-build

Run merged solution coverage for this repository's AppSurface-specific validation lane:

./scripts/coverage-solution.sh
dotnet run --project Cli/ForgeTrust.AppSurface.Cli -- coverage gate --coverage TestResults/coverage-merged/coverage.cobertura.xml --min-line 95 --min-branch 85 --diff-base origin/main --min-patch-line 95 --min-patch-branch 85

This command:

  • Runs each solution test project.
  • Collects coverage only for ForgeTrust.AppSurface.* modules.
  • Excludes test modules (*.Tests and *.IntegrationTests) from coverage.
  • Produces one merged Cobertura file at TestResults/coverage-merged/coverage.cobertura.xml.
  • Produces AppSurface-managed JUnit files as TestResults/coverage-merged/junit-coverage-<index>-<project-name-hash>.xml.
  • Writes a summary to TestResults/coverage-merged/summary.txt.
  • Writes machine-readable timing data to TestResults/coverage-merged/timings.json.
  • Writes slow-test diagnostics to TestResults/coverage-merged/slow-test-diagnostics.md and TestResults/coverage-merged/slow-test-diagnostics.json, including diagnostic aggregation overhead in seconds and as a percent of elapsed runner time at diagnostics generation.
  • Uses the source AppSurface CLI and its package-owned ReportGenerator dependency for the default full-solution lane.

Private package-consuming repositories should use the public CLI runner instead of this repository's script:

dotnet tool run appsurface coverage run --solution ./MyApp.slnx
dotnet tool run appsurface coverage gate --coverage ./TestResults/coverage-merged/coverage.cobertura.xml --min-line 85 --min-branch 75

The appsurface coverage run command discovers .sln/.slnx test projects or accepts repeated --test-project values, runs Coverlet-instrumented projects, writes private local artifacts under TestResults/coverage-merged, and merges Cobertura through the CLI package's ReportGenerator dependency without reading the consumer repo's tool manifest. No separate merge command is required for ordinary package consumers: coverage run produces TestResults/coverage-merged/coverage.cobertura.xml directly. Managed test results are opt-in with --test-results junit; this requires selected test projects to reference JunitXml.TestLogger. --slow-test-diagnostics implies managed JUnit results and writes slow-test-diagnostics.md and .json beside the merged coverage file. Use appsurface coverage merge --source ./TestResults/coverage-shards --output ./TestResults/coverage-merged when a matrix job or custom test workflow already produced shard files named coverage.cobertura.xml. The optional appsurface coverage gate command evaluates that merged Cobertura file locally, writes coverage-gate.json and coverage-gate.md, appends the Markdown report to $GITHUB_STEP_SUMMARY when GitHub Actions provides it, and fails with ASCOV020 when line, branch, or configured patch coverage is below threshold. Patch coverage accepts exactly one source: --diff-base for local Git history, --diff-file for a CI-produced unified diff artifact, or --diff-stdin for piped unified diff text. External diff artifacts are private local inputs, are bounded at 20 MiB, and fail closed when non-empty content is not unified diff text. The coverage commands are intentionally private-by-default: they do not upload coverage, call GitHub APIs, or store trends.

The script also supports bounded test groups for local or CI experiments:

BUILD_CONFIGURATION=Release BUILD_SOLUTION=false ./scripts/coverage-solution.sh --group web --output TestResults/coverage-groups/web
./scripts/coverage-solution.sh --list-groups
./scripts/coverage-solution.sh --merge-only TestResults/coverage-groups --output TestResults/coverage-merged

Available bounded groups are core, tools, web, docs, razorwire, and integration.

Default PR validation keeps solution coverage in one lane until measured group runs prove they reduce total GitHub Actions minutes, not just wall-clock time.

The current CI critical-path policy and timing baseline live in eng/ci-critical-path.md.

When tests need to launch child processes, prefer CliWrap-backed helpers over raw System.Diagnostics.Process setup. Keep process-output capture in the helper result so CI failures include stdout and stderr, and reserve raw Process usage for tests that intentionally exercise process-wrapper behavior.

Check out the examples to see how modules are composed in practice:

dotnet run --project examples/auth-aspnetcore-bridge
dotnet run --project examples/console-app
dotnet run --project examples/flow-approval-local/FlowApprovalLocalExample.csproj
dotnet run --project examples/product-readiness-lab/ProductReadinessLab.csproj -- --report
aspire run --non-interactive --apphost examples/product-readiness-lab-apphost/ProductReadinessLabAppHost.csproj -- verify
dotnet run --project examples/web-app
dotnet run --project examples/razorwire-mvc/RazorWireWebExample.csproj

For the intentional validation-failure shape, run dotnet run --project examples/config-validation.

The RazorWire MVC example includes a failed-form UX page at /Reactivity/FormFailures that shows server-handled validation, development anti-forgery diagnostics, default fallback rendering, and consumer styling hooks.

Release notes and upgrade policy

AppSurface is preparing to release the entire monorepo in unison. The public release contract now lives in the repository so teams can see what is queued for the next version, how pre-1.0 changes are handled, and where future migration notes will live.

Feedback and contributing

AppSurface uses GitHub issue forms to keep bug reports, feature requests, and docs/developer-experience feedback concrete enough to reproduce or evaluate. If an example, README, quickstart, or package API leaves you stuck, start with the contribution guide, choose an issue template, and file the form that matches the problem.

Use docs/DX feedback for confusing guidance, missing concepts, broken links, snippet drift, or first-run friction. Use feature requests for focused product capabilities, API shapes, workflows, or examples. Use bug reports when runtime behavior, generated output, or package APIs do something unexpected. Do not file suspected vulnerabilities, leaked secrets, or exploit details in public issues; follow the security policy instead.

Examples

The examples directory contains sample applications that demonstrate how to use this project.

  • Auth ASP.NET Core bridge example – proves an ASP.NET Core host-owned auth stack can flow named policy results into AppSurface auth contracts.
  • Console app example – builds a simple command line application using CliFx source-generated command descriptors.
  • Aspire AppHost example – shows local Aspire AppHost composition with AppSurface profiles and reusable Aspire components.
  • Web app example – shows a minimal ASP.NET Core app that composes middleware and endpoints from modules.
  • Web error-page proof – runs a one-command verifier for AppSurface Web browser status pages, production exception pages, and API-friendly non-HTML behavior.
  • Config validation example – shows scalar validation on a strongly typed config wrapper and the startup failure shape.
  • Local secrets example – shows OS-backed local secret posture, CLI setup, provider precedence, and paste-safe diagnostics for solo development before a remote vault exists.
  • Flow approval local example – shows a typed flow that waits for an approval event and resumes through the in-memory runner.
  • Product readiness lab – runs a composed local evaluator with AppSurface Web, Auth.AspNetCore, Flow, DurableTask-facing host-shape guidance, and Postgres product-state proof. Use its AppHost verify profile when the report should exercise Postgres and require that row to be proven-locally.

License

AppSurface is licensed under the Polyform Small Business License 1.0.0.

Free for individuals and businesses with fewer than 100 people and under $1,000,000 USD prior-year revenue (inflation-adjusted). Larger companies require a commercial license.

About

AppSurface is a modular application-surface framework for .NET.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors