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
7 changes: 6 additions & 1 deletion .downstream/native-domains.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,12 @@ cookie ordering are unchanged.
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.
the target identity, actual model, exit, client headers and TLS profile.
The probe clears the business STATE and validates its own two-shot STATE, so
echoing the next business STATE does not invalidate an otherwise identical live
proof. The sender keeps that exact business STATE and its existing continuation
isolation. Native session/window identity, model, tier, exit and the original
candidate expiry still constrain reuse; no credential gains a longer lease.
Target probes preserve the template's native session/thread/window headers and
generate only a fresh legacy `session_id` for each shot, matching ranxi's full
probe contract. The effective service tier in the gateway-owned routing hint is
Expand Down
8 changes: 6 additions & 2 deletions backend/internal/service/codex_gateway_borrow.go
Original file line number Diff line number Diff line change
Expand Up @@ -805,8 +805,12 @@ func (s *CodexGatewayBorrowService) prepareSource(ctx context.Context, rev uint6

func borrowRequestFingerprint(req *http.Request, model, proxy string, profile *tlsfingerprint.Profile, cookie string) string {
tlsJSON, _ := json.Marshal(profile)
values := []string{cookie, model, proxy, req.URL.EscapedPath(), req.Header.Get("Authorization"), req.Header.Get("ChatGPT-Account-ID"), req.Header.Get("User-Agent"), req.Header.Get("Originator"), req.Header.Get("Version"), req.Header.Get("X-Codex-Turn-State"), string(tlsJSON)}
values := []string{cookie, model, proxy, req.URL.EscapedPath(), req.Header.Get("Authorization"), req.Header.Get("ChatGPT-Account-ID"), req.Header.Get("User-Agent"), req.Header.Get("Originator"), req.Header.Get("Version"), string(tlsJSON)}
values = append(values, codexBorrowRequestServiceTier(req))
// Probes clear business STATE and mint their own independent two-shot STATE.
// A client's next turn must not discard that live route proof merely because
// it echoes a new business STATE. Apply still preserves the client's exact
// STATE; the candidate lease and native session identity remain part of reuse.
// Include the native identity and routing policy preserved by the probe.
// Per-request tracing IDs and the probe's random legacy session_id are not
// qualification identities and must not defeat reuse or failure cooldowns.
Expand Down Expand Up @@ -951,7 +955,7 @@ func (s *CodexGatewayBorrowService) apply(req *http.Request, account *Account, m
s.qualifications = make(map[string]codexGatewayBorrowTargetCheck)
}
qualificationKey := fmt.Sprintf("%d:%s:%s", account.ID, model, key)
// Bound request-specific STATE/header variants; expired proofs are discarded.
// Bound request-identity variants; expired proofs are discarded.
if len(s.qualifications) >= 1024 {
oldestKey := ""
var oldest time.Time
Expand Down
10 changes: 9 additions & 1 deletion backend/internal/service/codex_gateway_borrow_core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,15 @@ func TestCodexGatewayBorrowTargetCacheModelAndFingerprintIsolation(t *testing.T)
_, app, err := s.Apply(template, a, "gpt-6.1-sol", "target-exit", nil, false)
require.NoError(t, err)
require.True(t, app.Applied, "hot cache is read before global target lock")
for _, header := range []string{"Authorization", "ChatGPT-Account-ID", "User-Agent", "Originator", "Version", "X-Codex-Turn-State"} {
continuation := template.Clone(template.Context())
continuation.Header.Set("X-Codex-Turn-State", "next-business-state")
continuation.Header.Set("X-Client-Request-ID", "next-trace")
wire, reused, err := s.Apply(continuation, a, "gpt-6.1-sol", "target-exit", nil, true)
require.NoError(t, err, "business STATE is cleared from the independent probe, so it cannot invalidate its proof")
require.Equal(t, app.ExpiresAt, reused.ExpiresAt, "reuse never extends the candidate lease")
require.Equal(t, "next-business-state", wire.Header.Get("X-Codex-Turn-State"))
require.Equal(t, "business-state", template.Header.Get("X-Codex-Turn-State"), "business histories remain isolated")
for _, header := range []string{"Authorization", "ChatGPT-Account-ID", "User-Agent", "Originator", "Version", "session-id", "thread-id", "x-codex-window-id", "x-codex-installation-id"} {
req := template.Clone(template.Context())
req.Header.Set(header, req.Header.Get(header)+"-changed")
_, _, err := s.Apply(req, a, "gpt-6.1-sol", "target-exit", nil, true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ func testCodexBorrowDiagnoseHTTPToolContinuation(t *testing.T, tier string) {
}
require.True(t, results[1].ToolRoundTrip)
require.Equal(t, 2, business)
require.Equal(t, 2, probes, "the returned business STATE must not revalidate the same live route")
require.EqualValues(t, business+probes, count)
require.LessOrEqual(t, count, int32(8))
usage := s.Status().RecentUsage[0]
Expand Down
Loading