Descope FGA Schema

Author, edit, or apply a Descope FGA schema using the ReBAC/ABAC DSL — types, relations, permissions, and conditions.

You say
Buy it · $69 Read it before you buy $69 Written by descope · unverified publisher
Context cost
3.8k tokensestimated from the bundle, loaded when it triggers
Bundle
1 file · 15.3 kBtext throughout, nothing executable
Licence
MITpaid listing
Last change
no release on file
Servers it uses
Noneruns standalone

What it does

Author, edit, or apply a Descope FGA schema using the ReBAC/ABAC DSL. Use this skill whenever the user asks to create a new FGA schema, modify an existing one, add types/relations/permissions/conditions, review an authorization model, or apply schema changes to a Descope project. Trigger even if the user says things like "set up authorization", "define roles and permissions", "add team-based access", "make this endpoint check FGA", or "update my authz model" — these almost always mean an FGA schema change.

Installed, it changes the agent in these ways.

What this skill changes about the agent is not written down here yet. The listing was collected from its source, and the description is in its own SKILL.md.

Workflow

Runs a procedure end to end.

fgarebacauthorizationdescope

The skill itself

This is the whole product. A skill is instructions the model reads, so there is nothing behind the listing you cannot see first — the front matter loads with every session, and the body below it loads when the skill triggers.

