# gizlang:security

Shared identity, session proof and authorization decisions. Protocols adapt credentials and persistence; they do not define a second permission system.

```gizlang
import * as Security from "gizlang:security";
```

## Types

### `Signed`

```gizlang
type Signed = { who: string; issued: i64; }
```

A signed claim: who it names and when it was issued (ms since 1970).

- `who`: `string`
- `issued`: `i64`

### `Identity`

```gizlang
type Identity = { identity: string; tenant: string; role: string; secret: bytes; }
```

A registered identity: its name, tenant, role and secret.

- `identity`: `string`
- `tenant`: `string`
- `role`: `string`
- `secret`: `bytes`

### `Registration`

```gizlang
type Registration = { principal: Identity; enabled: bool; revokedBefore: i64; revision: i64; }
```

The registration of an identity: whether it is enabled, the time before which its sessions are void, and its revision.

- `principal`: `gizlang_security.Identity`
- `enabled`: `bool`
- `revokedBefore`: `i64`
- `revision`: `i64`

### `Authority`

```gizlang
type Authority
```

Share this authority across DB, HTTP and Agent entrypoints. Registry edits
are immediately visible to every holder; authenticated sessions pin a revision.

Opaque: create and use it through this module's functions.

### `ResourceRef`

```gizlang
type ResourceRef = { space: string; type: string; key: string; }
```

One reference to any record: its Space, the stable declaration of its record
type and its key. Trusted adapters supply the target and all its ancestors,
including itself. Its one text form is SPACE/TYPE/KEY (see ResourceRef.text).

- `space`: `string`
- `type`: `string`
- `key`: `string`

### `Role`

```gizlang
type Role = | { kind: "none"; } | { kind: "viewer"; } | { kind: "commenter"; } | { kind: "editor"; } | { kind: "manager"; } | { kind: "owner"; }
```

A role, from none to owner, in the order none < viewer < commenter < editor < manager < owner.

- `none`
- `viewer`
- `commenter`
- `editor`
- `manager`
- `owner`

### `Principal`

```gizlang
type Principal = | { kind: "person"; id: string; } | { kind: "agent"; id: string; ceiling: Role; onBehalf: string; } | { kind: "group"; team: ResourceRef; } | { kind: "link"; digest: bytes; } | { kind: "public"; }
```

Who acts or holds a grant. A group holds grants and never acts: a person who
holds a role on the group's reference (type `group`) is a member of it.

- `person`
- `agent`
- `group`
- `link`
- `public`

### `Grant`

```gizlang
type Grant = { principal: Principal; target: ResourceRef; role: Role; expires: i64; }
```

A grant of a role on a record to a principal, until `expires` (0 never).

- `principal`: `gizlang_security.Principal`
- `target`: `gizlang_security.ResourceRef`
- `role`: `gizlang_security.Role`
- `expires`: `i64`

### `Decision`

```gizlang
type Decision = { allowed: bool; role: Role; reason: string; }
```

The answer of a Policy decision: whether it is allowed, the role that the actor holds, and the reason.

- `allowed`: `bool`
- `role`: `gizlang_security.Role`
- `reason`: `string`

### `Capability`

```gizlang
type Capability = { principal: Principal; target: ResourceRef; role: Role; expires: i64; revision: i64; }
```

A signed, short-lived permission: `principal` holds `role` on `target` until
`expires`, as decided at policy `revision`. A holder of the key verifies it with
no lookup, so HTTP, S3, the mount and a CDN decide alike.

- `principal`: `gizlang_security.Principal`
- `target`: `gizlang_security.ResourceRef`
- `role`: `gizlang_security.Role`
- `expires`: `i64`
- `revision`: `i64`

## Functions

### `authority`

```gizlang
function authority(): Authority
```

An authority with no principals.

Effects: fail, write.

### `token`

```gizlang
function token(secret: bytes, principal: string, issued: i64): string
```

A session token for PRINCIPAL issued at ISSUED (milliseconds), signed with SECRET (HMAC-SHA-256).

Effects: fail.

### `verified`

```gizlang
function verified(secret: bytes, text: string, now: i64): Option<Signed>
```

The principal and issue time of the token TEXT when its signature with SECRET is valid, else none.

Effects: fail.

### `validSession`

```gizlang
function validSession(signed: Signed, enabled: bool, revokedBefore: i64, now: i64): bool
```

Whether the session SIGNED is valid at NOW: the principal is enabled, the token is newer than REVOKEDBEFORE, and its
age is within the session life.

Effects: fail.

### `parseResourceRef`

```gizlang
function parseResourceRef(text: string): ResourceRef throws string
```

The reference of the text SPACE/TYPE/KEY (ResourceRef.text). It fails on any other form.

Effects: fail.

### `rank`

```gizlang
function rank(role: Role): i64 effects {}
```

