---
title: Resource Types
description: Define valid relations and permissions for resource types.
---

## Overview

Resource types define the schema for your authorization model. They declare which relations and permissions are valid for each type of object.

Every config example on this page comes in two flavours: **Go** (programmatic — call the store directly) and **DSL** (declarative — write `.warden` source and apply it via [`warden apply`](/docs/warden/integration/dsl-tooling#cli) or [`dsl.ApplyFS`](/docs/warden/integration/dsl-tooling#programmatic-apply)). They produce identical state.

### ResourceType Fields

| Field | Type | Description |
|-------|------|-------------|
| `ID` | `id.ResourceTypeID` | TypeID (`rtype_...`) — auto-assigned on Create when unset |
| `Name` | `string` | Resource type name (e.g., "document") |
| `Description` | `string` | Human-readable description |
| `Relations` | `[]RelationDef` | Valid relations for this type |
| `Permissions` | `[]PermissionDef` | Derived permission expressions |

## Defining Relations

<Tabs items={["Go", "DSL"]}>
<Tab value="Go">

```go
import "github.com/xraph/warden/resourcetype"

// IDs and CreatedAt/UpdatedAt timestamps are auto-assigned by the
// store on Create — set them only when you need a specific value.
docType := &resourcetype.ResourceType{
    Name:        "document",
    Description: "A document resource",
    Relations: []resourcetype.RelationDef{
        {Name: "owner",  AllowedSubjects: []string{"user"}},
        {Name: "editor", AllowedSubjects: []string{"user", "team#member"}},
        {Name: "viewer", AllowedSubjects: []string{"user", "team#member", "group#member"}},
        {Name: "parent", AllowedSubjects: []string{"folder"}},
    },
    Permissions: []resourcetype.PermissionDef{
        {Name: "read",   Expression: "viewer or editor or owner or parent->read"},
        {Name: "write",  Expression: "editor or owner or parent->write"},
        {Name: "delete", Expression: "owner"},
    },
}
err := store.CreateResourceType(ctx, docType)
// docType.ID is now populated.
```

</Tab>
<Tab value="DSL">

```warden
warden config 1
tenant t1

resource document {
  description = "A document resource"

  relation owner:  user
  relation editor: user | team#member
  relation viewer: user | team#member | group#member
  relation parent: folder

  permission read   = viewer or editor or owner or parent->read
  permission write  = editor or owner or parent->write
  permission delete = owner
}
```

The expression algebra (`or`, `and`, `not`, `->`) is the same as the Go form — `Expression` strings parse with the same grammar. See the [language reference](/docs/warden/integration/dsl-reference#permission-expressions) for precedence rules and traversal semantics.

</Tab>
</Tabs>

## RelationDef

| Field | Type | Description |
|-------|------|-------------|
| `Name` | `string` | Relation name (e.g., "viewer") |
| `AllowedSubjects` | `[]string` | Allowed subject types — bare types (`user`) or subject sets (`group#member`) |

## PermissionDef

| Field | Type | Description |
|-------|------|-------------|
| `Name` | `string` | Permission name (e.g., "read") |
| `Expression` | `string` | Boolean expression over relations (e.g., `"viewer or editor or owner"`) |

## Example: Google Drive-like Model

<Tabs items={["Go", "DSL"]}>
<Tab value="Go">

```go
// Folder type
folderType := &resourcetype.ResourceType{
    Name: "folder",
    Relations: []resourcetype.RelationDef{
        {Name: "owner",  AllowedSubjects: []string{"user"}},
        {Name: "editor", AllowedSubjects: []string{"user", "group#member"}},
        {Name: "viewer", AllowedSubjects: []string{"user", "group#member"}},
    },
    Permissions: []resourcetype.PermissionDef{
        {Name: "read",  Expression: "viewer or editor or owner"},
        {Name: "write", Expression: "editor or owner"},
        {Name: "share", Expression: "owner"},
    },
}

// Document type — `parent->read` traverses the parent relation and
// re-checks read on the resulting folder.
docType := &resourcetype.ResourceType{
    Name: "document",
    Relations: []resourcetype.RelationDef{
        {Name: "owner",  AllowedSubjects: []string{"user"}},
        {Name: "editor", AllowedSubjects: []string{"user", "group#member"}},
        {Name: "viewer", AllowedSubjects: []string{"user", "group#member"}},
        {Name: "parent", AllowedSubjects: []string{"folder"}},
    },
    Permissions: []resourcetype.PermissionDef{
        {Name: "read",   Expression: "viewer or editor or owner or parent->read"},
        {Name: "write",  Expression: "editor or owner or parent->write"},
        {Name: "delete", Expression: "owner"},
    },
}
```

</Tab>
<Tab value="DSL">

```warden
warden config 1
tenant t1

// Folder type
resource folder {
  relation owner:  user
  relation editor: user | group#member
  relation viewer: user | group#member

  permission read  = viewer or editor or owner
  permission write = editor or owner
  permission share = owner
}

// Document type — `parent->read` traverses the parent relation and
// re-checks read on the resulting folder.
resource document {
  relation owner:  user
  relation editor: user | group#member
  relation viewer: user | group#member
  relation parent: folder

  permission read   = viewer or editor or owner or parent->read
  permission write  = editor or owner or parent->write
  permission delete = owner
}
```

</Tab>
</Tabs>

## CRUD Operations

These are runtime API calls — they don't have a DSL equivalent because the DSL is declarative (you describe the desired end state, not the operation). The applier picks the right CRUD call internally.

```go
// Create
store.CreateResourceType(ctx, rt)

// Get by ID
rt, _ := store.GetResourceType(ctx, rtID)

// Get by name
rt, _ := store.GetResourceTypeByName(ctx, tenantID, "document")

// List
types, _ := store.ListResourceTypes(ctx, &resourcetype.ListFilter{
    TenantID: "t1",
    Limit:    50,
})

// Update
store.UpdateResourceType(ctx, rt)

// Delete
store.DeleteResourceType(ctx, rtID)
```
