v0.1.0
Reference Implementation

Reference Implementation Examples

Current end-to-end examples from the live PDPP reference implementation. Not normative protocol documentation.

These examples document the current reference implementation, not a hypothetical future deployment. They show the real surfaces exposed by reference-implementation/server, reference-implementation/runtime, and reference-implementation/cli today.

If you want the public explainer and run/deploy posture first, start with /reference. If you want the detailed implementation notes, continue to /docs/reference-implementation.

Two boundaries matter when reading them:

  • Client requests are staged through POST /oauth/par, then approved through the current consent shell at GET /consent?request_uri=... and POST /consent/approve.
  • Owner self-export is a separate OAuth device flow using POST /oauth/device_authorization, POST /device/approve, and POST /oauth/token.

Two things remain deliberately out of scope in these examples:

  • a generic third-party authorization-code redirect flow

The current reference proves request staging, public-client self-registration, consent, issued grants, owner self-export, and honest native-versus-polyfill source identity.

Example 1: Longview requests compensation data from Northstar HR

This is the native-provider path. Longview requests compensation records from Northstar HR, so the request identifies the source with source: { kind: "provider_native", id: "northstar_hr" }.

Step 1: Longview stages the request through PAR

POST /oauth/par
Content-Type: application/json

{
  "client_id": "longview_planning_v1",
  "client_display": {
    "name": "Longview",
    "uri": "https://longview.example",
    "policy_uri": "https://longview.example/privacy",
    "tos_uri": "https://longview.example/terms"
  },
  "authorization_details": [
    {
      "type": "https://pdpp.org/data-access",
      "source": {
        "kind": "provider_native",
        "id": "northstar_hr"
      },
      "purpose_code": "https://longview.example/purpose/career-move-planning",
      "purpose_description": "Compare salary, equity, benefits, and tax tradeoffs before a career move",
      "access_mode": "continuous",
      "retention": { "duration": "P90D", "on_expiry": "delete" },
      "streams": [
        { "name": "pay_statements", "necessity": "required", "view": "summary" },
        { "name": "equity_grants", "necessity": "required", "view": "summary" },
        { "name": "benefits_enrollments", "necessity": "optional", "view": "summary" }
      ]
    }
  ]
}

Reference response:

{
  "request_uri": "urn:pdpp:pending-consent:dc_4f5f7c0f9b6a4f31",
  "authorization_url": "http://localhost:7662/consent?request_uri=urn%3Apdpp%3Apending-consent%3Adc_4f5f7c0f9b6a4f31",
  "expires_in": 300
}

The authorization server renders the request with:

GET /consent?request_uri=urn%3Apdpp%3Apending-consent%3Adc_4f5f7c0f9b6a4f31

The consent surface is server-rendered. It reads the staged request, shows the client identity and requested streams, and lets the user approve or deny it.

Step 3: Approval creates the grant and returns the client token

The current reference implementation uses a direct approval shortcut instead of a full authorization-code redirect.

POST /consent/approve
Content-Type: application/json

{
  "request_uri": "urn:pdpp:pending-consent:dc_4f5f7c0f9b6a4f31",
  "subject_id": "owner_local"
}

Reference response:

{
  "grant_id": "grt_3ce1d18c8b6f4f9b",
  "token": "9de5...opaque bearer token...",
  "grant": {
    "version": "0.1.0",
    "grant_id": "grt_3ce1d18c8b6f4f9b",
    "subject": { "id": "owner_local" },
    "client": {
      "client_id": "longview_planning_v1",
      "client_display": { "name": "Longview" }
    },
    "source": {
      "kind": "provider_native",
      "id": "northstar_hr"
    },
    "purpose_code": "https://longview.example/purpose/career-move-planning",
    "access_mode": "continuous",
    "streams": [
      { "name": "pay_statements", "view": "summary", "fields": ["employer", "pay_period_start", "pay_period_end", "gross_pay", "net_pay", "currency"] },
      { "name": "equity_grants", "view": "summary", "fields": ["employer", "grant_type", "quantity", "currency", "granted_at", "vesting_start_date", "vesting_end_date"] },
      { "name": "benefits_enrollments", "view": "summary", "fields": ["employer", "plan_name", "coverage_level", "effective_date", "employee_cost_monthly", "currency"] }
    ]
  }
}

