@permify-toolkit/cli
CLI for managing schemas, seeding relationships, and querying relationship data from your Permify instance.
Installation
pnpm add @permify-toolkit/cli
Configuration
The CLI relies on a permify.config.ts file in your project root. See Configuration for full details.
Schema Definition Options
Inline Schema (AST-based)
import {
defineConfig,
schema,
entity,
relation,
permission
} from "@permify-toolkit/core";
export default defineConfig({
tenant: "t1",
client: {
endpoint: "localhost:3478",
insecure: true
},
schema: schema({
user: entity({
relations: {
manager: relation("user")
},
permissions: {
manage: permission("manager")
}
}),
document: entity({
relations: {
owner: relation("user"),
viewer: relation("user")
},
permissions: {
view: permission("viewer or owner"),
edit: permission("owner")
}
})
}),
relationships: {
seedFile: "./relationships.json",
mode: "append"
}
});
File-based Schema
import { defineConfig, schemaFile } from "@permify-toolkit/core";
export default defineConfig({
tenant: "t1",
client: { endpoint: "localhost:3478", insecure: true },
schema: schemaFile("./schema.perm")
});
Tenant Configuration
The --tenant flag is optional if tenant is defined in permify.config.ts.
Resolution order:
--tenantCLI flag (orPERMIFY_TENANTenv var)tenantfield inpermify.config.ts- Error if neither is provided
Commands
Schema
schema push
Pushes the schema defined in your config to the Permify server.
permify-toolkit schema push [--tenant <tenant-id>] [flags]
Flags:
| Flag | Alias | Description | Default |
|---|---|---|---|
--tenant | Tenant ID to push to | From config | |
--create-tenant | -c | Create tenant if it doesn't exist | false |
Examples:
# Push using tenant from config
permify-toolkit schema push
# Push to a specific tenant
permify-toolkit schema push --tenant my-tenant-id
# Push and create tenant if needed
permify-toolkit schema push --tenant new-tenant-id --create-tenant
Schema Validation:
The Permify server validates your schema on push. If there are errors, you'll see a detailed message:
Error: Entity "usr" referenced in relation "document.owner" does not exist
schema validate
Validates your schema locally without connecting to a Permify server. Catches structural errors, broken references, permission cycles, and suspicious patterns before you push.
permify-toolkit schema validate
This command takes no flags. It reads schema configuration from permify.config.ts in the current directory.
What it checks:
| Category | Examples |
|---|---|
| Input | Schema source exists, file readable, .perm extension required |
| Structure | At least one entity defined |
| References | Relation targets exist, permission symbols resolve, traversal targets valid |
| Expression syntax | No dangling operators (owner or), balanced parentheses, no double-dot traversal (parent..view) |
| Cycles | Direct self-reference (view = view), indirect cycles (view → edit → view) |
| Warnings | Unused relations, entities with no permissions |
Output:
When the schema is valid:
✔ Schema is valid
When valid but with warnings:
⚠ Schema is valid with warnings
Warnings:
1. Entity "document": relation "viewer" is never used in any permission
2. Entity "organization": has no permissions defined
When validation fails:
Error: Schema validation failed:
Permission "document.view" references undefined relation or permission "viewer"
Examples:
# Validate schema in your current project
permify-toolkit schema validate
# Validate before pushing in CI
permify-toolkit schema validate && permify-toolkit schema push
Run schema validate before schema push for instant local feedback, no server connection needed.
schema diff
Previews what will change before pushing a schema update. Compares your local schema (from permify.config.ts) against the schema currently deployed on the Permify server — or against another local .perm file.
permify-toolkit schema diff [--tenant <id>] [flags]
Flags:
| Flag | Alias | Description | Required | Default |
|---|---|---|---|---|
--tenant | Tenant ID to diff against | No | From config | |
--create-tenant | -c | Create tenant if it doesn't exist | No | false |
--source | -s | Path to a .perm file to compare against (local-vs-local mode) | No | |
--verbose | -v | Show raw unified text diff after the structural summary | No | false |
--exit-code | -e | Exit with code 1 if changes are detected (useful for CI pipelines) | No | false |
Examples:
# Compare local schema against what's deployed on the server
permify-toolkit schema diff
# Compare against a specific tenant
permify-toolkit schema diff --tenant staging
# Compare two local schemas (no server connection needed)
permify-toolkit schema diff --source ./old-schema.perm
# Include a unified text diff for full detail
permify-toolkit schema diff --verbose
# CI mode — fail the pipeline if schema has drifted
permify-toolkit schema diff --exit-code
Output:
The command shows a structural summary of changes at the entity, relation, and permission level:

