# [Authority]

> What an app that imports your entity may do to the `security { }` you declared on it. The default is `Narrowable` — the app may state its own rules and they are conjoined with yours, so it can only ever tighten them. `Owned` means the app may not state rules at all; `Extensible` means its rules are ORed with yours, so it may grant more than you did.

<!-- id: security-contribution-authority · area: security · stability: stable · html: https://osysharp.com/reference/security/contribution-authority/ -->

## Summary        {#summary}
When you ship an entity in a capability or a package, the app that imports it can re-open it with
`partial entity X { security { … } }` and state its own rules. `[Authority]` is how you say what that is allowed to
do to yours.

The default, which you get by writing nothing, is **`Narrowable`**: the app's rules are **conjoined** with yours. It
may add conditions, deny things you allowed, and scope rows further — and it cannot grant a verb you withheld or
widen a row filter you wrote. A grant that would exceed yours is refused at the line the app's author wrote it on.

## Signature      {#signature}

```osy syntax
[Authority(Narrowable)]   // the default: the app may only tighten your rules
[Authority(Owned)]        // the app may not declare security for this entity at all
[Authority(Extensible)]   // the app's rules are ORed with yours — it may grant more than you did
```

It goes on the entity, beside `sealed`, and it is capability- and package-authoring surface: an app writing it on
its **own** entity is refused, because an entity with no consumer has no authority to hand out.

## Description    {#description}

### Why it is a declaration
`sealed` already lets you say *"you may not derive a new type from me"* — and says nothing about what happens to
your rules when somebody re-opens your entity. This is the same idea one axis over: `sealed` generalised from
extensibility to security.

Only you can answer it, because it depends on what you meant. Two capabilities in this platform mean opposite
things by the same-looking block:

- `Osysharp.Locale`'s `Culture` grants `read` to everyone and withholds every write **on purpose** — the rows are a
  reflection of the culture pack declared in source, reconciled from it on every compile, so a row an app created
  would simply disappear. Nothing an app can say makes writing it meaningful. That is `Narrowable` (the default).
- `Osysharp.SourceBudget`'s `UserSourceBudget` grants `read` of a person's own cap, and the widening is the point:
  an app whose owners set somebody's cap adds the `create` and `update` that do it. That is `Extensible`, declared.

⚠ **AND THE TEST IS "CAN AN APP MEAN SOMETHING BY IT?", NOT "WOULD I DO THAT?"** The per-user budget —
[`Osysharp.SourceBudget`](https://osysharp.com/reference/config/source-budget/)'s `UserSourceBudget` — shipped `Narrowable` on the reading that
*"a user may see their own cap and may not raise it"*, and that was the wrong axis: the person is not the app. The
capability ships an override table precisely so an app owner can set somebody's cap, and under `Narrowable` **no
Osy# whatsoever could write it** — the only remedy the compiler itself named, a `partial entity` granting
`create`, was refused by the next pass. It is `Extensible` now. A withholding whose only remedy is refused elsewhere is a dead end rather than a gate; if you
find yourself there, the question to ask is whether the thing you are protecting the app FROM is really the app.

### What `Narrowable` actually does
The platform derives a **floor** from your block: the complement of what you granted, as `deny` rules decided after
every layer an app's own rules sit in. So the conjunction is exact rather than advisory —

- a verb you never granted is denied outright;
- a verb you granted `where F` is denied on every row `F` does not match;
- a verb you granted `when W` is denied for every caller `W` does not hold for;
- and every `deny` you wrote, including a field deny, holds against any rule the app writes.

The app can still narrow further: its own `deny` rules sit below the floor and take effect normally.

⚠ **So an `allow` in the app's `partial` admits nobody new.** A verb you never granted is refused at compile; a verb
you granted to somebody else is not — `partial entity Principal { security { allow update when IsSteward; } }` over
a package that lets people update only their own account compiles, and stewards are still refused. `osy lint` names
that line (`security-partial-allow-grants-nothing`), says what the package does grant, and offers the ways that do
work: widen a policy the package's grant goes through when the app defines it, keep the data on a record of the
app's own, or — if the package is yours — declare it `Extensible`.

**A type you derive from a `Narrowable` one inherits the floor.** An app's `entity Member : User { … }` adds fields,
but a grant in its own block cannot reach past the package's: if the package lets a person update only their own row
(`allow update where Id == user.Id`), the app's desk staff cannot write a field on somebody else's row either —
however the subtype's block reads. Two ways through, and both are ordinary Osy#:

- **the package names a policy for it** — then the app defines that policy to include its staff, and the floor moves
  with it. The refusal names the policy when there is one;
- **the field lives on a record of the app's own** — a `Mentorship { Member Mentee; Member Mentor; }` the app
  grants as it likes, instead of a column on the inherited type. When the write must also change the inherited row,
  raise an event the package listens to, or call the function it ships for that change, and let it write under its
  own grants.

**What the consuming app is told.** A verb you withhold is your decision — orders are records, so a shop package
never grants `delete` on one — and the app that uses your package is not warned about it for its own sake. It hears
about it only when its own code PERFORMS that verb on your table: a `.Delete()` on a row of it, a member set outside
an initializer, a `new`. Then the warning on its `use` line names the verb and the line that does it, because that
is the one case where the runtime refusal would surprise anybody. A table you ship with no rules at all is different:
nobody chose a posture for it, and the app is always told.

⚠ State each verb in **one** rule. If you grant a verb through several rules that condition on different things —
one with a `when`, another with a `where` — the complement cannot be stated exactly, and the compile is refused
rather than given a floor that is quietly wider than your block.

### `Owned`
Nobody but you states the rules. A `partial entity` over it is refused by name, and the app is never asked to
declare security for it. This is the marker an organization's own package carries: rules the apps in that
organization inherit and cannot re-state.

## Examples       {#examples}

A package entity whose rules an app may only tighten — the default, so nothing is written:

```osy title="a table the consuming app may narrow but not widen" test app=security-contribution-authority
app Ledger { }

entity Invoice {
  [MaxLength(40)] string Number;
  [Required] User Customer;
  security { allow read where Customer == user; }
}

[Principal] entity User { [MaxLength(200)] string Email; }
```

Saying the other two out loud:

```osy title="the other two markers, side by side — who may state a rule at all" syntax
// nobody may re-state these rules — an org package's shape
[Authority(Owned)]
public sealed entity OrgUser { … }

// the rule below is the ownership rule; an app may add a support role on top of it
[Authority(Extensible)]
public sealed entity ChatSession { … }
```

## See also       {#see-also}
- [security { }](https://osysharp.com/reference/security/entity-security/) — the `security { }` block, and stating extra access with `partial entity`
- [capability rows that belong to a user](https://osysharp.com/reference/security/capability-row-ownership/) — capability tables whose rows belong to one signed-in user
- [secure by default (deny-all)](https://osysharp.com/reference/security/secure-by-default/) — why a table nobody granted is denied