Step 4: Longview queries the resource server

Because this is the native-provider path, the client relies on the grant-bound source object.

GET /v1/streams/pay_statements/records?limit=10
Authorization: Bearer 9de5...opaque bearer token...

Representative response:

{
  "object": "list",
  "data": [
    {
      "object": "record",
      "id": "ps_2026_04_15",
      "stream": "pay_statements",
      "data": {
        "employer": "Northstar HR",
        "pay_period_start": "2026-04-01",
        "pay_period_end": "2026-04-15",
        "gross_pay": 5400,
        "net_pay": 3912,
        "currency": "USD"
      },
      "emitted_at": "2026-04-16T12:00:00Z"
    }
  ],
  "has_more": false
}

Step 5: Longview performs incremental sync

GET /v1/streams/pay_statements/records?changes_since=cursor_01
Authorization: Bearer 9de5...opaque bearer token...

The resource server returns only records whose grant-authorized projection changed since cursor_01, plus a next_changes_since token on the terminal page.

Example 2: CLI owner login and self-export against the same provider

This is the owner path. It proves that a generic CLI can consume the provider’s metadata, authenticate the owner, and export records without using Longview at all.

Step 1: Discover the provider

The CLI starts from the resource server URL:

pdpp provider show --rs-url http://localhost:7762 --format json

Representative output:

{
  "resource_server": "http://localhost:7762",
  "authorization_server": "http://localhost:7662",
  "pushed_authorization_request_supported": true,
  "pushed_authorization_request_endpoint": "http://localhost:7662/oauth/par",
  "device_authorization_supported": true,
  "device_authorization_endpoint": "http://localhost:7662/oauth/device_authorization",
  "pdpp_self_export_supported": true,
  "pdpp_token_kinds_supported": ["owner", "client"]
}

Step 2: Start the owner device flow

POST /oauth/device_authorization
Content-Type: application/x-www-form-urlencoded

client_id=pdpp-cli

Reference response:

{
  "device_code": "dc_owner_2f58...",
  "user_code": "A1B2C3",
  "verification_uri": "http://localhost:7662/device",
  "verification_uri_complete": "http://localhost:7662/device?user_code=A1B2C3",
  "interval": 5,
  "expires_in": 300
}

Step 3: The user approves the device code

POST /device/approve
Content-Type: application/x-www-form-urlencoded

user_code=A1B2C3&subject_id=owner_local

Step 4: The CLI polls for the owner token

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:device_code&
device_code=dc_owner_2f58...&
client_id=pdpp-cli

Reference response:

