01 Documentation


Introduction to Security

This document covers both administrative security and application security.

FaunaDB Key System

Access to the FaunaDB API uses secrets that correspond to access keys, which authenticate connections as having particular permissions. The access key system applies both to administrator and server level connections, as well as to object and user level connections.


Keys belong to one of four roles, either admin, server, server-readonly, or client.

Admin Keys

Keys with the admin role are used for managing databases and keys. An admin key can be used to create and destroy databases and keys. They should be very well protected.

Admin keys for cloud accounts are created in the admin keys interface. For Enterprise customers the configured root key is used.

Server Keys

Keys with the server role bypass all permissions checks. Because they provide unrestricted access, they should be well-protected and only used in trusted or server-side environments.

Server Read-Only Keys

Keys with the server-readonly role allows read-only access to all data within the database it’s assigned to. Because they provide unrestricted access, they should be well-protected and only used in trusted or server-side environments.

Client Keys

Keys with the client role are restricted to actions and resources that are specifically marked with the public permission. Because their access is controlled, they are suitable for embedding in untrusted environments, such as mobile clients.

Typically they are used as part of an application’s user authentication flow, or to access a public data, such as an application’s logged-out view.

Token Access

Tokens allow direct access to FaunaDB by application users. Their use is optional. Each token has an associated instance. Requests made with a token apply resource-level permissions checks based on its instance.

Both server and client keys can generate tokens. Token generation with a client key requires a password. The password is optional when using a server key, but will be verified if provided.


FaunaDB has a built-in permissions system that allows for fine-grained control over access to data.

Access is controlled by a resource’s permissions object. The identity of a request’s key is checked against the value of the permission field corresponding to the resource type and action being taken.

Permissions themselves may be set to one of the following values:

Value Access Allowed
empty An empty or missing permission means that only server keys are allowed.
“public” A permission set to the string “public” means that any key is allowed.
instance ref Only tokens belonging to the instance specified are allowed.
class ref Only tokens belonging to instances of the class are allowed.


An instance may delegate access on its behalf to other instances by adding the other instances’ refs to its delegates list. Any token belonging to a member of delegates will be granted access as though they were the delegating instance.

For example, if classes/users/1 has read access to classes/spells/1 but classes/users/2 does not, classes/users/1 may grant access to classes/users/2 with the following query:

curl https://db.fauna.com/ \
    -d '{
          "update": { "ref": { "class": "users" }, "id": 1 },
          "params": {
            "object": {
              "delegates": [ { "ref": { "class": "users" }, "id": 2 } ]

    Ref(Class("users"), 1),
    Obj("delegates", Arr(Ref(Class("users"), 2)))));

    Ref(Class(Value("users")), Value(1)),
    Obj("delegates", Arr(Ref(Class(Value("users")), Value(2))))));

        f.Ref(f.Class("users"), 1),
        f.Obj{"delegates": f.Arr{f.Ref(f.Class("users"), 2)}},

    Ref(Class("users"), 1),
    Obj("delegates" -> Arr(Ref(Class("users"), 2)))))

    Ref(q.class_expr("users"), 1),
    {"delegates": [Ref(q.class_expr("users"), 2)]}

$client.query do
  update ref(class_('users'), 1),
         delegates: [ref(class_('users'), 2)]

        ref: Ref(class: Class("users"), id: 1),
        to: Obj(
            "delegates" => Arr(Ref(class: Class("users"), id: 2))

    q.Ref(q.Class("users"), 1),
    { delegates: [q.Ref(q.Class("users"), 2)] }));

Now, when classes/users/2 attempts to read classes/spells/1, they will be granted the same level of access as classes/users/1.

Delegates are not transitive–in the example above, classes/users/2 may not delegate classes/users/1’s permissions to another user.