# TraconSessionOwnershipOptions

> Tracon documentation. Tracon is a .NET package family that adds a control
> plane on Microsoft Agent Framework, published as NuGet packages that run
> inside your own ASP.NET Core application.
>
> Page: https://tracon.dev/api/tracon.traconsessionownershipoptions/
> Summary: TraconSessionOwnershipOptions: Turns per-user session ownership on, a second boundary drawn UNDER the tenant.

<div class="api-provenance">Namespace Tracon · Assembly <code>Tracon.Abstractions.dll</code></div>

Turns per-user session ownership on, a second boundary drawn UNDER the
tenant.

```csharp
public sealed class TraconSessionOwnershipOptions
```

<dl class="api-relations"><dt>Inheritance</dt><dd><a href="https://learn.microsoft.com/dotnet/api/system.object">object</a> ← <a href="/api/tracon.traconsessionownershipoptions/">TraconSessionOwnershipOptions</a></dd><dt>Inherited Members</dt><dd><a href="https://learn.microsoft.com/dotnet/api/system.object.gettype">object.GetType()</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.tostring">object.ToString()</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object)">object.Equals(object?)</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object)">object.Equals(object?, object?)</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.referenceequals">object.ReferenceEquals(object?, object?)</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.gethashcode">object.GetHashCode()</a></dd></dl>

## Remarks

Read from the `Tracon:SessionOwnership` configuration section.

**Default off, and off means nothing changes.** An application
that registers nothing keeps exactly its current behaviour: no owner is
written, no listing is filtered, and `sessions.owner_id` stays NULL.
The whole feature is inert until a deployment asks for it.

