Skip to content

Latest commit

 

History

History
395 lines (287 loc) · 13.7 KB

File metadata and controls

395 lines (287 loc) · 13.7 KB

import { Callout } from "nextra/components"; import YouTube from "@/components/youtube-wrapper";

Validation, Testing, Debugging SpiceDB Schemas

Whether you're designing the first iteration of your schema or you're running SpiceDB in production, you'll want tools to build confidence in performance, correctness, and design choices. Tools for validation, testing, and debugging are often overlooked by those building bespoke systems because they can only dedicate enough engineering resources to solving their problem, rather than creating the proper foundation they'll need to continue to be successful in the future.

SpiceDB has been designed with an eye towards being a foundation for authorization and subsequently provides various tools for working.

SpiceDB

Integration Test Server

In order for applications to more easily perform integration tests against SpiceDB, there is a command in SpiceDB for running an integration test server. The integration test server provides an isolated, empty datastore for each unique preshared key used to authenticate an API request. The result of this design is that applications can run integration tests in parallel all against a single SpiceDB so long as they provide a unique credential per test. By default, the server runs on port 50051 and also runs an additional read-only server on port 50052.

You can run the integration test server by executing spicedb serve-testing or by using our GitHub Action that runs the same command.

CheckPermission Tracing Header

While it is recommended that SpiceDB schema be validated and tested before production deployment, there are many scenarios where being able to see the actual paths taken against production data is incredibly important.

This method is deprecated. As of v1.30.0+, use the Check Tracing method with withTracing: true instead, which returns trace data directly in the response body.

In versions prior to v1.30.0, this method uses gRPC metadata headers to request debug trace information, with the trace data returned in the response trailer.

**Warning:** Collecting these traces has a notable performance overhead.

We do not recommend configuring your applications to enable this when debugging. Instead, we recommend using zed's explain flag for this purpose.

To request debug information, set the header io.spicedb.requestdebuginfo to 1.

**Note:** In SpiceDB v1.30.0+, this header still works but the trace data is returned in the response body (`debugTrace` field) rather than the trailer. The trailer `io.spicedb.respmeta.debuginfo` will contain a message indicating to check the response body instead.

Example

Using the zed CLI with the --explain flag (recommended):

zed permission check --explain document:firstdoc view user:fred

This will print a visual tree of the permission check trace.

Playground

Assertions

In order to ensure that particular invariants are maintained in a schema, assertions about permissionship can be made.

Assertions come in two flavors: positive and negative. Assertions are written as a YAML list containing zero or more relationships.

assertTrue:
  - "document:validation-testing-debugging#reader@user:you"
assertFalse: []

Caveat Context In Assertions

In order to escape JSON representation of the caveat context in an assertion you should use single-quotes.

You can provide caveat context as part of an assertion:

assertTrue:
  - 'document:validation-testing-debugging#reader@user:you with {"somecondition": 42, "anothercondition": "hello world"}'
assertFalse: []

You can also assert that a caveat context is required for a particular expression using assertCaveated:

assertTrue: []
assertCaveated:
  - "document:validation-testing-debugging#reader@user:you"
assertFalse: []

Check Watches

Check Watches are type of assertion that updates in real-time with changes in the Playground. This enables an even tighter feedback-loop when developing a schema.

Below is an example of configuring a Check Watch:


Watches can show any of the following states:

  • ✅ Permission Allowed
  • ❔ Permission Caveated
  • ❌ Permission Denied
  • ⚠️ Invalid Check

check-watches

Expected Relations

Expected Relations are a type of assertion that can be used to enumerate access to a specific relation. This is useful when you want to exhaustively determine all the possible ways that one might acquire access.

Expected Relations are written as YAML lists for each relation:

document:validation-testing-debugging#reader:
  - "[user:you] is <document:validation-testing-debugging#reader>"

Because access can be transitive, Expected Relations include how they achieved access. For example, if a schema is modeled hierarchically with a platform, organization, and project, Expected Relations for projects will include subjects from all points of the hierarchy that have access:

project:docs#admin:
  - "[organization:authzed] is <project:docs#owner>"
  - "[user:rauchg] is <platform:vercel#admin>"

Caveats in Expected Relations

When caveats are involved, and due to the unbounded nature of it, the Playground will focus on enumerating expected relations with "maybe" semantics. You can't specify an expected relation with a specific caveat context, because the Playground supports inferring those for you, and that would lead potentially to an infinite number of possible caveat context values.

What you'll see is an expected relation with the caveat context denoted as [...] right after the resource. This reads as user:rauchg may have admin permission over platform vercel.

project:docs#admin:
  - "[user:rauchg[...]] is <platform:vercel#admin>"

Exceptions in Expected Relations

There are also scenarios where an expected relation is described with an exception, which indicates that a permission holds for a specific resource and subject pair, but with a potential exception.

The following example reads like: user:rauchg has admin permission over platform vercel, unless user:rauchg is banned.

project:docs#admin:
  - "[user:rauchg[...]] is <platform:vercel#admin>/<platform:vercel#banned>"

Check Tracing

This is the modern, recommended method for collecting trace information (available in SpiceDB v1.30.0+). Unlike the legacy CheckPermission Tracing Header approach (which returned trace data in a response trailer in versions prior to v1.30.0), this method returns the trace as a structured field directly in the response message body.

SpiceDB supports tracing of check requests to view the path(s) taken to compute the result, as well as timing information.

To request tracing information, set withTracing: true in the request message. The trace data will be returned in the response message.

Key differences from the header-based approach:

  • More structured and easier to parse programmatically
  • Preferred for modern integrations
