Skip to content

Installation

Affiant ships as ten NuGet packages, all versioned together, targeting net10.0. You install only the ones your project needs — the dependency graph is a strict DAG rooted at Affiant.Abstractions, so referencing a higher package pulls in everything beneath it automatically.

  • The .NET 10 SDK. All ten packages target net10.0.
  • For a host application, one of Semantic Kernel, Microsoft Agent Framework, or Microsoft.Extensions.AI. Affiant’s interception seam is its own: the IToolInvocationFilter chain — the interface itself lives in Affiant.Abstractions — that Affiant.Core‘s ToolInvocationPipeline runs. Each adapter package bridges one of these three orchestrators’ tool-calling pipeline into it, so your project needs one of them already wired up (or one you’re about to wire up alongside Affiant — see Quickstart, which walks the Semantic Kernel path, and Interception Backends for how the other two differ). A contract-only project that implements Affiant.Abstractions interfaces and nothing else needs no orchestrator at all — see the scenarios below.
  • If you plan to use Affiant.EntityFramework for durable storage, a database: SQLite for local development, PostgreSQL for production. Those are its two SQL backing stores; a third selection, ef.UseInMemory(), keeps chat sessions in memory and registers no IDocketStore. Affiant.Docket itself needs no database — it ships an in-memory store and the backend-neutral expiry sweep, and it is what you install for that sweep whichever persistence backend you choose; a host that schedules the sweep itself may omit it (see below).

Every install command below uses --prerelease. Affiant has not reached a stable 1.0.0 release yet — read the version note at the bottom of this page before you pin a version in a production project.

Package What it’s for Depends on
Affiant.Abstractions Every primitive type (Affidavit, ProvenanceTag, ToolEnvelope, DocketEntry, …) and every backend-neutral contract a host implements (IWriteExecutor, IFieldMapper<T>, IDocketStore, IApprovalPolicy, …). Three packages carry an adapter-local interface of their own besides: Affiant.SemanticKernel (IManualToolInvoker), Affiant.Extensions.AI (IAffiantWrappedFunction) and Affiant.Transport.SignalR (IAffiantHubClient). The abstract classes a host derives from live outside it too — StandingOrderBase, ReferralRuleBase and RiskScoreCalculatorBase in Affiant.Policies, ContextExtractor in Affiant.Core, and AffiantHub in Affiant.Transport.SignalR (what AddAffiantSignalR<THub> constrains THub to). Zero Affiant dependencies. (none)
Affiant.Core Concrete services: ContextFabric, ReviewGate, task-inference merge, the deterministic short-circuit, DI wiring (AddAffiantCore, AddAffiantTool<T>). Abstractions
Affiant.SemanticKernel The Semantic Kernel (SK) interception bridge — wires Affiant’s neutral pipeline into SK’s IFunctionInvocationFilter and IAutoFunctionInvocationFilter positions, per-provider connector capabilities, structured-output inference (AddAffiantSemanticKernel, AddAffiantInferenceOrchestration). Core
Affiant.AgentFramework The Microsoft Agent Framework (MAF) interception bridge — reflects a tool type into an AffiantToolCatalog, attaches via AIAgent.WithAffiant(...), and runs the hosted-tool coverage audit (AddAffiantAgentFramework). Core
Affiant.Extensions.AI The Microsoft.Extensions.AI (M.E.AI) interception bridge — wraps each AIFunction and attaches via ChatOptions.WithAffiant(...), for hosts that talk to IChatClient directly with no agent framework (AddAffiantExtensionsAI). Core
Affiant.EntityFramework EF Core persistence for chat sessions and dockets — row-per-message schema, and the SQLite/PostgreSQL IChatSessionStore and IDocketStore implementations (AddAffiantEntityFramework). Abstractions, Core
Affiant.Docket The review queue’s backend-neutral half — the in-memory IDocketStore plus the expiry sweep (AddAffiantDocket). Needed for that sweep even when the actual store comes from Affiant.EntityFramework; a host that runs the sweep on its own schedule may omit it. Abstractions, Core
Affiant.Policies The fluent approval policy graph — Standing Orders (pre-authorized auto-approval), Referrals (escalation), and the risk-threshold hook (AddAffiantPolicies). Ships no scoring formula of its own: a Standing Order that declares a RiskThreshold needs a host RiskScoreCalculatorBase. Abstractions, Core
Affiant.Transport.SignalR SignalR streaming transport and the Evidence Card round-trip hub (AddAffiantSignalR<THub>). Abstractions, Core
Affiant.Testing.ComplianceHarness Test helper that proves every write strategy has a paired fixture asserting substantive provenance, not just shape, and — since 1.0.0-beta.3 — runs the protocol’s own conformance suite against the shipped packages, from a rulebook root you vendor and pass in (ConformanceSuite.Run(protocolRoot)); the fixtures and schemas are not inside the package. For your CI, not just ours. Core, Docket, Policies

Pick one of Affiant.SemanticKernel, Affiant.AgentFramework, or Affiant.Extensions.AI — never wire more than one over the same tool catalog. See Interception Backends for how to choose.

