CI Federation

RFC 8693 token exchange

Exchange a GitHub Actions OIDC JWT for a 15-min run402 session JWT. NO Authorization header — the OIDC JWT in subject_token is the sole authentication. JSON only (NOT form-encoded). Body cap 12 KiB; subject_token cap 8192 bytes (rejected before parse). Verification order: decode iss from PAYLOAD (NOT header), look up issuer in trusted_oidc_issuers (unknown → 401 without external JWKS fetch), jose.jwtVerify with algorithms:["RS256"] pinned and clockTolerance:30s, exact-string aud post-check, required GitHub claims as non-empty strings, candidate fetch via partial index, subject-pattern matching with deterministic precedence (exact > longer-prefix), event_name ∈ allowed_events, repository_id strong-bind if non-NULL, atomic conditional UPDATE (closes revoke-during-exchange race). The session JWT's exp = min(iat+900, binding.expires_at_unix) — expires_in may be < 900 if the binding expires sooner. Authority disclosed by the wallet-signed delegation: deployed function code runs with the project's runtime authority + database authority (migrations, RLS/expose changes); revocation does NOT undo deployed code.

post/ci/v1/token-exchange

Request

  • Base URL: https://api.run402.com
  • URL: https://api.run402.com/ci/v1/token-exchange
  • Auth: none required

Request body

grant_type'urn:ietf:params:oauth:grant-type:token-exchange' required
subject_tokenstring required

GitHub Actions OIDC JWT, signed RS256, with required claims sub/run_id/sha/actor/workflow_ref/repository/repository_id/event_name.

subject_token_type'urn:ietf:params:oauth:token-type:jwt' required
project_idstring required

Response

Session minted.

access_tokenstring

HS256 run402 session JWT signed with CI_SESSION_SECRET. Carries token_use:"ci_session", aud=sub=project_id, acts_as (binding wallet, lowercase), via (binding id), scopes (copy of binding.allowed_actions), and audit fields (run_id/sha/actor/workflow_ref).

token_type'Bearer'
expires_ininteger

Effective seconds remaining. May be < 900 if the binding's expires_at is closer than 15 minutes.

scopestring

Space-delimited list of binding.allowed_actions. v1: "deploy".

Changes

No recorded changes to this operation across all 1 revision of this API.