Metadata-Version: 2.5
Name: segmentstream-connections-runtime
Version: 0.1.0a4
Summary: Private OAuth connection runtime for SegmentStream workspaces
Project-URL: Homepage, https://segmentstream.com
Author: SegmentStream
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: <3.14,>=3.12
Requires-Dist: fastapi<1,>=0.117
Requires-Dist: httpx<1,>=0.28
Requires-Dist: pydantic<3,>=2.10
Requires-Dist: segmentstream-pipeline==0.1.0a9
Requires-Dist: uvicorn<1,>=0.35
Description-Content-Type: text/markdown

# SegmentStream connections runtime

`segmentstream-connections-runtime` is the private Cloud Run host for custom
OAuth 2.0 connections authored with the SegmentStream SDK. It loads the
workspace's `connections/connections.py` tuple named `connections`, resolves
customer-owned secret references, and performs authorization-code and
refresh-token exchanges. Token responses are written directly to a pre-created,
per-authorization-attempt Google Secret Manager secret. Authorization and token
write operations return only credential metadata. The IAM-private
`credentials/resolve` operation is the sole endpoint that returns token and
OAuth client values to the authenticated backend for a capability-scoped local
execution context.

The runtime is an infrastructure component, not a public user-facing API. Its
Cloud Run service must require IAM authentication.

```shell
python -m segmentstream_connections_runtime inspect manifest.json
python -m segmentstream_connections_runtime serve
```

The server listens on `PORT`, defaulting to `8080`. A declaration such as
`secret_ref("google-oauth-client-id")` is resolved from
`SEGMENTSTREAM_CONNECTION_SECRET_GOOGLE_OAUTH_CLIENT_ID` in the isolated runtime
environment. Client secrets use the same mapping convention.

Serving also requires `SEGMENTSTREAM_GOOGLE_CLOUD_PROJECT` and Cloud Run's
automatically injected `K_REVISION`. The Cloud Run service identity obtains a
short-lived access token from the Google metadata server and must have
`secretmanager.versions.add` and `secretmanager.versions.access` only for each
managed `ss-oauth-<authorization-attempt-id>` secret. The control plane creates
the secret and its IAM policy before completing OAuth authorization.

Every authorization, exchange, and refresh request includes the deployment's
`runtimeRevision`. The runtime compares it with `K_REVISION` before resolving
secrets or calling an OAuth provider and returns
`409 runtime_revision_mismatch` if Cloud Run routed the stable service URL to a
different revision. `POST /internal/v1/connections/{key}/token/exchange` also
accepts an authorization-attempt ID, its deterministic credential secret ID,
the effective `configurationDigest`, authorization code, redirect URI, and PKCE
verifier. The credential envelope binds its schema-v2 payload to the attempt,
connection key, and configuration digest. `POST .../token/refresh` accepts the
authorization-attempt ID, credential secret ID, and exact stored version. Both
token operations return only `credentialVersion`,
`tokenType`, `expiresAt`, `scopes`, and `hasRefreshToken`.

`POST /internal/v1/connections/{key}/credentials/resolve` additionally requires
the exact active credential version and configuration digest. It reads that
pinned version, recovers a newer version only when it belongs to the same
authorization attempt, key, provider, and digest, and refreshes credentials
that are expired or within two minutes of expiry. The response includes the
resolved client ID, client secret, access token, optional refresh token, token
URL, expiry, scopes, and resulting credential version. This operation must
remain IAM-private; it is not a user-facing credential API.

OAuth providers and Secret Manager do not share a transaction. After a token
exchange succeeds, the runtime retries retryable credential-version writes a
bounded number of times and checks the latest validated version before each
retry, so a response-lost successful write is reused instead of duplicated. If
every write and recovery attempt fails after a provider has rotated a refresh
token, the runtime returns only a sanitized availability failure and the
connection may require reauthorization. That persistent provider/storage
failure is an unavoidable atomicity limit; token values are never included in
the failure. The 5-second metadata, 12-second Secret Manager, and 20-second
provider timeouts plus the bounded retry count keep the worst-case
credential-resolution budget below 174 seconds. The backend separately bounds
workload-identity acquisition at 20 seconds and the whole private invocation at
230 seconds, so it retains the credential row lock throughout that persistence
window. OAuth, metadata, and Secret Manager use cancellation-safe async HTTP,
cap streamed response bodies, reject content encoding and redirects, and apply
one aggregate deadline to connection, write, and read. Slow-drip responses
therefore cannot extend the declared per-call budget, and bearer or Basic
credentials cannot be forwarded to another origin. The managed one-vCPU service
caps instance concurrency at four requests as an additional admission-control
boundary.
Once a token request begins, provider-result validation and credential
persistence are shielded as one bounded operation, so caller cancellation waits
for a rotated refresh token to reach durable storage before it propagates.
Normalized OAuth token material is capped at 60 KiB of compact UTF-8 JSON,
reserving enough space for identifiers and metadata in Secret Manager's 64 KiB
credential envelope.

A deterministic token response that is malformed, omits a required refresh
token, or grants fewer scopes than the selected connection is rejected as
`400 oauth_token_response_invalid` before a credential version is written.
Definitive provider 4xx rejections return `400 oauth_provider_rejected`, while
timeouts, rate limits, transport failures, and provider 5xx responses remain
`502 oauth_provider_unavailable` so an ambiguous exchange can be recovered.

## Releases

Releases use the version in `pyproject.toml` and are published from the
`connections-runtime-v<version>` Git tag by the protected
`connections-runtime-release.yml` workflow. PyPI releases are immutable, so the
package version must be incremented before creating another release tag.
