# Service credentials

A service, such as a partner's system or a worker, signs in with a key.
Each service has an owner, a status, and any number of keys. Each key
expires, can be revoked on its own, and can overlap with the next, so you
can rotate a key without downtime. A key is shown once when it is issued,
and Jadpo keeps only a verifier of it.

You declare only the service's entity:

```jadpo
// domains/identity/entities/service.jadpo
entity Service {
    id: Uuid
    owner_id: User.id {
        on_delete: OnDelete.restricted
    }
    name: Text {
        unique: true
    }
    status: ServiceStatus
}
```

Jadpo stores the keys, as it stores browser sessions, and generates three
operations on the entity to issue, list and revoke them:
`Service.issue_key(service_id, expires_at)`, `Service.keys(service_id)` and
`Service.revoke_key(key_id)`.

- The operations belong to the entity the principal's `service:` names,
  once `authentication { }` declares `service_key:`.
- Only an action calls them (`type.auth_sessions_context`), as it calls
  `sessions.start`. A `GET` route can also run `keys` itself
  ([Listing from a `GET` route](starting-and-ending-a-session.md#listing-from-a-get-route)).
- `Service.issue_key(service_id, expires_at)` takes the service's `id`
  and an `Instant`, keeps the new key in the action's transaction and
  gives it once as a `ServiceKey`, text an `Output` can send; the
  response that carries it is never kept for an idempotent repeat, and a
  `ServiceKey` can't be stored in an entity.
- `Service.keys(service_id)` lists the service's keys that haven't
  expired, oldest first, as `IssuedKey`s, which code reads `id`,
  `started_at` and `ends_at` from, never the key itself. A `GET` route
  runs it too, and an `Output` can send the list
  ([Listing from a `GET` route](starting-and-ending-a-session.md#listing-from-a-get-route)).
- `Service.revoke_key(key_id)` deletes one by its `id`; an `id` no key
  has revokes nothing.
- Keys live in the `jadpo_service_keys` system table, in the store the
  service entity lives in ([System tables](system-tables.md)).
  Jadpo looks a key up with its service's record, `active:` and all, in
  one statement, so a disabled service is refused (`service_disabled`) on
  its next request.
- A service key signs in only a service. A route that selects
  `current_principal.user` refuses it with 403 `not_permitted`, and one
  that selects `current_principal.service` refuses a user the same way.
