What is a playbook?
A playbook is one versioned JSON file that describes a business domain for AnythingGraph. Think of it as an installable package: it tells the platform which record types exist, how they connect in a graph, what sample rows to seed for demos, and optionally who may read or write which rows.
Playbooks appear in the dashboard Playbooks gallery. When you install a pack,
the backend creates entities (tables), relationship types, and—if provided—example_data
rows and links. The same file is read by rdf-cache for graph queries and, when configured, for
relationship-based access control (ReBAC) at query time.
File location: dashboard/backend/src/playbook/playbooks/<id>.json where
id matches the JSON id field (e.g.
hr-relationship-access.json).
What a playbook can do
Not every playbook uses every block. The HR example uses schema, graph, demo data, and ReBAC; a simple starter pack might only define entities and relationships.
| Capability | JSON block | What it does |
|---|---|---|
| Catalog entry | id, name, description, category |
Lists the pack in the Playbooks UI and supplies playbook_id for MCP/API. |
| Onboarding copy | instructions |
Post-install steps, domain notes, and MCP hints shown in the dashboard. |
| Record schema | entities[] |
Defines record types (entities) and columns (fields): types, identifiers, help text. |
| Graph schema | entity_relationships[] |
Declares allowed link types between entities (subject → object direction). |
| Demo graph | example_data |
Optional rows and instance links created on install for Graph View and testing. |
| Row access policy | relationship_access_rules |
Optional ReBAC: which rows each subject may read/write via paths and field matches. |
| Ingest mapping | field_mappings, relationship_routing |
Used in connector/document playbooks (not in HR) to map external data into entities. |
example_data. ReBAC only filters query results when
implementation_status is enforced and the client passes
subject_id (MCP, API, or data-layer).
How a playbook JSON is structured
A playbook file is one JSON object. Author in this logical order: identity and catalog text first, then schema (entities and relationship types), then policy, then instance demo data.
Schema is stable: entity names and relationship types define the graph shape for every tenant. example_data is optional sample content. ReBAC references schema names and walks instance links stored after install (or ingest).
Names are snake_case internally (employee,
member_of_department). The platform exports camelCase property IRIs to Turtle/RDF
(memberOfDepartment).
HR relationship access — example playbook
hr-relationship-access models a small HR domain: departments, employees, HR users
(managers and HR business partners), and compensation rows. It demonstrates graph-shaped org
data and relationship-based visibility—not only “who installed the playbook.”
Schema relationships
| relationship_name | subject → object |
|---|---|
| member_of_department | employee → department |
| manages_department | hr_user → department |
| hr_representative_for | hr_user → department |
| compensation_for | compensation_record → employee |
Sample cast (example_data)
Alice and Bob are department managers; Carol is an HR BP for Engineering. Jane and John are in Engineering; Sara is in Sales. One compensation row (COMP-9001) links to Jane.
Access subjects use hr_user.user_id as subject_id in MCP (e.g.
alice.mgr, carol.hr). Section 6 walks through who may see which rows;
section 8 shows MCP checks.
JSON reference (brief)
Condensed field guide for the HR file. Open the full JSON for every rule and row:
dashboard/backend/src/playbook/playbooks/hr-relationship-access.json
Top-level skeleton
{
"id": "hr-relationship-access",
"name": "HR relationship access",
"description": "...",
"category": "operations",
"instructions": "...",
"entities": [ { "name": "hr_user", "display_name": "...", "fields": [ ... ] } ],
"entity_relationships": [ { "relationship_name": "...", "subject_entity_name": "...", "object_entity_name": "..." } ],
"relationship_access_rules": { "subject_entity_name": "hr_user", "rules": [ ... ], "deny_by_default": true },
"example_data": { "rows": [ ... ], "relationships": [ ... ] }
}
Catalog fields
| Field | Notes |
|---|---|
| id | Stable id; filename <id>.json; MCP playbook_id. |
| name, description, category | Gallery card and tab (operations, start_here, …). |
| instructions | Optional long onboarding text for the UI. |
entities[] and fields
| Field | Notes |
|---|---|
| name | snake_case entity id (employee). |
| display_name | UI label. |
| fields[].field_name | Column key in row JSON and rules. |
| fields[].field_type | TEXT, INTEGER, REAL. |
| fields[].is_identifier | Business key; dedupe on install; subject id field for ReBAC. |
entity_relationships[]
| Field | Notes |
|---|---|
| relationship_name | Predicate; stored as subject ──name──▶ object. Also used in example_data.relationships. |
| subject_entity_name / object_entity_name | Tail and head of the directed link. |
example_data
{
"entity_name": "hr_user",
"values": { "user_id": "alice.mgr", "full_name": "Alice Manager", ... }
}
{
"relationship_name": "manager_manages_department",
"subject_row_ref": "hr_user::alice.mgr",
"object_row_ref": "department::ENG"
}
Example links use entity_name::identifier for
subject_row_ref / object_row_ref (derived from each row’s
identifier field). relationship_name must match a schema relationship,
and the row refs must match that relationship’s subject/object entities.
relationship_access_rules (summary)
| Field | Notes |
|---|---|
| subject_entity_name / subject_identifier_field | Who is calling (hr_user / user_id). |
| deny_by_default | If true, hide rows unless an allow rule matches. |
| implementation_status | catalog_only vs enforced. |
| rules[] | Per-entity allow/deny: path, match, or requires_prior_access_to. |
Rule authoring detail, scenarios, and direction pitfalls are in
Setting access rules.
Setting access rules — scenarios and walkthrough
Optional relationship_access_rules describe row-level read/write
policy using the instance graph. The HR pack uses hr_user as the subject; callers
pass subject_id equal to user_id (e.g. alice.mgr).
deny_by_default: true — if no allow rule
matches, the row is hidden. That is why managers do not see every employee in the company by
default.
HR scenarios in plain language
-
Self employee profile — A user may read an
employeerow when itsemailmatches theirhr_user.email(no graph hop). -
Manager department team — Users whose
role_titleis a manager type may read employees in departments theymanages_department(walk: employee → department → managing hr_user). -
HR BP department — HR Business Partners may read employees in departments
they
hr_representative_for. -
Compensation via visible employee — HR BP / HR Director / Department
Manager roles may read a
compensation_recordonly when it links to anemployeethey could already read (walk: compensation → employee, then check prior employee access).
Engineering Manager is in the manager rule but not in the compensation role list — so Alice sees Jane and John but not COMP-9001.
Who sees what
| subject_id | role_title | Can read employees | Can read compensation |
|---|---|---|---|
| alice.mgr | Engineering Manager | Jane Doe, John Lee (Engineering) | None |
| bob.mgr | Sales Manager | Sara Kim (Sales) | None |
| carol.hr | HR Business Partner | Jane, John | COMP-9001 (Jane’s row) |
Demo rows and links (example_data)
| Row key | Entity | Identifier values |
|---|---|---|
| department::ENG / department::SALES | department | ENG / SALES |
| hr_user::alice.mgr … | hr_user | alice.mgr, bob.mgr, carol.hr |
| employee::E-1001 … | employee | E-1001, E-1002, E-2001 |
| compensation_record::COMP-9001 | compensation_record | COMP-9001 |
Rule mechanics (path, match, prior access)
Each entry in rules[] targets one resource_entity_name with effect: allow (or deny).
| Mechanism | When to use | HR example |
|---|---|---|
match |
Compare resource fields to subject fields | Self employee: employee.email = hr_user.email |
path |
Walk instance links from resource row to subject | Manager: employee → dept → managing hr_user |
requires_prior_access_to |
Allow only if linked row already allowed | Compensation → employee, then employee must be visible |
subject_condition |
Limit which subjects the rule applies to | role_title in [Engineering Manager, …] |
Manager path (Alice → Engineering employees)
{
"path": [
{ "relationship_name": "member_of_department", "direction": "forward",
"from_entity_name": "employee", "to_entity_name": "department" },
{ "relationship_name": "manages_department", "direction": "reverse",
"from_entity_name": "department", "to_entity_name": "hr_user" }
]
}
Plain language: start on an employee → their department → find an hr_user who manages that department → is it the requester?
Self access (match)
{
"match": {
"type": "field_equals_subject",
"resource_field": "email",
"subject_field": "email"
}
}
Compensation (path + prior access)
{
"resource_entity_name": "compensation_record",
"path": [
{ "relationship_name": "compensation_for", "direction": "forward",
"from_entity_name": "compensation_record", "to_entity_name": "employee" }
],
"requires_prior_access_to": "employee"
}
direction: forward vs reverse
The most common authoring mistake. Direction refers to stored link orientation in
entity_relationships, not informal English.
| direction | Standing on… | Reach… |
|---|---|---|
| forward | subject side of stored link | object side |
| reverse | object side of stored link | subject side |
How to create your own playbook
Copy an existing pack (e.g. HR) as a template, rename id and filename, then edit
schema, relationships, optional example_data, and optional
relationship_access_rules.
Where files live
Ship playbook JSON under:
dashboard/backend/src/playbook/playbooks/<id>.json
The dashboard catalog loader picks up all *.json files in that folder. Restart the
dashboard backend if playbooks do not hot-reload. rdf-cache reads the same directory via
PLAYBOOKS_DIR — restart rdf-cache after rule changes.
Author checklist
-
Identity — choose
id,name,category; filename must matchid. -
Entities and fields — snake_case names;
is_identifieron business keys; pick one subject entity if using ReBAC. -
Relationships — declare subject → object consistently; document direction in
instructions. - Example data — enough rows and links to exercise every relationship and rule path in Graph View and MCP.
-
Access rules (optional) — start with
deny_by_default: true; add allow rules; verify each hop’sdirectionagainst stored links. -
Implementation status — use
catalog_onlywhile drafting; setenforcedwhen ready for MCP/data-layer filtering. - Install and sync — install from Playbooks UI; confirm Data Capsule / graph export.
-
Validate — use MCP
list_allowed_rowsand SPARQL withsubject_id(see next section).
How to test via MCP
Prerequisites: install playbook, sync Data Capsule, rdf-cache + MCP running.
list_data_capsules— confirmhr-relationship-accesscached.-
list_allowed_rowswithplaybook_id: hr-relationship-access,subject_id: alice.mgr,entity_name: employee— expect two row ids. -
Repeat for
bob.mgr(one employee) andcarol.hr+compensation_record(one row). -
run_sparqlwithsubject_idandSELECT ?row ?fullName ...— always include?rowwhen ReBAC is on.
See Documentation for MCP setup and environment variables.