The only role order: none < viewer < commenter < editor < manager < owner. An
Operation names the least role that it needs; viewer marks a read.

### `roleText`

```gizlang
function roleText(role: Role): string effects {}
```

The one text of a role: its kind.

### `parseRole`

```gizlang
function parseRole(text: string): Role throws string
```

The role of its text ("none", "viewer", "commenter", "editor", "manager", "owner"). It fails on any other text.

Effects: fail.

### `principalText`

```gizlang
function principalText(p: Principal): string
```

The one text of a grantee: person:ID, agent:ID, group:SPACE/group/KEY, link:HEX
or public. An agent's text names the agent only: its ceiling and its person come
from the actor, never from a grant.

Effects: fail.

### `parsePrincipal`

```gizlang
function parsePrincipal(text: string): Principal throws string
```

The principal of its text ("public", "person:ID", "agent:ID", "group:REF" or "link:HEX"). It fails on any other
text.

Effects: fail.

### `decide`

```gizlang
function decide(actor: Principal, ancestry: Vec<ResourceRef>, grants: Vec<Grant>, needed: Role, now: i64): Decision effects { fail }
```

Total, pure authorization: whether `actor` holds at least `needed` on the first
reference of `ancestry`, through grants on it or on its ancestors. Grants only
add; there is no deny. Public and link grants match only that actor. A person
also holds the grants of its groups (the membership grants are in `grants`).
Agents need their own grant and their person's grant, under their ceiling. A
grant never supplies an agent's trusted ceiling or delegation identity. A group
never acts. The compiler marks bounded Vec iteration as fail; no state or IO is used.

Effects: fail.

### `capability`

```gizlang
function capability(key: bytes, granted: Capability, now: i64): string throws string
```

The token of a capability that `decide` admitted: HEX(body).HEX(HMAC-SHA256).

Effects: fail.

### `admitCapability`

```gizlang
function admitCapability(key: bytes, token: string, now: i64): Capability throws string
```

The capability of a token that `key` signed and that is still live at `now`.

Effects: fail.

### `policyDigest`

```gizlang
function policyDigest(domain: string, definitions: Vec<string>): bytes
```

General policy identity; adapters provide canonical observed facts.

Effects: fail.

### `digest`

```gizlang
function digest(data: bytes): string
```

The SHA-256 of `data` in lowercase hex: the one content digest of tokens, keys and cache names.

Effects: fail.

### `passwordHash`

```gizlang
function passwordHash(password: string, salt: string): string
```

A password's stored hash under `salt`: PBKDF2-SHA-256, 32 bytes, in lowercase hex.

Effects: fail.

## Methods

### `Authority.register`

```gizlang
function Authority.register(self: Authority, principal: Identity): bool throws string
```

Adds PRINCIPAL. It fails when the principal is not valid or its identity is already registered.

Effects: fail, read, write.

### `Authority.lookup`

```gizlang
function Authority.lookup(self: Authority, identity: string): Option<Registration>
```

The registration of IDENTITY, or none.

Effects: fail, read.

### `Authority.authenticate`

```gizlang
function Authority.authenticate(self: Authority, identity: string, credential: bytes): Option<Registration>
```

The registration of IDENTITY when CREDENTIAL (32 bytes) matches its secret and it is enabled, else none.

Effects: fail, read.

### `Authority.replace`

```gizlang
function Authority.replace(self: Authority, principal: Identity, enabled: bool, revokedBefore: i64): bool throws string
```

Revocation/change occurs at the trusted authority, not in client claims.

Effects: fail, read, write.

### `Authority.current`

```gizlang
function Authority.current(self: Authority, evidence: Registration): bool
```

Whether EVIDENCE is still the current revision of an enabled registration.

Effects: fail, read.

### `Authority.resolve`

```gizlang
function Authority.resolve(self: Authority, signed: Signed, now: i64): Option<Registration>
```

The registration of the session SIGNED when it is valid at NOW, else none.

Effects: fail, read.

### `ResourceRef.text`

```gizlang
function ResourceRef.text(self: ResourceRef): string
```

SPACE/TYPE/KEY: SPACE and TYPE are 1 to 64 of a-z 0-9 _ . - and KEY is the
percent-encoded UTF-8 key (Http.encode). At most 512 bytes. Equal texts are
equal references: parseResourceRef accepts only this form.

Effects: fail.

## Constants

### `SESSION_LIFE`

```gizlang
const SESSION_LIFE = 2592000000
```

How long a session lasts, in milliseconds (30 days).

### `CAPABILITY_LIFE`

```gizlang
const CAPABILITY_LIFE = 300000
```

The longest life of a capability: the revocation bound of reads that check no grant.

### `PASSWORD_ROUNDS`

```gizlang
const PASSWORD_ROUNDS = 120000
```

How many rounds a password hash takes.