This type lives in the abstractions package because the ownership rule is
enforced in four places that do not see each other — the session manager
that stamps the owner (`Tracon.Core`), the listing endpoint that
narrows the query (`Tracon.AspNetCore`), the SQL stores that carry
the column (`Tracon.PostgreSql`, `Tracon.SqlServer`,
`Tracon.Sqlite`) and [ISessionStore](/api/tracon.isessionstore/)'s own contract,
whose [SessionRecord.OwnerId](/api/tracon.sessionrecord/#Tracon_SessionRecord_OwnerId) documentation cannot describe the
rule without naming this type. One shared type keeps them from drifting
apart; the same reason [TraconMcpSecurityOptions](/api/tracon.traconmcpsecurityoptions/) lives
here.

Ownership is **not** a replacement for
[IRunAuthorizationHandler](/api/tracon.irunauthorizationhandler/). The handler answers a question
Tracon asks; this option lets Tracon answer part of it itself, so a
consumer no longer has to keep its own session-to-user table just to build
a "my conversations" list. With ownership on, a handler that used to reject
a whole listing to keep users apart no longer needs to — the listing
arrives already narrowed.

## Constructors

### <a id="Tracon_TraconSessionOwnershipOptions__ctor"></a> TraconSessionOwnershipOptions\(\)

```csharp
public TraconSessionOwnershipOptions()
```

## Fields

### <a id="Tracon_TraconSessionOwnershipOptions_SectionName"></a> SectionName

Gets the configuration section name.

```csharp
public const string SectionName = "Tracon:SessionOwnership"
```

#### Field Value

 [string](https://learn.microsoft.com/dotnet/api/system.string)

## Properties

### <a id="Tracon_TraconSessionOwnershipOptions_Enabled"></a> Enabled

Gets or sets whether a session records the user it belongs to. Default
[false](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool): nothing changes.

```csharp
public bool Enabled { get; set; }
```

#### Property Value

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)

#### Remarks

With this on, a session opened through the HTTP surface is stamped with
the identity [IRunAttributionContext](/api/tracon.irunattributioncontext/) resolves, and
`GET /api/sessions` returns only the caller's own sessions unless
the caller satisfies [TraconSessionOwnershipOptions.ManagementPolicy](/api/tracon.traconsessionownershipoptions/#Tracon_TraconSessionOwnershipOptions_ManagementPolicy).

Rows written while it was off keep a [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null) owner
forever — Tracon cannot invent an owner for a session it did not
watch being opened. Those rows disappear from owner-filtered listings
and stay in management listings. Turning the option on is therefore
safe for existing data, but it is not retroactive.

### <a id="Tracon_TraconSessionOwnershipOptions_ManagementPolicy"></a> ManagementPolicy

Gets or sets the authorization policy whose holders see the WHOLE
tenant's sessions instead of only their own. Default
`TraconPolicies.Operator`.

```csharp
public string? ManagementPolicy { get; set; }
```

#### Property Value

 [string](https://learn.microsoft.com/dotnet/api/system.string)?

#### Remarks

Evaluated per request. A caller who satisfies it gets today's
unfiltered tenant listing — including the unowned rows written before
ownership was turned on, which is the only way those rows stay
discoverable. Every other caller gets a listing narrowed to their own
identity.

**Fail-closed.** If the policy is not registered in the
application's authorization configuration, or evaluating it throws, the
caller is treated as an ordinary user and the listing is narrowed. A
missing policy must not hand out an unfiltered listing — that is the
one failure mode this whole option exists to prevent. Set it to
[null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null) or an empty string to state deliberately that
no caller gets an unfiltered listing over HTTP.

The two failure cases are reported differently. A policy that is not
registered is the documented fallback and writes no log line. A policy
provider or requirement handler that throws writes an error log line in
the `Tracon.SessionOwnership` category, with the policy name and
the exception.

It grants no access to another user's session: reading, deleting and
branching a session that is OWNED by somebody else answers `404`
regardless of policy, because a management role is not a reason to leak
one user's conversation to another. The asymmetry is deliberate — a
listing is an operation with no single identity to leak; an owned
session is a resource whose content belongs to one user.

The one individual access it does govern is
[TraconSessionOwnershipOptions.RefuseUnownedSessions](/api/tracon.traconsessionownershipoptions/#Tracon_TraconSessionOwnershipOptions_RefuseUnownedSessions): an UNOWNED row belongs to nobody,
so there is no user whose conversation could leak, and refusing it to
the same caller who can already see it in the management listing would
leave support looking at a row it cannot open. That exemption covers
reading a session, never continuing one — see that property.

The default is written as a literal because
`TraconPolicies` lives in `Tracon.AspNetCore`, which
this package cannot see, while `Tracon.Core` has to read the
two options above. The two spellings are held together by a test rather
than by the compiler; use the constant, not the literal, when you name
this policy in your own code.

### <a id="Tracon_TraconSessionOwnershipOptions_RefuseUnownedSessions"></a> RefuseUnownedSessions

Gets or sets whether access to an EXISTING session row that carries no
owner is REFUSED. Default [false](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool).

```csharp
public bool RefuseUnownedSessions { get; set; }
```

#### Property Value

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)

#### Remarks

Only consulted while [TraconSessionOwnershipOptions.Enabled](/api/tracon.traconsessionownershipoptions/#Tracon_TraconSessionOwnershipOptions_Enabled) is on; on its own this
setting does nothing at all.

**What it changes.** Rows written before ownership was
turned on carry a [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null) owner forever, and ownership is
not retroactive. By default those rows keep the tenant-wide
reachability they had the day before the flip: they vanish from every
owner-filtered LISTING but are still readable one by one by anybody in
the tenant. Turn this on and they are refused instead — read, delete,
branch and voice answer the endpoint's own "session not found", and a
run that names one is refused with `403`. Listing behaviour does
not change; it already excluded them.

**What it deliberately does not change.** A session that
does not exist yet is untouched: the first turn still opens it and
claims it, which is how every owned session is born. "Not created yet"
and "created without an owner" are different rows and get different
answers.

A caller who satisfies [TraconSessionOwnershipOptions.ManagementPolicy](/api/tracon.traconsessionownershipoptions/#Tracon_TraconSessionOwnershipOptions_ManagementPolicy) still reaches an
unowned row through the session resource endpoints, so support and
audit keep the access they had — the same reason those rows stay in the
management listing. That exemption covers reading a session, not
continuing one: a run that names an unowned session is refused for every
caller, because starting a run appends to the conversation as somebody
else.

Default off because turning it on strands every conversation that was
live at the moment ownership was enabled. Turn it on once those
conversations no longer matter, or in a deployment that enabled
ownership from its first day and therefore has no unowned rows at all.

### <a id="Tracon_TraconSessionOwnershipOptions_RequireAuthenticatedOwner"></a> RequireAuthenticatedOwner

Gets or sets whether a session write is REJECTED when no authenticated
identity can be resolved to own it. Default [true](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool).

```csharp
public bool RequireAuthenticatedOwner { get; set; }
```

#### Property Value

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)

#### Remarks

Only consulted while [TraconSessionOwnershipOptions.Enabled](/api/tracon.traconsessionownershipoptions/#Tracon_TraconSessionOwnershipOptions_Enabled) is on. The rejection is
[TraconSessionOwnerRequiredException](/api/tracon.traconsessionownerrequiredexception/), which the HTTP
surface answers with `403`.

Turning this off is a deliberate weakening, not a convenience: an
unowned session is invisible to every owner-filtered listing, so the
caller writes a conversation they can never list again. It exists for
the mixed deployment that runs owned and unowned traffic side by side
through the same endpoints during a migration.
