Skip to content

Product documentation

Product API configuration

Generated reference for the oauthy.* configuration bound by Product API.

Product API configuration

The customer-plane management API (resource server): FGA, DSAR, audit export, capability entitlement, multi-issuer token validation, and the idp-facade.

oauthy.applications.hub

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/applications/HubApplicationsClient.kt

PropertyTypeDefaultDescription
oauthy.applications.hub.base-urlString"http://thoryn-hub:8080"
oauthy.applications.hub.service-identity-token-pathString"/var/run/secrets/tokens/hub-applications-facade"

oauthy.audit-export

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/compliance/audit/AuditExportStorageConfiguration.kt

SSO-78 — audit-export storage properties.

PropertyTypeDefaultDescription
oauthy.audit-export.s3.bucketString""
oauthy.audit-export.s3.regionString""
oauthy.audit-export.s3.key-prefixString"audit-export"
oauthy.audit-export.local.base-dirStringSystem.getProperty("java.io.tmpdir") + "/oauthy-audit-export"Filesystem directory used by LocalFsAuditExportStore.

oauthy.audit.internal.k8s

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/audit/internal/AuditInternalSecurityConfig.kt

Configuration properties for the K8s SA-token-authenticated audit ingestion endpoint.

PropertyTypeDefaultDescription
oauthy.audit.internal.k8s.enabledBooleanfalseDefaults to false so unit / integration tests don't drag the K8s JWKS into the test classpath.
oauthy.audit.internal.k8s.issuerString"https://kubernetes.default.svc.cluster.local"SSO-1477 — the expected iss claim.
oauthy.audit.internal.k8s.jwks-uriString"https://kubernetes.default.svc/openid/v1/jwks"
oauthy.audit.internal.k8s.audienceString"product-api"
oauthy.audit.internal.k8s.ca-pathString"/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"SSO-1468 — path to the PEM CA bundle whose certificate(s) sign the cluster API server's serving certificate.
oauthy.audit.internal.k8s.token-pathString"/var/run/secrets/kubernetes.io/serviceaccount/token"SSO-1471 — path to the projected ServiceAccount token presented as a Bearer credential on the JWKS discovery fetch.
oauthy.audit.internal.k8s.connect-timeoutDurationDuration.ofSeconds(2)SSO-1468 — connect timeout for the JWKS HTTP fetch.
oauthy.audit.internal.k8s.read-timeoutDurationDuration.ofSeconds(2)SSO-1468 — read timeout for the JWKS HTTP fetch.

oauthy.capability

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/capability/CapabilityEntitlementProperties.kt

SSO-1863 (Phase 3; ADR 2026-07-01-hub-product-agnostic-platform.md) — product-api's per-family enablement policy as operator config (oauthy.capability.*), lifted out of the hardcoded CapabilityFamilyPolicy literal so no product-family na...

PropertyTypeDefaultDescription
oauthy.capability.familiesMap<String, CapabilityPolicy>DEFAULT_FAMILY_POLICYoperator-supplied coarse-family → enable policy.
oauthy.capability.default-new-tenant-familiesSet<String>DEFAULT_NEW_TENANT_FAMILIESthe family set a brand-new tenant is entitled to.

oauthy.dsar

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/dsar/DsarStorageConfiguration.kt

DSAR bundle-storage properties (SSO-97).

PropertyTypeDefaultDescription
oauthy.dsar.s3.bucketString""
oauthy.dsar.s3.regionString""
oauthy.dsar.s3.key-prefixString"dsar"
oauthy.dsar.local.base-dirStringSystem.getProperty("java.io.tmpdir") + "/oauthy-dsar"Filesystem directory used by LocalFsDsarStore.

oauthy.dsar.fanout

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/dsar/DsarFanoutProperties.kt

Configuration for the DSAR orchestrator's fan-out to peer services (SSO-97).

PropertyTypeDefaultDescription
oauthy.dsar.fanout.hub-base-urlString"http://localhost:8080"
oauthy.dsar.fanout.identity-service-base-urlString"http://localhost:8081"
oauthy.dsar.fanout.product-api-base-urlString"http://localhost:8082"
oauthy.dsar.fanout.service-account-tokenString""Bearer token presented to peers on GET /admin/dsar/{sub}.

