Updating Migrating Schema in SpiceDB
Schema in SpiceDB represents the structural definitions of which relationships are allowed in SpiceDB and how permissions are computed.
SpiceDB processes all calls to the WriteSchema (opens in a new tab) in a safe manner: it is not possible to break the type safety of a schema.
As a result, certain operations are disallowed (as described below), but there is no risk in accidentally breaking an internal computation.
Allowed Operations
Adding a new relation
Adding a new relation
to a definition
is always allowed, as it cannot change any existing
types or computation:
definition resource {
relation existing: user
relation newrelation: user
permission view = existing
}
Changing a permission
Changing how a permission is computed is always allowed, so long as the expression references other defined permissions or relations:
definition resource {
relation viewer: user
relation editor: user
permission view = viewer + editor
}
Adding a new subject type to a relation
Adding a new allowed subject type to a relation is always allowed:
definition resource {
relation viewer: user | group#member
permission view = viewer
}
Deleting a permission
Removing a permission is always allowed, so long as it is not referenced by another permission or relation.
While this cannot break the schema, it can break API callers if they are making checks or other API requests against the permission. It is up to your own CI system to verify that removed permissions are no longer referenced externally.
Contingent Operations
For type safety reasons, any removal of a relation
with data, or a relation
or permission
referenced by another relation
or permission
is disallowed.
Removing a relation
A relation
can only be removed if all of the relationships referencing it
have been deleted and it is not referenced by any other relation
or permission
in the schema.
Process for removing a relation
Given this example schema and we wish to remove relation editor
:
definition resource {
relation viewer: user
relation editor: user
permission view = viewer + editor
}
To remove relation editor
:
- Change the schema to no longer reference the relation and call WriteSchema (opens in a new tab) with the changes:
definition resource {
relation viewer: user
relation editor: user
permission view = viewer
}
- Issue a DeleteRelationships (opens in a new tab) call to delete all relationships for the
editor
relation
- Update the schema to remove the relation entirely and call WriteSchema (opens in a new tab) with the changes:
definition resource {
relation viewer: user
permission view = viewer
}
Removing an allowed subject type
Similar to removing a relation itself, removing an allowed subject type can only be performed once all relationships with that subject type on the relation have been deleted.
Process for removing an allowed subject type
Given this example schema and we wish to remove supporting group#member
on viewer
:
definition resource {
relation viewer: user | group#member
permission view = viewer
}
-
Issue a DeleteRelationships (opens in a new tab) call to delete all relationships for the
viewer
relation with subject typegroup#member
-
Update the schema to remove the allowed subject type call WriteSchema (opens in a new tab) with the changes:
definition resource {
relation viewer: user
permission view = viewer
}
Migrating data from one relation
to another
Given the constraints described above, migrating relationships from one relation
to another
requires a few steps.
Let's take a sample schema and walk through migrating data from relation viewer
to a new relation new_viewer
:
definition resource {
relation viewer: user
permission view = viewer
}
- Add the new relation:
We start by adding the new relation and adding it to the view
permission:
definition resource {
relation viewer: user
relation new_viewer: user2
permission view = viewer + new_viewer
}
- Update the application:
We next update our application so that it writes relationships to both relation viewer
and relation new_viewer
.
This ensures that once we run the backfill (the next step), both relations are fully specified.
- Backfill the relationships:
We next backfill the relationships by having our application write the relationships
for the new_viewer
relation.
Make sure to copy all relevant relationships in this step.
- Drop
viewer
from the permission:
Once the relationships for new_viewer
have been fully written and the permission has been verified,
(typically by issuing a CheckPermission request directly to new_viewer
), the viewer
relation
can be dropped from the view
permission:
definition resource {
relation viewer: user
relation new_viewer: user2
permission view = new_viewer
}
- Update the application:
We next update our application to no longer write relationships to the viewer
relation,
as it is no longer used.
- Delete the relation
viewer
:
Finally, follow the instructions above for deleting a relation
to delete the now-unused relation.