- Green (
+) — added entities, relations, or permissions - Red (
-) — removed entities, relations, or permissions - Yellow (
~) — modified entities (contents changed), or individual relations/permissions whose definition changed
When no changes are detected:
✔ Schema is up to date — no changes detected (tenant: t1)
When no schema exists on the remote server yet (first-time push), everything is shown as additions:
Schema Diff — tenant: t1
ℹ No schema found on remote — showing full schema as additions
With --verbose, a unified text diff is appended below the structural summary:
--- remote (tenant: t1)
+++ local (permify.config.ts)
@@ -1,5 +1,8 @@
entity user {}
entity document {
relation owner @user
+ relation editor @user
- permission view = owner
+ permission view = owner or editor
+ permission edit = editor
}
Exit codes:
| Code | Meaning |
|---|---|
0 | No changes detected, or changes detected (default mode) |
1 | Changes detected and --exit-code flag is set |
Combine --exit-code with your CI to detect schema drift:
permify-toolkit schema diff --exit-code || echo "Schema has changed — review before pushing"
Run schema diff before schema push to review exactly what will change on the server. Pair with --verbose for a complete picture.
Relationships
relationships seed
Seeds relationship data from a JSON file.
permify-toolkit relationships seed [--tenant <id>] [--file-path <path>] [flags]
Flags:
| Flag | Alias | Description | Default |
|---|---|---|---|
--tenant | Tenant ID to seed to | From config | |
--file-path | -f | Path to JSON file with tuples | From config |
--create-tenant | -c | Create tenant if it doesn't exist | false |
Example relationships.json:
{
"tuples": [
{
"entity": { "type": "organization", "id": "org_1" },
"relation": "member",
"subject": { "type": "user", "id": "alice" }
},
{
"entity": { "type": "document", "id": "doc_1" },
"relation": "owner",
"subject": { "type": "user", "id": "bob" }
},
{
"entity": { "type": "document", "id": "doc_1" },
"relation": "viewer",
"subject": { "type": "user", "id": "charlie" }
}
]
}
Examples:
# Seed to existing tenant
permify-toolkit relationships seed --tenant my-tenant-id --file-path ./data/relationships.json
# Seed and create tenant
permify-toolkit relationships seed --tenant new-tenant-id --file-path ./relationships.json --create-tenant
relationships list
Queries and displays relationship tuples from a Permify tenant. Useful for debugging authorization — quickly see what relationships exist for a given entity type.
Unlike schema push or relationships seed, this command does not require a schema in your config. It only needs a client connection.
permify-toolkit relationships list --entity-type <type> [--tenant <id>] [flags]
Flags:
| Flag | Alias | Description | Required | Default |
|---|---|---|---|---|
--entity-type | -e | Entity type to query | Yes | |
--tenant | Tenant ID | No | From config | |
--entity-id | Filter by a specific entity ID | No | ||
--relation | -r | Filter by relation name | No | |
--subject-type | -s | Filter by subject type | No | |
--subject-id | Filter by subject ID | No | ||
--output | -o | Output format: table or compact | No | table |
--page-size | -p | Number of results per gRPC page | No | 50 |
Minimal config required:
// permify.config.ts — no schema needed for read-only commands
export default {
tenant: "t1",
client: {
endpoint: "localhost:3478",
insecure: true
}
};
Examples:
# List all relationships for the "document" entity type
permify-toolkit relationships list -e document
# Filter by relation and subject
permify-toolkit relationships list -e document -r viewer -s user
# Show a specific entity's relationships
permify-toolkit relationships list -e document --entity-id doc-1
# Use compact output (one tuple per line)
permify-toolkit relationships list -e document -o compact
# Specify tenant and page size
permify-toolkit relationships list -e document --tenant my-tenant -p 100
Output formats:
Table (default):
Entity Type Entity ID Relation Subject Type Subject ID Subject Relation
document doc-1 owner user alice
document doc-1 viewer user bob
document doc-2 viewer group eng member
✔ Found 3 relationships
Compact (one Zanzibar-style tuple per line):
document:doc-1#owner@user:alice
document:doc-1#viewer@user:bob
document:doc-2#viewer@group:eng#member
✔ Found 3 relationships
When no relationships match:
ℹ No relationships found.
Use relationships list to verify that the tuples you expect actually exist in Permify. If a permission check fails unexpectedly, list the relationships for that entity to see what's stored.
relationships export
Exports relationship tuples from a Permify tenant to a JSON file. The output format is identical to the relationships seed input format — so you can export from one tenant and seed into another.
Like relationships list, this command does not require a schema in your config.
permify-toolkit relationships export --entity-type <type> --file-path <path> [--tenant <id>] [flags]
Flags:
| Flag | Alias | Description | Required | Default |
|---|---|---|---|---|
--entity-type | -e | Entity type to query | Yes | |
--file-path | -f | Output file path (must be .json) | Yes | |
--tenant | Tenant ID | No | From config | |
--entity-id | Filter by a specific entity ID | No | ||
--relation | -r | Filter by relation name | No | |
--subject-type | -s | Filter by subject type | No | |
--subject-id | Filter by subject ID | No | ||
--page-size | -p | Number of results per gRPC page | No | 100 |
Examples:
# Export all document relationships to a file
permify-toolkit relationships export -e document -f ./backup/documents.json
# Export only viewer relationships
permify-toolkit relationships export -e document -r viewer -f viewers.json
# Export from a specific tenant
permify-toolkit relationships export -e document --tenant staging -f staging-docs.json
Output file format:
The exported JSON file uses the same structure as relationships seed, so you can directly re-import it:
{
"tuples": [
{
"entity": { "type": "document", "id": "doc-1" },
"relation": "owner",
"subject": { "type": "user", "id": "alice" }
},
{
"entity": { "type": "document", "id": "doc-1" },
"relation": "viewer",
"subject": { "type": "user", "id": "bob" }
}
]
}
Common workflows:
# Back up relationships before a migration
permify-toolkit relationships export -e document -f ./backup/documents.json
permify-toolkit relationships export -e organization -f ./backup/orgs.json
# Copy relationships from staging to dev
permify-toolkit relationships export -e document --tenant staging -f transfer.json
permify-toolkit relationships seed --tenant dev -f transfer.json
# Export, inspect, edit, then re-seed
permify-toolkit relationships export -e document -f tuples.json
# ... edit tuples.json manually ...
permify-toolkit relationships seed --tenant t1 -f tuples.json
The export format is designed to be seed-compatible. You can export from one environment, review or modify the JSON, and seed it into another — making it easy to migrate or duplicate relationship data across tenants.
Local Development
# Build the package
pnpm build
# Run using local bin script
./bin/permify-toolkit schema push --tenant dev-tenant -c