Skip to content

Commit 835c8e9

Browse files
committed
docs: document underscore prefix for private identifiers
1 parent e7390fe commit 835c8e9

File tree

10 files changed

+247
-93
lines changed

10 files changed

+247
-93
lines changed

docs/schema-language.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# SpiceDB Schema Language Guide
2+
3+
## Identifier Naming Conventions
4+
5+
### Private/Internal Identifiers
6+
7+
SpiceDB supports using underscore (`_`) as a prefix for identifiers to establish a convention for marking definitions as "private" or "internal". This is particularly useful for:
8+
9+
- **Synthetic permissions**: Permissions that exist only to compose other permissions
10+
- **Internal relations**: Relations not meant to be directly referenced by application code
11+
- **Implementation details**: Parts of your schema that may change without affecting the public API
12+
13+
### Examples
14+
15+
```zed
16+
definition document {
17+
// Public relation - exposed to application code
18+
relation viewer: user
19+
20+
// Private relation - used internally for permission composition
21+
relation _internal_viewer: user
22+
23+
// Public permission using private components
24+
permission view = viewer + _internal_viewer
25+
}
26+
27+
definition _internal_resource {
28+
// This entire resource type is marked as internal
29+
relation owner: user
30+
}
31+
```
32+
33+
### Best Practices
34+
35+
1. **Use underscore prefix for synthetic permissions**:
36+
```zed
37+
definition resource {
38+
relation owner: user
39+
relation editor: user
40+
41+
// Private synthetic permission
42+
permission _can_write = owner + editor
43+
44+
// Public permissions built on private ones
45+
permission edit = _can_write
46+
permission delete = owner
47+
}
48+
```
49+
50+
2. **Mark implementation details as private**:
51+
```zed
52+
definition folder {
53+
relation parent: folder
54+
relation viewer: user
55+
56+
// Private permission for traversal logic
57+
permission _parent_view = parent->view
58+
59+
// Public permission combining direct and inherited access
60+
permission view = viewer + _parent_view
61+
}
62+
```
63+
64+
3. **Future module system compatibility**: When SpiceDB introduces a module system for composing schemas via libraries, underscore-prefixed identifiers will help clearly distinguish between public and private APIs.
65+
66+
### Technical Details
67+
68+
- Identifiers can begin with either a letter (`a-z`) or underscore (`_`)
69+
- After the first character, identifiers can contain letters, numbers, and underscores
70+
- Identifiers must end with an alphanumeric character (not an underscore)
71+
- Valid: `_private`, `_internal_relation`, `_helper123`
72+
- Invalid: `_trailing_` (must end with alphanumeric), `123_start` (must start with letter or underscore)
73+
- Note: `__double` is technically valid by the regex pattern but discouraged for readability
74+
75+
This naming convention is enforced at the schema level and is compatible with all SpiceDB operations and queries.

e2e/newenemy/newenemy_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ definition {{.Resource}} {
6464
{{ end }}
6565
`
6666
objIDRegex = "[a-zA-Z0-9_][a-zA-Z0-9/_-]{0,127}"
67-
namespacePrefixRegex = "[a-z][a-z0-9_]{1,62}[a-z0-9]"
67+
namespacePrefixRegex = "[a-z_][a-z0-9_]{1,62}[a-z0-9]"
6868
)
6969

7070
var (

pkg/composableschemadsl/compiler/compiler_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ func TestCompile(t *testing.T) {
769769
"invalid definition name",
770770
nilPrefix,
771771
`definition someTenant/fo {}`,
772-
"parse error in `invalid definition name`, line 1, column 1: error in object definition someTenant/fo: invalid NamespaceDefinition.Name: value does not match regex pattern \"^([a-z][a-z0-9_]{1,62}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$\"",
772+
"parse error in `invalid definition name`, line 1, column 1: error in object definition someTenant/fo: invalid NamespaceDefinition.Name: value does not match regex pattern \"^([a-z_][a-z0-9_]{1,62}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9]$\"",
773773
[]SchemaDefinition{},
774774
},
775775
{
@@ -778,7 +778,7 @@ func TestCompile(t *testing.T) {
778778
`definition some_tenant/foos {
779779
relation ab: some_tenant/foos
780780
}`,
781-
"parse error in `invalid relation name`, line 2, column 5: error in relation ab: invalid Relation.Name: value does not match regex pattern \"^[a-z][a-z0-9_]{1,62}[a-z0-9]$\"",
781+
"parse error in `invalid relation name`, line 2, column 5: error in relation ab: invalid Relation.Name: value does not match regex pattern \"^[a-z_][a-z0-9_]{1,62}[a-z0-9]$\"",
782782
[]SchemaDefinition{},
783783
},
784784
{

pkg/development/wasm/operations_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func TestCheckOperation(t *testing.T) {
131131
tuple.MustParse("somenamespace:someobj#anotherrel@user:foo"),
132132
nil,
133133
&devinterface.DeveloperError{
134-
Message: "error in object definition fo: invalid NamespaceDefinition.Name: value does not match regex pattern \"^([a-z][a-z0-9_]{1,62}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$\"",
134+
Message: "error in object definition fo: invalid NamespaceDefinition.Name: value does not match regex pattern \"^([a-z_][a-z0-9_]{1,62}[a-z0-9]/)*[a-z_][a-z0-9_]{1,62}[a-z0-9]$\"",
135135
Kind: devinterface.DeveloperError_SCHEMA_ISSUE,
136136
Source: devinterface.DeveloperError_SCHEMA,
137137
Line: 1,

pkg/proto/core/v1/core.pb.go

Lines changed: 39 additions & 39 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)