1
+ syntax = "proto3" ;
2
+ package authzed.api.materialize.v0 ;
3
+
4
+ import "authzed/api/v1/core.proto" ;
5
+ import "google/protobuf/struct.proto" ;
6
+ import "google/protobuf/duration.proto" ;
7
+ import "validate/validate.proto" ;
8
+ import "authzed/api/v0/materialize/watchpermissions.proto" ;
9
+
10
+ option go_package = "github.com/authzed/authzed-go/proto/authzed/api/v0/materialize" ;
11
+ option java_package = "com.authzed.api.materialize.v0" ;
12
+ option java_multiple_files = true ;
13
+
14
+ service WatchPermissionSetsService {
15
+ // WatchPermissionSets returns a stream of changes to the sets which can be used to compute the watched permissions.
16
+ //
17
+ // WatchPermissionSets lets consumers achieve the same thing as WatchPermissions, but trades off a simpler usage model with
18
+ // significantly lower computational requirements. Unlike WatchPermissions, this method returns changes to the sets of permissions,
19
+ // rather than the individual permissions. Permission sets are a normalized form of the computed permissions, which
20
+ // means that the consumer must perform an extra computation over this representation to obtain the final computed
21
+ // permissions, typically by intersecting the provided sets.
22
+ //
23
+ // For example, this would look like a JOIN between the
24
+ // materialize permission sets table in a target relation database, the table with the resources to authorize access
25
+ // to, and the table with the subject (e.g. a user).
26
+ //
27
+ // In exchange, the number of changes issued by WatchPermissionSets will be several orders of magnitude less than those
28
+ // emitted by WatchPermissions, which has several implications:
29
+ // - significantly less resources to compute the sets
30
+ // - significantly less messages to stream over the network
31
+ // - significantly less events to ingest on the consumer side
32
+ // - less ingestion lag from the origin SpiceDB mutation
33
+ //
34
+ // The type of scenarios WatchPermissionSets is particularly well suited is when a single change
35
+ // in the origin SpiceDB can yield millions of changes. For example, in the GitHub authorization model, assigning a role
36
+ // to a top-level team of an organization with hundreds of thousands of employees can lead to an explosion of
37
+ // permission change events that would require a lot of computational resources to process, both on Materialize and
38
+ // the consumer side.
39
+ //
40
+ // WatchPermissionSets is thus recommended for any larger scale use case where the fan-out in permission changes that
41
+ // emerges from a specific schema and data shape is too large to handle effectively.
42
+ //
43
+ // The API does not offer a sharding mechanism and thus there should only be one consumer per target system.
44
+ // Implementing an active-active HA consumer setup over the same target system will require coordinating which
45
+ // revisions have been consumed in order to prevent transitioning to an inconsistent state.
46
+ rpc WatchPermissionSets (WatchPermissionSetsRequest ) returns (stream WatchPermissionSetsResponse ) {}
47
+
48
+ // LookupPermissionSets returns the current state of the permission sets which can be used to derive the computed permissions.
49
+ // It's typically used to backfill the state of the permission sets in the consumer side.
50
+ //
51
+ // It's a cursored API and the consumer is responsible to keep track of the cursor and use it on each subsequent call.
52
+ // Each stream will return <N> permission sets defined by the specified request limit. The server will keep streaming until
53
+ // the sets per stream is hit, or the current state of the sets is reached,
54
+ // whatever happens first, and then close the stream. The server will indicate there are no more changes to stream
55
+ // through the `completed_members` in the cursor.
56
+ //
57
+ // There may be many elements to stream, and so the consumer should be prepared to resume the stream from the last
58
+ // cursor received. Once completed, the consumer may start streaming permission set changes using WatchPermissionSets
59
+ // and the revision token from the last LookupPermissionSets response.
60
+ rpc LookupPermissionSets (LookupPermissionSetsRequest ) returns (stream LookupPermissionSetsResponse ) {}
61
+ }
62
+
63
+ message WatchPermissionSetsRequest {
64
+ // optional_starting_after is used to specify the SpiceDB revision to start watching from.
65
+ // If not specified, the watch will start from the current SpiceDB revision time of the request ("head revision").
66
+ authzed.api.v1.ZedToken optional_starting_after = 1 ;
67
+ }
68
+
69
+ message WatchPermissionSetsResponse {
70
+ oneof response {
71
+ // change is the permission set delta that has occurred as result of a mutation in origin SpiceDB.
72
+ // The consumer should apply this change to the current state of the permission sets in their target system.
73
+ // Once an event arrives with completed_revision instead, the consumer shall consider the set of
74
+ // changes originating from that revision completed.
75
+ //
76
+ // The consumer should keep track of the revision in order to resume streaming in the event of consumer restarts.
77
+ PermissionSetChange change = 1 ;
78
+
79
+ // completed_revision is the revision token that indicates the completion of a set of changes. It may also be
80
+ // received without accompanying set of changes, indicating that a mutation in the origin SpiceDB cluster did
81
+ // not yield any effective changes in the permission sets
82
+ authzed.api.v1.ZedToken completed_revision = 2 ;
83
+ }
84
+ }
85
+
86
+ message Cursor {
87
+ // limit is the number of permission sets to stream over a single LookupPermissionSets call that was requested.
88
+ uint32 limit = 1 ;
89
+ // token is the snapshot revision at which the cursor was computed.
90
+ authzed.api.v1.ZedToken token = 4 ;
91
+ // starting_index is an offset of the permission set represented by this cursor
92
+ uint32 starting_index = 5 ;
93
+ // completed_members is a boolean flag that indicates that the cursor has reached the end of the permission sets
94
+ bool completed_members = 6 ;
95
+ }
96
+
97
+ message LookupPermissionSetsRequest {
98
+ // limit is the number of permission sets to stream over a single LookupPermissionSets. Once the limit is reached,
99
+ // the server will close the stream. If more permission sets are available, the consume should open a new stream
100
+ // providing optional_starting_after_cursor, using the cursor from the last response.
101
+ uint32 limit = 1 ;
102
+ // optional_starting_after_cursor is used to specify the offset to start streaming permission sets from.
103
+ Cursor optional_starting_after_cursor = 4 ;
104
+ }
105
+
106
+ message LookupPermissionSetsResponse {
107
+ // change represents the permission set delta necessary to transition an uninitialized target system to
108
+ // a specific snapshot revision. In practice it's not different from the WatchPermissionSetsResponse.change, except
109
+ // all changes will be of time SET_OPERATION_ADDED because it's assumed there is no known previous state.
110
+ //
111
+ // Applying the deltas to a previously initialized target system would yield incorrect results.
112
+ PermissionSetChange change = 1 ;
113
+ // cursor points to a specific permission set in a revision.
114
+ // The consumer should keep track of the cursor in order to resume streaming in the event of consumer restarts. This
115
+ // is particularly important in backfill scenarios that may take hours or event days to complete.
116
+ Cursor cursor = 2 ;
117
+ }
118
+
119
+ message PermissionSetChange {
120
+ enum SetOperation {
121
+ SET_OPERATION_UNSPECIFIED = 0 ;
122
+ SET_OPERATION_ADDED = 1 ;
123
+ SET_OPERATION_REMOVED = 2 ;
124
+ }
125
+
126
+ // revision represents the revision at which the permission set change occurred.
127
+ authzed.api.v1.ZedToken at_revision = 1 ;
128
+ // operation represents the type of set operation that took place as part of the change
129
+ SetOperation operation = 2 ;
130
+ // parent_set represents the permission set parent of either another set or a member
131
+ SetReference parent_set = 3 ;
132
+
133
+ oneof child {
134
+ // child_set represents the scenario where another set is considered member of the parent set
135
+ SetReference child_set = 4 ;
136
+ // child_member represents the scenario where an specific object is considered member of the parent set
137
+ MemberReference child_member = 5 ;
138
+ }
139
+ }
140
+
141
+ message SetReference {
142
+ // object_type is the type of object in a permission set
143
+ string object_type = 1 ;
144
+ // object_id is the ID of a permission set
145
+ string object_id = 2 ;
146
+ // permission_or_relation is the permission or relation referenced by this permission set
147
+ string permission_or_relation = 3 ;
148
+ }
149
+
150
+ message MemberReference {
151
+ // object_type is the type of object of a permission set member
152
+ string object_type = 1 ;
153
+ // object_id is the ID of a permission set member
154
+ string object_id = 2 ;
155
+ // optional_permission_or_relation is the permission or relation referenced by this permission set member
156
+ string optional_permission_or_relation = 3 ;
157
+ }
0 commit comments