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(id = "production", class = ref(id = "databases")),
    ts: 1520225711026454,
    name: "production"
  },
  {
    ref: ref(id = "internal", class = ref(id = "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(id = "192903235319628288", class = ref(id = "keys")),
    ts: 1520225711064091,
    role: "admin",
    database: ref(id = "production", class = ref(id = "databases")),
    secret: "fnAC6ms4DzACAI7Fxc7ZZTZZ8eW3u9CqRAuc6Teu",
    hashed_secret: "$2a$05$4AucECSN9fEcpxaEvuYh8eYQUAVwut5QPe/kcDRmnc6X7WXypN0kO"
  },
  {
    ref: ref(id = "192903235319629312", class = ref(id = "keys")),
    ts: 1520225711064091,
    role: "admin",
    atabase: ref(id = "internal", class = ref(id = "databases")),
    secret: "fnAC6ms4D3ACAHZ9LjSEO1VzFRKb5Ho9o3_hRMAe",
    hashed_secret: "$2a$05$yrMM3CK0zMAhSStnRNZdheamolFrR1.ipdgHCJKWBVudZAgoARhPu"
  }
]
[
  {
    "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(id = "personnel", class = ref(id = "databases")),
    ts: 1520225711114944,
    name: "personnel"
  },
  {
    ref: ref(id = "bulletin-board", class = ref(id = "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(id = "192903235415048704", class = ref(id = "keys")),
    ts: 1520225711155247,
    role: "server",
    database: ref(id = "personnel", class = ref(id = "databases")),
    secret: "fnAC6mqlqqACAJJjiGTlhHDrz5qyvhSKLAoTbIho",
    hashed_secret: "$2a$05$qoSM2o7Rb.p9Sds5RJyx8uPmLoP/CRPjbni3/hCTlJ/rJmeWnnoIS"
  },
  {
    ref: ref(id = "192903235415049728", class = ref(id = "keys")),
    ts: 1520225711155247,
    role: "server",
    database: ref(id = "bulletin-board", class = ref(id = "databases")),
    secret: "fnAC6mqlrDACAFXcquZ1ZsP_OLeN-YVS5ACBoeXR",
    hashed_secret: "$2a$05$Uf1Tl0XE9RlKpucKjFPWIuSlsRv/aOPtbvHDP08jJs2HHjxZ/0ERG"
  }
]
[
  {
    "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.

Was this article helpful?

We're sorry to hear that.
Tell us how we can improve! documentation@fauna.com

Thank you for your feedback!