SKILL.md15.3 kB · 320 lines
--- name: descope-fga-schema description: Author, edit, or apply a Descope FGA schema using the ReBAC/ABAC DSL. Use this skill whenever the user asks to create a new FGA schema, modify an existing one, add types/relations/permissions/conditions, review an authorization model, or apply schema changes to a Descope project. Trigger even if the user says things like "set up authorization", "define roles and permissions", "add team-based access", "make this endpoint check FGA", or "update my authz model" — these almost always mean an FGA schema change. ---
6# FGA DSL Authoring
7
8Help the user design and apply Descope FGA schemas. The workflow is: understand the requirement → draft the DSL → validate via dry run → show the user + any data loss warnings → get confirmation → apply.
9
10## MCP Setup — check first, stop if missing
11
12**Before doing anything else**, check whether the Descope Management MCP is connected by looking for tools whose names contain FGASchema or DryRunSchema (e.g. mcp__descope__DryRunSchema). The exact prefix depends on how the user installed the MCP, but the operation IDs are DryRunSchema, CreateFGASchema, and GetFGASchema.
13
14**If the tools are not found:** output only the message below, then end your turn. Do not generate a schema, do not say "here's what I'll apply once connected", do not do any design work, do not continue:
15
16> The Descope Management MCP is required. If not yet installed, install and authorize it, then restart Claude Code and re-run /descope-fga-schema.
17> If already installed, it may need authorization. Authorize the Descope MCP, then restart Claude Code and re-run /descope-fga-schema.
18
19**If the tools are found:** call GetFGASchema immediately as a connectivity probe before doing any other work. If this call returns an authorization error, output only the message below and end your turn:
20
21> The Descope MCP is installed but not authorized. Authorize it, restart Claude Code, and re-run /descope-fga-schema.
22
23All FGA operations go through MCP tool calls — never make raw HTTP requests yourself.
24
25Once connected, use the GetFGASchema tool to read the current schema before editing — always do this when the user asks to modify an existing schema.
26
27## Grammar
28
29Every schema begins with exactly:
30```
31model AuthZ 1.0
32```
33No other name or version is accepted by the API.
34
35Full structure:
36```
37model AuthZ 1.0
38
39[constraint <Name>[:<Kind>][(args...)]]*
40[condition <Name>(<param type, ...>) { <CEL bool expr> }]*
41
42type <TypeName>
43 [relation <name>: <TypeRef> [| <TypeRef>]* [with <condExpr>]]*
44 [permission <name>: <expr> [with <condExpr>]]*
45```
46
47Keywords: model type relation permission condition constraint with
48
49Operators:
50- Permission expr: | union, & intersect, - subtract. Mix operators with parens: a | (b - c)
51- Set arrow: relation.permission — walks a stored relation to reach the subject's own permissions (e.g. parent.can_view)
52- Target set: Type#relation — see dedicated section below
53- with clause (relations and permissions): & AND, | OR, ! NOT, parens: with A & (B | !C). Conditions are evaluated at **check time** — with gates whether the relation or permission counts during evaluation. Only one with clause is allowed per relation or permission definition — combine multiple conditions inside it with &/|/!.
54
55**No comments** — the DSL parser has no comment token.
56
57Naming: **PascalCase** for Types, Conditions, Constraints. **snake_case** for relations and permissions.
58
59## Target Set Pattern (Type#relation)
60
61When a relation should be held by members of a group (e.g. "any member of this Team"), put Type#relation directly in the relation definition. This stores individual member subjects — the right granularity for permission checks.
62
63The indirect way — storing the group itself and deriving membership via a permission — produces correct relation expansion, but it introduces a contributor_team relation with no semantic meaning of its own. The only meaningful entity is the individual member. The target set syntax is more concise and directly expresses the intent.
64
65**Avoid (extra relation with no semantic value):**
66```
67type Repository
68 relation contributor_team: Team
69 permission contributor: contributor_team.member
70```
71
72**Prefer (concise, direct):**
73```
74type Repository
75 relation contributor: Team#member
76```
77
78You can mix direct subjects with target set subjects: relation editor: User | Team#member
79
80## ABAC Anti-Patterns to Avoid
81
82### Never use a "blocked" relation + subtraction to express a condition
83
84with conditions are evaluated at **check time** — when a permission check is made against the context passed in the request. Relations are always stored unconditionally; the condition only affects whether the relation counts during permission evaluation.
85
86The blocked relation + subtraction pattern is wrong because it requires manually maintaining a separate set of blocked edges in the DB for every excluded user. It's the wrong tool: use with !Condition on the relation that grants access instead — it is evaluated automatically at check time with no extra stored relations.
87
88```
89// NEVER do this — requires maintaining a separate "blocked" edge per user in the DB
90relation creator: User
91relation blocked: User with NorthKorea
92permission can_delete: creator - blocked
93
94// Right — condition evaluated automatically at check time; no extra edges
95relation creator: User with !NorthKorea
96permission can_delete: creator
97```
98
99### Don't write custom CEL when a built-in constraint covers it
100
101A custom condition that checks a numeric range is just reinventing NumRange (or NumAtLeast/NumAtMost). Built-in constraints are more concise, less error-prone, and form a common vocabulary that makes schemas easier for both humans and agents to read and reason about. Use them.
102
103**Wrong:**
104```
105condition DuringBusinessHours(seconds_since_midnight int) { seconds_since_midnight >= 32400 && seconds_since_midnight < 61200 }
106```
107
108**Right:**
109```
110constraint BusinessHours:NumRange(32400, 61200)
111```
112
113(Use a named alias when you want a descriptive name for the constraint.)
114
115## Relations vs Permissions
116
117A **relation** adds an edge to the pure relations graph. A **permission** is a derived rule that reuses existing relations — it adds edges only in the ReBAC graph without introducing new pure-graph edges. Fewer pure-graph edges means less to iterate during checks and a higher chance of cache hits across all checks in the schema, so permissions are more concise and keeping the pure graph lean tends to improve overall check performance as the system scales. Prefer satisfying a requirement with a permission whenever possible. Only introduce a new relation when a direct stored link is truly needed.
118
119When a permission is a strict superset of another, express it by referencing the narrower permission rather than repeating its expansion. This keeps schemas concise and makes the access hierarchy self-documenting — a reader immediately sees that can_admin implies can_write, which implies can_read.
120
121**Avoid (repeats relations across permissions):**
122```
123permission can_admin: owner
124permission can_write: owner | editor
125permission can_read: owner | editor | viewer
126```
127
128**Prefer (each permission builds on the previous):**
129```
130permission can_admin: owner
131permission can_write: can_admin | editor
132permission can_read: can_write | viewer
133```
134
135## Built-in Constraints
136
137Use built-in constraints before reaching for custom CEL.
138
139| Constraint | Runtime params (zero-arg form) | Hardcoded form |
140|---|---|---|
141| IpRange | ip ipaddress, ip_range string | IpRange("10.0.0.0/8") |
142| IpList | ip ipaddress, allowed_ips list | IpList("1.2.3.4","5.6.7.8") |
143| DateExpiryEpochSeconds | now_epoch_seconds int, expiry_epoch_seconds int | DateExpiryEpochSeconds(1735689600) |
144| StringMatchRegex | str string | StringMatchRegex("^admin_.*") (regex required) |
145| NumAtLeast | num double, min int | NumAtLeast(18) |
146| NumAtMost | num double, max int | NumAtMost(100) |
147| NumRange | num double, min int, max int | NumRange(0,100) (min ≤ max) |
148| BoolCheck | bool bool, expected bool | BoolCheck(true) |
149| GeoCountry | country_code string, allowed_countries list | GeoCountry("US","GB") (ISO 3166-1 alpha-2) |
150| IntList | int int, allowed_ints list | IntList(1,2,3) |
151| LabelList | label string, allowed_labels list | LabelList("foo","bar") |
152
153**Multiple constraints of the same kind:** You cannot declare the same constraint kind more than once without a named alias — the alias is required to distinguish them. Named aliases share the same runtime param names as the original kind (the alias only changes the constraint's identifier, not its params). This is fine when both constraints operate on the same parameter. If you need two constraints that operate on genuinely different parameters, use a custom CEL condition with a unique param name instead:
154```
155// Two GeoCountry constraints sharing the same country_code param — alias required, shared param is intentional
156constraint FiveEyes:GeoCountry("US","GB","CA","AU","NZ")
157constraint Sanction:GeoCountry("KP","IR","SY","RU")
158
159// Need a second IP check with a different param name? Use a custom condition
160condition OfficeNetwork(office_ip ipaddress, office_range string) { office_ip.in_cidr(office_range) }
161```
162
163**Custom CEL** — only when no built-in covers the logic, or when alias-based param separation isn't enough:
164```
165condition InNetwork(user_ip ipaddress, allowed_range string) { user_ip.in_cidr(allowed_range) }
166```
167CEL param types: int, string, bool, double, list, ipaddress. Body must return bool. Avoid nested exists — the evaluator enforces a cost limit.
168
169## Edit-Safety Protocol
170
171When editing an existing schema, first read the current schema with GetFGASchema so you have the real state.
172
173- If the user asks to add something already present, tell them exactly what exists and stop — don't silently overwrite.
174- Removing an entire **type** or a **relation definition** from the schema will cause all relation tuples of that type or relation to be permanently deleted from the database. **Editing the target type(s) of a relation definition is equivalent to deleting it and recreating it** — the same data loss risk applies. Always confirm with the user and make sure they understand the impact before proceeding.
175- **Exception: editing only the with condition of a relation does NOT delete tuples.** Relations are stored unconditionally; the condition is evaluated at check time. Changing with CondA to with CondB on an otherwise unchanged relation preserves all existing tuples — they simply start being evaluated against the new condition. This is safer than a full relation edit, but still requires caution: callers relying on the old condition's behavior will get different access results after the change.
176- Removing or editing a **permission** deletes no relation tuples, but any downstream permissions or checks that depended on it will silently stop working. Confirm with user.
177- Adding a new type, relation, or permission is generally safe.
178
179## Validation and Apply Workflow
180
181Follow this sequence every time you generate or edit a DSL:
182
183### Step 1 — Dry run
184
185Use the DryRunSchema MCP tool with the proposed DSL. This validates the schema and reports what data would be deleted if applied.
186
187- On error: the schema is invalid. Read the error message, fix the DSL, retry. Cap at 5 iterations — if still failing, stop and show the user the last error.
188- On success: continue to Step 2.
189
190The response contains:
191```json
192{
193 "deletesPreview": {
194 "hasDeletes": true,
195 "relations": ["folder#viewer", "doc#editor"],
196 "types": ["LegacyRole"]
197 }
198}
199```
200
201### Step 2 — Show the user
202
203Present:
2041. The full proposed DSL (formatted in a code block)
2052. If hasDeletes is true — a clear warning listing every relation type and namespace type that will be **permanently deleted** from the database
206
207Example warning:
208> **Warning: applying this schema will permanently delete all stored relations of these types:**
209> - folder#viewer
210> - doc#editor
211>
212> This cannot be undone. Confirm to proceed.
213
214If hasDeletes is false, just show the schema and ask for confirmation.
215
216### Step 3 — Get confirmation
217
218End your turn after Step 2. Do not call CreateFGASchema in the same turn as DryRunSchema — the user must see the schema and any deletion warnings before you proceed. Wait for the user to reply with explicit approval ("yes", "apply", "go ahead", etc.).
219
220### Step 4 — Apply
221
222Before calling CreateFGASchema, verify all three of the following are true:
223- You showed the full DSL in a code block in a prior turn (not in this turn)
224- You surfaced all deletion warnings from the dry-run response (or confirmed hasDeletes was false)
225- The user's most recent message is an explicit approval in response to your confirmation prompt
226
227If any of these are not true, do not call CreateFGASchema. Go back to Step 2 instead.
228
229When all three are confirmed, call CreateFGASchema with the same DSL from the dry run. Confirm success to the user.
230
231The reason this gate matters: CreateFGASchema is irreversible. Relation tuples deleted by a schema change cannot be recovered. Skipping confirmation is never safe, even when the change looks minor.
232
233## Examples
234
235### Basic ReBAC with hierarchy
236
237```
238model AuthZ 1.0
239
240type User
241
242type Folder
243
244type Doc
245 relation owner: User
246 relation parent: Folder
247 permission can_view: owner | parent.owner
248 permission can_edit: owner
249```
250
251### Group membership via target set
252
253```
254model AuthZ 1.0
255
256type User
257
258type Team
259 relation member: User
260
261type Repository
262 relation owner: User
263 relation contributor: User | Team#member
264 permission can_push: owner | contributor
265 permission can_read: can_push
266```
267
268### ABAC: time-gated access
269
270```
271model AuthZ 1.0
272
273constraint ShiftHours:NumRange
274
275type User
276
277type PatientRecord
278 relation viewer: User with ShiftHours
279 relation owner: User
280 permission can_view: viewer | owner
281```
282
283### Reused constraint kind with aliases — and with on a permission
284
285```
286model AuthZ 1.0
287
288constraint FiveEyes:GeoCountry("US","GB","CA","AU","NZ")
289constraint Sanction:GeoCountry("KP","IR","SY","RU")
290constraint OfficeOnly:IpRange("10.0.0.0/8")
291
292type User
293
294type Resource
295 relation allowed: User with FiveEyes & !Sanction
296 relation owner: User
297 permission can_access: allowed
298 permission can_delete: owner with OfficeOnly
299```
300
301allowed carries geo-gating on the relation — it applies to every permission that uses allowed. can_delete uses with on the permission itself so the IP restriction scopes only deletion, not access.
302
303### Nested permissions with with — conditions stack
304
305```
306model AuthZ 1.0
307
308constraint BusinessHours:NumRange(32400, 61200)
309constraint OfficeNetwork:IpRange("10.0.0.0/8")
310
311type User
312
313type Document
314 relation reader: User
315 permission can_read: reader with BusinessHours
316 permission can_edit: can_read with OfficeNetwork
317```
318
319can_edit requires both BusinessHours (from can_read) **and** OfficeNetwork (from can_edit's own with). Both conditions must be true at check time — with clauses on nested permissions accumulate.
320
In the file
SKILL.md2,243 words
Files1
LicenceMIT
Why you can read it

Nothing in a skill executes. The client loads the text and the model follows it, so a skill can be audited the way a runbook is — by reading it.

What it costs in context

Skills are not billed by the call. They are paid for in context: every token the instructions occupy is a token your code, your diff and your conversation cannot use. Here is what this one takes and when it takes it.

≈140
always loaded
The name and description, so the model knows the skill exists and when to reach for it.
3,685
on trigger
The instruction body, read only when the skill fires.
1.9%
of a 200k window
Ten skills this size would take about 19% of the window before you open a file.
050k100k150k200k context window

3.8k tokens, estimated from the bundle at four bytes to the token, held for the rest of the session once it triggers. Middling. Fine to keep on in a project where you use it weekly, worth unloading in one where you never do.

Servers bill, skills cost

A server charges by the month. A skill charges once per session, in context, and then keeps charging it for as long as the session lives.

Before and after

The same question, put to the same model twice: once as it comes, and once with these instructions loaded.

No worked example has been published for this skill yet.

Adoption
Installsnone yet
Ratingno reviews yet

The procedure it runs

The procedure has not been published here. It is in the skill’s own SKILL.md, which its author has not sent to the marketplace yet.

Prose, not code

These steps are written for a model to follow, not executed by a runtime. It can still be told to skip one, and it will say so when it does.

Servers it uses

None. This skill calls no MCP servers at all.

Everything it needs is in the instructions, so it works in a project with nothing connected — the model reads the file and changes how it works with what it can already reach.

It writes no files and reaches no network. All it changes is how the model reasons and writes.

What it asks for
Writes filesno
Network accessno

Read from the allowed-tools line of this skill’s own SKILL.md. A skill grants no permissions of its own — it can only ask for tools your client already has.

What it will not do

Every skill is narrow, and the useful ones say where they stop. These are the jobs this one is the wrong tool for.

What this skill is not for has not been published here. Nothing is implied by that: it is a section the author has not filled in.

What is in the bundle

1 file, 15.3 kB on disk. A bundle is text throughout: the instructions the model reads, plus the templates it fills in.

  • SKILL.md15.3 kB
What is not in it

No dependencies and nothing executable: a skill is text the agent reads, so the bundle is 1 file you can review in full before installing. The MIT licence covers the templates and examples as well as the instructions.

Install

Installing copies the bundle into your project. Nothing runs at install time — the files sit on disk until the model reads them.

$69 once
Descope FGA Schema · MIT · descope
one-time
Price$69 once
LicenceMIT — the author’s, unchanged by this purchase
Paid throughStripe, once, on the card you add at the checkout
Keeps workingfor good — the files are yours once they are on disk
Updatesevery update its author ships, delivered through this account

You can read the whole bundle before paying — the SKILL.md above is the product, not a preview of it. What the money buys is the delivery: the folder packaged and handed to your machine by key, every update its author ships, and our support if it does not do what this listing says. The terms of use are MIT, set by the author and unchanged by buying it here.

Payment runs through Stripe, on a page like this one rather than a redirect. Once there is an account it joins the same mcprush invoice as everything else you run, so there is never a second card to enter.

Which clients pick it up on their own

A skill is a folder of text. A client with a skills folder reads it without being told; everywhere else the same text works, it is just handed to the model rather than found.

Claude Code.claude/skills/
Claude Desktop
ChatGPT
Cursor.cursor/skills/
VS Code.github/skills/
Codex CLI.agents/skills/
Gemini CLI.gemini/skills/
Grok.grok/skills/
Zed.agents/skills/
Windsurf.windsurf/skills/
Agent SDK.claude/skills/
HTTP / API
This release
Versionnot versioned
Publishedno release date on file
Price$69
Referencedescope/descope-fga-schema

Versions

Its author publishes no version number, so there is nothing here to pin to: what you install is the folder as it stands today. Instructions change more often than APIs do — a skill can be rewritten entirely without anything it depends on moving.

v
  • No earlier releases have been published to the marketplace.
Pinning

Nothing to pin to: this skill carries no version number of its own. What you install is what the folder holds on the day you install it.

Reviews

no reviews yet · no installs yet

Nobody has reviewed this skill yet. The rating is the mean of the reviews written here, so there is none until somebody writes the first.

Who can post

Only accounts that have had the skill installed for fourteen days, so a review is written after living with it rather than after reading it. Publishers may reply once.

Publisher
Servers0