@ultimat3/query
Advanced tools
+14
-0
@@ -148,2 +148,16 @@ # @ultimat3/query | ||
| with no hook wired and counts the evaluations: exactly one. | ||
| - **`meta.auth` is derived from a WALK of the policy tree**, never from the root combinator. | ||
| `target.policy.kind === 'allow'` answered `'required'` for `or(allow(), can('x:y'))`, so the | ||
| pipeline's `auth` stage 401'd an anonymous caller the policy itself ALLOWS — while the MCP tool | ||
| and a direct server read let that caller through the same object. One policy, a different answer | ||
| per surface. `'public'` here is `meta.auth` only: the read is still `cache: no-store` and | ||
| `runQuery` still evaluates the policy per caller. | ||
| **`admitsAnonymous` is `@ultimat3/policy`'s** (`policy.ts`, beside `policyPermissions`) and | ||
| reaches this package through `policy-gate.ts` like every other authz question — never a copy | ||
| here. It cannot be one: `@ultimat3/action` needs the identical answer and is the same tier, so a | ||
| copy in either is a second answer for the other. It is EXACT rather than heuristic — with | ||
| `actor === null`, `can()` short-circuits before its predicate and `allow()`/`deny()` ignore their | ||
| arguments, so the tree alone decides. `packages/policy/src/policy.test.ts` asserts it against | ||
| `policy.run({ actor: null })` itself, case for case; `http.test.ts` proves this projection reads | ||
| the answer, over the real pipeline. | ||
| - `registry.ts` announces `registerQueries` in core's registrar table at import. That is how | ||
@@ -150,0 +164,0 @@ `defineApi({ queries })` in `@ultimat3/action` registers a read without importing this package |
+6
-6
| { | ||
| "name": "@ultimat3/query", | ||
| "version": "7.0.0", | ||
| "version": "8.0.0", | ||
| "description": "The query primitive: a policy-checked read, optionally live, with cursor pagination and an incremental matcher", | ||
@@ -34,8 +34,8 @@ "license": "MIT", | ||
| "dependencies": { | ||
| "@ultimat3/cache": "7.0.0", | ||
| "@ultimat3/core": "7.0.0", | ||
| "@ultimat3/http": "7.0.0", | ||
| "@ultimat3/policy": "7.0.0", | ||
| "@ultimat3/schema": "7.0.0" | ||
| "@ultimat3/cache": "8.0.0", | ||
| "@ultimat3/core": "8.0.0", | ||
| "@ultimat3/http": "8.0.0", | ||
| "@ultimat3/policy": "8.0.0", | ||
| "@ultimat3/schema": "8.0.0" | ||
| } | ||
| } |
+7
-4
@@ -17,3 +17,3 @@ /** | ||
| import { derivePath } from './naming'; | ||
| import { policyCapability } from './policy-gate'; | ||
| import { admitsAnonymous, policyCapability } from './policy-gate'; | ||
| import type { AnyQuery } from './query'; | ||
@@ -62,5 +62,8 @@ import { queryName, runQuery } from './read'; | ||
| name, | ||
| // `allow(...)` is the only way a read is public, and saying so explicitly is what | ||
| // keeps "forgot the policy" from ever looking like "meant to be readable". | ||
| auth: target.policy.kind === 'allow' ? 'public' : 'required', | ||
| // Derived from a WALK of the policy tree, never from the root combinator alone. | ||
| // `policy.kind === 'allow'` answered `required` for `or(allow(), can('x:y'))`, so the pipeline | ||
| // 401'd an anonymous caller the policy itself allows — while the MCP tool and a direct server | ||
| // read let the same caller through the same object. `public` here is not "unguarded": | ||
| // `enforcedBy: 'handler'` below means `runQuery` still evaluates the policy for every read. | ||
| auth: admitsAnonymous(target.policy) ? 'public' : 'required', | ||
| policy: policyCapability(target.policy), | ||
@@ -67,0 +70,0 @@ // `runQuery` is this route's one evaluation and it decides from the PARSED input the |
+13
-2
@@ -73,4 +73,15 @@ /** | ||
| export type { QueryPolicy, QuerySubject, QuerySurface } from './policy-gate'; | ||
| /** `policyCapability` is the display label; `policyPermissions` is what a report MATCHES on. */ | ||
| export { actorOf, guard, policyCapability, policyPermissions } from './policy-gate'; | ||
| /** | ||
| * `policyCapability` is the display label; `policyPermissions` is what a report MATCHES on. | ||
| * `admitsAnonymous` is `@ultimat3/policy`'s, re-exported here beside them: it is what | ||
| * `toQueryRoute` derives `meta.auth` from, so a plain `route` sets that field from the same walk | ||
| * rather than re-reading the root combinator. | ||
| */ | ||
| export { | ||
| actorOf, | ||
| admitsAnonymous, | ||
| guard, | ||
| policyCapability, | ||
| policyPermissions, | ||
| } from './policy-gate'; | ||
| export type { | ||
@@ -77,0 +88,0 @@ AnyQuery, |
+18
-1
@@ -10,3 +10,7 @@ /** | ||
| import type { Policy, Surface as PolicySurface } from '@ultimat3/policy'; | ||
| import { enforce, policyPermissions as flattenedPermissions } from '@ultimat3/policy'; | ||
| import { | ||
| enforce, | ||
| policyPermissions as flattenedPermissions, | ||
| admitsAnonymous as policyAdmitsAnonymous, | ||
| } from '@ultimat3/policy'; | ||
| import { QueryDeniedError } from './errors'; | ||
@@ -79,1 +83,14 @@ | ||
| } | ||
| /** | ||
| * Whether a policy admits an ANONYMOUS caller — `@ultimat3/policy`'s answer, re-exported here so | ||
| * `http.ts` reads it through this file like every other authz question. `toQueryRoute` derives | ||
| * `meta.auth` from it, never from `policy.kind === 'allow'`: that read looked at the ROOT | ||
| * combinator only, so `or(allow(), can('x:y'))` was 401'd by the pipeline before `runQuery` ran | ||
| * while the MCP tool and a direct server read allowed it. `true` is `meta.auth` only — the read is | ||
| * still `no-store` and `runQuery` still evaluates the policy per caller. Declared once in | ||
| * `policy.ts`, exactly as `policyPermissions` is: the answer is a property of the combinators. | ||
| */ | ||
| export function admitsAnonymous(policy: QueryPolicy): boolean { | ||
| return policyAdmitsAnonymous(policy); | ||
| } |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
202767
1.34%2904
1.04%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated
Updated
Updated