From 4b26b6e3fec73dff99470d3c9e7da0cbea307318 Mon Sep 17 00:00:00 2001 From: ivanauth Date: Fri, 19 Dec 2025 18:45:09 -0500 Subject: [PATCH] feat: allow underscore prefix for definition, relation, and permission identifiers Updates regex patterns across all API protos to allow identifiers to begin with an underscore (`_`). This enables a convention for marking identifiers as "private" or "internal". Use cases: - **Synthetic permissions**: Permissions that exist only to compose other permissions - **Internal relations**: Relations not meant to be directly referenced by application code - **Implementation details**: Parts of your schema that may change without affecting the public API Example: ```zed definition document { relation viewer: user relation _internal_viewer: user // Private synthetic permission permission _can_view = viewer + _internal_viewer // Public permission permission view = _can_view } ``` Companion PR to authzed/spicedb#2733 --- authzed/api/v1/core.proto | 12 +++--- authzed/api/v1/experimental_service.proto | 16 ++++---- authzed/api/v1/permission_service.proto | 48 +++++++++++------------ authzed/api/v1/watch_service.proto | 8 ++-- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/authzed/api/v1/core.proto b/authzed/api/v1/core.proto index ca49675..15f600d 100644 --- a/authzed/api/v1/core.proto +++ b/authzed/api/v1/core.proto @@ -23,11 +23,11 @@ message Relationship { // relation is how the resource and subject are related. string relation = 2 [ (validate.rules).string = { - pattern: "^[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 64 } ]; @@ -81,11 +81,11 @@ message SubjectReference { ]; string optional_relation = 2 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; @@ -95,11 +95,11 @@ message SubjectReference { message ObjectReference { string object_type = 1 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^([a-z_][a-z0-9_]{1,61}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 128 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^([a-z_][a-z0-9_]{1,61}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 128 } ]; diff --git a/authzed/api/v1/experimental_service.proto b/authzed/api/v1/experimental_service.proto index 5988da1..f70c624 100644 --- a/authzed/api/v1/experimental_service.proto +++ b/authzed/api/v1/experimental_service.proto @@ -140,11 +140,11 @@ message ExperimentalRegisterRelationshipCounterRequest { // name is the name of the counter being registered. string name = 1 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; @@ -163,11 +163,11 @@ message ExperimentalCountRelationshipsRequest { // name is the name of the counter whose count is being requested. string name = 1 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; @@ -198,11 +198,11 @@ message ExperimentalUnregisterRelationshipCounterRequest { // name is the name of the counter being unregistered. string name = 1 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; @@ -229,11 +229,11 @@ message BulkCheckPermissionRequestItem { string permission = 2 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; diff --git a/authzed/api/v1/permission_service.proto b/authzed/api/v1/permission_service.proto index 6337588..1d30b55 100644 --- a/authzed/api/v1/permission_service.proto +++ b/authzed/api/v1/permission_service.proto @@ -201,11 +201,11 @@ message RelationshipFilter { // NOTE: It is not prefixed with "optional_" for legacy compatibility. string resource_type = 1 [ (validate.rules).string = { - pattern: "^(([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^(([a-z_][a-z0-9_]{1,61}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 128 }, (buf.validate.field).string = { - pattern: "^(([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^(([a-z_][a-z0-9_]{1,61}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 128 } ]; @@ -239,11 +239,11 @@ message RelationshipFilter { // relation is the *optional* relation of the relationship. string optional_relation = 3 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; @@ -260,11 +260,11 @@ message SubjectFilter { message RelationFilter { string relation = 1 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; @@ -272,11 +272,11 @@ message SubjectFilter { string subject_type = 1 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^([a-z_][a-z0-9_]{1,61}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 128 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^([a-z_][a-z0-9_]{1,61}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 128 } ]; @@ -482,11 +482,11 @@ message CheckPermissionRequest { // the check. string permission = 3 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; @@ -582,11 +582,11 @@ message CheckBulkPermissionsRequestItem { string permission = 2 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; @@ -663,11 +663,11 @@ message ExpandPermissionTreeRequest { // expansion for the resource. string permission = 3 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; @@ -692,11 +692,11 @@ message LookupResourcesRequest { // be returned. string resource_object_type = 2 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^([a-z_][a-z0-9_]{1,61}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 128 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^([a-z_][a-z0-9_]{1,61}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 128 } ]; @@ -705,11 +705,11 @@ message LookupResourcesRequest { // must Check. string permission = 3 [ (validate.rules).string = { - pattern: "^[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 64 } ]; @@ -802,11 +802,11 @@ message LookupSubjectsRequest { // the subjects. string permission = 3 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; @@ -815,11 +815,11 @@ message LookupSubjectsRequest { // be returned. string subject_object_type = 4 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^([a-z_][a-z0-9_]{1,61}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 128 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$" + pattern: "^([a-z_][a-z0-9_]{1,61}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 128 } ]; @@ -827,11 +827,11 @@ message LookupSubjectsRequest { // optional_subject_relation is the optional relation for the subject. string optional_subject_relation = 5 [ (validate.rules).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 }, (buf.validate.field).string = { - pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" + pattern: "^([a-z_][a-z0-9_]{1,62}[a-z0-9])?$" max_bytes: 64 } ]; diff --git a/authzed/api/v1/watch_service.proto b/authzed/api/v1/watch_service.proto index 0fba97b..99e3e1a 100644 --- a/authzed/api/v1/watch_service.proto +++ b/authzed/api/v1/watch_service.proto @@ -45,14 +45,14 @@ message WatchRequest { (buf.validate.field).repeated.min_items = 0, (validate.rules).repeated.items.string = { pattern: - "^([a-z][a-z0-9_]{1,62}[a-z0-9]/" - ")*[a-z][a-z0-9_]{1,62}[a-z0-9]$" + "^([a-z_][a-z0-9_]{1,62}[a-z0-9]/" + ")*[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 128 }, (buf.validate.field).repeated.items.string = { pattern: - "^([a-z][a-z0-9_]{1,62}[a-z0-9]/" - ")*[a-z][a-z0-9_]{1,62}[a-z0-9]$" + "^([a-z_][a-z0-9_]{1,62}[a-z0-9]/" + ")*[a-z_][a-z0-9_]{1,62}[a-z0-9]$" max_bytes: 128 } ];