# capability rows that belong to a user

> Some capability tables hold rows that belong to one signed-in user — a chat conversation is yours, not the app's. Those capabilities scope every read and write to your app's `[Principal]` automatically, with no rule for you to write. The one thing they need from you is a `[Principal]` entity to scope to: an app that imports such a capability and declares none is a compile error, because a row owned by a user has no meaning in an app with no users.

<!-- id: security-capability-row-ownership · area: security · stability: stable · html: https://osysharp.com/reference/security/capability-row-ownership/ -->

## Summary        {#summary}
A capability can ship tables whose rows belong to **one signed-in user**. Conversation memory is the clearest case:
a chat session is *yours*, and another user of the same app must not be able to list or open it.

Those capabilities carry that rule themselves. You do not write it, you cannot forget it, and it holds wherever the
read comes from — a page, a function, an MCP tool. What the capability cannot supply is **who the users are**: that
is your app's `[Principal]` entity. So the one requirement is that you declare one.

```osy syntax
using Osysharp.Agents;

[Principal] entity User { string Email; }     // ← the only thing the capability needs from you

// Nothing else. Every ChatSession read and write is already scoped to the signed-in user.
```

## Signature      {#signature}
```osy syntax
using Osysharp.Agents;                     // a capability whose rows are owner-scoped
[Principal] entity User { … }                 // REQUIRED — the type its rows are scoped to
```

Importing such a capability without a `[Principal]` is refused at compile time, naming the capability and both ways
out:

```text
`using Osysharp.Agents;` brings in 'ChatSession', whose `UserId` belongs to a signed-in user — and this app
declares no `[Principal]` entity, so there is no user for it to belong to. Either declare one
(`[Principal] entity User { … }`), or drop `using Osysharp.Agents;`.
```

## Description    {#description}
Under [secure by default (deny-all)](https://osysharp.com/reference/security/secure-by-default/) a table nobody has granted access to is denied to everyone. A capability's
owner-scoped tables come with their grant already written — *this row is reachable by the principal it belongs to* —
so opting in gives you a working, private store rather than a locked one.

Three consequences worth knowing:

- **The rule is on the entity, not on the route.** It is enforced by the read engine, so a conversation you do not
  own is absent from a query, not merely hidden by whichever endpoint you went through. Reaching the same table from
  a function or a tool gets the same answer.
- **An unowned row is reachable by nobody.** If a row is written with no owner, it does not match the rule for any
  principal — it is not shared, it is stranded. Anonymous callers own nothing, so a feature that must work signed-out
  needs a different store.
- **You can grant MORE, never less.** A `partial entity` block of your own lands *alongside* the capability's rule
  and the grants combine, so you can add a support role that reads every conversation. You cannot use it to take the
  owner's access away.

⚠ **That combining rule is specific to this mechanism.** A row that is *part of* another row
([rows that are part of another row](https://osysharp.com/reference/security/part-of-derived-access/)) composes the other way: declaring a block there REPLACES the derived rule
rather than adding to it. The two differ because the questions do — a capability's own grant is a promise it makes
about its rows and yours cannot revoke it, while a derived rule is only a default standing in for a decision you had
not made yet.

```osy syntax
// Add to what the capability already grants — the owner's access stays.
partial entity ChatSession {
  security { allow read when user.IsSupportAgent; }
}
```

## Examples       {#examples}
An app that opts into conversation memory. There is no security block for `ChatSession` anywhere in it — the
capability brought its own, and the `[Principal]` is what it scopes to:

```osy title="opting in, with the one thing it requires" test app=security-capability-row-ownership
app Helpdesk { use Osysharp.Agents; }

using Osysharp.Agents;

// The capability needs a principal type to scope its rows to. Without this the compile is refused.
[Principal] entity User { string Email; }

entity Ticket {
  string Subject;
  security { allow read, create where Reporter == user; }
  User Reporter;
}
```

## See also       {#see-also}
- [Reading a capability's source](https://osysharp.com/reference/local/reading-a-capability/) — what a capability declares, and how to read its source
- [security { }](https://osysharp.com/reference/security/entity-security/) — the `security { }` block, and stating extra access with `partial entity`
- [secure by default (deny-all)](https://osysharp.com/reference/security/secure-by-default/) — why a table nobody granted is denied