Affiant.Docket and Affiant.EntityFramework are peers, not a chain: Affiant.Docket supplies the in-memory store and the expiry sweep — the hosted DocketExpiryService, whose tick runs three bounded phases: it commits the lapsed entries and, for a host that has registered an IStreamingTransport, announces each entry it just expired (DocketExpired), then warns about the entries approaching their deadline (DocketExpiring), then re-broadcasts the still-pending entries’ Evidence Cards — and Affiant.EntityFramework goes in beside it, not instead of it, whenever you want a durable, SQL-backed Docket. All three sends are guarded on that transport: with none registered, a tick commits the lapsed entries and sends nothing. That service owns a schedule, not the expiry rule: an entry past its ExpiresAt reads expired whether or not the sweep has reached it, so a host with no long-lived process to run it — a serverless deployment, a cron entry, a queue worker — can leave Affiant.Docket out and call IDocketStore.ExpireDueAsync on its own cadence, as long as its IDocketStore comes from somewhere. Installing Affiant.Docket alone does not pull Affiant.EntityFramework, EF Core, SQLite, or Npgsql along with it: at every published 1.0.0 version Affiant.Docket depends on Affiant.Abstractions and Affiant.Core and nothing else of Affiant’s, so a fully in-memory host doesn’t carry a database driver it never uses.

An agent host that writes to a real database typically wants the full review path: context extraction and the review gate, one interception bridge, a place to persist chat sessions and docket entries, and a transport for streaming Evidence Cards to a reviewer. This example uses the Semantic Kernel bridge; swap Affiant.SemanticKernel for Affiant.AgentFramework or Affiant.Extensions.AI if your host runs on one of those instead.

Terminal window
dotnet add package Affiant.Core --prerelease
dotnet add package Affiant.SemanticKernel --prerelease
dotnet add package Affiant.EntityFramework --prerelease
dotnet add package Affiant.Docket --prerelease
dotnet add package Affiant.Transport.SignalR --prerelease

Add Affiant.Policies if you want Standing Orders (auto-approval for low-risk writes) or Referrals (escalation to a different reviewer), rather than routing every Affidavit to a reviewer unconditionally:

Terminal window
dotnet add package Affiant.Policies --prerelease

The package brings the policy graph, not a risk formula. AddAffiantPolicies registers a placeholder RiskScoreCalculatorBase whose ComputeAsync throws an InvalidOperationException naming the registration that is missing, so a Standing Order that declares a RiskThreshold needs a scorer of your own, registered with SetRiskScoreCalculator<T>() inside AddAffiantPolicies(...). Wire one and the order can auto-approve; leave it out and the startup validator refuses the host, rather than letting an order that can never fire look wired.

If you’re implementing a framework interface — a custom IDocketStore, an IFieldMapper<T> for your domain model, an IWriteExecutor — without needing any of the concrete services, Affiant.Abstractions alone is enough. It has zero Affiant dependencies.

Terminal window
dotnet add package Affiant.Abstractions --prerelease

To verify that your write strategies produce substantive provenance — not just structurally-shaped Affidavits — add the compliance harness to your test project:

Terminal window
dotnet add package Affiant.Testing.ComplianceHarness --prerelease

After adding packages, a restore should pull in your chosen interception bridge’s own dependencies and, for Affiant.EntityFramework, the EF Core provider packages (SQLite and PostgreSQL) transitively — you don’t need to add those yourself. At 1.0.0-beta.3 that SQLite provider brings a transitive SQLitePCLRaw.lib.e_sqlite3 with a published high-severity advisory, so the restore also emits NU1903; a build that treats NuGet audit warnings as errors will stop there until you apply your usual audit policy to it.

Pin every Affiant package in the project to the same version, and check that pin whenever you bump one. The packages declare their Affiant dependencies as minimum-version floors (version="1.0.0-beta.3", not an exact [1.0.0-beta.3] range), so the two halves of a partial upgrade fail in opposite ways. Bump an adapter and leave Affiant.Core behind and the restore fails outright — error NU1605: Warning As Error: Detected package downgrade: Affiant.Core from 1.0.0-beta.3 to 1.0.0-beta.1, naming both packages and both versions, before anything compiles. Bump Affiant.Core and leave the adapter behind and the restore and the build both succeed with no diagnostic at all: that skew is silent, which is why the pin is worth keeping deliberate.

If your host boots without registering an IDocketStore or IStreamingTransport at all — neither Affiant.EntityFramework nor Affiant.Docket’s in-memory store, or no transport package — AddAffiantCore()’s startup validator throws an AffiantStartupException at boot, naming the missing registration and which package supplies it, rather than failing silently at the first write. A third contract joins that same list conditionally: register a tool whose operation is update-shaped and leave IPreviousValueSource unregistered, and the exception names it too — an update-shaped Affidavit swears to the value each field replaces, and only your own system of record knows it — with AddPreviousValueSource<TSource>() named as the fix. Two things change that. Setting AffiantCoreOptions.AcknowledgeMissingReviewWiring = true downgrades it to one startup warning per missing contract — but only for a host with no unresolved gate wiring of its own; when a write-capable tool is registered and IReviewContextProvider, ReviewGate or IDecisionAuthorizationPolicy is missing, or a registered approval policy reports a configuration fault of its own (the Standing Order with a RiskThreshold and no scorer above), the acknowledgment does not apply and the boot still fails. And a DI container that does not provide IServiceProviderIsService skips all of that — the missing-contract checks and the policy-configuration check alike — logging one debug line instead: the validator cannot enumerate registered contracts there without resolving them. The review-deadline check runs first in either case, so a DefaultDocketTtl under a millisecond — or one large enough to overflow the deadline stamp — fails the boot even in a container that skips the rest.

All ten packages release under one shared version number — if you install Affiant.Core at a given version, install every other Affiant package you reference at that same version. The framework has not reached a stable 1.0.0 yet, and the public API surface — type shapes, DI extension signatures, package boundaries — may still change before it does. Pin an exact version in any project you deploy, and re-test after every bump. Once you’ve installed what you need, continue to Quickstart to wire up a first write tool end to end.