Users, roles and permissions in Stardog, and how one switch makes the PII graph vanish for marketing while HR still sees it.
Two colleagues paste the same SPARQL into Studio at the same moment: "show me Jolynn Bithany's name and SSN." Priya from HR gets Jolynn Bithany | 609-38-5573. Marco from marketing gets Jolynn Bithany | with an empty cell. No error, no "access denied": for Marco, the part of the graph that holds SSNs simply doesn't exist. Nobody wrote a second query, a masked view, or a GRANT in Aurora. By the end of this chapter you'll have built exactly that, and your Chapter 14 agent inherits it for free: an LLM running as marketing that asks for SSNs gets nothing to leak.
| Term | What it is | Why it exists | Tiny example |
|---|---|---|---|
| User | A login on the Stardog server (username + password). | Every request is made as someone. | stardog_admin, and soon hr_user |
| Role | A named bundle of permissions that users are assigned to. | You change a policy in one place, not per person. | marketing |
| Permission | A triple of action + resource type + resource. | To say exactly what may be done to what. | read + db + kit-c360 |
| Named-graph security | A database option that makes each named graph a separately permissioned resource. | So access can be narrower than "the whole database." | read on named-graph:kit-c360\virtual://aurora_c360_safe |
| Sensitive property | A property whose values are masked for users without explicit permission. | Hides one column-like property wherever it appears, without splitting graphs. | :ssn shows as a masked string for marketing. |
Stardog's docs define a permission as an action on a resource of some resource type. The valid actions are read, write, create, delete, grant, revoke, execute, and all. The resource types you'll meet most:
| Resource type | Written as (CLI) | Controls |
|---|---|---|
db | db:kit-c360 | querying / updating a database |
named-graph | named-graph:kit-c360\<graph IRI> | one graph, only if named-graph security is on |
virtual-graph | virtual-graph:virtual://name | managing a virtual graph's definition |
data-source | data-source:data-source://name | managing a JDBC data source |
sensitive-properties | sensitive-properties:kit-c360 | seeing unmasked sensitive values |
metadata, admin, dbms-admin, stored-query, icv-constraints… | database options, admin tasks, and the rest |
Your server (read with a read-only API call) has two roles. cloud has every action on every resource type and belongs to stardog_admin and to your Stardog Cloud portal login. It looks like it covers everything, but once named-graph security is on it reads no graphs at all; build step 6 shows this and how to keep your own access. reader has read on db, named-graph, virtual-graph, data-source, metadata, stored-query and icv-constraints, all with the wildcard *. (* means "every resource of that type": every database, every named graph, and so on.) In Studio → Security, select the cloud role and its Permissions table shows seven rows, one per action (EXECUTE, GRANT, WRITE, CREATE, READ, REVOKE, DELETE), each with resource type * and resource *: everything. The USERS pane also lists two accounts you didn't create: admin, marked superuser (Stardog Cloud's built-in account; you never log in as it), and your Stardog Cloud portal login (your email address), which also has the cloud role.
hr gets the PII graph.By default a user who can read a database can read every graph in it. The option security.named.graphs changes that: "If enabled, named graphs are an explicit resource type in Stardog's security model." It's false by default and on kit-c360 today, and can be changed while the database is online. Once it's on:
named-graph:<db>\<graph IRI>, with a backslash between database and graph. The docs' example is stardog-admin user grant -a read -o 'named-graph:myDB\http://example.org/g1' myUser.read and write apply to named graphs, and "Wildcards cannot be used to grant permissions over named graphs," so you list each graph.Do virtual graphs count? Yes. The docs' Virtual Graph Security page says "Accessing virtual graphs is controlled the same way as regular named graphs": with named-graph security off, all virtual graphs are accessible, and with it on, "users will be able to query only the virtual graphs for which they have been granted access." So the permission for our PII graph is read on named-graph:kit-c360\virtual://aurora_c360_pii. (That exact string isn't printed on the page; it follows from the named-graph format. If Studio rejects it, tell Claude.) The same page distinguishes query forms:
| How the query names the graph | User lacks permission → |
|---|---|
GRAPH <virtual://aurora_c360_pii> { … } | treated as empty, no error |
FROM <virtual://aurora_c360_pii> | treated as empty, no error |
SERVICE <virtual://aurora_c360_pii> { … } | an error |
This is why Chapter 8 split Aurora into aurora_c360_safe and aurora_c360_pii: security works per graph, and the PII mapping holds only :ssn and :cardNumber, so hiding that graph hides exactly those two properties.
Same SPARQL, two roles, two result sets. HR sees the SSN. For marketing the PII graph was silently removed from the query's dataset, so the OPTIONAL part finds nothing.
Splitting graphs means designing mappings around security. Stardog's other tool works per property: you list property IRIs in the database option security.properties.sensitive (named groups go in security.properties.sensitive.groups). Users without read on sensitive-properties:kit-c360 (or sensitive-properties:kit-c360\GroupName for a group) get the row with the value masked, by default as a SHA-256 hash. kit-c360 has a friendlier mask in security.masking.function: the string "Insufficient privileges to view this value".
So with security.properties.sensitive = tag:stardog:api:ecomm:ssn, marketing would see Jolynn Bithany | Insufficient privileges to view this value instead of an empty cell, arguably clearer because they learn the value exists. The blog describes this route as tagging :ssn as m:SensitiveProperty in the model (m: = urn:stardog:modules:); the server docs describe the database option, not that tag, so if you try it, set the option.
| Grants in each warehouse | Named-graph security in Stardog | |
|---|---|---|
| Where policy lives | Aurora GRANTs + Redshift GRANTs, kept in sync by hand | One place, stated in business terms (the "PII graph") |
| Cross-warehouse questions | Each engine enforces its half; the join happens in someone's code | One query, one policy, applied before any SQL is sent |
| An agent writing arbitrary queries | Needs its own DB credentials per warehouse | Runs as one Stardog user and can't reach what that user can't read |
| Adding a third source (say, Athena) | A third grant system | One more graph to permission |
The honest limit: Stardog reaches Aurora and Redshift through the data sources' own logins (Chapter 7), so the warehouses see one service account, not Priya or Marco. Keep that account least-privileged (read-only on schema c360) and treat Stardog's roles as the per-person layer on top; warehouse controls stay as defense in depth. (Stardog can pass through a user's own credentials for some sources, such as Databricks with Microsoft Entra ID, but not in our setup.)
Users and roles live in Studio → Security (the padlock in the left sidebar), available on your Free tier. Per the docs: + in the ROLES pane to add a role; select a role → Add Permission (action, resource type, resource); + in the USERS pane to add a user; select a user → Assign Role. And "When adding permissions via Studio, you can omit the resource type prefix," so you type kit-c360\virtual://aurora_c360_safe, not named-graph:kit-c360\….
Action, resource type, resource. For example read · named-graph · kit-c360\virtual://aurora_c360_pii (CLI form named-graph:kit-c360\virtual://aurora_c360_pii).
Yes. The docs' Virtual Graph Security page says access to virtual graphs "is controlled the same way as regular named graphs."
GRAPH <virtual://aurora_c360_pii> { ?c :ssn ?ssn }. Error or empty? And with SERVICE?GRAPH (and FROM) give an empty result with no error, because the graph is silently dropped from the dataset. SERVICE gives an error.
Named-graph security grants whole graphs. Putting only :ssn and :cardNumber in aurora_c360_pii lets you hide exactly the sensitive properties by withholding one graph.
Marketing would get the row with the SSN masked ("Insufficient privileges to view this value" here). The docs call the feature not production-ready (full-text search and zero-length paths can bypass it) and don't say whether it covers virtual graphs.
No. The warehouses see only Stardog's data source login, which you keep read-only and least-privileged; Stardog roles are the per-person layer on top.
cloud (everything, on stardog_admin) and reader (read on everything with *, which, tested, opens no named graph once named-graph security is on).security.named.graphs=true makes each graph a resource: named-graph:kit-c360\<IRI>, read/write only, no wildcards. Unreadable graphs are silently dropped.GRAPH/FROM give empty results, SERVICE an error.hr and marketing, users hr_user and marketing_user, and the same SSN query returning 609-38-5573 for HR and an empty cell for marketing.
Paste it to Claude with what you saw (the counts or results), so we can confirm it matches before the next build step relies on it.