Skip to content

Add fail-closed SotyHub OIDC job boundary - #11

Merged
descambiado merged 1 commit into
mainfrom
codex/sotyhub-oidc-receiver
Jul 30, 2026
Merged

Add fail-closed SotyHub OIDC job boundary#11
descambiado merged 1 commit into
mainfrom
codex/sotyhub-oidc-receiver

Conversation

@descambiado

Copy link
Copy Markdown
Owner

What changed

  • adds dedicated /execution/service/* routes for SotyHub service-to-service calls
  • verifies Google OIDC signatures before enforcing exact issuer, audience, service-account email, numeric subject, time window, and route
  • accepts only the immutable offline forensics/hash_calculator canary with network_mode=none and evidence_read
  • binds the SotyHub request, operation, authorization, usage scope, source manifest, BOFA key, profile, and worker image digest into the signed JobSpec
  • adds atomic one-use source-manifest claims and worker replay rejection
  • signs worker receipts with a separate one-job Ed25519 key and verifies receipt tamper resistance
  • updates the locked-down worker fixture, OCI workflow, environment template, and integration documentation

Why

SotyHub needs a machine-to-machine bridge into BOFA without forwarding Firebase user tokens, introducing standing BOFA credentials, or enabling cloud mutation. This PR creates that narrow identity and evidence boundary while keeping actual dispatch disabled.

Operational boundary

  • dispatch_performed=false
  • no service account has been created or pinned
  • no receiver is deployed
  • no cloud resource, external target, network-active capability, privileged execution, or mutation is enabled
  • the previously published worker digest predates receipt signing and is not represented as compatible with this change
  • local Docker Desktop did not become ready, so no container canary is claimed; deterministic in-process and worker-contract canaries pass

Validation

  • tools/verify_sotyhub_service_identity.py
  • tools/verify_execution_fabric.py
  • tools/verify_worker_protocol.py
  • tools/verify_worker_oci.py
  • tools/verify_execution_api.py
  • Python syntax compilation without bytecode
  • Flake8 import/unused-symbol checks and project-config checks on new files
  • git diff --check

Follow-up before merge or deployment

CI and review must pass. Deployment, sender identity creation, live OIDC exchange, image publication, and any managed dispatcher remain separate explicit decisions.

@descambiado
descambiado marked this pull request as ready for review July 30, 2026 17:28

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f55abaa685

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/main.py
return {
"status": "preflight_allowed",
"dispatch_performed": False,
**result,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not return executable envelopes from preflight

Every successful call to this endpoint returns result["envelope"], even though the source-manifest hash is only claimed by /execution/service/jobs. Repeating preflight therefore produces independently signed JobSpecs with unique manifest IDs and digests; each can pass the worker's digest-based replay check and execute without ever creating the promised one-use source claim. Return only the policy decision and unsigned manifest here, reserving signed envelope issuance for the endpoint that atomically claims the source hash.

Useful? React with 👍 / 👎.

Comment thread api/main.py
Comment on lines +618 to +621
return service_identity_verifier.verify(
credentials.credentials,
path=request.url.path,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move JWKS resolution off the async event loop

On a cache miss or attacker-supplied unknown kid, verify() calls the synchronous PyJWKClient.get_signing_key_from_jwt() and may perform an outbound HTTPS refresh. Because this dependency is async but invokes it directly, that network operation blocks the entire event loop; unauthenticated requests with rotating kid values can occupy all API workers while JWKS requests complete or time out. Run verification in a worker thread or use a nonblocking, bounded key-cache path.

Useful? React with 👍 / 👎.

@descambiado
descambiado merged commit 82cbd9d into main Jul 30, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant