Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .downstream/native-domains.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ groups, quota or scheduling state. Normal credential refresh remains available.
Source acquisition shares one process-local `__oailb` candidate. Its maximum
lease is 230 seconds, capped by an earlier upstream expiry; receiving the same
value during a live lease does not extend it. Source preparation is sequential
and has a 90-second budget. Each target/model is validated with two completed
and uses the ordinary OAuth account-test payload (default instructions, fixed
short user prompt, medium effort), with its own configured normal connection
policy. It does not inherit target-probe session/window headers, routing hints,
encrypted-output options or connection-close behavior.
It has a 90-second budget. Each target/model is validated with two completed
HTTP 200 streams, at most 45 seconds each: the first must return STATE, and
the second may omit STATE or return the same value. Qualification includes
the target identity, actual model, exit, client headers, STATE and TLS profile.
Expand Down
4 changes: 4 additions & 0 deletions .github/buildkitd.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Google-operated cache of public Docker Hub images. BuildKit keeps the original
# image references and validates their content digests; no credentials change.
[registry."docker.io"]
mirrors = ["mirror.gcr.io"]
27 changes: 27 additions & 0 deletions .github/scripts/prepare_test_images.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
"""Preload unchanged CI image names from their publishers' alternate registries.

The OCI index digests below were checked against the matching Docker Hub tags.
Postgres/Redis are Docker Official Images on ECR Public; Ryuk is published by
Testcontainers on GHCR. No Docker Hub credentials or pull-limit workarounds are
used. Docker verifies immutable digests; existing tests use these local aliases.
"""

import subprocess


IMAGES = (
("public.ecr.aws/docker/library/postgres@sha256:aa6eb304ddb6dd26df23d05db4e5cb05af8951cda3e0dc57731b771e0ef4ab29", "postgres:18.1-alpine3.23"),
("public.ecr.aws/docker/library/redis@sha256:0514fa59e3d84e2f3be66731c5401fc2a85b8ea71b49c0116c850f6a558076f4", "redis:8.4-alpine"),
("ghcr.io/testcontainers/ryuk@sha256:31b31269d06603366cbfd0284708dcd2e281e8a4188e53fce3d3304439d0df3d", "testcontainers/ryuk:0.13.0"),
)


def prepare(run=subprocess.run):
for source, local_name in IMAGES:
run(["docker", "pull", "--platform", "linux/amd64", source], check=True)
run(["docker", "tag", source, local_name], check=True)


if __name__ == "__main__":
prepare()
4 changes: 3 additions & 1 deletion .github/workflows/backend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ jobs:
run: make test-unit
- name: Integration tests
working-directory: backend
run: make test-integration
run: |
python3 ../.github/scripts/prepare_test_images.py
make test-integration

frontend:
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/downstream-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ jobs:
python3 .github/scripts/release_preflight.py --stage before-build \
--tag "$RELEASE_TAG" --source-sha "$SOURCE_SHA"
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
with:
driver-opts: image=mirror.gcr.io/moby/buildkit@sha256:cec9f139f45e93c5c69c60f8b07cfad9f43f4ef6b6a6cd917527fea5ff2e3dea
buildkitd-config: .github/buildkitd.toml
- name: Build release image without publication
id: build
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/downstream-verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
- name: Native gateway borrowing contracts
working-directory: backend
run: |
python3 ../.github/scripts/prepare_test_images.py
go test -p=1 -tags unit ./internal/service ./internal/repository ./internal/handler ./internal/handler/admin -run '^Test(CodexGatewayBorrow|Pelican|AccountObservation|ObservedAccountLease)' -count=1
go test -p=1 -tags integration ./internal/repository -run '^TestCodexGatewayBorrowTestRepositoryIntegrationLifecycle$' -count=1
- name: Codex preparation coordination race tests
Expand Down Expand Up @@ -194,6 +195,9 @@ jobs:
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
with:
driver-opts: image=mirror.gcr.io/moby/buildkit@sha256:cec9f139f45e93c5c69c60f8b07cfad9f43f4ef6b6a6cd917527fea5ff2e3dea
buildkitd-config: .github/buildkitd.toml
- name: Build candidate
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
Expand Down
37 changes: 35 additions & 2 deletions backend/internal/service/codex_gateway_borrow_probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,11 @@ func (s *CodexGatewayBorrowService) acquireSource(ctx context.Context, id int64)
if err != nil {
return nil, err.Error(), err
}
shot, err := s.fireObservation(ctx, template.Header, a, codexGatewayBorrowSourceModel, "Reply with OK only.", "medium", proxy, nil,
"", nil, HTTPUpstreamProfileCodexBorrowSource, codexGatewayBorrowProbeMaxBody)
req, err := borrowSourceObservationRequest(ctx, template.Header)
if err != nil {
return nil, err.Error(), err
}
shot, err := s.sendObservation(req, a, proxy, nil, codexGatewayBorrowSourceModel, codexGatewayBorrowProbeMaxBody)
if err != nil {
return nil, shot.errorText, err
}
Expand All @@ -125,6 +128,36 @@ func (s *CodexGatewayBorrowService) acquireSource(ctx context.Context, id int64)
return candidate, "", nil
}

