Playbook Cookbook

A playbook is a single JSON pack for AnythingGraph: schema, graph links, optional demo data, and optional access rules. This guide explains the model, walks through the HR relationship access example, then shows how to author and test your own pack.

Reference file: dashboard/backend/src/playbook/playbooks/hr-relationship-access.json

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.
Install vs enforce: Installing always applies schema and optional 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.

Layer 1 — Catalog: id, name, description, category, instructions Layer 2 — Schema: entities[] + entity_relationships[] Layer 3 — Policy: relationship_access_rules (optional) Layer 4 — Instances: example_data.rows + example_data.relationships (optional)

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.”

hr_user — access subject department employee compensation_record

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

FieldNotes
idStable id; filename <id>.json; MCP playbook_id.
name, description, categoryGallery card and tab (operations, start_here, …).
instructionsOptional long onboarding text for the UI.

entities[] and fields

FieldNotes
namesnake_case entity id (employee).
display_nameUI label.
fields[].field_nameColumn key in row JSON and rules.
fields[].field_typeTEXT, INTEGER, REAL.
fields[].is_identifierBusiness key; dedupe on install; subject id field for ReBAC.

entity_relationships[]

FieldNotes
relationship_namePredicate; stored as subject ──name──▶ object. Also used in example_data.relationships.
subject_entity_name / object_entity_nameTail 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)

FieldNotes
subject_entity_name / subject_identifier_fieldWho is calling (hr_user / user_id).
deny_by_defaultIf true, hide rows unless an allow rule matches.
implementation_statuscatalog_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).

Default posture: 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

  1. Self employee profile — A user may read an employee row when its email matches their hr_user.email (no graph hop).
  2. Manager department team — Users whose role_title is a manager type may read employees in departments they manages_department (walk: employee → department → managing hr_user).
  3. HR BP department — HR Business Partners may read employees in departments they hr_representative_for.
  4. Compensation via visible employee — HR BP / HR Director / Department Manager roles may read a compensation_record only when it links to an employee they 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)
Graph wiring (instance links in example_data): Jane, John ──member_of_department──▶ Engineering (ENG) Sara ──member_of_department──▶ Sales (SALES) alice.mgr ──manages_department──▶ ENG bob.mgr ──manages_department──▶ SALES carol.hr ──hr_representative_for──▶ ENG COMP-9001 ──compensation_for──▶ Jane

Demo rows and links (example_data)

Row keyEntityIdentifier values
department::ENG / department::SALESdepartmentENG / SALES
hr_user::alice.mgr …hr_useralice.mgr, bob.mgr, carol.hr
employee::E-1001 …employeeE-1001, E-1002, E-2001
compensation_record::COMP-9001compensation_recordCOMP-9001

Rule mechanics (path, match, prior access)

Each entry in rules[] targets one resource_entity_name with effect: allow (or deny).

MechanismWhen to useHR 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.

directionStanding on…Reach…
forwardsubject side of stored linkobject side
reverseobject side of stored linksubject side
hr_user ──manages_department──▶ department Path department → hr_user: reverse compensation_record ──compensation_for──▶ employee Path compensation_record → employee: forward (not reverse)
Restart rdf-cache after editing rules — playbook JSON is cached in memory.

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

  1. Identity — choose id, name, category; filename must match id.
  2. Entities and fields — snake_case names; is_identifier on business keys; pick one subject entity if using ReBAC.
  3. Relationships — declare subject → object consistently; document direction in instructions.
  4. Example data — enough rows and links to exercise every relationship and rule path in Graph View and MCP.
  5. Access rules (optional) — start with deny_by_default: true; add allow rules; verify each hop’s direction against stored links.
  6. Implementation status — use catalog_only while drafting; set enforced when ready for MCP/data-layer filtering.
  7. Install and sync — install from Playbooks UI; confirm Data Capsule / graph export.
  8. Validate — use MCP list_allowed_rows and SPARQL with subject_id (see next section).

How to test via MCP

Prerequisites: install playbook, sync Data Capsule, rdf-cache + MCP running.

  1. list_data_capsules — confirm hr-relationship-access cached.
  2. list_allowed_rows with playbook_id: hr-relationship-access, subject_id: alice.mgr, entity_name: employee — expect two row ids.
  3. Repeat for bob.mgr (one employee) and carol.hr + compensation_record (one row).
  4. run_sparql with subject_id and SELECT ?row ?fullName ... — always include ?row when ReBAC is on.

See Documentation for MCP setup and environment variables.