# IRunAttributionContext

> 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.irunattributioncontext/
> Summary: IRunAttributionContext: Resolves who a run belongs to and what work it was made for.

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

Resolves who a run belongs to and what work it was made for.

```csharp
public interface IRunAttributionContext
```

## Remarks

This is the sibling of [ITenantContext](/api/tracon.itenantcontext/). The tenant answers
"whose data is this"; this interface answers "which *user* spent
this" and "which *job* was it spent on". Both questions are needed
to break a cost report down further than the tenant.

**The user identity is never read from the request body.**
A `userId` field on `POST /api/agents/{name}/run` would let any
client write spend against another user's name, which forges the cost
record outright. The consumer binds this interface to its own identity
pipeline — a claim, an API key, a resolved principal.

The interface is registered with `TryAdd` and the built-in
implementation returns [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null) for both members unless
[AmbientRunAttributionScope](/api/tracon.ambientrunattributionscope/) is active, so an application that
registers nothing keeps its current behaviour exactly: the columns stay
NULL and nothing else changes.

The implementation must be a **singleton**, for the same
reason as [ITenantContext](/api/tracon.itenantcontext/): singleton services take a
dependency on it, and a scoped registration would be a captive dependency.
Resolve per-request state through `IHttpContextAccessor`.

**Tenant behavior — TENANT-INDEPENDENT.** This interface
carries no tenant parameter and applies no tenant filtering of its own:
attribution (who spent this, what job was it for) is a per-request
identity concern, orthogonal to [ITenantContext](/api/tracon.itenantcontext/)'s
data-tenancy concern. It answers "which user", never "which tenant".

## Properties

### <a id="Tracon_IRunAttributionContext_Labels"></a> Labels

Gets the labels of the current run, or [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null) when
there are none.

```csharp
IReadOnlyDictionary<string, string>? Labels { get; }
```

#### Property Value

 [IReadOnlyDictionary](https://learn.microsoft.com/dotnet/api/system.collections.generic.ireadonlydictionary\-2)<[string](https://learn.microsoft.com/dotnet/api/system.string), [string](https://learn.microsoft.com/dotnet/api/system.string)\>?

#### Remarks

Labels are a **query dimension, not a metric dimension**.
They live in the `runs` table and are never added to
`tracon.tokens` or `tracon.run.cost`: turning a free
label set into a metric dimension blows up time-series cardinality. The
same rule applies to [IRunAttributionContext.UserId](/api/tracon.irunattributioncontext/#Tracon_IRunAttributionContext_UserId).
The limits are on [RunLabels](/api/tracon.runlabels/).

### <a id="Tracon_IRunAttributionContext_UserId"></a> UserId

Gets the user the current run belongs to, or [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null)
when it is unknown.

```csharp
string? UserId { get; }
```

#### Property Value

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

The value is an **opaque string**. Tracon neither
resolves nor validates its meaning, and stores no personal detail of
its own; the consumer decides what the identifier means. This is the
same stance `IDataSubjectResolver` takes, and the data
subject erasure flow covers this column too.