// The pinned ranxi source acquisition uses the ordinary OAuth account-test
// payload, not the target's two-shot STATE-probe protocol. Preserve the shared
// account identity and observation isolation, but do not manufacture probe
// sessions, a window or a routing hint, or force this normal source connection
// closed. The source keeps its own configured OpenAI transport and proxy.
func borrowSourceObservationRequest(ctx context.Context, headers http.Header) (*http.Request, error) {
ctx = WithCodexGatewayBorrowObservation(WithHTTPUpstreamRedirectsDisabled(WithHTTPUpstreamProfile(ctx, HTTPUpstreamProfileCodexBorrowSource)))
payload := createOpenAITestPayload(codexGatewayBorrowSourceModel, true, "Reply with OK only.")
payload["reasoning"] = map[string]string{"effort": "medium"}
body, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, chatgptCodexURL, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Host, req.Header = "chatgpt.com", headers.Clone()
if req.Header == nil {
req.Header = make(http.Header)
}
for _, name := range []string{"Cookie", "Content-Length", "Content-Encoding", "Content-MD5", "X-Codex-Turn-State", "X-Codex-Turn-Metadata", "X-OpenAI-Internal-Codex-Responses-Lite", "conversation_id", "session_id", "session-id", "thread-id", "x-client-request-id", "x-codex-window-id", openAICodexRoutingHintHeader} {
deleteOpenAIHeaderEqualFold(req.Header, name)
}
req.Header.Set("Accept", "text/event-stream")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("OpenAI-Beta", "responses=experimental")
return req, nil
}

func borrowCookieScoped(cookie *http.Cookie, path string) bool {
if cookie == nil || (cookie.Domain != "" && !strings.EqualFold(strings.TrimPrefix(cookie.Domain, "."), "chatgpt.com")) {
return false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package service

import (
"context"
"net/http"
"testing"

"github.com/stretchr/testify/require"
"github.com/tidwall/gjson"
)

func TestCodexBorrowSourceUsesNormalAccountTestContract(t *testing.T) {
headers := http.Header{"Authorization": {"Bearer synthetic-source"}, "User-Agent": {"synthetic-persisted-identity"}, "Originator": {"codex-tui"}, "Version": {"0.162.1"}}
for _, name := range []string{"session-id", "thread-id", "session_id", "x-codex-window-id", "x-codex-turn-state", "x-codex-turn-metadata", "x-codex-routing-hint", "Cookie", "Content-Encoding"} {
headers.Set(name, "target-only-value")
}
original := headers.Clone()
req, err := borrowSourceObservationRequest(context.Background(), headers)
require.NoError(t, err)
require.False(t, req.Close, "source acquisition keeps normal connection policy")
require.Equal(t, original, headers)
require.Equal(t, "Bearer synthetic-source", req.Header.Get("Authorization"))
require.Equal(t, "synthetic-persisted-identity", req.Header.Get("User-Agent"))
for _, name := range []string{"session-id", "thread-id", "session_id", "x-codex-window-id", "x-codex-turn-state", "x-codex-turn-metadata", "x-codex-routing-hint", "Cookie", "Content-Encoding"} {
require.Empty(t, req.Header.Get(name), name)
}
body := borrowCoreBody(t, req)
want := createOpenAITestPayload("gpt-6-astra", true, "Reply with OK only.")
require.Equal(t, want["instructions"], gjson.GetBytes(body, "instructions").String())
require.Equal(t, "Reply with OK only.", gjson.GetBytes(body, "input.0.content.0.text").String())
require.Equal(t, "medium", gjson.GetBytes(body, "reasoning.effort").String())
require.False(t, gjson.GetBytes(body, "parallel_tool_calls").Exists())
require.False(t, gjson.GetBytes(body, "include").Exists())
require.Equal(t, HTTPUpstreamProfileCodexBorrowSource, HTTPUpstreamProfileFromContext(req.Context()))
require.True(t, IsCodexGatewayBorrowObservation(req.Context()))
require.True(t, HTTPUpstreamRedirectsDisabled(req.Context()))
}
Loading