Skip to main content

Quick start

This walks through running Heimdall locally, pointing a dotnet nuget client at it, and watching a recent version disappear from the listing.

1. Run Heimdall

The simplest configuration declares one upstream NuGet feed with a 14-day minimum-age rule and an allow-list. Create config.yml:

heimdall:
server:
listen: "http://0.0.0.0:8080"
publicBaseUrl: "http://localhost:8080"
ecosystems:
nuget:
feeds:
- name: strict
upstream: "https://api.nuget.org/v3/index.json"
cacheTtl: "00:10:00"
rules:
- type: minAgeDays
days: "14"
- type: allowDeny
patterns: "Microsoft.*;System.*;Newtonsoft.*;!Internal.*"

Then either run from source:

dotnet run --project src/Heimdall.Api

Or run the container image:

docker run --rm -p 8080:8080 -v $(pwd)/config.yml:/app/config.yml \
ghcr.io/markeli/heimdall:latest

publicBaseUrl is what Heimdall advertises in NuGet @id fields, so set it to the address clients reach Heimdall on. See server for details.

2. Wire up a client

dotnet nuget add source http://localhost:8080/nuget/strict/v3/index.json \
-n heimdall-strict

You now have a NuGet source named heimdall-strict pointing at the strict feed configured above. Try installing a stable, allowed package:

dotnet new console -o demo
cd demo
dotnet add package Newtonsoft.Json --source heimdall-strict

The install succeeds and the .nupkg is streamed through Heimdall. The metadata cache is populated for the configured TTL.

3. See a rule fire

Try to pull a package that does not match the allow-list:

dotnet add package Some.Internal.Lib --source heimdall-strict

The flat-container versions list returns 200 with an empty versions array (filtered), so dotnet reports the package as unfound. If the client requests the binary directly, Heimdall returns 403 ProblemDetails and the body names the rule:

curl -sS -o /dev/null -w '%{http_code}\n' \
http://localhost:8080/nuget/strict/v3/flatcontainer/some.internal.lib/1.0.0/some.internal.lib.1.0.0.nupkg
# 403

Use -D - to inspect the body — it includes ruleName and a human-readable message.

4. Try the age gate

Edit config.yml and bump minAgeDays to a large number (say 9999). The file is hot-reloaded; no restart needed (see Configuration overview). The next request to the versions list will return an empty array because every published version is now too young.

You're ready to move on to the configuration reference or filtering rules.