{
  "access_token": "1b9b...owner bearer token...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Step 5: Export data as the owner

Native owner reads use the provider-native source:

pdpp owner export pay_statements --rs-url http://localhost:7762 --owner-token 1b9b...

Polyfill owner reads are different:

  • they use the same owner token model
  • they identify the source as { "kind": "connector", "id": "<registry URI>" } because the public source identity is connector-scoped

Example 3: Polyfill source access uses a connector source object

The reference also supports a polyfill path for collected sources like Spotify. In that path:

  • the request sent to /oauth/par includes source: { kind: "connector", id }
  • the resulting grant has source.kind = "connector"
  • owner reads and runtime ingest/state operations remain connector-scoped

That split is intentional. The reference implementation is proving one engine substrate with two honest realizations:

  • native provider access identified with source.kind = "provider_native"
  • polyfill access identified with source.kind = "connector"

Example 4: Token introspection — verify an issued token and read its grant

The AS exposes RFC 7662-style token introspection at POST /introspect with PDPP extensions. The RS uses this endpoint internally to authorize every request; third parties (e.g., an RS operator building a proxy layer) can call it directly.

grant_storage_binding is the one internal field the AS redacts before returning the response — the public envelope never exposes storage-layer connector ids.

Step 1: Introspect a live client token

# TOKEN = the opaque bearer token returned by /consent/approve
curl -sX POST "$AS_URL/introspect" \
  -H 'Content-Type: application/json' \
  -d "{\"token\": \"$TOKEN\"}" | jq .

Reference response for a valid, active client-scoped token:

{
  "active": true,
  "pdpp_token_kind": "client",
  "subject_id": "owner_local",
  "exp": 1749081600,
  "grant_id": "grt_3ce1d18c8b6f4f9b",
  "client_id": "longview_planning_v1",
  "grant": {
    "version": "0.1.0",
    "grant_id": "grt_3ce1d18c8b6f4f9b",
    "subject": { "id": "owner_local" },
    "client": {
      "client_id": "longview_planning_v1",
      "client_display": { "name": "Longview" }
    },
    "source": {
      "kind": "provider_native",
      "id": "northstar_hr"
    },
    "purpose_code": "https://longview.example/purpose/career-move-planning",
    "access_mode": "continuous",
    "streams": [
      {
        "name": "pay_statements",
        "view": "summary",
        "fields": ["employer", "pay_period_start", "pay_period_end", "gross_pay", "net_pay", "currency"]
      }
    ]
  },
  "trace_id": "trc_a1b2c3d4e5f60001",
  "scenario_id": null
}

PDPP extension fields in the response:

FieldMeaning
pdpp_token_kind"client" for grant-scoped tokens, "owner" for owner self-export tokens, "mcp_package" for MCP grant-package tokens
grant_idThe grant that backs this token (client tokens only)
grantFull persisted grant object — source, streams, access_mode, purpose
subject_idThe data owner who approved the grant
expUnix timestamp at which the token expires (null if no expiry)
trace_idAudit trace id from the original consent ceremony; null if none
scenario_idTest/scenario tag when present; null in production flows

Step 2: Token inactive — grant revoked

When the underlying grant is revoked (or the token has expired), introspection returns active: false with a typed reason. Note that a consumed single_use grant does not flip its already-issued token to inactive — consumption blocks new token issuance, not the existing token (see Example 6). Introspection returns active: false with a typed reason in these cases:

{
  "active": false,
  "inactive_reason": "grant_revoked",
  "grant_id": "grt_3ce1d18c8b6f4f9b",
  "client_id": "longview_planning_v1",
  "subject_id": "owner_local"
}

Possible inactive_reason values:

ValueCause
grant_revokedGrant was explicitly revoked; token is now dead
grant_expiredToken's expires_at has passed
token_revokedToken itself was revoked (owner tokens)
token_expiredOwner/mcp_package token past expiry
grant_invalidGrant's persisted state no longer matches the registered manifest contract

Step 3: Introspect an MCP grant-package token

MCP grant-package tokens return a different active shape — grant_package_id instead of grant_id, and a package object instead of grant:

{
  "active": true,
  "pdpp_token_kind": "mcp_package",
  "subject_id": "owner_local",
  "exp": 1749081600,
  "grant_package_id": "gpkg_7f8a9b0c1d2e3f40",
  "client_id": "my_mcp_client",
  "package": {
    "package_id": "gpkg_7f8a9b0c1d2e3f40",
    "grants": [
      { "grant_id": "grt_abc1", "source": { "kind": "connector", "id": "https://registry.pdpp.org/connectors/gmail" } },
      { "grant_id": "grt_abc2", "source": { "kind": "connector", "id": "https://registry.pdpp.org/connectors/github" } }
    ]
  },
  "trace_id": "trc_b2c3d4e5f6a70002"
}

Example 5: Record-level audience binding with resources[]

resources[] on a stream scopes a grant to specific record keys, implementing RFC 8707-style resource indicators at the record level. The RS enforces the list as a SQL WHERE record_key IN (...) predicate — records not in the list return blob_not_found or are simply absent from query results. This is how a client requesting "just these three invoices" can receive a narrower grant than one requesting an entire stream.

Step 1: Stage a PAR request with record-scoped resources[]

curl -sX POST "$AS_URL/oauth/par" \
  -H 'Content-Type: application/json' \
  -d '{
    "client_id": "my_client",
    "authorization_details": [{
      "type": "https://pdpp.org/data-access",
      "source": { "kind": "connector", "id": "https://registry.pdpp.org/connectors/spotify" },
      "purpose_code": "assist.summarize",
      "purpose_description": "Retrieve the three named artists for a comparison task.",
      "access_mode": "single_use",
      "streams": [{
        "name": "top_artists",
        "fields": ["id", "name", "popularity"],
        "resources": ["artist_01", "artist_02", "artist_03"]
      }]
    }]
  }' | jq -r .request_uri

