0021: Embedded PDP, hosted ADS and control plane
PermDock is a policy decision point you embed and that never needs a network call to decide; PermDock Cloud is an optional Authorization Decision Service and control plane that implements interfaces anyone can implement, lives in its own repository, and is reached through the thin permdock/cloud entry.
Status
Accepted, September 2026. Supersedes the "hosted policy service or SaaS PDP" non-goal wording in the roadmap and PRODUCT.md; the underlying rule (decisions never depend on a remote service) is kept and made explicit.
Context
PermDock's differentiators are that it is embedded, typed, zero-dependency and non-blocking (0003, 0010, 0015). The hosted authorization products it is compared against (Cerbos Hub, Permit.io, AuthZed Cloud, Oso Cloud, WorkOS FGA) put the decision behind a network call and take strings in, booleans out (landscape). Writing "no hosted service" as a non-goal protected that difference while the library did not exist.
Three things make a hosted offering worth defining now, as long as the difference is preserved:
- The operational layer around decisions is real work that teams pay for. Cerbos, whose engine is free and complete, sells exactly this: policy distribution, an audit store, data enrichment (commercial landscape). PermDock already specifies the artefacts such a layer would manage: the
DecisionEvent(audit), the approvaltoken(approvals), the snapshot and the catalog (wire formats). - Agent runtimes hand the hard part to the application. Eve's
approvalhook, the AI SDK'stoolApproval, the OpenAI Agents SDK'sinterruptionsand MCP elicitation all pause a run and expect the application to store the pending approval, notify a person and resume. Eve's documentation says a four-eyes flow needs "an application-owned approval request" that a separate approver resolves. Nobody sells that at the application-permission layer in a developer-first form. - AuthZEN is final and gateways speak it. Kong, Envoy, Tyk, Zuplo and WSO2 ship AuthZEN policy enforcement points. A hosted AuthZEN endpoint serving a TypeScript-authored policy lets a Go service or an API gateway enforce the same policy with no PermDock SDK at all, which is a story neither the TypeScript libraries nor the hosted PDPs tell from the TypeScript side.
The terminology question has an answer in the field: a policy decision point is an engine embedded in or beside the application; an Authorization Decision Service is an API-first service applications call (Schwartz, 2025). PermDock is the former; a hosted PermDock is the latter.
The constraint from the maintainers is that nobody may be forced into a subscription: whatever the Cloud does must be doable by an application on its own, the way Supabase users can run Postgres and the Supabase services themselves.
Decision
- PermDock is a PDP you embed. It never requires a network call to decide.
can,decide,assert,filter,where,simulateandsnapshotrun in-process on the policy in the bundle. The pdp provider is the one place a decision may wait on a remote service, and only for permissions the application explicitly delegates. This is now invariant 15 inAGENTS.mdand the threat model. - Cloud-shaped capabilities are interfaces in the open-source package, with in-process defaults. Approvals are an
ApprovalStore(0022) withmemoryApprovalStore()as the default. Audit is aDecisionSinkfed byon('decision')with an in-memory sink as the default. Snapshot distribution is aSnapshotSourcewith the local policy as the default. Every agent and HTTP adapter acceptsstoreandsinkoptions typed against these interfaces. An application that wants persistence implements the interface over its own database; the docs carry a recipe. - PermDock Cloud is an Authorization Decision Service and control plane that implements those interfaces. It hosts the AuthZEN endpoint from
permdock/authzenfor non-TypeScript callers and gateways, a persistentApprovalStorewith an inbox UI and notification delivery, a persistentDecisionSinkwith query and retention, snapshot distribution with SSF / CAEP invalidation, and catalog and usage dashboards. It adds hosting and interface; it does not add decision semantics. - The Cloud lives in a separate repository. This repository defines the contracts (interfaces, wire formats, the HTTP API the client speaks) and ships the client. Persistence, UI, billing and the Vercel Marketplace integration server live in
PermDock-Cloudand follow this repository's wire-format versions. - The client is
permdock/cloud, a thin MIT subpath with zero required dependencies.cloud({ url, key })returns anApprovalStore, aDecisionSinkand aSnapshotSourceover documented HTTP endpoints usingfetch. It is optional, server-only, andtests/bundleasserts that no client entry reaches it. Invariant 12 (no runtime dependencies in core) is untouched. - The hosted decision path speaks AuthZEN and is authenticated like any PEP-to-PDP call. Callers present a Vercel OIDC token when deployed on Vercel, or an OAuth client-credentials token elsewhere; the handler verifies it with
permdock/jwt. This is the concrete answer to the decision-endpoint authentication question for hosted deployments; the in-app decision endpoint keeps its session-or-bearer rule (authzen adapter). - Naming. The library and package stay
permdock; the hosted product is "PermDock Cloud". The docs use "PDP" for the embedded engine and "ADS" for the hosted endpoint, never the other way round.
Consequences
- The roadmap non-goal becomes "requiring a network call to decide"; the Cloud is listed as optional infrastructure in Phase 2, after core and the Phase 1 agent adapters exist to feed it.
- New entries:
permdock/approvals(Phase 1),permdock/cloud(Phase 2). New agent adapters that surface approvals:permdock/eve,permdock/openai(Phase 1), alongsideai-sdk,claude-agentandmcp. DecisionEvent,ApprovalRequest, the snapshot and the catalog are the Cloud API. They are already versioned wire formats; changing them follows the RFC-lite process and updates both repositories.- The threat model gains rows for Cloud key handling, approval-store tampering, approver identity and ADS caller authentication.
- The comparison page states that PermDock is embedded and can be consumed as a hosted AuthZEN ADS, and stops describing hosted services purely as the competition.
- Marketing and README copy may name the Cloud, but every feature page must show the in-process default first and the Cloud second.
Alternatives considered
- A hosted permissions gateway or MCP proxy in the decision path. Rejected: it is the strings-in, boolean-out product the library is positioned against, it adds latency to every check, and the proxy category (Permit MCP Gateway, Arcade, Oso for Agents, many enterprise gateways) is crowded and well funded. Per-tool bindings inside the server remain the design (landscape, "Adapt").
- Cloud-only approvals and audit. Rejected: it forces a subscription for a security feature and contradicts the Supabase-style requirement that everything be self-hostable. Interfaces with in-process defaults keep the open-source package complete.
- A separate
@permdock/cloudnpm package. Rejected: one install path and one version line are simpler for consumers and for thewire-permdockskill; a subpath with no required dependencies costs nothing for apps that never import it. - Shipping persistent reference stores (Postgres, Redis) in the open-source package. Deferred, not rejected: the interfaces are small enough that a documented recipe suffices at Phase 1, and shipping stores would pull database clients into the package's optional-peer list before any are measured. Revisit when the Cloud's own store has stabilised the interface.
- Renaming the library to a broader brand. Rejected for now: the naming convention is public API (0005) and the product surface already covers agents and decisions under the PermDock name. A broader name for the service is a future option, not a Phase 0 decision.
Related
0020: Measure bundle size, then set a baseline
The 3 kB gzip figure in 0015 is not a cap. Core is built first, then tests/bundle records the measured size as the regression baseline.
0022: Approvals are pluggable
Pending approvals live in an ApprovalStore interface with an in-memory default; the ApprovalRequest wire shape, approver identity, expiry, the PermDock-Approval resume header and plan-level approvals are fixed here so every agent adapter, self-hosted app and PermDock Cloud share one model.