oauthy.erasure

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/erasure/ErasureProperties.kt

Configuration for the GDPR Art.

PropertyTypeDefaultDescription
oauthy.erasure.grace-windowDurationDuration.ofDays(7)Two-phase grace window between deactivate and hard-erase.
oauthy.erasure.max-hold-windowDurationDuration.ofDays(90)Maximum time a hold may stand before the request is DEFERRED + escalated.
oauthy.erasure.lock-ttlDurationDuration.ofSeconds(60)Redis per-request lock TTL — bounds the orchestrator's drive.
oauthy.erasure.sweep-cronString"0 11 * * * *"Cron for the re-consult / advance sweep.
oauthy.erasure.veto-callback-timeoutDurationDuration.ofSeconds(5)SSO-2510 — per-veto-client callback timeout for com.devnow.productapi.erasure.hold.RpCallbackHoldProvider.

oauthy.fga

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/fga/FgaProperties.kt

FGA configuration (oauthy.fga.*), owned by product-api (ADR 2026-07-15-fine-grained-authorization-fga.md, epic SSO-1889 + SSO-1901).

PropertyTypeDefaultDescription
oauthy.fga.model.max-object-typesInt100Maximum number of object types a single model may define.
oauthy.fga.model.max-relations-per-typeInt50Maximum number of relations a single object type may define.
oauthy.fga.model.max-model-bytesInt128 * 1024Maximum serialized model size, in bytes (128 KiB).
oauthy.fga.model.max-rewrite-nesting-depthInt10Maximum nesting depth of a relation's userset-rewrite tree (union / intersection / exclusion) — SSO-2576.
oauthy.fga.check.max-depthInt10Rewrite / userset / tupleToUserset recursion depth.
oauthy.fga.check.max-nodesInt1000Total tuple-expansion steps per check — the fanout / DoS budget.
oauthy.fga.check.max-fanoutInt100Rows read per single object#relation expansion.
oauthy.fga.check.max-batch-sizeInt50Maximum number of checks a single batch POST /check may carry.
oauthy.fga.check.cache.ttl-secondsLong60
oauthy.fga.limits.max-tuples-per-tenantLong1_000_000Maximum number of relation tuples a single tenant may store.
oauthy.fga.limits.max-writes-per-requestInt100Maximum writes + deletes a single POST /tuples may carry.
oauthy.fga.limits.max-import-tuples-per-requestInt5_000Maximum tuples a single bulk POST /tuples:import may carry (SSO-1954).
oauthy.fga.list-objects.max-depthInt10Reverse-expansion BFS depth (hops out from the subject).
oauthy.fga.list-objects.max-nodesInt1_000Total reverse-lookup steps per request — the expansion / DoS budget.
oauthy.fga.list-objects.max-fanoutInt100Rows read per single reverse lookup (one subject descriptor's edges).
oauthy.fga.list-objects.max-candidatesInt500Maximum distinct candidate objects of the requested type collected before confirmation.
oauthy.fga.list-objects.max-resultsInt100Maximum objects a single response may carry, and the ceiling on the request's own limit.
oauthy.fga.list-objects-export.max-objectsInt50_000maxObjects is the hard ceiling on candidate objects scanned per job: hitting it ends the scan with truncated: true (the honest "export too large" signal — the same prefix posture the interactive endpoint's truncated flag has).
oauthy.fga.list-objects-export.scan-batch-sizeInt1_000scanBatchSize is the DISTINCT keyset-scan page size.
oauthy.fga.list-objects-export.ttl-hoursLong24ttlHours is how long a completed export's result stays downloadable.
oauthy.fga.list-objects-export.stale-after-minutesLong60staleAfterMinutes is when an in-flight (PENDING/RUNNING) job is considered abandoned (its node died) and may be reclaimed so the one-active-export slot is not stuck forever.

oauthy.hooks.test-delivery

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/hooks/HookTestDeliveryProperties.kt

SSO-2144 — configuration for the per-tenant abuse cap on the hook test-delivery endpoint (POST /api/v1/hooks/{id}/test, SSO-2102).

PropertyTypeDefaultDescription
oauthy.hooks.test-delivery.rate-limit.limitLong10Maximum test deliveries a single tenant may fire within window.
oauthy.hooks.test-delivery.rate-limit.windowDurationDuration.ofMinutes(1)The fixed window the limit applies over.

oauthy.hub

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/config/MultiIssuerValidationConfig.kt

SSO-1521 — properties for multi-issuer validation.

PropertyTypeDefaultDescription
oauthy.hub.issuerStringrequired
oauthy.hub.platform-domainStringrequiredthe tenant-subdomain suffix, leading-dot included (OAUTHY_TENANCY_PLATFORM_DOMAIN, staging .hub.stg.thoryn.org).
oauthy.hub.in-cluster-jwks-uriString"http://thoryn-hub:8080/oauth2/jwks"
oauthy.hub.issuer-cache-ttl-millisLongTrustedTenantIssuers.DEFAULT_CACHE_TTL_MILLISpositive+negative cache TTL for the slugExists lookup; a freshly-registered tenant validates within this window (ADR §4, ≈60 s).
oauthy.hub.trust-test-issuersBooleanfalseSSO-2146 (SSO-1887 S8) — the per-tenant test/sandbox-mode gate.
oauthy.hub.enforce-tnt-iss-consistencyBooleanfalseSSO-2285 (defence-in-depth from the SSO-2283 audit) — the tnt <-> iss consistency gate.

oauthy.idp-facade

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/loginexperience/IdpFacadeProperties.kt

SSO-1926 / SSO-1928: configuration for the IdP-facade's server-to-server calls to identity-service's /internal/tenants/{tenantId}/[] surface (ADR 2026-07-14-idp-frontend-tenant-facade.md).

PropertyTypeDefaultDescription
oauthy.idp-facade.identity-service-base-urlString"http://localhost:8081"identity-service base URL.
oauthy.idp-facade.token-pathString"/var/run/secrets/tokens/identity-facade"On-disk path of product-api's projected Kubernetes ServiceAccount token for the facade surface (audience identity-tenant-facade), mounted by the chart at /var/run/secrets/tokens/identity-facade.

oauthy.organizations.invitation

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/organizations/OrganizationInvitationEmailProperties.kt

SSO-2261: configuration for the B2B organization-invitation email.

PropertyTypeDefaultDescription
oauthy.organizations.invitation.accept-base-urlString""Public origin of the console that hosts the invitation-accept landing page, e.g.

oauthy.product-api.internal-auth

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/internalauth/InternalEndpointAuthProperties.kt

SSO-2321 — configuration for authenticating product-api's /internal/[] server-to-server surface with projected Kubernetes ServiceAccount tokens.

PropertyTypeDefaultDescription
oauthy.product-api.internal-auth.modeModeMode.DETECTdetect (default) — validate-and-log-only; the request is ALWAYS allowed through regardless of the token's presence or validity.
oauthy.product-api.internal-auth.exempt-pathsList<String>listOf("/internal/claims-enrichment")/internal/[] path prefixes that are NEVER subject to the SA-token check (always allowed, never enforced).

oauthy.token-claims

Owner: product-api · Source: servers/product-api/src/main/kotlin/com/devnow/productapi/tokenclaims/TokenClaimsModels.kt

SSO-1904 (slice B) — tunables for the claims-in-token capability.

PropertyTypeDefaultDescription
oauthy.token-claims.enrichment-urlString"http://thoryn-product-api:8082/internal/claims-enrichment"The URL product-api registers with the hub as the client's claims webhook when the toggle is enabled.
oauthy.token-claims.max-response-bytesInt65536Product-api-side ceiling on the serialized claims object returned by the enrichment endpoint.
oauthy.token-claims.timestamp-tolerance-secondsLong300Accepted clock skew, in seconds, between the hub's X-Thoryn-Timestamp and this pod's clock (both directions).
oauthy.token-claims.elevated-scope-cache-ttl-secondsLong10Short TTL (seconds) for the PIM/entitlement active-scope Redis cache (ElevatedScopeCache, SSO-1321/1322).

Every property above is set as a YAML key under application.yml or, equivalently, as an environment variable using Spring's relaxed binding (oauthy.hub.platform-client-ids -> OAUTHY_HUB_PLATFORM_CLIENT_IDS). A _required_ default means the service will not start until the value is supplied.