Step 2: Owner approves — grant has resources[] embedded

APPROVED=$(curl -sX POST "$AS_URL/consent/approve" \
  -H 'Content-Type: application/json' \
  -d "{\"request_uri\": \"$REQUEST_URI\", \"subject_id\": \"owner_local\"}")
TOKEN=$(echo $APPROVED | jq -r .token)

The issued grant embeds resources on the stream:

{
  "streams": [{
    "name": "top_artists",
    "fields": ["id", "name", "popularity"],
    "resources": ["artist_01", "artist_02", "artist_03"]
  }]
}

Step 3: RS enforces the resources[] list — only those records are visible

curl -s "$RS_URL/v1/streams/top_artists/records" \
  -H "Authorization: Bearer $TOKEN" | jq '.data[].id'
# → "artist_01"
# → "artist_02"
# → "artist_03"
# records outside resources[] are absent from results even if they exist

Aggregate queries are equally scoped: a sum over popularity on this token counts only those three records. The enforcement is in SQL (record_key IN (?) pushdown), so the RS never loads the hidden records into application memory.

Example 6: Single-use grant consumption

access_mode is a protocol-enforced grant constraint with two values: continuous (the standing-authorization default shown in Examples 1 and 4) and single_use. A single_use grant is consumed atomically on the first token issuance: the issued token stays valid until its own expiry, but the grant can never mint a second token. This is how a client that needs a one-time bootstrap ("read my data once to seed a profile, then forget about me") gets exactly that, enforced by the AS rather than promised in prose.

The behaviour in this example is asserted against the running server by reference-implementation/test/b6-single-use-consumption-conformance.test.js (and the consumption rejection by test/pdpp.test.js, "single_use grant: second token issuance is rejected with grant_consumed"). If the runtime drifts from what is documented here, those suites fail.

Step 1: Stage a single-use PAR request

The only difference from a continuous request is "access_mode": "single_use".

REQUEST_URI=$(curl -sX POST "$AS_URL/oauth/par" \
  -H 'Content-Type: application/json' \
  -d '{
    "client_id": "longview",
    "authorization_details": [{
      "type": "https://pdpp.org/data-access",
      "source": { "kind": "connector", "id": "spotify" },
      "purpose_code": "https://pdpp.org/purpose/personalization",
      "purpose_description": "One-time recommendation bootstrap",
      "access_mode": "single_use",
      "streams": [{ "name": "top_artists", "fields": ["id", "name", "popularity"] }]
    }]
  }' | jq -r .request_uri)

Step 2: Approval issues the first (and only) token

APPROVED=$(curl -sX POST "$AS_URL/consent/approve" \
  -H 'Content-Type: application/json' \
  -d "{\"request_uri\": \"$REQUEST_URI\", \"subject_id\": \"owner_local\"}")
TOKEN=$(echo "$APPROVED" | jq -r .token)

The issued grant carries access_mode: "single_use" and — unlike a continuous grant, whose expires_at may be null — always a bounded expires_at (the reference default is 24h from issuance):

{
  "grant_id": "grt_18146807dc63d26c",
  "token": "a5461cc8…3f617a",
  "grant": {
    "version": "0.1.0",
    "grant_id": "grt_18146807dc63d26c",
    "issued_at": "2026-06-13T22:48:20.933Z",
    "subject": { "id": "owner_local" },
    "client": {
      "client_id": "longview",
      "registration_mode": "pre_registered_public",
      "client_display": { "name": "Longview" }
    },
    "source": { "kind": "connector", "id": "spotify" },
    "manifest_version": "1.0.0",
    "purpose_code": "https://pdpp.org/purpose/personalization",
    "purpose_description": "One-time recommendation bootstrap",
    "access_mode": "single_use",
    "streams": [{ "name": "top_artists", "fields": ["id", "name", "popularity"] }],
    "expires_at": "2026-06-14T22:48:20.933Z"
  }
}

