Skip to main content

NuGetGallery

Overview

NuGetGallery is the primary web application for nuget.org. It is an ASP.NET MVC 5 / Web API 2 project targeting .NET Framework 4.7.2, hosted on IIS via an OWIN pipeline. The application handles every user-facing concern: browsing and searching packages, uploading and deleting packages via the NuGet push API, managing user accounts and organizations, rendering package detail pages, serving the legacy V2 OData feed consumed by older NuGet clients, and providing a restricted admin panel. The project is built as a Library assembly (OutputType=Library) deployed to IIS — the OWIN startup class (OwinStartup) is detected automatically by the Microsoft.Owin.Host.SystemWeb host. All request routing flows through this single process; background work (validation, indexing, catalog) is handled by separate job executables.
The web application operates in two modes: full mode (UI + API) and feed-only mode (feedOnlyMode=true), in which all MVC UI routes are suppressed and only the V2 OData API is registered. Feed-only mode exists to allow separate “feed-only” deployments behind the load balancer that serve NuGet client traffic without serving browser traffic.

Role in the NuGetGallery Ecosystem

Upstream: Package Authors

Authors push .nupkg files via the V2 push API (PUT /api/v2/package). The gallery validates the upload, persists it to Azure Blob Storage in the uploads container, and enqueues a validation message via AsynchronousPackageValidationInitiator.

Downstream: Validation Orchestrator

After a push, the gallery writes a PackageValidationSet record and sends a Service Bus message. The Validation Orchestrator drives the package through validators until it is marked Available or FailedValidation.

Downstream: Search / Azure Search

The gallery calls out to an external search service (Azure AI Search via NuGet.Services.AzureSearch) for package search. The IHijackSearchServiceFactory pattern redirects OData V2 queries to the search service to avoid database fan-out.

Downstream: Db2Catalog / V3 Feed

When a package is made Available, its LastEdited timestamp is updated. The Db2Catalog job detects this change and publishes the package into the V3 NuGet protocol (registration, flat container, catalog).

Request Pipeline

The OWIN pipeline is bootstrapped in this order by OwinStartup.Configuration:
  1. ServicePointManager tuning (connection limits, TLS 1.2, Nagle disabled).
  2. Autofac DI container composition (AutofacConfig.UseAutofacInjection) — scans the assembly for IAutofacModule registrations and all controller types.
  3. OWIN cookie authentication middleware (CookieAuthenticationMiddleware) with sliding expiration.
  4. External OAuth providers (AAD v2, Microsoft Account) registered via Authenticator subclasses.
  5. MVC route table (Routes.RegisterRoutes) and Web API (WebApiConfig.Register) with OData V1/V2 endpoints.
  6. Feature flag service wired through NuGet.Services.FeatureFlags.

Key Files and Classes

File PathClass / InterfacePurpose
App_Start/OwinStartup.csOwinStartupOWIN entry point; configures TLS, Autofac, cookie auth, external providers, and routes. Auto-detected by [assembly: OwinStartup].
App_Start/AutofacConfig.csAutofacConfigBuilds the Autofac ContainerBuilder; registers MVC controllers (property injection), Web API controllers, model binders, filter providers, and all IAutofacModule modules found in the assembly.
App_Start/Routes.csRoutesRegisters all MVC UI routes and the V2 OData/Web API routes. Supports feedOnlyMode to suppress UI routes.
App_Start/DefaultDependenciesModule.csDefaultDependenciesModuleMain Autofac module wiring all service interfaces to their implementations (package services, file storage, search, auditing, messaging, telemetry, etc.).
Controllers/ApiController.csApiControllerHandles NuGet V2 push (PUT), delete (DELETE), deprecate, and autocomplete endpoints. Validates API keys, calls PackageUploadService, initiates validation.
Controllers/PackagesController.csPackagesControllerMVC controller for all package-related UI: listing, detail page, upload wizard, readme/license rendering, owner management, deprecation UI, vulnerability display.
Controllers/AuthenticationController.csAuthenticationControllerLogin/logout, external OAuth sign-in (AAD v2 / MSA), password reset, two-factor authentication, account linking.
Controllers/AccountsController.csAccountsController<TUser,TVM>Abstract base for UsersController and OrganizationsController; handles profile, email confirmation, security policy, certificate management.
Controllers/ODataV1FeedController.csODataV1FeedControllerNuGet V1 OData feed (/api/v1). Largely legacy; thin wrapper around NuGetODataController.
Controllers/ODataV2FeedController.csODataV2FeedControllerNuGet V2 OData feed (/api/v2). Primary feed consumed by nuget.exe and older SDK clients. Implements search hijacking via IHijackSearchServiceFactory.
Controllers/StatisticsController.csStatisticsControllerServes download statistics pages fed by IStatisticsService (backed by pre-built JSON reports in blob storage).
GalleryConstants.csGalleryConstantsCentral repository of string constants, regex patterns for email/username validation, size limits, header names, and content type names.
Infrastructure/HijackSearchServiceFactory.csHijackSearchServiceFactoryDecides at request time whether to redirect an OData V2 query to the external search service or fall back to direct database queries.
Infrastructure/Lucene/Lucene indexing helpersLocal Lucene.NET index used for IIndexingService in development / fallback scenarios. Not used in production.
OData/NuGetODataController.csNuGetODataControllerBase class for OData feed controllers; handles $format, DataServiceVersion headers, and search adapter integration.
OData/QueryInterceptors/Query interceptorsNormalizeVersionInterceptor, PackageHijackingInterceptor, CountInterceptor — applied to LINQ queries before EF materializes them.
Services/PackageUploadService.csPackageUploadServiceValidates a new package upload (ID, version, license, readme, icon, typosquatting, metadata validation) and commits to the DB and blob storage.
Services/ValidationService.csValidationServiceWraps AsynchronousPackageValidationInitiator; called after a successful upload to kick off the validation pipeline.
Services/PackageDeleteService.csPackageDeleteServiceHard- and soft-delete logic, including audit trail creation and removal of blob files.
Services/TyposquattingService.csTyposquattingServiceChecks a new package ID against a cached list of existing IDs for confusable-character similarity; blocks uploads that would typosquat.
Services/ReadMeService.csReadMeServiceReads, renders, and writes README files from blob storage; converts Markdown to sanitized HTML for display.
Telemetry/ITelemetryService.csITelemetryServiceDeclares all Application Insights custom events and metrics tracked by the web app (uploads, searches, OData queries, auth events, etc.).
Filters/MVC / WebAPI filtersApiKeyThrottle, ReadOnlyModeErrorFilter, AntiForgeryErrorFilter, RequiresAccountConfirmationFilter — applied globally or per-controller.

