pattern

Policy Decision Point

also called PDP, Authorization Service, Policy Engine

The component that evaluates an authorisation question and returns a decision, kept separate from the enforcement points that ask, so policy can change without redeploying every service.

zero trustauthorizationavailabilityopalatency

Authorisation written into each service means that changing a rule is a code change in every service that implements it. Separating the decision from the enforcement gives one place where policy lives, one audit trail and one language, with enforcement points scattered through gateways, sidecars and libraries that merely ask and obey.

The separation is right. What it costs depends entirely on whether "ask" means a network call.

Why it matters

A remotely called decision point is a hard dependency of every request in the platform. At 99.9% availability in a five-hop chain, the chain inherits roughly five times that unavailability, and when it is down the platform does not degrade, it stops. It adds 1 to 5 ms per hop in-region, so a five-hop request pays 5 to 25 ms. At 100000 internal requests per second it is a fleet whose only output is yes or no.

The alternative implementation of the same architecture - evaluate policy in-process from a locally cached bundle, and use the central service to distribute bundles - has microsecond latency, no request-path dependency and the same single source of policy. The architectural shape is identical; the availability properties are not.

Implementation patterns

  • Distribute policy, do not answer queries. The decision point compiles and publishes bundles; enforcement points pull them on an interval and evaluate locally.
  • Version and stage bundles like code. A canary set of enforcement points takes a new bundle first, because policy with global reach can now deny everything globally in seconds.
  • Keep the data the policy needs local too. A rule requiring a lookup of group membership per request reintroduces the network call through the back door; push the membership data into the bundle or into a short-lived token claim.
  • Fail-closed at the enforcement point, fail-static on the bundle. If the bundle cannot be refreshed, keep serving the last known good one and alarm; if no bundle exists at all, deny. These are different failures and conflating them is how a distribution outage becomes a platform outage.
  • Decide the staleness number explicitly. A 30 to 60 second refresh means a revocation takes that long; short-lived credentials cover the gap.
  • A break-glass path that does not require the decision point, alarmed rather than prevented.

Industry example

WeChat's DAGOR overload control, published at SoCC 2018, is the instructive contrast from an adjacent domain: admission decisions are made locally by each service from a propagated priority threshold rather than by a central admission service, because a central component in the request path of every call becomes the thing that fails under the conditions it exists to manage. The same reasoning applies to authorisation, where the failure is less dramatic and more total.

Failure scenarios

  • Decision point saturation during a traffic spike, adding latency to every hop and pushing callers into timeouts.
  • A bad policy push denying all traffic, including the traffic needed to push the rollback.
  • Silent fail-open introduced as a resilience measure and never revisited, so an outage of the decision point means no authorisation at all.
  • Cold bundles after a mass restart, where every enforcement point fetches at once.
  • Policy that queries external data per request, making the "local" decision remote again.
  • Divergent bundle versions across the fleet, so the same request is allowed in one zone and denied in another, which presents as an intermittent bug.

Trade-offs

Local evaluation trades revocation latency for availability, and that trade should be written down as a number rather than assumed. Central evaluation trades availability for immediacy and is occasionally correct: a decision that must be exact at the instant it is made, such as a high-value payment approval, is worth a synchronous call.

The other trade is comprehensibility. One policy language covering the whole estate is powerful and becomes an artefact only two people understand, which is its own operational risk; tests and policy review practices are not optional.

When not to use it

For a handful of services in one team, authorisation in a shared library with configuration is simpler, has no new failure domain, and is honest. Introduce a decision point when the number of services and rules makes per-service implementation an unmaintainable approximation of intent, or when an auditor requires per-request evidence.

And for most internal service-to-service calls, a central decision per request buys sub-second revocation that nobody needs, since the calling workload's credential expires in minutes anyway. Pay for it only where revocation speed is a stated requirement.

Interview question

Q: You are asked to put a central authorisation service in front of every internal call. Say yes, then tell me exactly how you would build it so that it is not the least available component in the platform.

What a strong answer covers: the availability arithmetic of a shared synchronous dependency across a multi-hop chain · local evaluation from distributed bundles as the shape that keeps the architecture and drops the dependency · bounded staleness stated as a number, with short-lived credentials covering the revocation gap · fail-closed versus fail-static as distinct behaviours · staged policy rollout because policy now has global reach in seconds · and the cases where a synchronous decision is genuinely worth it.

Quick check

Quiz: What is the main risk of a per-request authorisation service? It becomes a hard dependency of every request, so its unavailability multiplies across the call chain and stops the platform rather than degrading it.

Flashcard: How do you keep central policy without central latency? Distribute compiled policy bundles and evaluate locally in the enforcement point, accepting a stated staleness of 30 to 60 seconds.