Skip to main content
A query policy limits how a querying company can use the columns an access rule exposes. This page describes the policy object, how Narrative applies it to a query, and how it validates a policy. For the reasons to use one, see Query policies. For step-by-step instructions, see Applying query policies.

The policy object

All three fields are optional, and the object accepts no other fields. An empty policy, {}, denies every use of every column.

Columns: attributes and fields

A policy names a column in one of two ways: Every column a policy names must be one the access rule’s NQL selects:
  • An attribute must be selected from _rosetta_stone. An access rule that selects a dataset column mapped to email, but not _rosetta_stone.email itself, does not expose the email attribute to a policy.
  • A field that renames an attribute is that attribute. When the access rule selects _rosetta_stone.email AS email, {"field": "email"} and {"attribute": "email"} name the same column, so a policy can hold a rule for only one of them.

Usage rules

Each usage_rules entry sets rules for one column.

How a column’s rule is resolved

Narrative resolves each usage of a column (projection, group by, and join) on its own, taking the first rule it finds:
  1. The usage_rules entry for the column, if that entry sets the usage
  2. The entry for each enclosing column, from the nearest up. A rule for address covers address.zip unless an entry for address.zip sets the same usage
  3. The default rule for the usage
  4. Deny
An entry that sets only projection therefore takes group_by and join from the enclosing column’s entry, or from default.

Rules and modes

Every rule has a required mode: Under restrict, each kind of rule accepts these function lists:
  • scalar_operators — Scalar functions, such as SHA2, UPPER, or SUBSTRING
  • aggregate_operators — Aggregate functions, such as COUNT or SUM. Add _DISTINCT to a name to permit its distinct form: COUNT_DISTINCT permits COUNT(DISTINCT …). Listing COUNT does not permit COUNT(DISTINCT …)
  • external_operators — Functions that send values outside the query: AI_COMPLETE, AI_CONVERSATION, CALL_MODEL_FUNCTION, EMBED_TEXT_768, and EMBED_TEXT_1024. See External functions
Function names are case-insensitive. A list a rule leaves out is empty. Under allow and deny, the lists are not used. When a query applies several functions to a column, each one must be listed. UPPER(SHA2(email, 256)) needs both SHA2 and UPPER. Window functions are never permitted under restrict.

What each usage covers

A policy rules on three usages. Each query clause counts as one of them: These uses are not checked:
  • WHERE filters
  • ORDER BY
  • A column a subquery selects but whose value never reaches the result. SELECT COUNT(1) FROM (SELECT email FROM …) reads no value of email
  • UNION ALL
  • The nio_last_modified_at column that DELTA filters on
Because filters are not checked, a column whose projection is denied can still appear in a WHERE clause. A query such as WHERE email = '[email protected]' can test whether a value is present. Leave a column out of the access rule’s NQL entirely when partners must not be able to filter on it.

External functions

External functions are checked wherever a query applies them to a column, filters included, against the column’s projection rule: Listing an external function in external_operators does not permit it in a join or group_by. Those rules accept only scalar_operators, and an external function is not a scalar function.

Rosetta Stone attributes the access rule does not select

When an access rule has a query policy, a query can read a Rosetta Stone attribute through it only if the access rule’s NQL selects that attribute from _rosetta_stone. A query that reads any other attribute through the access rule, such as one mapped from a column the rule selects or a derived attribute, gets no rows from it.

Join-required rule

A join-required rule makes the access rule usable only in queries that join it to the querying company’s own data. A query satisfies a match entry when all of these hold:
  • The join is direct. Each key appears in an ON equality whose other side reads columns of one table: a dataset the querying company owns, or the querying company’s own Rosetta Stone data (company_data.rosetta_stone, or rosetta_stone under its own company slug). The other side can apply any functions.
  • One table matches every key. All keys of the entry are equated with columns of the same consumer table.
  • Each key is in its form. A raw key has no function applied on the access rule’s side. An operator key has exactly the named function applied, so SUBSTRING(SHA2(email, 256), 1, 4) does not satisfy {"operator": "SHA2"}.
  • Every join is an inner join. A LEFT, RIGHT, or FULL join anywhere in the tree of joins that contains the access rule breaks the rule, on either side of the access rule.
These do not satisfy a match entry:
  • Reading the access rule without a join, or through a CROSS JOIN
  • Joining it only to another company’s access rule or dataset, or to Rosetta Stone data the querying company does not own
  • Equating the key only with a literal, or with a subquery
  • Joining it to the consumer’s table through an intermediate table, or in a separate join tree
  • An OR in the ON condition, or a comparison other than equality between the two tables, such as ar.name > c.full_name. A condition on one table, such as c.status = 'active', can sit beside the key equality
A query can join the consumer’s table to further tables of its own, or to other companies’ tables, once the key equality is in place. Each key must also be joinable under the policy’s own rules. The column’s resolved join rule must be allow, or restrict with the key’s operator listed in its scalar_operators. A raw key needs allow.

How Narrative enforces a policy

Narrative checks the policy when it compiles a query, once for each access rule the query reads. That includes access rules a query reaches through Rosetta Stone, such as narrative.rosetta_stone. When the query breaks the policy, Narrative leaves that access rule out of the query. The access rule contributes no rows, and the query runs on the access rules that remain. The query does not fail, and the response does not report which access rule was left out or why. Only reads through an access rule are checked. A query that reads a dataset directly, as you do with your own data under company_data, is not.

Managing a policy through the API

Every v2 access rule response includes query_policy, which is null when the access rule has none. That includes responses to companies the rule is shared with, so a partner can read the terms before querying. For request and response schemas, see the API reference.

Validation

Narrative validates a policy against the access rule’s NQL when you create an access rule with one and when you PUT one. An invalid policy returns 400. A POST with an invalid policy creates nothing, and a PUT with one leaves the current policy in place. error_description lists every problem, separated by semicolons. Each problem starts with where it is in the policy:
Messages about a field read field '<name>' where the table shows attribute '<name>'. A request body that does not match the schema, such as an unknown field or mode, also returns 400.

Example

A company with the slug acme shares an access rule named customer_segments_ar. Its NQL selects the email attribute and a segment_id column:
This policy lets a partner see each segment and how many distinct email addresses it holds, but never an email address itself. The partner can only use the access rule by joining it to their own data on the SHA-256 hash of the email:
This query satisfies the policy. It joins on SHA2 of the email, which the join rule lists and the join-required key names. It returns the email only inside COUNT(DISTINCT …), and it selects and groups by segment_id, which the policy allows:
The same query with SELECT ar.email would get no rows from the access rule, because a raw email reaching the result breaks the projection rule. Dropping the join would get none either, because nothing would satisfy the join-required rule.

Access Rules

What query policies are for and how they fit with access rules

Applying Query Policies

Attach, check, and remove a query policy