Documentation

TOPICS
LANGUAGE

Multitenancy

Many database systems provide multi-tenant capabilities. They can contain multiple databases, each with their own access controls. FaunaDB takes this much further by allowing any database to have multiple child databases. This enables an operator to manage a single large FaunaDB cluster, create a few top-level databases, and give full administrative access of those databases to associated teams. Each team is free to create as many databases as they need without requiring operator intervention. As far as the team is concerned, they have their own full FaunaDB cluster.

This page will walk through how multiple databases and child databases are created and maintained. In this example a company is using FaunaDB for customer data as well as internal data.

Create Top Level Databases

First, the operator will need to create a set of top-level databases that can be handed over to individual teams. In the following example we will create a “production” database and an “internal” database using the configured secret.


curl https://db.fauna.com/ \
    -u fnACrVRyZhACAOvXOTRzOpg1Qqq5oPHKMfOLgcHQ: \
    -d '{
          "map": {
            "lambda": "name",
            "expr": {
              "create_database": { "object": { "name": { "var": "name" } } }
            }
          },
          "collection": [ "production", "internal" ]
        }'

client.Query(
  Map(
    Arr("production", "internal"),
    name => CreateDatabase(Obj("name", name))));

client.query(
  Map(
    Arr(Value("production"), Value("internal")),
    Lambda(
      Value("name"),
      CreateDatabase(Obj("name", Var("name"))))));

client.Query(
    f.Map(
        f.Arr{"production", "internal"},
        f.Lambda(
            "name",
            f.CreateDatabase(f.Obj{"name": f.Var("name")}),
        ),
    ),
)

client.query(
  Map(
    Arr("production", "internal"),
    Lambda { name => CreateDatabase(Obj("name" -> name)) }))

client.query(
  q.map_expr(
    lambda name: q.create_database({"name": name}),
    ["production", "internal"]
  ))

$client.query do
  map ['production', 'internal'] do |name|
    create_database(name: name)
  end
end

client.query(
    Map(
        collection: Arr("production", "internal"),
        to: { name in CreateDatabase(Obj("name" => name)) }
    )
)

client.query(
  q.Map(
    ["production", "internal"],
    function(name) {
      return q.CreateDatabase({ name: name });
    }));

=> HTTP/1.1 200 OK
{
  "resource": [
    {
      "ref": { "@ref": "databases/production" },
      "class": { "@ref": "databases" },
      "ts": 1520225711026454,
      "name": "production"
    },
    {
      "ref": { "@ref": "databases/internal" },
      "class": { "@ref": "databases" },
      "ts": 1520225711026454,
      "name": "internal"
    }
  ]
}

