> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cordango.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Roles and access

> Who may read and write what.

Access is expressed as roles, and a role is a set of grants, entity by entity.

```yaml theme={null}
role: viewer
name: Investor / Viewer
description: Read-only access to the plan, for investors and advisors.
grants:
  scenario:
    read: true
  period:
    read: true
  hiring_line:
    read: true
```

## No inheritance, no wildcards

An entity a role does not name is an entity that role cannot touch. There is no `*`, and no role
extends another.

This is more typing, and it buys the thing worth buying: reading one role file tells you exactly
what that role can do. You never have to resolve a chain to find out, and a new entity is not
silently readable by everyone the moment it is added.

<Warning>
  Adding an entity does not add it to any role. Until you grant it, nobody but the owner and a
  platform administrator can see it. That is the intended direction to fail in.
</Warning>

## What enforcement actually does

Grants are checked on every operation, through the API as well as through the interface. Two
behaviours are worth knowing because they are not what people expect.

**Read responses drop fields the role may not read.** Not an error, just absent.

**A list silently drops a filter on a hidden field, but an aggregate refuses it.** These look
inconsistent and are not. A list returning fewer rows leaks nothing. An aggregate that quietly
ignored a `groupBy` would hand back a confidently wrong number, so it answers 403 instead.

## 404 and 403 mean different things

<ResponseField name="404" type="no relationship">
  You have no relationship to this app at all. It answers exactly as a missing app does. Whether an
  app exists is not something an unrelated caller gets to learn.
</ResponseField>

<ResponseField name="403" type="the role denies it">
  You have a relationship, and the roles it grants do not permit this operation.
</ResponseField>

## Writing them

```bash theme={null}
cordango apply ops.json --app support --scope access
```

`upsert_role` and `remove_behaviour` are the operations. Or edit `roles/*.cordango.yaml` directly.

```bash theme={null}
cordango vocabulary role
```

## In a generated application

A standalone application generated by `cordango build` carries its own users, authentication and
permissions, built from the same role definitions. The enforcement is a second implementation of
the same rules, pinned by hand-written fixtures that both products assert against.

Same roles, same grants, no shared code path. That is on purpose: two implementations that must
agree catch mistakes one implementation cannot.
