You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On permitio/pdp-v2:0.9.13, an additive policy schema change applied to a running PDP makes POST /user-permissions stop returning ReBAC-derived resource instances. Directly-assigned instances still come back, and POST /allowed still answers allow: true for the instances that vanished.
Restarting the pod — which forces a full data re-pull — restores them.
It fails silently: 200 with a smaller but well-formed payload, and /health, /healthy and /ready all stay green.
Environment
permitio/pdp-v2:0.9.13, 2 replicas on Kubernetes, cloud control plane (not offline mode), no FactDB backup configured. Policy is applied through the Permit API by an infrastructure-as-code tool.
Users are assigned a role on account. brand derives from that; campaign and location derive from brand. Relationship tuples are written through the API when a resource is created.
Steps to reproduce
Confirm /user-permissions returns derived instances for all three types.
Apply an additive schema change through the API: add one action to the brand resource, then grant it to the brand:owner and brand:manager roles. Nothing deleted, nothing recreated, relations and derivations untouched.
The PDP applies it as OPAL deltas:
Update on Role x2
Insert on RolePermission x2
Insert on ResourceAction x1
Update on Resource x1
Re-query /user-permissions for the same user.
Result
One user, before and after a pod restart, with no API writes in between:
Expected: the counts match. An additive change shouldn't alter which instances are enumerated.
Both replicas behaved identically. Two things ruled out:
Not truncation — only 84 campaigns exist, and all 84 return after the restart.
Not a filter bug — omitting resource_types gives the same result.
Why this is easy to miss
While /user-permissions returned zero campaigns, /allowed granted those same campaigns:
// POST /allowed {"user":{"key":"<user>"},"action":"read-manage",// "resource":{"type":"campaign","key":"<id>","tenant":"default"}}
{"allow": true, "debug": {"rbac": {"allow": false, "code": "no_permission"}}}
// denied by RBAC, granted via ReBAC derivation
check() evaluates the derivation live, while /user-permissions appears to serve a materialised set that the delta invalidated without recomputing. Anyone verifying with check() will conclude the PDP is healthy.
Impact
Any app that uses /user-permissions to list what a user may see will show an empty list after a routine policy change, with no error and every health check green. Ours emptied a user-facing list in production until we restarted the PDP. Because nothing forces a reload after an apply, it recurs on every schema change.
Questions
Is /user-permissions meant to return ReBAC-derived instances? The docs don't say, so it's unclear which result is the correct one.
If it is, should a ResourceAction / RolePermission delta recompute the derived set rather than drop it?
Summary
On
permitio/pdp-v2:0.9.13, an additive policy schema change applied to a running PDP makesPOST /user-permissionsstop returning ReBAC-derived resource instances. Directly-assigned instances still come back, andPOST /allowedstill answersallow: truefor the instances that vanished.Restarting the pod — which forces a full data re-pull — restores them.
It fails silently:
200with a smaller but well-formed payload, and/health,/healthyand/readyall stay green.Environment
permitio/pdp-v2:0.9.13, 2 replicas on Kubernetes, cloud control plane (not offline mode), no FactDB backup configured. Policy is applied through the Permit API by an infrastructure-as-code tool.Policy shape
Roles derive across two hops:
Users are assigned a role on
account.brandderives from that;campaignandlocationderive frombrand. Relationship tuples are written through the API when a resource is created.Steps to reproduce
/user-permissionsreturns derived instances for all three types.brandresource, then grant it to thebrand:ownerandbrand:managerroles. Nothing deleted, nothing recreated, relations and derivations untouched./user-permissionsfor the same user.Result
One user, before and after a pod restart, with no API writes in between:
/user-permissionscampaign(derived, 2 hops)location(derived, 2 hops)brand(derived, 1 hop)One-hop derivations survive; two-hop derivations disappear entirely.
Expected: the counts match. An additive change shouldn't alter which instances are enumerated.
Both replicas behaved identically. Two things ruled out:
resource_typesgives the same result.Why this is easy to miss
While
/user-permissionsreturned zero campaigns,/allowedgranted those same campaigns:check()evaluates the derivation live, while/user-permissionsappears to serve a materialised set that the delta invalidated without recomputing. Anyone verifying withcheck()will conclude the PDP is healthy.Impact
Any app that uses
/user-permissionsto list what a user may see will show an empty list after a routine policy change, with no error and every health check green. Ours emptied a user-facing list in production until we restarted the PDP. Because nothing forces a reload after an apply, it recurs on every schema change.Questions
/user-permissionsmeant to return ReBAC-derived instances? The docs don't say, so it's unclear which result is the correct one.ResourceAction/RolePermissiondelta recompute the derived set rather than drop it?POST /data-updater/trigger(feat: replace legacy /update_policy* 307 redirects with direct gated calls (PER-15246) #320, feat: debounce forced-reload trigger endpoints (PER-15248) #327) the right way to force a recompute after an apply?Happy to run further probes or share more logs.