Sylin.Koan.Identity.Tenancy 1.0.74

dotnet add package Sylin.Koan.Identity.Tenancy --version 1.0.74
                    
NuGet\Install-Package Sylin.Koan.Identity.Tenancy -Version 1.0.74
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Sylin.Koan.Identity.Tenancy" Version="1.0.74" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sylin.Koan.Identity.Tenancy" Version="1.0.74" />
                    
Directory.Packages.props
<PackageReference Include="Sylin.Koan.Identity.Tenancy" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Sylin.Koan.Identity.Tenancy --version 1.0.74
                    
#r "nuget: Sylin.Koan.Identity.Tenancy, 1.0.74"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Sylin.Koan.Identity.Tenancy@1.0.74
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Sylin.Koan.Identity.Tenancy&version=1.0.74
                    
Install as a Cake Addin
#tool nuget:?package=Sylin.Koan.Identity.Tenancy&version=1.0.74
                    
Install as a Cake Tool

Sylin.Koan.Identity.Tenancy

Koan's durable Identity × Tenancy bridge. Reference it when an authenticated person should enter a tenant only through a current membership. The package composes request scoping, tenant-role projection, access explanation, and lifecycle closure through the application's existing AddKoan()—there is no bridge-specific setup call.

Install

dotnet add package Sylin.Koan.Identity.Tenancy
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKoan();
var app = builder.Build();
await app.RunAsync();

The application also needs its ordinary Web Auth connector and a Data provider. Identity and Tenancy arrive through the bridge package; add them directly only when application code intentionally uses their public types.

Meaningful behavior

Create a tenant seat as business data:

await new Membership
{
    TenantId = tenant.Id,
    IdentityId = person.Id,
    Roles = { "orders:review" }
}.Save();

An authenticated request can now select that tenant by the default tenant claim, X-Koan-Tenant header, or /t/{tenantCode} path. The bridge verifies the durable person is active and the membership still exists, establishes Tenant.Current, and projects only that membership's tenant roles for the rest of the request. A forged carrier, anonymous caller, inactive person, or removed seat proceeds unscoped; tenant-managed operations then fail closed.

Subdomain routing is available but inert until the application's base hosts are configured.

Configuration

{
  "Koan": {
    "Data": {
      "Tenancy": {
        "Resolution": {
          "ClaimType": "tenant",
          "HeaderName": "X-Koan-Tenant",
          "PathPrefix": "/t/",
          "BaseHosts": [ "app.example.com" ]
        }
      }
    }
  }
}

Carrier order is claim, header, subdomain, then path; the first resolved candidate wins. Every carrier always requires an active durable member. Invalid empty carrier settings fail host startup through standard .NET options validation. Startup reporting lists the effective carriers and says whether subdomain routing is live.

Lifecycle closure

DeprovisioningService.RemoveFromTenantAsync(personId, tenantId) removes one seat. DeactivateAsync(personId) marks the person deactivated first, revokes all Koan cookie sessions, then removes every tenant seat. Both return a DeprovisioningReceipt whose content hash can detect later changes with HasValidHash().

These operations are ordered, idempotent Entity writes—not a cross-provider transaction. A receipt is emitted only after the requested workflow completes and attests only to its own recorded fields.

The bridge also contributes automatically to IdentityLifecycleService.EraseAsync. Whole-person erasure removes memberships and tenant-scoped AgentGrant rows for registered tenants, then de-identifies matching deprovisioning receipts and tenancy audit summaries. The final identity-erasure receipt reports this package as Koan.Identity.Tenancy; no extra registration is required.

Invitations (PMC-035)

InviteIssuanceService issues a tenant seat to one email address and returns the raw token once — only its SHA-256 hash is stored. InviteAcceptanceService.AcceptAsync(token, identityId) runs the claim: the signed-in person must own a verified IdentityEmail matching the invitation, and the claim itself is a conditional write on the invitation row (Status == Pending && ClaimedBy == null). Two identities racing one token — on one host or across a fleet — converge to one claimant and one deterministic Membership seat; a claimant whose run was interrupted re-drives the same token idempotently until they complete or an operator revokes. Issuance, revocation, and acceptance are audited through the tenant audit log. Reserved host roles (koan:tenancy-operator) can never travel through an invitation.

var issued = await issuance.IssueAsync(actor, tenantId, "ada@example.com", "editor");

// the invited person, signed in and holding a verified IdentityEmail for that address:
var result = await acceptance.AcceptAsync(issued.Token, identityId);
// result.Outcome: Accepted | AlreadyMember | AlreadyClaimed | NotFound | Expired | Revoked |
//                 EmailNotOwned | ReservedRoleRefused — reported, never thrown (except the
//                 conditional-write guarantee: an adapter without write.conditionalReplace refuses at boot of the ceremony).

Over HTTP, Koan.Tenancy.Web exposes the ceremony: POST /api/tenancy/invitations (issue, operator policy), POST /api/tenancy/invitations/{id}/revoke (operator policy), and POST /api/tenancy/invitations/accept (any authenticated subject).

Boundaries

  • The bridge scopes inbound ASP.NET Core requests. Background work must establish its tenant through the normal captured/explicit Koan context rather than copying a raw header or path value.
  • Membership roles cannot project Koan host-operator roles, and invitations cannot grant them either.
  • Already-issued bearer tokens remain governed by their issuer outside tenant scope; this package does not revoke OAuth tokens or claim global authorization closure.
  • Public/anonymous tenant routing is not a switch on this security boundary and is not currently provided.
  • Tenant suspension and custom-domain ownership verification are not enforced here.
  • Invitation delivery (email send) is the application's job; this package owns the token, the claim, and the seat.

See TECHNICAL.md and the public tenancy guide.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Sylin.Koan.Identity.Tenancy:

Package Downloads
Sylin.Koan.Tenancy.Web

Reference-mounted tenant registry and membership administration for Koan: a bundled operator UI and API with separated exposure/authority controls, idempotent seat grants, and mutation audit.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.74 90 9/13/2026
1.0.70 98 9/13/2026
1.0.68 90 9/13/2026
1.0.66 90 9/12/2026
1.0.62 99 9/12/2026
1.0.59 111 9/10/2026
1.0.54 105 9/10/2026
1.0.49 105 9/9/2026
1.0.44 102 9/9/2026
1.0.39 116 9/9/2026
1.0.37 90 9/9/2026
1.0.31 103 9/9/2026
1.0.28 104 9/9/2026
1.0.22 109 9/5/2026
1.0.21 119 8/30/2026
1.0.20 104 8/30/2026
1.0.19 106 8/28/2026
1.0.18 105 8/28/2026
1.0.17 104 8/28/2026
1.0.16 96 8/28/2026
Loading failed