Dependencies

Internal Project References

ProjectRole
NuGetGallery.CoreEntitiesContext, ICoreFileStorageService, ICorePackageService, IAuditingService, PackageMetadata, blob wrappers
NuGetGallery.ServicesExtended service layer (IPackageService, IUserService, IOrganizationService, security policies, search)
NuGet.Services.EntitiesEF entity types (Package, PackageRegistration, User, Organization, Certificate, etc.)
NuGet.Services.ConfigurationIGalleryConfigurationService, ISecretInjector (KeyVault-backed config)
NuGet.Services.ContractsShared interface contracts across NuGet services
NuGet.Services.FeatureFlagsIFeatureFlagService — runtime feature toggles stored in blob storage
NuGet.Services.KeyVaultAzure KeyVault secret resolution at startup
NuGet.Services.LicensesLicense expression parsing and SPDX validation
NuGet.Services.LoggingApplication Insights integration (TelemetryClientWrapper)
NuGet.Services.Messaging / Messaging.EmailIMessageService, async email enqueuing via Service Bus
NuGet.Services.OwinShared OWIN middleware helpers
NuGet.Services.ServiceBusService Bus topic/subscription clients for validation initiation
NuGet.Services.SqlIDbConnectionFactory, retry-aware SQL connection management
NuGet.Services.Validation / Validation.IssuesPackageValidationSet entity, validation status enums, issue types

Key NuGet Package Dependencies

PackagePurpose
Autofac / Autofac.Mvc5 / Autofac.WebApi2DI container; property injection on MVC controllers
Microsoft.AspNet.WebApi.OData / Microsoft.Data.ServicesOData V1/V2 feed infrastructure
EntityFrameworkORM for Gallery SQL Server database (code-first, migrations disabled at runtime)
Microsoft.Owin.* / OwinOWIN host pipeline, cookie auth, external OAuth
NuGet.Packaging.nupkg reading, .nuspec parsing during upload
NuGet.VersioningNuGetVersion, VersionRange parsing and comparison
Lucene.Net / Lucene.Net.ContribLocal search index (development/fallback only)
HtmlSanitizerStrips unsafe HTML when rendering README/markdown content
CommonMark.NETMarkdown-to-HTML conversion for README rendering
Microsoft.ApplicationInsights.WebRequest telemetry, dependency tracking, exception logging
NuGet.StrongName.elmahError Logging Modules and Handlers for unhandled exception capture
Strathweb.CacheOutput.WebApi2Output caching on OData feed endpoints
WebGrease / Microsoft.AspNet.Web.OptimizationCSS/JS bundling and minification

Notable Patterns and Implementation Details

Search hijacking on OData V2. Rather than letting all FindPackagesById, Search, and GetUpdates OData queries hit SQL Server, ODataV2FeedController uses IHijackSearchServiceFactory to redirect eligible queries to the Azure Search-backed search service. The decision is logged via the internal X-NuGet-CustomQuery response header, and telemetry tracks whether each query was hijacked or fell back to the database.
Property injection on MVC controllers. Unlike Web API controllers (which use constructor injection), MVC controllers in this project use Autofac property injection (PropertiesAutowired()). This is a legacy pattern from when the project used an older DI approach; public service properties on controller classes are set by the Autofac container.
Migrations are disabled at runtime. EntitiesContext calls Database.SetInitializer<EntitiesContext>(null) in its static constructor. Schema changes are applied by a separate DatabaseMigrationTools executable — never by the running web application. Running EF migrations against a live production database from within the app would be unsafe at scale.
Feed-only mode. When feedOnlyMode=true is passed to Routes.RegisterRoutes, only the V2 OData routes and a stub home route (returning HTTP 200 for health probes) are registered. This allows Azure Load Balancer nodes that handle only client protocol traffic to run a leaner version of the app without the full MVC UI surface.
OWIN on .NET Framework 4.7.2. The application uses Microsoft.Owin.Host.SystemWeb to run the OWIN pipeline inside an IIS/ASP.NET integrated pipeline — not Kestrel. This means the host is bound to Classic System.Web infrastructure (HttpContext, HttpResponse) and cannot be moved to ASP.NET Core without significant rearchitecting.
Typosquatting protection. TyposquattingService uses a cached list of existing package IDs (ITyposquattingCheckListCacheService) and a confusable-character mapping to block uploads that would appear visually similar to popular packages. The check runs synchronously during the upload validation phase before the package is committed to the database.