diff --git a/docs/concepts/pdp/new-edge-pdp-architecture.mdx b/docs/concepts/pdp/new-edge-pdp-architecture.mdx new file mode 100644 index 000000000..6346bcc34 --- /dev/null +++ b/docs/concepts/pdp/new-edge-pdp-architecture.mdx @@ -0,0 +1,85 @@ +--- +title: New Edge PDP — Architecture +sidebar_label: Architecture +description: What runs inside the New Edge PDP container, which component is responsible for what, and the path a single authorization request takes. +sidebar_position: 2 +--- + +# New Edge PDP Architecture + +What runs inside the [New Edge PDP](./new-edge-pdp) container, which component is responsible for what, and the path a single authorization request takes. + +Everything runs inside one container. The only externally exposed listeners are the authorization API and the health endpoint. + +![Architecture of the New Edge PDP: your app and orchestrator on the left, the single New Edge PDP container holding the authorization API, OPA, the health server, the query and bundle loopbacks, the embedded database, the change-ingest worker and the NATS leaf, with Permit.io and your policy store on the right](/images/pdp/new-edge-pdp-architecture.svg) + +

+ Click the diagram to enlarge it. +

+ +:::note Reading the diagram +The diagram labels the change stream by its internal name, the **WAL** (write-ahead log). `LOCAL_WAL` is this container's own durable copy of it. Elsewhere in these pages it is called the *change stream*. +::: + +--- + +## What runs where + +| Component | Kind | Role | +| --- | --- | --- | +| **Authorization API** | In-process | The public REST surface on port `7000`. Opens **only once the PDP is ready**. | +| **OPA policy engine** | Supervised child process | Evaluates Rego. Bound to loopback only — never reachable from outside the container. | +| **Embedded database** | In-process (SurrealDB over RocksDB) | Your environment's fact graph — users, tenants, resource instances, relationships — **on disk**. | +| **NATS leaf node** | Supervised child process | Durable, on-disk event store. Holds the link to Permit's control plane. | +| **Change ingest and repair** | In-process task | Applies incoming changes to the embedded database; detects and repairs gaps. | +| **Query / bundle loopbacks** | In-process | Serve OPA its graph data and its policy bundle over `127.0.0.1`. | +| **Health and readiness** | In-process | Port `7001`. Answers throughout startup, including a long cold start. | + +### Ports + +| Port | Bound to | Purpose | +| --- | --- | --- | +| `7000` | All interfaces | Authorization API — the only port your services need | +| `7001` | All interfaces | Health and readiness — the port your orchestrator probes | +| `7002` | Loopback | Query loopback: serves OPA its graph data | +| `7003` | Loopback | Bundle loopback: serves OPA its Rego bundle | +| `8181` | Loopback | OPA | +| `4222` / `8222` | Loopback | NATS leaf client and monitoring | + +Only `7000` and `7001` are exposed by the container image. Everything else is reachable solely from inside it. + +--- + +## The decision path + +``` +client → :7000 authorization API + → 127.0.0.1 OPA + → 127.0.0.1 query loopback + → embedded database (local disk) +``` + +Every hop is loopback or local disk. **No step in answering an authorization query contacts Permit's API**, by construction — the New Edge PDP has no control plane other than its event stream, and cannot reach `api.permit.io` for a decision even if it wanted to. + +This is also what separates policy evaluation from graph traversal. OPA evaluates the Rego policy, but whenever that policy needs to know whether a user reaches a resource through a chain of roles and relationships, it calls out to the query loopback, which answers from the embedded database. Relationship queries run against a database built for them rather than against a JSON document held in memory. + +--- + +## Separation of duties + +The container is deliberately split so that the thing serving decisions is never blocked by the thing keeping data fresh: + +- **The request path** — authorization API, OPA, query loopback, embedded database — only ever reads. +- **The sync path** — NATS leaf, change ingest, gap detector, rebuild orchestrator — only ever writes, on its own tasks. + +They meet at the embedded database, which is why a control-plane outage costs freshness but never availability, and why a rebuild can run underneath a PDP that is still answering. See [How It Works](./new-edge-pdp-how-it-works) for what happens along the sync path. + +--- + +## Related documentation + +- [New Edge PDP](./new-edge-pdp) — overview +- [How It Works](./new-edge-pdp-how-it-works) — sync, consistency, cold start, and resource profile +- [Feature Parity](./new-edge-pdp-feature-parity) — capability comparison against the container PDP +- [Deployment](./new-edge-pdp-deployment) — requirements, observability, and the security model +- [Configuration](./new-edge-pdp-configuration) — environment variable reference (beta) diff --git a/docs/concepts/pdp/new-edge-pdp-configuration.mdx b/docs/concepts/pdp/new-edge-pdp-configuration.mdx new file mode 100644 index 000000000..83ab79781 --- /dev/null +++ b/docs/concepts/pdp/new-edge-pdp-configuration.mdx @@ -0,0 +1,105 @@ +--- +title: New Edge PDP — Configuration +sidebar_label: Configuration +description: Environment variable reference for the New Edge PDP (permitio/pdp-v3) — credentials, storage, ports, logging, and storage-engine tuning. +sidebar_position: 6 +--- + +# New Edge PDP Configuration + +Environment variable reference for the [New Edge PDP](./new-edge-pdp) (`permitio/pdp-v3`). + +:::caution Beta — expect changes +The New Edge PDP is in early access and **this configuration surface is not yet stable**. Variables and defaults on this page may be renamed, replaced, or removed before general availability. + +Treat it as a working reference rather than a contract: **pin an image tag**, and re-read this page when you upgrade. If you are depending on a specific variable, [tell us](mailto:support@permit.io) so we can flag it before it moves. +::: + +For the [container PDP](/concepts/pdp/overview#run-an-edge-pdp-with-docker) (`permitio/pdp-v2`), see [Configurations](/concepts/pdp/configuration) instead. The two runtimes do not share a configuration surface, and **a variable of the same name may mean something different** on each. + +--- + +## Credentials + +| Variable | Default | Description | +| --- | --- | --- | +| `PDP_API_KEY` | **required** | Your environment-level Permit API key. | + +`PDP_API_KEY` is the entire credential. This single value: + +- binds the container to exactly one Permit environment, +- authenticates it to the control plane, +- carries the control-plane routing, so the PDP finds the right plane on its own, +- and is the bearer token the authorization API accepts from your services. + +:::tip No control-plane URL to set +Unlike the container PDP, there is **no `PDP_CONTROL_PLANE` value to configure**. The API key routes the PDP to the correct control plane automatically. +::: + +--- + +## Storage and ports + +| Variable | Default | Description | +| --- | --- | --- | +| `EDGE_DB_PATH` | `/var/lib/edge-pdp/db` | Embedded database directory. **Must be on a persistent volume.** | +| `EDGE_DATA_DIR` | `/var/lib/edge-pdp` | Event store and generated credentials. **Must be on a persistent volume.** | +| `EDGE_PUBLIC_PORT` | `7000` | Authorization API — the only port your services need. | +| `EDGE_HEALTH_PORT` | `7001` | Health and readiness. Probe this port, [never `7000`](./new-edge-pdp-deployment#health-and-readiness). | + +Both storage paths must be on persistent storage that survives restarts. On ephemeral storage every restart becomes a full cold start. See [Deployment requirements](./new-edge-pdp-deployment#deployment-requirements). + +Ports `7002`, `7003`, `8181`, `4222`, and `8222` are used internally and bind to loopback only — see [Architecture](./new-edge-pdp-architecture#ports). Expose **`7000` and `7001`** and nothing else. + +--- + +## Behavior and tuning + +| Variable | Default | Description | +| --- | --- | --- | +| `RUST_LOG` | `info` | Log level. Try `info,edge_pdp=debug` when debugging. | +| `PDP_DEBUG` | `false` | Include debug detail in authorization responses. | +| `OPA_TIMEOUT_MS` | `800` | Per-request policy evaluation timeout. | +| `EDGE_PARALLELISM` | `4` | How many change transactions are applied concurrently. | +| `EDGE_DECISION_LOG_OPT_OUT` | `false` | Set to `true` to stop shipping decision logs. | +| `EDGE_DRAIN_TIMEOUT_SECS` | `10` | Shutdown drain budget. | +| `EDGE_CHILD_TERMINATION_TIMEOUT_SECS` | `30` | Child-process shutdown budget. | + +Your `terminationGracePeriodSeconds` must be at least the sum of the last two — **40 seconds at defaults**. See [Deployment requirements](./new-edge-pdp-deployment#deployment-requirements). + +### Child process logging + +| Variable | Default | Description | +| --- | --- | --- | +| `EDGE_NATS_LOG_LEVEL` | follows `RUST_LOG` | Control-plane client log level. | +| `EDGE_OPA_LOG_LEVEL` | follows `RUST_LOG` | Policy engine log level. | + +:::warning Debug levels expose your data +`EDGE_NATS_LOG_LEVEL=trace` logs raw message payloads — which means **your authorization data** — and `EDGE_OPA_LOG_LEVEL=debug` logs authorization request and response bodies. Use these only for short, deliberate debugging sessions, and never leave them on in production. +::: + +--- + +## Storage engine + +The embedded database inherits storage-engine defaults tuned for cloud-scale workloads, not for a small container. These are the levers that matter for memory footprint: + +| Variable | Default | Effect | +| --- | --- | --- | +| `SURREAL_ROCKSDB_BLOCK_CACHE_SIZE` | `536870912` (512 MiB) | Read cache. The single largest lever on resident memory. | +| `SURREAL_ROCKSDB_WRITE_BUFFER_SIZE` | `268435456` (256 MiB) | In-memory write buffer before flushing to disk. | +| `SURREAL_ROCKSDB_MAX_WRITE_BUFFER_NUMBER` | `32` | How many write buffers may exist at once. | +| `SURREAL_ROCKSDB_BACKGROUND_THREADS` | `4` | Background compaction threads. | + +At these defaults a container given only a few hundred MiB will be OOM-killed on startup. Start at **4 GiB** and tune down against your own data set. See [Resource footprint](./new-edge-pdp-how-it-works#resource-footprint). + +--- + +## Related documentation + +- [New Edge PDP](./new-edge-pdp) — overview +- [Architecture](./new-edge-pdp-architecture) — what runs inside the container and the request path +- [How It Works](./new-edge-pdp-how-it-works) — sync, consistency, cold start, and resource profile +- [Feature Parity](./new-edge-pdp-feature-parity) — capability comparison against the container PDP +- [Deployment](./new-edge-pdp-deployment) — requirements, observability, and the security model +- [Configurations](/concepts/pdp/configuration) — configuration reference for the container PDP diff --git a/docs/concepts/pdp/new-edge-pdp-deployment.mdx b/docs/concepts/pdp/new-edge-pdp-deployment.mdx new file mode 100644 index 000000000..dfde924bd --- /dev/null +++ b/docs/concepts/pdp/new-edge-pdp-deployment.mdx @@ -0,0 +1,100 @@ +--- +title: New Edge PDP — Deployment +sidebar_label: Deployment +description: Deployment requirements, health and readiness probes, logging, and the security model for the New Edge PDP. +sidebar_position: 5 +--- + +# Deploying the New Edge PDP + +Operational requirements, observability surfaces, and the security model for the [New Edge PDP](./new-edge-pdp). + +--- + +## Deployment requirements + +The New Edge PDP has real operational requirements that the container PDP does not. Read these before deploying. + +**Persistent storage is mandatory.** Both the embedded database directory and the event store must survive restarts. On ephemeral storage, every restart becomes a full cold start — minutes of startup, and a full snapshot transfer each time. + +**Set `terminationGracePeriodSeconds` to at least 40.** That is the combined shutdown budget for draining and for stopping the child processes, at default settings. If the grace period is too short, the container is killed mid-flush and the durable event store is corrupted — forcing a cold start on the next boot. + +**Set a generous startup probe.** Cold start can take minutes. Point liveness at `:7001/health` and readiness at `:7001/health/ready`, and give startup enough headroom that a first boot is not killed as a hang. + +**Run one container per Permit environment.** The API key binds the container to exactly one environment; there is no multi-environment mode. + +**Memory: start at 4 GiB.** See [Resource footprint](./new-edge-pdp-how-it-works#resource-footprint) for tuning. + +**Expose ports `7000` and `7001` only.** Everything else binds to loopback inside the container. + +**Runs as non-root** (uid/gid `10001`). Ensure your mounted volumes are writable by that user. + +:::tip Running locally on macOS +Port `7000` collides with AirPlay Receiver on macOS. Map it to a different host port, for example `-p 7766:7000`, and point your SDK at `http://localhost:7766`. +::: + +--- + +## Health and readiness + +Health is served on the **management port (`7001`)**, which is where all probes belong: + +| Endpoint | Auth | Behavior | +| --- | --- | --- | +| `GET /health` | None | Always `200`. Reports per-component status. Answers throughout startup. | +| `GET /health/ready` | None | `200` only when every gating component is up, otherwise `503`. | +| `GET /health/detail` | `PDP_API_KEY` bearer | Full diagnostic detail per component. | + +Wire **liveness** to `:7001/health` and **readiness** to `:7001/health/ready`. Nine components gate readiness — the control-plane link, each of the four sync planes, the embedded database, OPA, and both loopbacks. + +:::warning Probe `7001`, never `7000` +The authorization port (`7000`) also answers `GET /health` — and `GET /healthy`, an alias kept so tooling written for the container PDP keeps working. Neither is a substitute for the management port: + +- There is **no `/health/ready` on port `7000`**. A readiness probe pointed at `:7000/health/ready` gets a `404` and the pod never becomes ready. +- Port `7000` does not open at all until the PDP is ready, so any probe against it **fails for the entire cold start** — which can be minutes. A liveness probe there will kill the container before it ever finishes starting. + +Port `7001` answers from the moment the process starts, which is exactly what makes it the safe probe target. +::: + +`/health/detail` is where you diagnose a lagging or stale PDP: it reports child-process restart counts and exit codes, the PDP's position in the change stream against the control plane's oldest retained change, whether the PDP is currently **serving stale** data and for how long, and whether decision-log shipping is healthy. + +:::note Staleness never blocks decisions +Neither staleness nor decision-log failures gate readiness. A PDP that cannot ship its audit logs keeps answering authorization queries — the design deliberately refuses to trade the primary function for the secondary one. +::: + +--- + +## Logs + +Structured logs go to stdout at `INFO` by default; `RUST_LOG` overrides (`info,edge_pdp=debug` is a good verbose setting). Both child processes' output is captured and re-emitted on the main log stream with attribution and correct log levels, so container logs are the single place to look. + +:::warning Child debug levels expose data +The child processes have their own log levels, and turning them all the way up exposes data: the control-plane client logs raw message payloads at `trace` — which means **your authorization data** — and the policy engine logs authorization request and response bodies at `debug`. Use these only for short, deliberate debugging sessions, and never leave them on in production. +::: + +### Decision logs + +Decision logs ship from the OPA child directly to Permit over HTTPS, batched, with a bounded in-memory buffer. They require **both** that decision logging is enabled for your environment in Permit **and** that the PDP's local opt-out is not set — the local setting can only turn logging off, never on. + +--- + +## Security model + +**Requests are authenticated against the container's own key.** The bearer token on an authorization request is compared, in constant time, against the container's `PDP_API_KEY`. Permit's API is **never** contacted to authenticate or authorize a request — the code that could do so is not compiled into this binary at all. + +**One container serves exactly one environment.** The environment is fixed at startup from the API key. A valid key for a *different* environment does not authenticate, and there is no code path by which a caller can steer a request at data outside the container's own environment. + +**Credentials never reach the child processes.** Both OPA and the NATS leaf are started with a cleared environment; only the specific variables each one needs are re-added. Credential files are written `0600` with protections against symlink attacks from other containers sharing the volume, and are referenced by path rather than inlined into any generated config. + +**The blast radius of a compromised PDP is one environment's data.** It holds no credential that grants access to any other environment, and no ability to reach Permit's management API. + +--- + +## Related documentation + +- [New Edge PDP](./new-edge-pdp) — overview +- [Architecture](./new-edge-pdp-architecture) — what runs inside the container and the request path +- [How It Works](./new-edge-pdp-how-it-works) — sync, consistency, cold start, and resource profile +- [Feature Parity](./new-edge-pdp-feature-parity) — capability comparison against the container PDP +- [Configuration](./new-edge-pdp-configuration) — environment variable reference (beta) +- [Monitoring PDPs](/how-to/monitoring-pdps) — fleet visibility in the Permit dashboard diff --git a/docs/concepts/pdp/new-edge-pdp-feature-parity.mdx b/docs/concepts/pdp/new-edge-pdp-feature-parity.mdx new file mode 100644 index 000000000..8bbfeb728 --- /dev/null +++ b/docs/concepts/pdp/new-edge-pdp-feature-parity.mdx @@ -0,0 +1,63 @@ +--- +title: New Edge PDP — Feature Parity +sidebar_label: Feature Parity +description: Capability-by-capability comparison between the container PDP (permitio/pdp-v2) and the New Edge PDP (permitio/pdp-v3). +sidebar_position: 4 +--- + +# Feature Parity + +Capability-by-capability comparison between the [container PDP](/concepts/pdp/overview#run-an-edge-pdp-with-docker) (`permitio/pdp-v2`) and the [New Edge PDP](./new-edge-pdp) (`permitio/pdp-v3`). + +The New Edge PDP is **wire-compatible on the endpoints it implements** — same paths, same request and response shapes, so existing SDK integrations that use those endpoints work unchanged. But it implements a **subset** of the container PDP's surface. + +Its capability envelope is that of the **managed Cloud PDP, deployed in your own network** — not a port of the `pdp-v2` container. + +**✅ Supported · 🚧 On the roadmap · ❌ Not supported** + +| Capability | Container PDP | New Edge PDP | +| --- | :-: | :-: | +| [**Check**](/how-to/enforce-permissions/check) — `POST /allowed` | ✅ | ✅ | +|   └ RBAC | ✅ | ✅ | +|   └ ReBAC, including role derivation | ✅ | ✅ | +|   └ Multi-tenancy | ✅ | ✅ | +|   └ ABAC — condition, user and resource sets | ✅ | ❌ | +| [**Bulk check**](/how-to/enforce-permissions/bulk-check) — `POST /allowed/bulk` | ✅ | ✅ | +| [**User permissions**](/how-to/enforce-permissions/user-permissions) — `POST /user-permissions` | ✅ | ✅ | +| [**Authorized users**](/how-to/enforce-permissions/authorized-users) — `POST /authorized_users` | ✅ | ✅ | +| [**All-tenants check**](/how-to/enforce-permissions/all-tenants-check) — `POST /allowed/all-tenants` | ✅ | ❌ | +| [**URL-based enforcement**](/how-to/enforce-permissions/url-mapping/url-mapping-check) — `POST /allowed_url` | ✅ | ❌ | +| **AuthZen access evaluation** — `POST /access/v1/evaluation` | ✅ | ✅ | +| **AuthZen bulk evaluations** — `POST /access/v1/evaluations` | ✅ | ✅ | +| **AuthZen subject / resource / action search** | ✅ | ✅ | +| **AuthZen discovery** — `GET /.well-known/authzen-configuration` | ✅ | ✅ | +| **Policy-as-Code / custom Rego bundles** | ✅ | ❌ | +| **Local facts / read-your-own-writes** | ✅ | ❌ | +| [**Local enforcement APIs**](/how-to/enforce-permissions/list-role-assignments) — `/local/*` | ✅ | ❌ | +| **Custom data sources** | ✅ | ❌ | +| **Kong and nginx integration** | ✅ | ❌ | +| [**Offline / air-gapped first boot**](/how-to/deploy/offline-mode) | ✅ | ❌ | +| **Decision logs to Permit** | ✅ | ✅ | +| **Health and readiness endpoints** | ✅ | ✅ | +| **Prometheus metrics** — `GET /metrics` | ✅ | ❌ | +| **OpenTelemetry (OTLP traces, metrics, logs)** | ❌ | 🚧 | + +--- + +## Notes on specific rows + +**Data filtering.** Filtering through the information graph — [`/user-permissions`](/how-to/enforce-permissions/user-permissions) and [`/authorized_users`](/how-to/enforce-permissions/authorized-users) — works on both PDPs, and covers most "which resources can this user see?" cases. See [Data Filtering](/how-to/enforce-permissions/data-filtering) for the available approaches. + +**Offline first boot.** A New Edge PDP that cannot reach the control plane on its **first** boot cannot start. Once it has started, it survives control-plane outages indefinitely, serving decisions from its local copy. + +**ABAC.** A check evaluated against an ABAC policy returns a deny on the New Edge PDP rather than an error. If your policies use condition sets, user sets, or resource sets, use the container PDP. + +--- + +## Related documentation + +- [New Edge PDP](./new-edge-pdp) — overview +- [Architecture](./new-edge-pdp-architecture) — what runs inside the container and the request path +- [How It Works](./new-edge-pdp-how-it-works) — sync, consistency, cold start, and resource profile +- [Deployment](./new-edge-pdp-deployment) — requirements, observability, and the security model +- [Configuration](./new-edge-pdp-configuration) — environment variable reference (beta) diff --git a/docs/concepts/pdp/new-edge-pdp-how-it-works.mdx b/docs/concepts/pdp/new-edge-pdp-how-it-works.mdx new file mode 100644 index 000000000..84df2f01c --- /dev/null +++ b/docs/concepts/pdp/new-edge-pdp-how-it-works.mdx @@ -0,0 +1,168 @@ +--- +title: New Edge PDP — How It Works +sidebar_label: How It Works +description: How the New Edge PDP keeps its local copy in sync, what consistency that gives you, how cold start and warm resume differ, and what it costs to run. +sidebar_position: 3 +--- + +# How the New Edge PDP Works + +How the [New Edge PDP](./new-edge-pdp) keeps its local copy of your policy and authorization data current, what consistency that gives you, and what it costs to run. + +--- + +## How policy and data stay in sync + +The container's NATS leaf node holds a persistent link to Permit's control plane and **sources** four independent planes onto local, on-disk storage: + +| Plane | Carries | +| --- | --- | +| **Change stream** | Transactions of authorization facts — users, tenants, resource instances, relationship tuples | +| **Policy files** | The compiled Rego bundle OPA evaluates | +| **Policy schema** | Role and permission definitions from your policy | +| **Snapshot** | A one-shot bulk transfer, used only during cold start | + +Each change arrives as a **transaction** — an ordered set of operations with a timestamp and a transaction ID — rather than as a "something changed, come and fetch it" signal. The data is in the message. + +### Retention is per-PDP, and server-side + +Each PDP has its own durable position in the change stream. The control plane **retains a change until that specific PDP has acknowledged applying it** — so a PDP that is restarting, disconnected, or slow does not lose updates, and does not need to re-fetch its whole data set to catch up. Interest is held by the subscription's *existence*, not by an open connection; a PDP that is disconnected for minutes resumes exactly where it left off. + +### Self-healing + +Two background tasks keep the local copy honest: + +- **A gap detector** compares the PDP's position in the change stream against the oldest change the control plane still holds. If the PDP has fallen so far behind that the stream can no longer bridge the difference, it signals for a rebuild. +- **A rebuild orchestrator** then builds a fresh copy of the database **in a side directory** and swaps it in atomically. + +Crucially, **the PDP keeps answering from the existing database while the rebuild runs**. The public port is never re-bound, no request is refused, and no decision waits on the rebuild. Staleness is surfaced on the health endpoint rather than turned into errors. + +:::info Design principle +The New Edge PDP **serves stale before it serves nothing**. A control-plane outage costs you freshness, never availability — decisions keep flowing from the local copy indefinitely. +::: + +--- + +## Consistency + +Consistency is a property of the design rather than something you configure. + +**Three mechanisms combine:** + +1. **A durably ordered log.** Changes are carried on an ordered, persisted stream. Nothing is dropped, and nothing is trimmed before every PDP that needs it has confirmed it. + +2. **Order-independent application.** Each fact carries `(timestamp, transaction id)`, and a write is applied **only if it is strictly newer than what is already stored**. Because the merge rule is convergent, the final state does not depend on the order in which changes were applied — which is what makes it safe to apply several transactions concurrently without risking a stale write landing on top of a fresh one. + +3. **Per-transaction atomicity.** All operations within a transaction are applied together, so a decision never observes half of a multi-part change. + +Together these give **convergence by construction**: two PDPs in the same environment that have seen the same set of changes hold the same state, regardless of the order or timing in which they received them, and re-delivering a change that was already applied is a no-op. + +:::note What this does and does not guarantee +The consistency model is **eventual, with a convergent merge**. It guarantees that PDPs converge, that no update is lost, and that replays are safe. + +It does **not** provide read-your-own-writes. The New Edge PDP is read-only with respect to your data: a fact written through Permit's API becomes visible once it propagates. If you need write-then-immediately-read semantics today, use the container PDP's [local facts uploader](/how-to/manage-data/local-facts-uploader). + +A PDP that restarts with a backlog becomes ready and begins serving while it is still catching up. Whether it is serving stale data, and by how long, is reported on the health endpoint. +::: + +--- + +## Propagation latency + +The sync path is structurally shorter than the container PDP's: + +| | Container PDP (`pdp-v2`) | New Edge PDP (`pdp-v3`) | +| --- | --- | --- | +| Change notification | WebSocket notification | Push delivery on a durable per-PDP subscription | +| Data fetch | **A second call back to Permit's API** | None — the change travels *in* the message | +| Round trips per change | 2 or more | 1 | +| Missed while offline | Reconnect, then re-fetch | Retained server-side; resumes at its exact position | +| Calls to `api.permit.io` | Per update | **Never** | + +Removing the fetch leg removes both a round trip and a dependency: propagation no longer depends on the PDP being able to reach Permit's API, only on the event stream. + +:::caution Numbers not yet published +Permit has not yet published measured propagation-latency figures for the New Edge PDP. The improvement described above is architectural. If you have a latency target to validate against, [talk to us](mailto:support@permit.io) before designing around a specific number. +::: + +--- + +## Cold start and warm resume + +The PDP decides which path to take from the state of its persistent volume. + +### Warm resume — a restart with an intact volume + +The embedded database, the event store, and every subscription position are already on disk. The PDP reopens the database and resumes applying changes **from its last acknowledged position**. There is no snapshot transfer and no re-fetch. + +This is the normal case for a pod restart, a rolling update, or a short disconnection — and it is the path that benefits most from the design, because a disconnected PDP's changes were retained for it rather than discarded. + +### Cold start — first boot, or an unbridgeable gap + +On a genuinely fresh PDP — or when the gap detector determines the change stream can no longer bridge the difference — the PDP requests a **snapshot** of its environment. + +The snapshot is not a stream of individual records. It is a set of **pre-built database files**, transferred in chunks, checksum-verified, and then **bulk-loaded directly into the storage engine** — bypassing the normal per-record write path entirely. The snapshot also carries the Rego bundle and policy schema, so a cold-started PDP comes up with policy and data together. + +The boundary between the two is exact: the snapshot records the precise stream position it was cut at, and the PDP resumes the change stream from the very next change. Nothing is applied twice in a way that matters, and nothing is skipped. + +A checksum mismatch, a missing chunk, or an empty snapshot is **fail-closed** — the PDP refuses to come up on a partial data set rather than serving decisions against one. + +Bulk-loading pre-built database files is substantially faster than the container PDP's cold start, which fetches and applies its data set through the normal write path. + +:::tip Still give startup room +A cold start transfers and loads your environment's entire data set, so it is not instant — how long depends on how much data your environment holds. + +Set a **generous startup probe** and let readiness, not liveness, gate traffic. The health endpoint answers throughout, and the authorization port does not open until the PDP is ready, so there is no window in which it accepts a request it cannot answer correctly. +::: + +--- + +## Running at high volume + +What makes the New Edge PDP suitable for high request rates is mostly what it *doesn't* do per request: + +- **No network in the decision path** — every hop is loopback or local disk. +- **No control-plane call to authenticate** — the API key is verified against the PDP's own key in constant time. Permit's API is never contacted to authorize a request. +- **No dependency on Permit's availability** — a control-plane outage affects freshness, not throughput or availability. +- **Reads do not contend with syncing** — incoming changes are applied to the embedded database concurrently and independently of the request path. +- **The same evaluation core as the managed Cloud PDP**, which is already tuned for large-scale checks. + +:::caution Numbers not yet published +As with propagation latency, Permit has not published measured throughput figures for the New Edge PDP. The [Cloud PDP benchmarks](/concepts/pdp/cloud-pdp-benchmarks) measure a different deployment and should not be read as New Edge PDP numbers. +::: + +--- + +## Resource footprint + +The New Edge PDP changes **where** your authorization data lives, and that is the change that matters for sizing. + +| | Container PDP (`pdp-v2`) | New Edge PDP (`pdp-v3`) | +| --- | --- | --- | +| Fact graph | In OPA's **in-memory** document | **On disk**, in an embedded database | +| Memory vs. data size | Scales with your data set | Bounded by a configurable cache | +| Processes | Rust API server + Python Horizon (OPAL client) + OPA | Rust binary + NATS leaf + OPA | +| Python runtime | Required, for the OPAL client | Not present | +| Persistent storage | Not required | **Required** | + +The architectural consequence is that **memory stops scaling linearly with your data set**. On the container PDP the fact graph is raw JSON held in memory, which [OPA documents](https://www.openpolicyagent.org/docs/policy-performance) as costing roughly **20x** what the same data occupies in a compact on-disk form. On the New Edge PDP the data set lives on disk in exactly that compact form, and memory is governed by the database's cache size, which you set. + +:::caution Size generously to start, then tune +The New Edge PDP's **default** configuration is not tuned for a small footprint. It inherits cloud-scale storage-engine defaults — a 512 MiB block cache and a 256 MiB write buffer — so a container given only a few hundred MiB will be OOM-killed on startup. Permit currently sizes it at **4 GiB** of memory. + +A smaller footprint is possible by tuning the storage engine's cache and write-buffer sizes — see [Storage engine](./new-edge-pdp-configuration#storage-engine). Benchmark against your own data set before committing to a size. +::: + +### Disk + +Two paths must be on persistent storage — the embedded database and the event store. Size for **roughly twice your data set**, plus headroom: during a rebuild, two generations of the database exist side by side until the new one is swapped in. + +--- + +## Related documentation + +- [New Edge PDP](./new-edge-pdp) — overview +- [Architecture](./new-edge-pdp-architecture) — what runs inside the container and the request path +- [Feature Parity](./new-edge-pdp-feature-parity) — capability comparison against the container PDP +- [Deployment](./new-edge-pdp-deployment) — requirements, observability, and the security model +- [Configuration](./new-edge-pdp-configuration) — environment variable reference (beta) diff --git a/docs/concepts/pdp/new-edge-pdp.mdx b/docs/concepts/pdp/new-edge-pdp.mdx new file mode 100644 index 000000000..daaca8881 --- /dev/null +++ b/docs/concepts/pdp/new-edge-pdp.mdx @@ -0,0 +1,87 @@ +--- +title: New Edge PDP +sidebar_label: Overview +description: A PDP with an embedded on-disk database that keeps a local copy of your policy and authorization data in sync over a durable event stream — no network in the decision path. +sidebar_position: 1 +--- + +# New Edge PDP + +**A Policy Decision Point with an embedded on-disk database, kept in sync over a durable event stream.** + +The **New Edge PDP** is a ground-up rewrite of the PDP runtime, shipped as `permitio/pdp-v3`. It runs one container per Permit environment, holds a **local, on-disk copy** of that environment's policy and authorization data, and answers authorization queries with **no network call in the decision path**. + +:::note Availability +The New Edge PDP is a **new deployment option**, not a replacement. The [container PDP](/concepts/pdp/overview#run-an-edge-pdp-with-docker) (`permitio/pdp-v2`) remains fully supported and continues to be the right choice for the capabilities listed under [Feature Parity](./new-edge-pdp-feature-parity). + +It is currently in early access and enabled per account. [Contact us](mailto:support@permit.io) to have it turned on. +::: + +--- + +## Why a new runtime + +The container PDP holds your authorization data inside OPA, and **OPA's data store is a single in-memory JSON document**. That one design choice sets two ceilings. + +### It is not a graph store + +A JSON document has no indexes and no concept of edges. It cannot be optimized for graph traversal, or for the granular, relationship-heavy queries that ReBAC and derived roles depend on — questions like *"does this user reach this resource through some chain of roles and relationships?"* + +The New Edge PDP moves those questions out of the JSON document entirely. Relationship data lives in an embedded database built for graph traversal, and OPA queries it over a local loopback while it evaluates policy. Policy evaluation and graph traversal each run on something designed for the job. + +### It is expensive in memory + +Because the fact graph lives in memory, a large environment needs a large PDP. OPA's own documentation is direct about the cost: + +> Memory usage scales with the size of the policy (i.e., Rego) and data (e.g., JSON) that you load into OPA. Raw JSON data loaded into OPA uses approximately 20x more memory compared to the same data stored in a compact, serialized format (e.g., on disk). +> +> — [OPA documentation: Policy Performance](https://www.openpolicyagent.org/docs/policy-performance) + +The New Edge PDP stores exactly that compact, serialized form on disk, and keeps a bounded cache in memory. Memory becomes a number you set rather than a function of how much data your environment holds. + +### Updates cost a round trip + +There is a third ceiling, in how data arrives rather than where it lives. The container PDP refreshes by reacting to a notification and then **fetching the changed data back from Permit's API**. A notification says *that* something changed; a second call is needed to find out *what*. And catching up after a disconnect means re-fetching, because there is no server-side record of what a given PDP has and hasn't seen. + +On the New Edge PDP, updates **arrive already containing the change**, and the control plane **durably retains** each PDP's unacknowledged changes until that specific PDP confirms it applied them. + +--- + +## What it gives you + +- **Decisions that never leave the container.** Every hop in the request path is loopback or local disk, so no authorization query depends on reaching Permit. +- **Memory you control.** The data set lives on disk; a configurable cache bounds what is resident. +- **Sync that survives disconnection.** Changes are retained per-PDP, server-side, until that PDP acknowledges them — no re-fetch on reconnect. +- **Availability over freshness.** A control-plane outage costs you freshness, never the ability to answer. The PDP serves from its local copy indefinitely, and reports how stale it is. + +See [Architecture](./new-edge-pdp-architecture) for what runs inside the container, and [How It Works](./new-edge-pdp-how-it-works) for how the local copy is kept current. + +--- + +## Choosing between the two + +| Choose the **container PDP** when you need | Choose the **New Edge PDP** when you need | +| --- | --- | +| ABAC, condition sets, user or resource sets | Large data sets without a large memory footprint | +| Policy-as-Code / custom Rego bundles | Relationship queries served from a graph-capable store | +| Read-your-own-writes / local facts | Decisions that never depend on reaching Permit's API | +| URL-based enforcement or Kong integration | Sync that survives long disconnections without a re-fetch | +| Custom data sources | To drop the Python and OPAL layer entirely | +| A Prometheus `/metrics` pipeline, or deployment without persistent storage | Observability through structured logs and detailed health endpoints | + +Both connect to the same Permit environment and enforce the same policies you author in Permit. You can run them side by side while you evaluate. + +See [Feature Parity](./new-edge-pdp-feature-parity) for the full comparison. + +--- + +## Related documentation + +- [Architecture](./new-edge-pdp-architecture) — what runs inside the container and the request path +- [How It Works](./new-edge-pdp-how-it-works) — sync, consistency, cold start, and resource profile +- [Feature Parity](./new-edge-pdp-feature-parity) — capability comparison against the container PDP +- [Deployment](./new-edge-pdp-deployment) — requirements, observability, and the security model +- [Configuration](./new-edge-pdp-configuration) — environment variable reference (beta) +- [PDP Overview](/concepts/pdp/overview) — deployment models for the container PDP +- [Cloud PDP Capabilities](/concepts/pdp/cloud-pdp-capabilities) — the managed PDP's capability envelope, which the New Edge PDP mirrors +- [Monitoring PDPs](/how-to/monitoring-pdps) — fleet visibility in the Permit dashboard diff --git a/sidebars.js b/sidebars.js index ccbb886e2..8d1d26ce1 100644 --- a/sidebars.js +++ b/sidebars.js @@ -379,6 +379,21 @@ const sidebars = { "concepts/pdp/configuration", "concepts/pdp/cloud-pdp-capabilities", "concepts/pdp/cloud-pdp-benchmarks", + { + type: "category", + label: "New Edge PDP", + link: { + type: "doc", + id: "concepts/pdp/new-edge-pdp", + }, + items: [ + "concepts/pdp/new-edge-pdp-architecture", + "concepts/pdp/new-edge-pdp-how-it-works", + "concepts/pdp/new-edge-pdp-feature-parity", + "concepts/pdp/new-edge-pdp-deployment", + "concepts/pdp/new-edge-pdp-configuration", + ], + }, "how-to/manage-data/local-facts-uploader", ], }, diff --git a/static/images/pdp/new-edge-pdp-architecture.svg b/static/images/pdp/new-edge-pdp-architecture.svg new file mode 100644 index 000000000..6d883b428 --- /dev/null +++ b/static/images/pdp/new-edge-pdp-architecture.svg @@ -0,0 +1,310 @@ + + + + + New Edge PDP Architecture + Architecture of the New Edge PDP: the authorization API, OPA, health server, query and bundle loopbacks, embedded database, change ingest and NATS leaf inside one container, with Permit.io and your policy store outside it. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Your app / service · Permit SDK · Your VPC / network + + + + Your app / service + Permit SDK + + + + Your orchestrator · liveness & readiness probes · Your VPC / network + + + + Your orchestrator + liveness & readiness probes + + + + Authorization API · :7000 — opens once ready · Your VPC / network › New Edge PDP — one container per Permit environment · bearer: PDP_API_KEY + + + + Authorization API + :7000 — opens once ready + bearer: PDP_API_KEY + + + + OPA policy engine · supervised child · Your VPC / network › New Edge PDP — one container per Permit environment + + + + OPA policy engine + supervised child + + + + Health & readiness · :7001 — 9 gating components · Your VPC / network › New Edge PDP — one container per Permit environment + + + + Health & readiness + :7001 — 9 gating components + + + + Query loopback · 127.0.0.1:7002 — graph data · Your VPC / network › New Edge PDP — one container per Permit environment + + + + Query loopback + 127.0.0.1:7002 — graph data + + + + Bundle loopback · 127.0.0.1:7003 — rego bundle · Your VPC / network › New Edge PDP — one container per Permit environment + + + + Bundle loopback + 127.0.0.1:7003 — rego bundle + + + + Embedded database · SurrealDB + RocksDB, on disk · Your VPC / network › New Edge PDP — one container per Permit environment · persistent volume + + + + Embedded database + SurrealDB + RocksDB, on disk + persistent volume + + + + WAL ingest & repair · applies changes; self-heals · Your VPC / network › New Edge PDP — one container per Permit environment + + + + WAL ingest & repair + applies changes; self-heals + + + + NATS leaf · supervised child · Your VPC / network › New Edge PDP — one container per Permit environment · durable JetStream + + + + NATS leaf + supervised child + durable JetStream + + + + Policy Store · your own Git service · Architecture component + + + + Policy Store + your own Git service + + + + Permit.io · app.permit.io · api.permit.io · Architecture component · multi-tenant + + + + Permit.io + app.permit.io · api.permit.io + multi-tenant + + + + + + Authorize (REST) + + + + Evaluate + + + + Probes + + + + Graph data + + + + Policy bundle + + + + Reads + + + + Policy KV + + + + LOCAL_WAL + + + + Applies + + + + Event channel (WSS :443) + + + + Decision logs (HTTPS) + + + + Policy as code (SSH) + + + + + + Your VPC / network + + + + + New Edge PDP — one container per Permit environment + + + + + Legend + + + Backend + + + + Database + + + + Cloud + + + + Message bus + + + + External + + + \ No newline at end of file