=> [
  {
    "ref": { "@ref": "databases/production" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "production"
  },
  {
    "ref": { "@ref": "databases/internal" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "internal"
  }
]

=> [
  {
    "ref": { "@ref": "databases/production" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "production"
  },
  {
    "ref": { "@ref": "databases/internal" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "internal"
  }
]

=> [
  {
    "ref": { "@ref": "databases/production" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "production"
  },
  {
    "ref": { "@ref": "databases/internal" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "internal"
  }
]

=> [
  {
    "ref": { "@ref": "databases/production" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "production"
  },
  {
    "ref": { "@ref": "databases/internal" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "internal"
  }
]

=> [
  {
    "ref": { "@ref": "databases/production" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "production"
  },
  {
    "ref": { "@ref": "databases/internal" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "internal"
  }
]

=> [
  {
    "ref": { "@ref": "databases/production" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "production"
  },
  {
    "ref": { "@ref": "databases/internal" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "internal"
  }
]

=> [
  {
    "ref": { "@ref": "databases/production" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "production"
  },
  {
    "ref": { "@ref": "databases/internal" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "internal"
  }
]

=> [
  {
    "ref": { "@ref": "databases/production" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "production"
  },
  {
    "ref": { "@ref": "databases/internal" },
    "class": { "@ref": "databases" },
    "ts": 1520225711026454,
    "name": "internal"
  }
]

Next we will create an admin key for each database that can be handed to each team.


curl https://db.fauna.com/ \
    -u fnACrVRyZhACAOvXOTRzOpg1Qqq5oPHKMfOLgcHQ: \
    -d '{
          "map": {
            "lambda": "db",
            "expr": {
              "create_key": {
                "object": { "role": "admin", "database": { "var": "db" } }
              }
            }
          },
          "collection": [ { "database": "production" }, { "database": "internal" } ]
        }'

client.Query(
  Map(
    Arr(Database("production"), Database("internal")),
    db => CreateKey(Obj("role", "admin", "database", db))));

client.query(
  Map(
    Arr(
      Database(Value("production")),
      Database(Value("internal"))
    ),
    Lambda(
      Value("db"),
      CreateKey(
        Obj("role", Value("admin"), "database", Var("db"))))));

client.Query(
    f.Map(
        f.Arr{f.Database("production"), f.Database("internal")},
        f.Lambda(
            "db",
            f.CreateKey(
                f.Obj{"role": "admin", "database": f.Var("db")},
            ),
        ),
    ),
)

client.query(
  Map(
    Arr(Database("production"), Database("internal")),
    Lambda { db =>
      CreateKey(Obj("role" -> "admin", "database" -> db))
    }))

client.query(
  q.map_expr(
    lambda db: q.create_key({"role": "admin", "database": db}),
    [q.database("production"), q.database("internal")]
  ))

$client.query do
  map [database('production'), database('internal')] do |db|
    create_key(role: 'admin', database: db)
  end
end

client.query(
    Map(
        collection: Arr(Database("production"), Database("internal")),
        to: { db in
            CreateKey(
                Obj("role" => "admin", "database" => db)
            )
        }
    )
)

client.query(
  q.Map(
    [q.Database("production"), q.Database("internal")],
    function(db) {
      return q.CreateKey({ role: "admin", database: db });
    }));

=> HTTP/1.1 200 OK
{
  "resource": [
    {
      "ref": { "@ref": "keys/192903235319628288" },
      "class": { "@ref": "keys" },
      "ts": 1520225711064091,
      "role": "admin",
      "database": { "@ref": "databases/production" },
      "secret": "fnACrVR4cUACAFxeJHGB7yuNakuLhwUDLFHe3ghP",
      "hashed_secret": "$2a$05$W96WId0vdaO4ABunz/BXVuuCl0WXVMj1d6lGbwiIahWX/gUyyt/wy"
    },
    {
      "ref": { "@ref": "keys/192903235319629312" },
      "class": { "@ref": "keys" },
      "ts": 1520225711064091,
      "role": "admin",
      "database": { "@ref": "databases/internal" },
      "secret": "fnACrVR4cUAGAMbQBucJV2d-yiDy2mDDlOBWUd6A",
      "hashed_secret": "$2a$05$kUDZRHxhgjMtrUFoUk.gT.IN2TTyxW4mdKnqr3KPj0wEzw6qVL4zS"
    }
  ]
}

=> [
  {
    "ref": { "@ref": "keys/192903235319628288" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/production" },
    "secret": "fnACrVR4cUACAFxeJHGB7yuNakuLhwUDLFHe3ghP",
    "hashed_secret": "$2a$05$W96WId0vdaO4ABunz/BXVuuCl0WXVMj1d6lGbwiIahWX/gUyyt/wy"
  },
  {
    "ref": { "@ref": "keys/192903235319629312" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/internal" },
    "secret": "fnACrVR4cUAGAMbQBucJV2d-yiDy2mDDlOBWUd6A",
    "hashed_secret": "$2a$05$kUDZRHxhgjMtrUFoUk.gT.IN2TTyxW4mdKnqr3KPj0wEzw6qVL4zS"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235319628288" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/production" },
    "secret": "fnACrVR4cUACAFxeJHGB7yuNakuLhwUDLFHe3ghP",
    "hashed_secret": "$2a$05$W96WId0vdaO4ABunz/BXVuuCl0WXVMj1d6lGbwiIahWX/gUyyt/wy"
  },
  {
    "ref": { "@ref": "keys/192903235319629312" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/internal" },
    "secret": "fnACrVR4cUAGAMbQBucJV2d-yiDy2mDDlOBWUd6A",
    "hashed_secret": "$2a$05$kUDZRHxhgjMtrUFoUk.gT.IN2TTyxW4mdKnqr3KPj0wEzw6qVL4zS"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235319628288" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/production" },
    "secret": "fnACrVR4cUACAFxeJHGB7yuNakuLhwUDLFHe3ghP",
    "hashed_secret": "$2a$05$W96WId0vdaO4ABunz/BXVuuCl0WXVMj1d6lGbwiIahWX/gUyyt/wy"
  },
  {
    "ref": { "@ref": "keys/192903235319629312" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/internal" },
    "secret": "fnACrVR4cUAGAMbQBucJV2d-yiDy2mDDlOBWUd6A",
    "hashed_secret": "$2a$05$kUDZRHxhgjMtrUFoUk.gT.IN2TTyxW4mdKnqr3KPj0wEzw6qVL4zS"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235319628288" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/production" },
    "secret": "fnACrVR4cUACAFxeJHGB7yuNakuLhwUDLFHe3ghP",
    "hashed_secret": "$2a$05$W96WId0vdaO4ABunz/BXVuuCl0WXVMj1d6lGbwiIahWX/gUyyt/wy"
  },
  {
    "ref": { "@ref": "keys/192903235319629312" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/internal" },
    "secret": "fnACrVR4cUAGAMbQBucJV2d-yiDy2mDDlOBWUd6A",
    "hashed_secret": "$2a$05$kUDZRHxhgjMtrUFoUk.gT.IN2TTyxW4mdKnqr3KPj0wEzw6qVL4zS"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235319628288" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/production" },
    "secret": "fnACrVR4cUACAFxeJHGB7yuNakuLhwUDLFHe3ghP",
    "hashed_secret": "$2a$05$W96WId0vdaO4ABunz/BXVuuCl0WXVMj1d6lGbwiIahWX/gUyyt/wy"
  },
  {
    "ref": { "@ref": "keys/192903235319629312" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/internal" },
    "secret": "fnACrVR4cUAGAMbQBucJV2d-yiDy2mDDlOBWUd6A",
    "hashed_secret": "$2a$05$kUDZRHxhgjMtrUFoUk.gT.IN2TTyxW4mdKnqr3KPj0wEzw6qVL4zS"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235319628288" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/production" },
    "secret": "fnACrVR4cUACAFxeJHGB7yuNakuLhwUDLFHe3ghP",
    "hashed_secret": "$2a$05$W96WId0vdaO4ABunz/BXVuuCl0WXVMj1d6lGbwiIahWX/gUyyt/wy"
  },
  {
    "ref": { "@ref": "keys/192903235319629312" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/internal" },
    "secret": "fnACrVR4cUAGAMbQBucJV2d-yiDy2mDDlOBWUd6A",
    "hashed_secret": "$2a$05$kUDZRHxhgjMtrUFoUk.gT.IN2TTyxW4mdKnqr3KPj0wEzw6qVL4zS"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235319628288" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/production" },
    "secret": "fnACrVR4cUACAFxeJHGB7yuNakuLhwUDLFHe3ghP",
    "hashed_secret": "$2a$05$W96WId0vdaO4ABunz/BXVuuCl0WXVMj1d6lGbwiIahWX/gUyyt/wy"
  },
  {
    "ref": { "@ref": "keys/192903235319629312" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/internal" },
    "secret": "fnACrVR4cUAGAMbQBucJV2d-yiDy2mDDlOBWUd6A",
    "hashed_secret": "$2a$05$kUDZRHxhgjMtrUFoUk.gT.IN2TTyxW4mdKnqr3KPj0wEzw6qVL4zS"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235319628288" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/production" },
    "secret": "fnACrVR4cUACAFxeJHGB7yuNakuLhwUDLFHe3ghP",
    "hashed_secret": "$2a$05$W96WId0vdaO4ABunz/BXVuuCl0WXVMj1d6lGbwiIahWX/gUyyt/wy"
  },
  {
    "ref": { "@ref": "keys/192903235319629312" },
    "class": { "@ref": "keys" },
    "ts": 1520225711064091,
    "role": "admin",
    "database": { "@ref": "databases/internal" },
    "secret": "fnACrVR4cUAGAMbQBucJV2d-yiDy2mDDlOBWUd6A",
    "hashed_secret": "$2a$05$kUDZRHxhgjMtrUFoUk.gT.IN2TTyxW4mdKnqr3KPj0wEzw6qVL4zS"
  }
]

Per-Team Child Databases

Given the new keys, each team can create databases that fit their needs. In this case, production will not create any child databases, but the internal tools team will create a set of databases for the products they create.


curl https://db.fauna.com/ \
    -u fnACrVR4cUAGAMbQBucJV2d-yiDy2mDDlOBWUd6A: \
    -d '{
          "map": {
            "lambda": "name",
            "expr": {
              "create_database": { "object": { "name": { "var": "name" } } }
            }
          },
          "collection": [ "personnel", "bulletin-board" ]
        }'

client.Query(
  Map(
    Arr("personnel", "bulletin-board"),
    name => CreateDatabase(Obj("name", name))));

client.query(
  Map(
    Arr(Value("personnel"), Value("bulletin-board")),
    Lambda(
      Value("name"),
      CreateDatabase(Obj("name", Var("name"))))));

client.Query(
    f.Map(
        f.Arr{"personnel", "bulletin-board"},
        f.Lambda(
            "name",
            f.CreateDatabase(f.Obj{"name": f.Var("name")}),
        ),
    ),
)

client.query(
  Map(
    Arr("personnel", "bulletin-board"),
    Lambda { name => CreateDatabase(Obj("name" -> name)) }))

client.query(
  q.map_expr(
    lambda name: q.create_database({"name": name}),
    ["personnel", "bulletin-board"]
  ))

$client.query do
  map ['personnel', 'bulletin-board'] do |name|
    create_database(name: name)
  end
end

client.query(
    Map(
        collection: Arr("personnel", "bulletin-board"),
        to: { name in CreateDatabase(Obj("name" => name)) }
    )
)

client.query(
  q.Map(
    ["personnel", "bulletin-board"],
    function(name) {
      return q.CreateDatabase({ name: name });
    }));

=> HTTP/1.1 200 OK
{
  "resource": [
    {
      "ref": { "@ref": "databases/personnel" },
      "class": { "@ref": "databases" },
      "ts": 1520225711114944,
      "name": "personnel"
    },
    {
      "ref": { "@ref": "databases/bulletin-board" },
      "class": { "@ref": "databases" },
      "ts": 1520225711114944,
      "name": "bulletin-board"
    }
  ]
}

=> [
  {
    "ref": { "@ref": "databases/personnel" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "personnel"
  },
  {
    "ref": { "@ref": "databases/bulletin-board" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "bulletin-board"
  }
]

=> [
  {
    "ref": { "@ref": "databases/personnel" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "personnel"
  },
  {
    "ref": { "@ref": "databases/bulletin-board" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "bulletin-board"
  }
]

=> [
  {
    "ref": { "@ref": "databases/personnel" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "personnel"
  },
  {
    "ref": { "@ref": "databases/bulletin-board" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "bulletin-board"
  }
]

=> [
  {
    "ref": { "@ref": "databases/personnel" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "personnel"
  },
  {
    "ref": { "@ref": "databases/bulletin-board" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "bulletin-board"
  }
]

=> [
  {
    "ref": { "@ref": "databases/personnel" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "personnel"
  },
  {
    "ref": { "@ref": "databases/bulletin-board" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "bulletin-board"
  }
]

=> [
  {
    "ref": { "@ref": "databases/personnel" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "personnel"
  },
  {
    "ref": { "@ref": "databases/bulletin-board" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "bulletin-board"
  }
]

=> [
  {
    "ref": { "@ref": "databases/personnel" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "personnel"
  },
  {
    "ref": { "@ref": "databases/bulletin-board" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "bulletin-board"
  }
]

=> [
  {
    "ref": { "@ref": "databases/personnel" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "personnel"
  },
  {
    "ref": { "@ref": "databases/bulletin-board" },
    "class": { "@ref": "databases" },
    "ts": 1520225711114944,
    "name": "bulletin-board"
  }
]

Finally, we create a server key for each new internal database. These keys will be used within the app fronting the database.


curl https://db.fauna.com/ \
    -u fnACrVR4cUAGAMbQBucJV2d-yiDy2mDDlOBWUd6A: \
    -d '{
          "map": {
            "lambda": "db",
            "expr": {
              "create_key": {
                "object": { "role": "server", "database": { "var": "db" } }
              }
            }
          },
          "collection": [
            { "database": "personnel" },
            { "database": "bulletin-board" }
          ]
        }'

client.Query(
  Map(
    Arr(Database("personnel"), Database("bulletin-board")),
    db => CreateKey(Obj("role", "server", "database", db))));

client.query(
  Map(
    Arr(
      Database(Value("personnel")),
      Database(Value("bulletin-board"))
    ),
    Lambda(
      Value("db"),
      CreateKey(
        Obj("role", Value("server"), "database", Var("db"))))));

client.Query(
    f.Map(
        f.Arr{f.Database("personnel"), f.Database("bulletin-board")},
        f.Lambda(
            "db",
            f.CreateKey(
                f.Obj{"role": "server", "database": f.Var("db")},
            ),
        ),
    ),
)

client.query(
  Map(
    Arr(Database("personnel"), Database("bulletin-board")),
    Lambda { db =>
      CreateKey(Obj("role" -> "server", "database" -> db))
    }))

client.query(
  q.map_expr(
    lambda db: q.create_key({"role": "server", "database": db}),
    [q.database("personnel"), q.database("bulletin-board")]
  ))

$client.query do
  map [database('personnel'), database('bulletin-board')] do |db|
    create_key(role: 'server', database: db)
  end
end

client.query(
    Map(
        collection: Arr(Database("personnel"), Database("bulletin-board")),
        to: { db in
            CreateKey(
                Obj("role" => "server", "database" => db)
            )
        }
    )
)

client.query(
  q.Map(
    [q.Database("personnel"), q.Database("bulletin-board")],
    function(db) {
      return q.CreateKey({ role: "server", database: db });
    }));

=> HTTP/1.1 200 OK
{
  "resource": [
    {
      "ref": { "@ref": "keys/192903235415048704" },
      "class": { "@ref": "keys" },
      "ts": 1520225711155247,
      "role": "server",
      "database": { "@ref": "databases/personnel" },
      "secret": "fnACrVR4dvACAE_6j47h083I_y0JwDduoIc0mxU_",
      "hashed_secret": "$2a$05$L3MV3akG.sTzRARbjbZ3xO5d96Xj8EGkJbkYRO1igIWo1kbQh0N2C"
    },
    {
      "ref": { "@ref": "keys/192903235415049728" },
      "class": { "@ref": "keys" },
      "ts": 1520225711155247,
      "role": "server",
      "database": { "@ref": "databases/bulletin-board" },
      "secret": "fnACrVR4dvAGABy_lWsYaoWFk8VEh9204DjFeQS8",
      "hashed_secret": "$2a$05$mBsAZ9X0PUf3g0naTM7m.eeeb6eS7aO2aNiDgY7R7yG8Hg0kL/tjW"
    }
  ]
}

=> [
  {
    "ref": { "@ref": "keys/192903235415048704" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/personnel" },
    "secret": "fnACrVR4dvACAE_6j47h083I_y0JwDduoIc0mxU_",
    "hashed_secret": "$2a$05$L3MV3akG.sTzRARbjbZ3xO5d96Xj8EGkJbkYRO1igIWo1kbQh0N2C"
  },
  {
    "ref": { "@ref": "keys/192903235415049728" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/bulletin-board" },
    "secret": "fnACrVR4dvAGABy_lWsYaoWFk8VEh9204DjFeQS8",
    "hashed_secret": "$2a$05$mBsAZ9X0PUf3g0naTM7m.eeeb6eS7aO2aNiDgY7R7yG8Hg0kL/tjW"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235415048704" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/personnel" },
    "secret": "fnACrVR4dvACAE_6j47h083I_y0JwDduoIc0mxU_",
    "hashed_secret": "$2a$05$L3MV3akG.sTzRARbjbZ3xO5d96Xj8EGkJbkYRO1igIWo1kbQh0N2C"
  },
  {
    "ref": { "@ref": "keys/192903235415049728" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/bulletin-board" },
    "secret": "fnACrVR4dvAGABy_lWsYaoWFk8VEh9204DjFeQS8",
    "hashed_secret": "$2a$05$mBsAZ9X0PUf3g0naTM7m.eeeb6eS7aO2aNiDgY7R7yG8Hg0kL/tjW"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235415048704" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/personnel" },
    "secret": "fnACrVR4dvACAE_6j47h083I_y0JwDduoIc0mxU_",
    "hashed_secret": "$2a$05$L3MV3akG.sTzRARbjbZ3xO5d96Xj8EGkJbkYRO1igIWo1kbQh0N2C"
  },
  {
    "ref": { "@ref": "keys/192903235415049728" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/bulletin-board" },
    "secret": "fnACrVR4dvAGABy_lWsYaoWFk8VEh9204DjFeQS8",
    "hashed_secret": "$2a$05$mBsAZ9X0PUf3g0naTM7m.eeeb6eS7aO2aNiDgY7R7yG8Hg0kL/tjW"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235415048704" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/personnel" },
    "secret": "fnACrVR4dvACAE_6j47h083I_y0JwDduoIc0mxU_",
    "hashed_secret": "$2a$05$L3MV3akG.sTzRARbjbZ3xO5d96Xj8EGkJbkYRO1igIWo1kbQh0N2C"
  },
  {
    "ref": { "@ref": "keys/192903235415049728" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/bulletin-board" },
    "secret": "fnACrVR4dvAGABy_lWsYaoWFk8VEh9204DjFeQS8",
    "hashed_secret": "$2a$05$mBsAZ9X0PUf3g0naTM7m.eeeb6eS7aO2aNiDgY7R7yG8Hg0kL/tjW"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235415048704" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/personnel" },
    "secret": "fnACrVR4dvACAE_6j47h083I_y0JwDduoIc0mxU_",
    "hashed_secret": "$2a$05$L3MV3akG.sTzRARbjbZ3xO5d96Xj8EGkJbkYRO1igIWo1kbQh0N2C"
  },
  {
    "ref": { "@ref": "keys/192903235415049728" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/bulletin-board" },
    "secret": "fnACrVR4dvAGABy_lWsYaoWFk8VEh9204DjFeQS8",
    "hashed_secret": "$2a$05$mBsAZ9X0PUf3g0naTM7m.eeeb6eS7aO2aNiDgY7R7yG8Hg0kL/tjW"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235415048704" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/personnel" },
    "secret": "fnACrVR4dvACAE_6j47h083I_y0JwDduoIc0mxU_",
    "hashed_secret": "$2a$05$L3MV3akG.sTzRARbjbZ3xO5d96Xj8EGkJbkYRO1igIWo1kbQh0N2C"
  },
  {
    "ref": { "@ref": "keys/192903235415049728" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/bulletin-board" },
    "secret": "fnACrVR4dvAGABy_lWsYaoWFk8VEh9204DjFeQS8",
    "hashed_secret": "$2a$05$mBsAZ9X0PUf3g0naTM7m.eeeb6eS7aO2aNiDgY7R7yG8Hg0kL/tjW"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235415048704" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/personnel" },
    "secret": "fnACrVR4dvACAE_6j47h083I_y0JwDduoIc0mxU_",
    "hashed_secret": "$2a$05$L3MV3akG.sTzRARbjbZ3xO5d96Xj8EGkJbkYRO1igIWo1kbQh0N2C"
  },
  {
    "ref": { "@ref": "keys/192903235415049728" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/bulletin-board" },
    "secret": "fnACrVR4dvAGABy_lWsYaoWFk8VEh9204DjFeQS8",
    "hashed_secret": "$2a$05$mBsAZ9X0PUf3g0naTM7m.eeeb6eS7aO2aNiDgY7R7yG8Hg0kL/tjW"
  }
]

=> [
  {
    "ref": { "@ref": "keys/192903235415048704" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/personnel" },
    "secret": "fnACrVR4dvACAE_6j47h083I_y0JwDduoIc0mxU_",
    "hashed_secret": "$2a$05$L3MV3akG.sTzRARbjbZ3xO5d96Xj8EGkJbkYRO1igIWo1kbQh0N2C"
  },
  {
    "ref": { "@ref": "keys/192903235415049728" },
    "class": { "@ref": "keys" },
    "ts": 1520225711155247,
    "role": "server",
    "database": { "@ref": "databases/bulletin-board" },
    "secret": "fnACrVR4dvAGABy_lWsYaoWFk8VEh9204DjFeQS8",
    "hashed_secret": "$2a$05$mBsAZ9X0PUf3g0naTM7m.eeeb6eS7aO2aNiDgY7R7yG8Hg0kL/tjW"
  }
]

Conclusion

In this page we walked through setting up a hierarchy of databases starting with two top-level, broadly scoped databases and continuing down to individual databases for internal products. Keys for the individual app databases are unable to access anything outside of their associated database.

FaunaDB Cloud works identically. An organization can build their database hierarchy according to their structure without the overhead of operating their own FaunaDB cluster.