Quickstart
Install the developer tools, build a matrix, and install it to your workspace.
A matrix is the semantic operating model for a specific set of capabilities. It defines what your system can do, what data it works with, and how it validates that data, all expressed as RDF. A matrix is the digital world that RARS manipulates, producing real-world side effects via your systems of record and other services.
This guide walks you through setting up the tools, building a simple matrix, and installing it to your workspace. You don't need a running backend to complete this guide. The focus is on authoring and installing the semantic specification for an operating model.
Prerequisites
- A Poliglot account with access to a workspace
- Python 3.11+
Install the RARS CLI
The RARS CLI builds, validates, and installs matrices.
pip:
pip install rarsuv:
uv pip install rarspipx:
pipx install rarsVerify the installation and authenticate:
rars version
rars auth loginrars auth login opens a browser for OAuth authentication and syncs your available workspaces automatically.
Project Structure
A matrix specification is designed to live alongside the source code for your system of record. The system of record is the actual application: a REST API, a database, a GraphQL server, etc. The matrix is the semantic layer that describes what that system can do and how your AI workloads interact with it.
my-project/
├── poliglot.yml # Package and build configuration
├── src/ # Your system of record (Flask API, Express, etc.)
└── spec/ # Matrix specification
├── matrix.ttl # Matrix declaration and imports
├── ontology.ttl # Domain model (classes and properties)
├── shapes.ttl # Validation constraints (SHACL)
├── actions.ttl # Actions and service integrations
├── dprod.ttl # Data products and data services
├── mappings.ttl # Response mappings (RML)
├── secrets.ttl # Secret declarations (API keys, tokens)
└── iam.ttl # Roles and access policiesBuild Configuration
Create poliglot.yml at the project root. This tells the CLI where to find your spec files, artifacts, and where to output the build.
version: "1"
package:
name: "my-project"
version: "1.0.0"
engineVersion: "1"
matrix:
tasks:
path: "."
spec:
- ./spec
outputDir: ./.matrixWrite the Matrix
We'll build a simple task management matrix that integrates with a REST API backend. The matrix defines tasks as a domain concept, enforces validation, and declares actions that call the backend through a data service.
Matrix Declaration
Every matrix starts with matrix.ttl, which declares its identity, description, and imports.
# spec/matrix.ttl
@base <https://example.org/spec/tasks#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix rars-mtx: <https://poliglot.io/rars/spec/matrix#> .
@prefix rars-iam: <https://poliglot.io/rars/spec/iam#> .
@prefix tasks: <https://example.org/spec/tasks#> .
tasks:
a rars-mtx:Matrix ;
rars-mtx:name "tasks" ;
rars-mtx:description "Task management matrix. Semantic operating model for a task tracking backend." ;
rars-mtx:repositoryUrl "https://github.com/example/my-project"^^xsd:anyURI ;
rars-mtx:proprietor tasks:TaskAgent .
tasks:TaskAgent
a rars-iam:Agent ;
rdfs:label "Task Agent" ;
skos:definition "The security principal responsible for this matrix's internal operations." ;
rdfs:isDefinedBy tasks: .Domain Model
Define the vocabulary (the classes and properties that describe your domain) in ontology.ttl:
# spec/ontology.ttl
@base <https://example.org/spec/tasks#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rars-iam: <https://poliglot.io/rars/spec/iam#> .
@prefix tasks: <https://example.org/spec/tasks#> .
tasks:Task
a owl:Class ;
rdfs:label "task" ;
skos:definition "A unit of work with a title, status, and assignee." ;
rdfs:isDefinedBy tasks: .
tasks:CreateTaskPayload
a owl:Class ;
rdfs:label "create task payload" ;
skos:definition "Input data for creating a new task." ;
rdfs:isDefinedBy tasks: .
tasks:title
a owl:DatatypeProperty ;
rdfs:domain tasks:Task ;
rdfs:range xsd:string ;
rdfs:label "title" ;
skos:definition "The title of a task." ;
rdfs:isDefinedBy tasks: .
tasks:status
a owl:DatatypeProperty ;
rdfs:range xsd:string ;
rdfs:label "status" ;
skos:definition "The current status of a task." ;
skos:example "open" ;
rdfs:isDefinedBy tasks: .
tasks:assignee
a owl:ObjectProperty ;
rdfs:range rars-iam:Principal ;
rdfs:label "assignee" ;
skos:definition "The principal assigned to this task." ;
rdfs:isDefinedBy tasks: .Validation Constraints
Define SHACL shapes in shapes.ttl to enforce data integrity. These constraints are checked during assembly and at runtime.
# spec/shapes.ttl
@base <https://example.org/spec/tasks#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix dct: <http://purl.org/dc/terms/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix tasks: <https://example.org/spec/tasks#> .
tasks:TaskShape
a sh:NodeShape ;
dct:description "Validates that a task has a title and status." ;
sh:targetClass tasks:Task ;
sh:property tasks:Task-title ;
sh:property tasks:Task-status ;
rdfs:isDefinedBy tasks: .
tasks:Task-title
a sh:PropertyShape ;
sh:path tasks:title ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:severity sh:Violation ;
sh:message "A task must have exactly one title." ;
dct:description "The title of the task." ;
rdfs:isDefinedBy tasks: .
tasks:Task-status
a sh:PropertyShape ;
sh:path tasks:status ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:severity sh:Violation ;
sh:message "A task must have exactly one status." ;
dct:description "The current status of the task." ;
rdfs:isDefinedBy tasks: .
## ---- Create Task Payload Shape ----
tasks:CreateTaskPayloadShape
a sh:NodeShape ;
dct:description "Validates the input for creating a task." ;
sh:targetClass tasks:CreateTaskPayload ;
sh:property tasks:Task-title ;
sh:property tasks:Task-status ;
rdfs:isDefinedBy tasks: .Data Products and Services
This is where the matrix connects to the physical world. The data product model follows the DPROD standard (which extends W3C DCAT) and defines a chain from business-level data products down to technical endpoints:
- Data Product: declares what data capabilities exist and their purpose
- Dataset: a collection of data exposed by the product
- Distribution: a specific representation of that dataset (JSON, CSV, XML, etc.)
- Data Service: the technical endpoint that serves the distribution
dprod.ttl bridges the semantic world (matrix) and the system of record (your backend):
# spec/dprod.ttl
@base <https://example.org/spec/tasks#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix dct: <http://purl.org/dc/terms/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix dprod: <https://ekgf.github.io/dprod/> .
@prefix rars-svc: <https://poliglot.io/rars/spec/services#> .
@prefix rars-http: <https://poliglot.io/rars/spec/http#> .
@prefix tasks: <https://example.org/spec/tasks#> .
## ---- Data Product ----
tasks:TaskManagementDataProduct
a dprod:DataProduct ;
rdfs:label "Task Management Data Product" ;
dct:description "Provides task management capabilities through a REST API backend." ;
dprod:purpose "To enable creation, retrieval, and management of tasks through the task tracking system." ;
dprod:outputDataset tasks:TasksDataset ;
dprod:outputPort tasks:TaskAPI ;
rdfs:isDefinedBy tasks: .
## ---- Dataset ----
tasks:TasksDataset
a dcat:Dataset ;
rdfs:label "Tasks Dataset" ;
dct:description "Collection of task records managed by the task tracking system." ;
dct:conformsTo tasks:Task ;
dcat:distribution tasks:TasksJsonDistribution ;
rdfs:isDefinedBy tasks: .
## ---- Distribution ----
tasks:TasksJsonDistribution
a dcat:Distribution ;
rdfs:label "Tasks JSON Distribution" ;
dct:description "JSON representation of task records." ;
dct:format <https://www.iana.org/assignments/media-types/application/json> ;
dcat:accessService tasks:TaskAPI ;
rdfs:isDefinedBy tasks: .
## ---- Data Service ----
tasks:TaskAPI
a rars-svc:RemoteDataService ;
rdfs:label "Task API" ;
dct:description "REST API for task management operations." ;
rars-svc:endpointUrl "https://your-api.example.com/api/v1/tasks"^^xsd:anyURI ;
rars-svc:endpointDescription "RESTful task management API" ;
rars-svc:protocol rars-http:HTTP ;
rdfs:isDefinedBy tasks: .The endpoint URL should point to a service that is in the same network as the
engine that provisioned the working context. Poliglot hosted contexts will
call your service over the internet, so local URLs like localhost won't
work. See Connecting Systems of
Record for details on
connecting a live backend and authentication setup.
Secrets
If your API requires authentication, you can declare a secret in secrets.ttl. Secrets are E2E encrypted values managed per-workspace. You set the actual value through the Poliglot console after installation. Because of our engine architecture, connected LLMs will never see this value.
# spec/secrets.ttl
@base <https://example.org/spec/tasks#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rars-iam: <https://poliglot.io/rars/spec/iam#> .
@prefix rars-scrt: <https://poliglot.io/rars/spec/secrets#> .
@prefix tasks: <https://example.org/spec/tasks#> .
tasks:TaskAPIKey
a rars-scrt:ManagedSecret ;
rdfs:label "Task API Key" ;
rars-scrt:description "API key for authenticating requests to the task management backend." ;
rars-iam:hasPolicy tasks:TaskAPIKeyPolicy ;
rdfs:isDefinedBy tasks: .
tasks:TaskAPIKeyPolicy
a rars-iam:ResourcePolicy ;
rars-iam:effect rars-iam:Allow ;
rars-iam:action rars-scrt:ResolveSecret ;
rars-iam:role tasks:TaskManagerRole ;
rdfs:isDefinedBy tasks: .The secret is declared but has no value in the spec. After installing the matrix, workspace administrators set the actual API key through the console. At runtime, rars-scrt:resolve_secret resolves the value for use in request headers (shown in the next section).
Actions
Actions are the capabilities your matrix exposes. They use service integrations to call the data service defined in your data product. This is how the AI OS invokes your system of record.
# spec/actions.ttl
@base <https://example.org/spec/tasks#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dct: <http://purl.org/dc/terms/> .
@prefix rars-act: <https://poliglot.io/rars/spec/actions#> .
@prefix rars-http: <https://poliglot.io/rars/spec/http#> .
@prefix rars-os: <https://poliglot.io/rars/spec/os#> .
@prefix rars-scrt: <https://poliglot.io/rars/spec/secrets#> .
@prefix rars-ws: <https://poliglot.io/rars/spec/workspace#> .
@prefix tasks: <https://example.org/spec/tasks#> .
## ---- Shared request components ----
tasks:ApiKeyHeader
rars-act:predicate rars-http:hasHeader ;
rars-act:objectMap [
rars-act:objectMap [
rars-act:predicate rars-http:headerName ;
rars-act:value "X-Api-Key"
] ;
rars-act:objectMap [
rars-act:predicate rars-http:headerValue ;
rars-act:value [
a rars-os:RDFFunction ;
rars-os:executesFunction rars-scrt:resolve_secret ;
rars-os:functionInput [
rars-os:functionParameter rars-scrt:secret ;
rars-os:functionInputValue tasks:TaskAPIKey
]
]
]
] .
## ---- List Tasks Action ----
tasks:ListTasks
a rars-act:Action ;
rars-act:isClassifiedAs rars-act:Query ;
rdfs:label "list tasks" ;
dct:description "List all tasks from the task management system." ;
rars-act:subjectScheme rars-ws:Workspace ;
rars-act:subjectRequired true ;
rars-act:resultScheme tasks:Task ;
rars-act:handler tasks:ListTasksIntegration ;
rdfs:isDefinedBy tasks: .
tasks:ListTasksIntegration
a rars-act:ServiceIntegration ;
rars-act:service tasks:TaskAPI ;
rars-act:requestTemplate tasks:ListTasksRequest ;
rars-act:responseTemplate tasks:TaskResponseTemplate ;
rdfs:isDefinedBy tasks: .
tasks:ListTasksRequest
a rars-act:RequestTemplate ;
rars-act:requestType rars-http:Request ;
rars-act:requestObjectMap tasks:ApiKeyHeader ;
rars-act:requestObjectMap [
rars-act:predicate rars-http:method ;
rars-act:value "GET"
] .
## ---- Create Task Action ----
tasks:CreateTask
a rars-act:Action ;
rars-act:isClassifiedAs rars-act:Mutation ;
rdfs:label "create task" ;
dct:description "Create a new task in the task management system." ;
rars-act:subjectScheme rars-ws:Workspace ;
rars-act:subjectRequired true ;
rars-act:payloadScheme tasks:CreateTaskPayload ;
rars-act:resultScheme tasks:Task ;
rars-act:handler tasks:CreateTaskIntegration ;
rdfs:isDefinedBy tasks: .
tasks:CreateTaskIntegration
a rars-act:ServiceIntegration ;
rars-act:service tasks:TaskAPI ;
rars-act:requestTemplate tasks:CreateTaskRequest ;
rars-act:responseTemplate tasks:TaskResponseTemplate ;
rdfs:isDefinedBy tasks: .
tasks:CreateTaskRequest
a rars-act:RequestTemplate ;
rars-act:requestType rars-http:Request ;
rars-act:requestObjectMap tasks:ApiKeyHeader ;
rars-act:requestObjectMap [
rars-act:predicate rars-http:method ;
rars-act:value "POST"
] ;
rars-act:requestObjectMap [
rars-act:predicate rars-http:body ;
rars-act:value tasks:CreateTaskBodyMapping
] .
tasks:CreateTaskBodyMapping
rars-os:fromJSON """
PREFIX tasks: <https://example.org/spec/tasks#>
PREFIX rars-act: <https://poliglot.io/rars/spec/actions#>
PREFIX rars-os: <https://poliglot.io/rars/spec/os#>
JSON {
"title": ?title,
"status": ?status
} WHERE {
?_process rars-os:parent ?invocation .
?invocation rars-act:payload ?payload .
?payload a tasks:CreateTaskPayload .
?payload tasks:title ?title .
?payload tasks:status ?status .
}
""" .Here's what's happening in the actions:
ApiKeyHeaderis a shared request component that resolves theTaskAPIKeysecret usingrars-scrt:resolve_secretand injects it as anX-API-Keyheader. Both actions reference it, so the header is added to every request.ListTasksis a Query action that calls the Task API with a GET request. The JSON response is mapped back into RDF triples using the response template.CreateTaskis a Mutation action that constructs a JSON body from the invocation payload and POSTs it to the API. The response is mapped the same way.
Response Mappings
Response mappings use RML (RDF Mapping Language) to transform JSON responses from your API back into RDF triples. Put these in mappings.ttl:
# spec/mappings.ttl
@base <https://example.org/spec/tasks#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rml: <http://w3id.org/rml/> .
@prefix tasks: <https://example.org/spec/tasks#> .
tasks:TaskResponseTemplate
a rml:TriplesMap ;
rml:logicalSource [
rml:referenceFormulation rml:JSONPath ;
rml:iterator "$[*]"
] ;
rml:subjectMap [
rml:template "https://example.org/tasks/{id}" ;
rml:class tasks:Task
] ;
rml:predicateObjectMap [
rml:predicate tasks:title ;
rml:objectMap [ rml:reference "title" ; rml:datatype xsd:string ]
] ;
rml:predicateObjectMap [
rml:predicate tasks:status ;
rml:objectMap [ rml:reference "status" ; rml:datatype xsd:string ]
] ;
rdfs:isDefinedBy tasks: .The template iterates over the JSON array response and creates tasks:Task instances with title and status properties. This is the core pattern: actions call your system of record through service integrations, and response templates map the results back into the semantic world that the AI operates in.
Access Control
There are two separate concerns here:
- The agent (
tasks:TaskAgent) is the security principal for the matrix's internal processes. It reads from and writes to the graph as actions execute. It needs statement-level access scoped to this matrix's resources. - The task manager role is for external callers (users, other agents) who invoke the matrix's actions. They don't need graph access; they just need permission to invoke actions.
iam.ttl defines both roles and wires them up:
# spec/iam.ttl
@base <https://example.org/spec/tasks#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix dct: <http://purl.org/dc/terms/> .
@prefix rars-iam: <https://poliglot.io/rars/spec/iam#> .
@prefix rars-act: <https://poliglot.io/rars/spec/actions#> .
@prefix rars-os: <https://poliglot.io/rars/spec/os#> .
@prefix tasks: <https://example.org/spec/tasks#> .
## ---- Agent Role ----
## The agent's internal processes need to read and write task data
## in the graph. Scoped to only this matrix's resources.
tasks:TaskAgentRole
a rars-iam:Role, owl:NamedIndividual ;
rdfs:label "Task Agent Role" ;
dct:description "Internal role for the task agent's graph operations." ;
rars-iam:hasPolicy tasks:StatementAccessPolicy ;
rdfs:isDefinedBy tasks: .
tasks:StatementAccessPolicy
a rars-iam:IdentityPolicy ;
rars-iam:effect rars-iam:Allow ;
rars-iam:action rars-os:ReadStatement, rars-os:WriteStatement ;
rars-iam:condition [
a rars-iam:Condition ;
rars-iam:scope rars-iam:Resource ;
rars-iam:onProperty rdfs:isDefinedBy ;
rars-iam:hasValue tasks:
] ;
rdfs:isDefinedBy tasks: .
tasks:TaskAgent
rars-iam:hasRole tasks:TaskAgentRole .
## ---- Task Manager Role ----
## For external callers who can invoke the matrix's actions.
## No graph access needed; action invocation only.
tasks:TaskManagerRole
a rars-iam:Role, owl:NamedIndividual ;
rdfs:label "Task Manager" ;
dct:description "Role for invoking task management actions." ;
rars-iam:hasPolicy tasks:InvokeTaskActionsPolicy ;
rdfs:isDefinedBy tasks: .
tasks:InvokeTaskActionsPolicy
a rars-iam:IdentityPolicy ;
rars-iam:effect rars-iam:Allow ;
rars-iam:action rars-act:InvokeAction ;
rars-iam:resource tasks:ListTasks, tasks:CreateTask ;
rdfs:isDefinedBy tasks: .
## ---- Resource Policies ----
## Attached to each action, controlling who can invoke from outside.
tasks:TaskActionsResourcePolicy
a rars-iam:ResourcePolicy ;
rars-iam:effect rars-iam:Allow ;
rars-iam:action rars-act:InvokeAction ;
rars-iam:role tasks:TaskManagerRole ;
rdfs:isDefinedBy tasks: .
tasks:ListTasks rars-iam:hasPolicy tasks:TaskActionsResourcePolicy .
tasks:CreateTask rars-iam:hasPolicy tasks:TaskActionsResourcePolicy .Here's what each piece does:
TaskAgentRolegives the agent read/write access to statements for task resources only. The condition onrdfs:isDefinedBy tasks:scopes it so the agent can't touch resources from other matrices. This is the internal process identity.TaskManagerRoleis for external callers. Its identity policy allows invoking specific actions (ListTasks,CreateTask) but grants no graph access. This role can be assigned to any principal in the workspace, both users and other agents.TaskActionsResourcePolicyis attached to each action as a resource policy. Both the identity policy (on the caller's role) and the resource policy (on the action) must allow for invocation to succeed.- The agent and manager roles are separate. A user assigned the
TaskManagerRolecan invoke actions, but the graph reads and writes happen under the agent's identity.
All access is denied by default. Both identity policies (on the role) and resource policies (on the action) must allow access for an operation to succeed. See the Engine Reference for the full IAM model.
succeed.
Build and Deploy
Build the matrix:
rars build --config poliglot.ymlThis validates your spec files against SHACL shapes, merges them into an assembly, and packages the result. If there are validation errors, the build fails with details about what needs to be fixed.
Install to your workspace:
rars install --config poliglot.yml --workspace my-workspaceVerify the installation:
rars list-lifecycle-commands my-project # package name, not the matrixOnce installed, the matrix is available in your workspace. The actions are defined but won't execute until the data service endpoint is reachable. See Connecting Systems of Record to wire up a live backend.
What's Next
- Connecting Systems of Record: make your actions callable by connecting a live backend
- Architecture: understand the semantic model and platform design - Engine Reference: full reference for all matrix resource types - API Reference: REST API endpoints and authentication