Search & Filter Access
The Search endpoints answer the list and filter questions: which resources can a subject act on?, which subjects can act on a resource?, which actions can a subject take? Uniquely, they can return a UCAST-style filter: a condition tree you push into your own data store to filter large datasets by policy without evaluating each row.
Natively conformant with the AuthZEN 1.0 Search API.
POST /access/v1/search/resource # which resources can subject do action on?
POST /access/v1/search/subject # which subjects can do action on resource?
POST /access/v1/search/action # which actions can subject do on resource?
Two modes: filter vs results
Set via options.return (default filter):
"UCAST-style" means the tree follows the shape popularized by UCAST (a type /
operator / conditions node tree): it is Vengtoo's own condition tree, not a
spec we claim conformance to.
| Mode | Returns | Use when |
|---|---|---|
filter | A UCAST-style predicate describing all matching rows | Your data lives in your store (large / unregistered): push the filter into your query |
results | A concrete list of registered resources/subjects (RBAC) | Your resources are registered in Vengtoo |
both | filter + results | — |
For enumerating registered resources without a filter,
resultsmode is equivalent to the reverse-query capability. Thefiltermode is what lets you filter data Vengtoo never stores. The filter mode returns a UCAST-style predicate (see the filter format).
Request
Headers
| Header | Value |
|---|---|
Authorization | Bearer vgt_... |
Content-Type | application/json |
Body: resource search
Omit resource.id: you're asking about all resources of the type:
{
"subject": { "external_id": "alice", "type": "user" },
"action": { "name": "read" },
"resource": { "type": "document" },
"options": { "return": "filter" }
}
- subject search: omit
subject.id(asks which subjects can do the action). - action search: omit the
action(asks which actions the subject can take); returns an action list.
Response
filter mode
{
"filter": {
"type": "logical",
"operator": "and",
"conditions": [
{ "type": "field", "field": "owner", "operator": "eq", "value": "alice" },
{ "type": "logical", "operator": "not",
"conditions": [ { "type": "field", "field": "status", "operator": "eq", "value": "archived" } ] }
]
},
"context": { "reason": "policy matched", "access_paths": ["direct"] }
}
results mode
{
"results": [ { "resource_id": "doc-1", "actions": ["read"], "access_path": "role" } ],
"context": { "reason": "policy matched" }
}
The filter format (UCAST-style)
The filter is a UCAST-style condition tree: Vengtoo's own predicate AST,
following the UCAST (Universal Condition AST) shape rather than conforming to an
external spec. Three node types:
| Node | Shape |
|---|---|
| field | {"type":"field","field":"owner","operator":"eq","value":"alice"} |
| logical | {"type":"logical","operator":"and"|"or"|"not","conditions":[…]} |
| const | {"type":"const","value":true}: true = all rows, false = no rows |
Field operators: eq, ne, gt, gte, lt, lte, in, nin, regex.
A pure-RBAC grant (no attribute conditions) yields {"type":"const","value":true} for
that path (all rows via that grant); no grant yields {"type":"const","value":false}.
Translate to SQL
The response is a condition tree, not SQL: Vengtoo stays database-agnostic. Each SDK
ships a reference ucast_to_sql helper (our own translator; no third-party UCAST
dependency) that turns the filter into a parameterized SQL WHERE clause you run
against your own database:
resp = vengtoo.search_resource(SearchRequest(...)) # -> resp.filter
where, params = ucast_to_sql(resp.filter) # e.g. "(owner_id = $1) AND ..."
rows = db.execute(f"SELECT * FROM documents WHERE {where}", params)
const resp = await vengtoo.searchResource({ ... }); // -> resp.filter
const { where, params } = ucastToSQL(resp.filter); // "$1, $2, …" placeholders
const rows = await db.query(`SELECT * FROM documents WHERE ${where}`, params);
resp, _ := client.SearchResource(ctx, req) // -> resp.Filter
where, params, _ := vengtoo.UCASTToSQL(resp.Filter, nil) // "$1, $2, …" placeholders
rows, _ := db.QueryContext(ctx, "SELECT * FROM documents WHERE "+where, params...)
It is a Postgres reference translator: it emits $1, $2, … numbered placeholders
and maps regex to Postgres' ~; other drivers may need a different placeholder or
regex style. Values are always bound as parameters (never string-interpolated), so
the output is SQL-injection safe. Pass a field→column map as the second argument
when your column names differ from the filter's field names (e.g. {"owner": "owner_id"}); column identifiers are validated before use.
What is not in the filter
Request-time gates, time windows, IP allowlists, MFA, rate limits, are not
encoded in the row filter (they can't be meaningfully expressed as a WHERE clause).
They are enforced at the actual access-evaluation call. The filter covers resource
attribute conditions and RBAC grants only.
Error responses
| Status | Description |
|---|---|
400 | Invalid request body |
401 | Missing or invalid credential |
501 | Partial evaluation unavailable (e.g., the local agent, which evaluates in-memory) |
Example
curl -X POST https://pdp.vengtoo.com/access/v1/search/resource \
-H "Authorization: Bearer vgt_..." \
-H "Content-Type: application/json" \
-d '{
"subject": { "external_id": "alice", "type": "user" },
"action": { "name": "read" },
"resource": { "type": "document" },
"options": { "return": "filter" }
}'