ppoppo-sdk-core 0.2.0

Internal shared primitives for the Ppoppo SDK family (pas-external, pas-plims, pcs-external) — verifier port, audit trait, session liveness port, OIDC discovery, perimeter Bearer-auth Layer kit, identity types. Not a stable public API; do not depend on this crate directly. Consume the SDK crates that re-export from it (e.g. `pas-external`).
Documentation

Ppoppo SDK shared primitives — internal mechanism for the Ppoppo SDK family (pas-external, future pas-plims, pcs-external).

Stability — not a stable public API

This crate is published to crates.io as a transitive dependency of the SDK family above (cargo enforces "all transitive deps of a published crate must themselves be published"). It is not a stable public API; it carries no SemVer guarantees beyond the current minor and may rearrange shapes between minors. Consume the SDK crates that re-export from it (pas-external::*, eventually pas-plims::* / pcs-external::*); do not depend on ppoppo-sdk-core directly from a 3rd-party application.

Phase A foundation (RFC RFC_2026-05-08_app-credential-collapse.md) — collapses the verifier port + audit trait + session-liveness port + discovery primitive + perimeter Bearer-auth Layer kit + identity types out of pas-external so multiple SDK crates and 1st-party services (chat-auth, chat-api) all consume the same primitives.

Surface (Slice 1a)

  • error::SdkCoreError — narrow error type for sdk-core primitives.
  • types::{Ppnum, PpnumId, SessionId, UserId, KeyId} — SDK-shared identity types. Ppnum::TryFrom<String> is the validated boundary.
  • audit::*AuditSink trait + AuditEvent + VerifyErrorKind + IdTokenFailureKind + RateLimiter trait + RateLimitKey + compose_source_id + compose_id_token_source_id. Plus the in-tree utility impls (NoopAuditSink, MemoryAuditSink, MemoryRateLimiter, RateLimitedAuditSink) — utility impls live here too because the audit module is one cohesive unit.
  • session_liveness::{SessionLiveness, SessionLivenessError} — per-request L2 row liveness port. Wired into the verifier slot. Trait-only (no cipher/liveness AES wrapper code — that stays in pas-external as a feature-gated submodule).

Surface (Slice 1b — populated when verifier cohesive group lands)

  • verifier::*BearerVerifier trait + JwtVerifier impl + MemoryBearerVerifier + VerifiedClaims + VerifyConfig + VerifyError + JwksCache. Audit decision E (cohesive group — no trait/impl split across crates).

Surface (Slice 2 — populated)

  • discovery::{Discovery, DiscoveryError, fetch_discovery} — OIDC discovery document fetch (<issuer>/.well-known/openid-configuration). RFC 8414 §3.3 issuer-mismatch defense lives here. Gated on well-known-fetch (HTTP fetch + URL types).

Surface (Slice 4 — populated)

  • bearer::* — perimeter Bearer-auth Layer kit. AuthProvider<S> port + BearerAuthLayer<Sess, P> tower::Layer + BearerAuthConfig
    • VerifyError + MemoryAuthProvider<S> (test-support). Gated on axum feature. 1st-party services (chat-auth) import direct from ppoppo_sdk_core::bearer::* (audit decision B); 3rd-party consumers (RCW/CTW) reach the same kit through pas_external::bearer::* re-export (audit decision D).

Surface (Phase E — populated)

  • token_cache::{TokenCache, TokenCacheConfig, TokenSource, TokenCacheError, ClientCredentialsSource} — JWT retention layer with single-flight refresh. TokenCache owns a Box<dyn TokenSource>; the built-in ClientCredentialsSource covers the OAuth2 client_credentials grant. Gated on token-cache (cache+trait) and client-credentials (HTTP source).
  • interceptor::{AuthInterceptor, BearerCredential} — tonic sync Interceptor that attaches Authorization: Bearer <token> per outgoing gRPC call. Constructed from a pre-fetched BearerCredential (one instance per call). Gated on tonic-interceptor.

Out of scope (later phases)

  • scopes::* — sealed-trait family parent for SDK-specific scope marker types.