**Warning:** In versions older than v1.30.0, you must request tracing information via a header, and the trace data will be returned in the response trailer as JSON.

Example Request/Response

Request (using grpcurl):

grpcurl \
  -H "Authorization: Bearer your-token-here" \
  -d '{
    "resource": {"objectType": "document", "objectId": "firstdoc"},
    "permission": "view",
    "subject": {"object": {"objectType": "user", "objectId": "fred"}},
    "withTracing": true
  }' \
  your-spicedb-instance:443 \
  authzed.api.v1.PermissionsService/CheckPermission

Response:

{
  "permissionship": "PERMISSIONSHIP_HAS_PERMISSION",
  "checkedAt": {
    "token": "GhUKEzE3MDYxMTIwMDAwMDAwMDAwMDA="
  },
  "debugTrace": {
    "check": {
      "resource": {
        "objectType": "document",
        "objectId": "firstdoc"
      },
      "permission": "view",
      "permissionType": "PERMISSION_TYPE_PERMISSION",
      "subject": {
        "object": {
          "objectType": "user",
          "objectId": "fred"
        }
      },
      "result": "PERMISSIONSHIP_HAS_PERMISSION",
      "duration": "0.000125s",
      "subProblems": {
        "traces": [
          {
            "resource": {
              "objectType": "document",
              "objectId": "firstdoc"
            },
            "permission": "reader",
            "permissionType": "PERMISSION_TYPE_RELATION",
            "subject": {
              "object": {
                "objectType": "user",
                "objectId": "fred"
              }
            },
            "result": "PERMISSIONSHIP_HAS_PERMISSION",
            "duration": "0.000045s"
          }
        ]
      }
    },
    "schemaUsed": "definition document {\n  relation reader: user\n  permission view = reader\n}\n\ndefinition user {}"
  }
}

The debugTrace field contains a DebugInformation object with:

  • check: A CheckDebugTrace object containing the trace tree
  • schemaUsed: The schema that was used for the check

Each CheckDebugTrace includes:

  • resource: The resource being checked
  • permission: The permission or relation name
  • permissionType: Whether it's a PERMISSION_TYPE_PERMISSION or PERMISSION_TYPE_RELATION
  • subject: The subject of the check
  • result: The permissionship result
  • duration: Time spent on this check (as a Duration string, e.g., "0.000125s")
  • subProblems: Contains traces array with nested CheckDebugTrace objects, or wasCachedResult: true if cached

Zed

Zed Validate

The zed binary provides a means of validating and testing a schema locally and in CI:

zed validate my-schema.zed

It will load and validate the schema using the same parsing logic that the SpiceDB binary uses, ensuring that a schema that passes validation will be considered a valid schema by your SpiceDB instance.

Note that a schema write can still fail if a relation is removed and there are still instances of that relation in your database. `zed` doesn't know about your data.

You can validate the functionality of your schema using validation yaml files, such as those exported by the Playground:

zed validate schema-and-validations.yaml

Validation files take this form:

schema: |-
  // schema goes here
# -- OR --
schemaFile: "./path/to/schema.zed"

# Note that relations are a single heredoc string rather than a yaml list
relationships: |-
  object:foo#relation@subject:bar
  object:baz#relation@subject:qux

assertions:
  assertTrue:
    - object:foo#relation@subject:bar
  assertFalse:
    - object:foo#relation@subject:qux
validation:
  object:foo#relation:
    - "[subject:bar] is <object:foo#user>"

As of version v0.25.0, zed validate command can take multiple files as arguments:

zed validate some-validations.yaml some-other-validations.yaml

This means you can validate a folder full of files using shell globbing:

zed validate validations/*

There's an example of this available in the examples repository.

If you're using GitHub, there's a GitHub Action for running this validation.

Explain Flag

The zed permission check command has an optional flag, --explain, that will cause SpiceDB to collect the actual paths taken against the live system to compute a permission check. If you're interested in learning more about this functionality in SpiceDB, you can read about the tracing header above.

Here's an example using --explain:

$ zed permission check --explain document:firstdoc view user:fred
true
�[32m✓�[0m �[37mdocument�[0m:�[37mfirstdoc�[0m �[38;5;35mview�[0m (66.333µs)
├── �[31m⨉�[0m �[90mdocument�[0m:�[90mfirstdoc�[0m �[90mwriter�[0m (12.375µs)
└── �[32m✓�[0m �[37mdocument�[0m:�[37mfirstdoc�[0m �[38;5;166mreader�[0m (20.667µs)
   └── �[38;5;99muser:fred �[0m

This command will also highlight which parts of the traversal were cached and if a cycle is detected.

SpiceDB GitHub Actions

This GitHub Action runs the SpiceDB Integration Test Server for your workflows with the ability to configure different versions of SpiceDB.

Here's an example snippet of a GitHub Workflow:

steps:
  - uses: "authzed/action-spicedb@v1"
    with:
      version: "latest"
**Info:** This tool is highly recommended because it can prevent deployments of unverified changes.

The Playground offers a variety of tools that are useful for validating a design, but running the playground isn't designed for operating within a typical CI/CD environment.

Zed provides a command for validation of files exported from the playground which is a perfect fit for being executed within a typical CI/CD environment.

This GitHub Action runs the zed validation command on a provided file for your workflows.

Here's an example snippet of a GitHub Workflow:

steps:
  - uses: "actions/checkout@v4"
  - uses: "authzed/action-spicedb-validate@v1"
    with:
      validationfile: "your-schema.yaml"