Warden
1.x
Docs/Warden/Relations & Tuples
Open

Reading6 min
Updated31 Jul 2026
Sourcev1/authorization/relations-tuples.mdx

Relation Tuples01

A relation tuple represents a relationship between a subject and an object. It is the fundamental building block of ReBAC.

Every config example on this page comes in two flavours: Go (programmatic — call the store directly) and DSL (declarative — .warden source applied via warden apply or dsl.ApplyFS). They produce identical tuples in the relations table.

What goes in source vs runtime? Schema declarations (resource types, relation definitions) are config — keep them in .warden. Individual relation tuples (document:welcome owner = user:alice) usually accumulate at runtime as users share documents, join teams, etc. — those are written via the store API. The relation declaration in .warden is for fixtures and bootstrap state, not high-volume tuple writes.

Tuple Fields

FieldTypeDescription
IDid.RelationIDTypeID (rel_...) — auto-assigned on Create when unset
ObjectTypestringType of the object (e.g., "document")
ObjectIDstringID of the object
RelationstringRelationship name (e.g., "viewer", "editor", "owner")
SubjectTypestringType of the subject (e.g., "user", "team")
SubjectIDstringID of the subject
SubjectRelationstringOptional: relation on the subject (for subject sets)

Notation

Tuples are often written in this notation:

object_type:object_id#relation@subject_type:subject_id

Examples:

document:doc-123#viewer@user:user-42
project:alpha#editor@team:engineering
team:engineering#member@user:user-42
folder:root#parent@document:doc-123

Creating Tuples02

go
import "github.com/xraph/warden/relation"

// "user:42 is a viewer of document:123" — ID is auto-assigned by the store on Create.
t := &relation.Tuple{
    ObjectType:  "document",
    ObjectID:    "doc-123",
    Relation:    "viewer",
    SubjectType: "user",
    SubjectID:   "user-42",
}
err := store.CreateRelation(ctx, t)

Graph Traversal03

Warden uses BFS (breadth-first search) to traverse relation graphs and find transitive permissions.

Example: Team Membership

user:42 ──member──▶ team:eng ──editor──▶ project:alpha
go
// Create membership
store.CreateRelation(ctx, &relation.Tuple{
    ObjectType:  "team",
    ObjectID:    "eng",
    Relation:    "member",
    SubjectType: "user",
    SubjectID:   "user-42",
})

// Create team's access to project
store.CreateRelation(ctx, &relation.Tuple{
    ObjectType:  "project",
    ObjectID:    "alpha",
    Relation:    "editor",
    SubjectType: "team",
    SubjectID:   "eng",
})

// Check: can user:42 edit project:alpha?
result, _ := eng.Check(ctx, &warden.CheckRequest{
    Subject:  warden.Subject{Kind: "user", ID: "user-42"},
    Action:   warden.Action{Name: "write"},
    Resource: warden.Resource{Type: "project", ID: "alpha"},
})
// result.Allowed == true (via transitive relation through team:eng)

Example: Document Sharing

go
// Share document with specific user
store.CreateRelation(ctx, &relation.Tuple{
    ObjectType:  "document",
    ObjectID:    "doc-456",
    Relation:    "viewer",
    SubjectType: "user",
    SubjectID:   "user-99",
})

// Share entire folder with a team
store.CreateRelation(ctx, &relation.Tuple{
    ObjectType:  "folder",
    ObjectID:    "shared",
    Relation:    "viewer",
    SubjectType: "team",
    SubjectID:   "marketing",
    SubjectRelation: "member",
})

// Document belongs to folder
store.CreateRelation(ctx, &relation.Tuple{
    ObjectType:  "document",
    ObjectID:    "doc-456",
    Relation:    "parent",
    SubjectType: "folder",
    SubjectID:   "shared",
})

Graph Walker Configuration04

The BFS graph walker has configurable limits:

eng, _ := warden.NewEngine(
    warden.WithStore(store),
    warden.WithConfig(warden.Config{
        MaxGraphDepth: 10, // Maximum BFS depth (default: 10)
    }),
)
  • Cycle detection — The walker tracks visited nodes to avoid infinite loops.

  • Max depth — Limits traversal depth to prevent runaway queries.

  • Returns ErrMaxDepthReached if the limit is hit without finding a path.

  • Returns ErrCycleDetected if a cycle is found.

Listing Tuples05

tuples, _ := store.ListRelations(ctx, &relation.ListFilter{
    TenantID:   "t1",
    ObjectType: "document",
    ObjectID:   "doc-123",
    Relation:   "viewer",
    Limit:      50,
})

Deleting Tuples06

err := store.DeleteRelationTuple(ctx, "t1",
    "document", "doc-123", "viewer", "user", "user-42")