verdverm.com
09/06/2025, 11:17 PM<did>/<space>/[nsid]/<rkey>)
[full schema](https://github.com/blebbit/atproto/blob/main/packages/pds/src/authz/spicedb/schema/atproto.zed)
I have a resource type for each segment of the segments, with a parenting setup to support nesting/hierarchy, and each AtUri has an associate data record tied to it, except for nsid. They are only for structuring records and granting permissions, in OAuth scopes today, and content/methods tbd, that's what I'm working on. NSID are defined by any DID, it's a very dynamic list. There are nsid for queries and procedures that will never have records, but we still want to put permissions over them. Certain nsid are expected to have high numbers of records and storing the parenting relationship in spice, which seems inefficient?
Are caveats something that can help me here? What if those caveats are large sets?
The new way to specify OAuth scopes over the dynamic NSID is as a permission set
https://github.com/bluesky-social/proposals/blob/main/0011-auth-scopes/README.md#permission-sets
I image we need something similar for the custom roles that we want in the content permission system, and then have to reflect those within spice by making a number of calls, or a bulk input? (maybe one day even replace the OAuth permission setup and unify the two... #futurology)yetitwo
09/07/2025, 2:25 AMverdverm.com
09/08/2025, 8:21 AMyetitwo
09/08/2025, 3:03 PMverdverm.com
09/08/2025, 9:00 PMyetitwo
09/08/2025, 9:08 PMverdverm.com
09/09/2025, 1:05 AMverdverm.com
09/09/2025, 1:13 AMdefinition superuser {} // PDS admin / moderation
definition anon {}
definition acct {}
definition oauth {}
definition apikey {}
definition svcacct {}
definition service {} // appview, labeler, feedgen, ... needs a DID
partial negative {
// various negating relations (should / do we need all of these here)
relation blocked: acct | service
relation muted: acct | service
relation banned: acct | service
relation takendown: acct | service
// is there a meta-permission here like
permission negated = blocked | muted | banned | takendown
}
definition record {
// space containment / nesting
relation parent: space
...owned
...negative
...record_crud
...record_iam
}
partial record_crud {
// Role CRUD relations
relation record_deleter: superuser |
acct | acct with nsid_allowed |
oauth | oauth with nsid_allowed |
apikey | apikey with nsid_allowed |
svcacct | svcacct with nsid_allowed |
service | service with nsid_allowed |
space#member | space#member with nsid_allowed |
group#member | group#member with nsid_allowed |
role#member | role#member with nsid_allowed
...
// Role CRUD permissions
permission record_delete = owner + record_deleter + parent->record_delete
permission record_update = record_delete + record_updater + parent->record_update
permission record_create = record_update + record_creator + parent->record_create
permission record_list = record_create + record_lister + parent->record_list
permission record_read = record_list + record_reader + parent->record_readverdverm.com
09/09/2025, 1:15 AMyetitwo
09/09/2025, 3:45 PMyetitwo
09/09/2025, 3:45 PMyetitwo
09/09/2025, 3:46 PMyetitwo
09/09/2025, 3:46 PMyetitwo
09/09/2025, 3:47 PMyetitwo
09/09/2025, 3:51 PMdefinition resource {
relation user: user
relation active: user:*
permission view = user & active
}
// becomes
definition resource {
relation user: user
relation active: resource
permission view = active->user
}
and you'd write a relation from a resource to itself to make it active.yetitwo
09/09/2025, 3:52 PMyetitwo
09/09/2025, 3:52 PMyetitwo
09/09/2025, 4:20 PMverdverm.com
09/09/2025, 5:02 PMverdverm.com
09/09/2025, 5:05 PMverdverm.com
09/09/2025, 6:08 PMverdverm.com
09/09/2025, 8:47 PMverdverm.com
09/09/2025, 8:48 PMverdverm.com
09/09/2025, 8:53 PMyetitwo
09/09/2025, 9:38 PMyetitwo
09/09/2025, 10:19 PMverdverm.com
09/09/2025, 10:46 PMverdverm.com
09/09/2025, 10:48 PM//
// Caveats
//
// NSID scoping
caveat nsid_allowed(nsid string, allowed_nsid list<string>) {
nsid in allowed_nsid
}
// Custom scoping for apps
caveat context_allowed(context string, allowed_contenxt list<string>) {
context in allowed_context
}
// for special use-cases, not expected to be generally used
caveat time_frame(beg string, end string, mode string) {
// tbd...
}verdverm.com
09/09/2025, 10:49 PMyetitwo
09/09/2025, 11:00 PMverdverm.com
09/09/2025, 11:04 PMyetitwo
09/09/2025, 11:32 PMverdverm.com
09/10/2025, 12:45 AMyetitwo
09/10/2025, 1:52 AMyetitwo
09/10/2025, 1:52 AMverdverm.com
09/10/2025, 10:08 PMverdverm.com
09/17/2025, 6:58 PMverdverm.com
09/20/2025, 10:08 PMyetitwo
09/21/2025, 3:13 PMyetitwo
09/21/2025, 3:14 PMverdverm.com
09/21/2025, 3:56 PMverdverm.com
09/21/2025, 4:12 PM<space>
- 100s <user> in 1+ <group>
- 100s <nsid>
- 1000s and beyond <records>
i.e. there is significant fan-out in the content tree, and also the records can refer to each other, which is probably a good relation to capture in Spice. Caveats seem like they will work for this, but I wonder about performance...
caveat nsids(allowed list<string>, nsid string) {
nsid in allowed <- this appears to be O(n) instead of O(n_logn)?
}
Though if it as a map... it could be O(1) and support both positive and negative associations with an NSID...?
It just seems like with NSIDs in the schema as a resource, to give access, we need to write 100s of relations to the "virtual" NSID (they are not stored, they are part of content addressing), versuss one with the caveats on itverdverm.com
09/21/2025, 6:49 PM// NSID filtering (think oauth permission sets and check)
// map vs list: while specifying is more cumbersome,
// O(1) vs O(N) runtime performance is compelling
caveat nsids(allowed map<bool>, nsid string) {
allowed[nsid] || false
}verdverm.com
09/21/2025, 6:55 PMverdverm.com
09/21/2025, 7:00 PM&& and || if any of their operands uniquely determines the result (false for && and true for ||) the other operand may or may not be evaluated, and if that evaluation produces a runtime error, it will be ignored.
This is not what I'm seeing, is this a bug?
11:59AM ERR terminated with errors error="rpc error: code = InvalidArgument desc = evaluation error for caveat nsids: no such key: bsky_blob"
ERROR: got expected false
from: https://github.com/google/cel-spec/blob/master/doc/langdef.md#logical-operatorsverdverm.com
09/21/2025, 11:21 PMcaveat nsids(allowed map<bool>, default bool, nsid string) {
(nsid in allowed) ? allowed[nsid] : (default || false)
}yetitwo
09/29/2025, 2:56 PMverdverm.com
09/29/2025, 3:02 PM[" caveat bryan", [_space, "record_viewer", _bryan, #"nsids:{"default": false, "allowed": {"bsky_post":true,"bsky_like":true}}"#]],
[" caveat devin", [_space, "record_viewer", _devin, #"nsids:{"allowed": {"bsky_post":true}}"#]],verdverm.com
09/29/2025, 3:11 PMverdverm.com
09/29/2025, 3:16 PMyetitwo
09/29/2025, 3:17 PMyetitwo
09/29/2025, 3:17 PMverdverm.com
09/29/2025, 3:37 PMyetitwo
09/29/2025, 3:37 PMverdverm.com
09/29/2025, 3:38 PMyetitwo
09/29/2025, 3:39 PMJoey
09/29/2025, 3:41 PM