Step 3: The issued token still serves queries — consumption is not revocation

Consumption applies to new token issuance, not to the already-issued token. The token works for as many reads as the client needs until it expires — single_use bounds how many tokens a grant mints, not how many queries one token may perform.

curl -s "$RS_URL/v1/streams/top_artists/records?limit=10" \
  -H "Authorization: Bearer $TOKEN" | jq '.data | length'
# → 200 OK, records returned

Introspecting that token confirms it is still active even though the grant is already consumed (the grant object reports access_mode: "single_use"):

{
  "active": true,
  "pdpp_token_kind": "client",
  "grant_id": "grt_18146807dc63d26c",
  "client_id": "longview",
  "grant": { "access_mode": "single_use", "...": "full grant object" }
}

Step 4: A second token issuance is refused with grant_consumed

The grant was marked consumed atomically on the first issuance. Whichever HTTP re-issuance path a client reaches for next — an OAuth refresh_token exchange at POST /oauth/token, or a device re-exchange — bottoms out in the same internal issueToken primitive, which refuses a consumed single_use grant:

issueToken(grant_id="grt_18146807dc63d26c", …)
  → Error: Grant has already been consumed
    code: grant_consumed   (mapped to HTTP 403 by server/routes/ref-error-status.ts)

A continuous grant under the identical sequence mints a second token and keeps serving — that contrast is the control test in the B6 suite. Single-use grants additionally persist no STATE: the runtime refuses to attach grant-scoped sync state to a single_use grant (test/pdpp.test.js, "grant-scoped polyfill state rejects single_use grants"), so a one-time run leaves no resumable cursor behind.

PDPP normatively constrains how a conformant AS renders the parts of a selection request and grant, even though it does not standardize consent-screen layout. The spec (spec-core.md, "Semantic classes and consent-surface rendering") defines three primary semantic classes, plus client_display as a separate identity category. The reference consent surface keeps them visually distinct; a reviewer can map every field in the JSON above to exactly one class.

Semantic classWhat it isv0.1 fieldsTrust treatment
Protocol-enforced constraintsValues the AS and/or RS actually validate or enforcestreams, field projection, time_range, resources, access_modeAuthoritative. Rendered as the binding terms of the grant.
Structured policy declarationsMachine-readable statements that matter for consent and audit but are not generally self-enforcingpurpose_code, purpose_description, retentionDisplayed as declared policy. (https://pdpp.org/purpose/ai_training is the one exception that adds a protocol-level consent requirement.)
Attributed client claimsClient-authored, unverifiable statements about this requestclient_claims.commitmentsRendered separately and attributed to the client (e.g. "Longview says:"). MUST NOT share the visual register of enforced terms.

client_display is a separate category, not one of the three classes: requester identity metadata used to identify who is asking, not a grant constraint. It is entity-scoped and appears at the top level of the authorization request, outside authorization_details. client_claims, by contrast, is request-scoped and lives inside each authorization_details entry — a client may make different commitments for different requests. Inline client_display values are client-asserted; the AS renders identity under its own resolution and trust policy (local registration → trust registry → validated software statement → inline → client_id fallback).

{
  "client_display": { "name": "Longview", "uri": "https://longview.example" },
  "authorization_details": [{
    "type": "https://pdpp.org/data-access",
    "access_mode": "single_use",
    "purpose_code": "https://pdpp.org/purpose/personalization",
    "client_claims": { "commitments": ["Data used only for this study"] }
  }]
}

A separate authorship rule governs data descriptions: a stream's display.detail is manifest-authored (the provider's connector manifest), never client-authored. A client cannot describe what your data is; it can only request access to it and attach its own attributed claims. The AS MUST keep manifest-authored descriptions distinct from client claims, so a client can never borrow the provider's voice to make data look more innocuous than it is.