Skip to content
75 changes: 75 additions & 0 deletions docs/schema-language.md
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we writing this doc here? This will not get published anywhere. Maybe we should update https://authzed.com/docs/spicedb/concepts/schema instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, i removed docs from this pr and created a new docs pr.

Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# SpiceDB Schema Language Guide

## Identifier Naming Conventions

### Private/Internal Identifiers

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:

- **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

### Examples

```zed
definition document {
// Public relation - exposed to application code
relation viewer: user

// Private relation - used internally for permission composition
relation _internal_viewer: user

// Public permission using private components
permission view = viewer + _internal_viewer
}

definition _internal_resource {
// This entire resource type is marked as internal
relation owner: user
}
```

### Best Practices

1. **Use underscore prefix for synthetic permissions**:
```zed
definition resource {
relation owner: user
relation editor: user

// Private synthetic permission
permission _can_write = owner + editor

// Public permissions built on private ones
permission edit = _can_write
permission delete = owner
}
```

2. **Mark implementation details as private**:
```zed
definition folder {
relation parent: folder
relation viewer: user

// Private permission for traversal logic
permission _parent_view = parent->view

// Public permission combining direct and inherited access
permission view = viewer + _parent_view
}
```

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.

### Technical Details

- Identifiers can begin with either a letter (`a-z`) or underscore (`_`)
- After the first character, identifiers can contain letters, numbers, and underscores
- Identifiers must end with an alphanumeric character (not an underscore)
- Valid: `_private`, `_internal_relation`, `_helper123`
- Invalid: `_trailing_` (must end with alphanumeric), `123_start` (must start with letter or underscore)
- Note: `__double` is technically valid by the regex pattern but discouraged for readability

This naming convention is enforced at the schema level and is compatible with all SpiceDB operations and queries.
2 changes: 1 addition & 1 deletion e2e/newenemy/newenemy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ definition {{.Resource}} {
{{ end }}
`
objIDRegex = "[a-zA-Z0-9_][a-zA-Z0-9/_-]{0,127}"
namespacePrefixRegex = "[a-z][a-z0-9_]{1,62}[a-z0-9]"
namespacePrefixRegex = "[a-z_][a-z0-9_]{1,62}[a-z0-9]"
)

var (
Expand Down
4 changes: 2 additions & 2 deletions pkg/composableschemadsl/compiler/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ func TestCompile(t *testing.T) {
"invalid definition name",
nilPrefix,
`definition someTenant/fo {}`,
"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]$\"",
"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]$\"",
[]SchemaDefinition{},
},
{
Expand All @@ -778,7 +778,7 @@ func TestCompile(t *testing.T) {
`definition some_tenant/foos {
relation ab: some_tenant/foos
}`,
"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]$\"",
"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]$\"",
[]SchemaDefinition{},
},
{
Expand Down
2 changes: 1 addition & 1 deletion pkg/development/wasm/operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func TestCheckOperation(t *testing.T) {
tuple.MustParse("somenamespace:someobj#anotherrel@user:foo"),
nil,
&devinterface.DeveloperError{
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]$\"",
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]$\"",
Kind: devinterface.DeveloperError_SCHEMA_ISSUE,
Source: devinterface.DeveloperError_SCHEMA,
Line: 1,
Expand Down
78 changes: 39 additions & 39 deletions pkg/proto/core/v1/core.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading