Repository navigation
[Roadmap] Centrally hosted multi-user OpenCodex with tenant isolation #95
Description
Area
Server architecture / authentication / multi-user hosting / policy / observability
Goal
Support OpenCodex as a centrally hosted proxy for multiple independent users without leaking credentials, usage, request state, routing policy, or administrative capabilities across tenants.
The intended deployment is:
many Codex Desktop / CLI clients
↓
central OpenCodex instance
↓
native ChatGPT passthrough + routed providers / gateways
Each user may bring their own ChatGPT subscription credentials while the operator centrally manages additional routed models such as a LiteLLM gateway.
This issue is the long-term tracker for making that deployment a supported OpenCodex architecture rather than a collection of reverse-proxy workarounds.
Current status
A substantial part of the original use case already works.
Already available
OpenCodex can already support important pieces of a centrally hosted deployment:
- non-loopback proxy binding;
- authenticated data-plane access;
- ChatGPT credential forwarding without OpenCodex taking ownership of the user's ChatGPT credential;
- routed OpenAI-compatible providers such as a centrally managed LiteLLM gateway;
- remote
/v1/modelsmodel discovery; - background catalog refresh;
- standard reverse-proxy/TLS deployment in front of OpenCodex.
Those foundations mean the remaining problem is not simply “make OpenCodex reachable over the network.”
The remaining problem is safely introducing a first-class tenant boundary.
Remaining architecture gap
Current OpenCodex still fundamentally operates as a single-user proxy.
Multiple remote users can reach the same server, but there is no complete security and policy model ensuring that one user's activity is isolated from another.
The remaining design must cover at least:
tenant identity
tenant authentication
tenant authorization
routing policy
credential ownership
request / usage attribution
account-pool boundaries
management-plane isolation
concurrency / capacity controls
auditability
1. Tenant identity
Every request must have a trustworthy tenant identity established before tenant-specific policy is applied.
Do not derive authorization identity from caller-controlled informational headers alone.
Possible identity sources may include:
- a dedicated per-user OpenCodex client credential;
- an authenticated reverse-proxy identity;
- another explicit identity contract designed for centrally managed deployments.
chatgpt-account-id may be useful as provider metadata, but it must not automatically become the OpenCodex authorization principal unless that relationship is explicitly proven and secured.
Likewise, do not parse arbitrary OAuth token contents merely to guess a user identity.
2. Admission credential versus forwarded provider credential
A centrally hosted deployment may need both:
OpenCodex admission identity
+
user ChatGPT Authorization credential
These are separate security concepts.
OpenCodex admission must not consume, replace, or reinterpret the user's forwarded ChatGPT bearer token.
Likewise, the upstream ChatGPT credential must not accidentally grant OpenCodex management access.
Tests must establish the separation directly.
3. Tenant authorization and model policy
The operator should be able to define which routes each tenant may use.
Examples:
User A
native ChatGPT
litellm/claude-sonnet
litellm/gpt-x
User B
native ChatGPT
litellm/internal-small-model
The exact policy format can be designed separately, but tenant identity must be able to constrain:
- providers;
- models;
- routing profiles;
- possibly account pools or quotas;
- other tenant-scoped capabilities introduced later.
A user must never gain access merely because a model exists in the server-wide catalog.
4. Credential isolation
User-owned credentials must remain associated with the correct tenant.
This includes any future centrally managed representation of:
- ChatGPT/OAuth identity;
- provider OAuth accounts;
- provider API keys;
- account-pool membership.
Tenant A must not be able to:
- use Tenant B's credential;
- see Tenant B's credential metadata;
- trigger Tenant B's account selection;
- inherit Tenant B's affinity/cooldown state.
Where ChatGPT credentials remain pure forward-auth, OpenCodex should continue avoiding persistence unless the architecture explicitly requires otherwise.
5. Request and usage isolation
Current single-user request/usage views are insufficient for supported multi-user hosting.
Request history and usage need a tenant attribution contract that is:
- trustworthy;
- bounded;
- privacy-safe;
- usable for filtering and auditing.
At minimum, operators should be able to answer:
Which tenant generated this request?
Which model/provider did it route to?
How much usage did that tenant generate?
Which failures belong to that tenant?
A normal tenant must not be able to read another tenant's request or usage history.
Raw provider credentials, prompts, or other private payloads must not become necessary for attribution.
6. Management-plane boundary
The OpenCodex management API remains an operator surface.
A normal inference client must not gain management privileges simply because it is a valid tenant.
Tenant credentials should not automatically authorize:
/api/config
/api/providers
/api/oauth/*
/api/codex-auth/*
/api/stop
or equivalent administrative operations.
Any future tenant self-service API should be deliberately scoped rather than implemented by weakening /api/*.
7. Catalog distribution
Remote clients need the generated Codex catalog without receiving management credentials.
That focused least-privilege problem is tracked separately in:
#809
#809 owns the proposed read-only data-plane:
GET /v1/catalog
This roadmap issue should consume that capability when it lands rather than duplicating catalog-distribution policy here.
8. Catalog and model visibility
A server-wide catalog and a tenant-visible catalog may eventually differ.
If tenant model authorization exists, the client-facing model surface must not advertise routes that the tenant cannot actually invoke.
The implementation must define whether filtering occurs at:
/v1/models;/v1/catalog;- both;
- or another canonical tenant projection.
Catalog filtering and request admission must agree.
9. Routing and account pools
Multi-user hosting must define which routing state is:
global
tenant-scoped
conversation-scoped
account-scoped
Examples include:
- route selection;
- conversation affinity;
- provider cooldowns;
- OAuth account selection;
- API-key pools;
- quotas.
A global upstream-health observation may legitimately be shared.
A tenant's identity, affinity, credential, or entitlement must not be shared accidentally.
The generic OAuth-pool work in #695 should remain reusable rather than becoming a second tenant-routing implementation.
10. Concurrency and capacity
Supported multi-user hosting needs explicit load validation.
The server must define and test behavior when several users simultaneously:
- stream long Responses turns;
- invoke tools;
- reconnect;
- trigger retries;
- use different providers;
- consume account pools.
Required safeguards may include:
- global concurrency limits;
- tenant-level limits;
- bounded queues or fail-fast admission;
- memory/stream budgets;
- fair scheduling where appropriate.
This should align with the bounded-concurrency work tracked in #820 rather than creating an independent resource model.
11. Reverse proxy / TLS deployment
Production multi-user deployments will normally require TLS and may use an external reverse proxy or authenticated network layer.
Documentation should cover a supported deployment shape without making one specific product mandatory.
Examples may include:
nginx
Caddy
Tailscale / private network
enterprise ingress
OpenCodex must not assume that the apparent socket peer alone establishes the end-user identity.
Forwarded identity headers should only be trusted under an explicit trusted-proxy contract.
Security invariants
A supported multi-user mode must prove:
- Tenant A cannot invoke a model forbidden to A.
- Tenant A cannot use Tenant B's credentials.
- Tenant A cannot inspect Tenant B's requests or usage.
- Tenant A cannot mutate server configuration.
- Tenant identity cannot be forged through an untrusted header.
- Forwarded ChatGPT OAuth credentials remain distinct from OpenCodex admission credentials.
- Provider/account affinity cannot cross tenant boundaries accidentally.
- Tenant-visible catalog entries agree with tenant request authorization.
- Logs do not expose authentication secrets.
Suggested delivery strategy
This should remain a roadmap/architecture program rather than one giant PR.
Useful focused slices include:
- tenant identity and admission contract;
- tenant authorization/model policy;
- tenant-safe request and usage attribution;
- tenant-aware catalog projection;
- routing/account-pool isolation;
- concurrency/resource policy;
- supported remote deployment documentation and acceptance harness.
Security-sensitive slices should receive explicit maintainer review.
Related work
- [Feature]: add least-privilege GET /v1/catalog for remote Codex clients #809 — least-privilege remote catalog distribution.
- [Feature]: Generic OAuth account-pool failover with session affinity and quota-aware selection #695 — generic OAuth account-pool selection/failover.
- [architecture][memory] Make 32 concurrent tool-recall sessions protocol-safe and memory-bounded #820 — bounded multi-session concurrency and memory architecture.
- V2 cross-provider sub-agent loses NEW_TASK body in encrypted_content #92 — V2 cross-provider child-task compatibility; separate protocol bug rather than the tenant architecture itself.
Out of scope
This issue does not require:
- OpenCodex centrally owning every user's ChatGPT OAuth credential;
- weakening the management API for remote users;
- trusting arbitrary user-id headers;
- making public-internet exposure the default;
- embedding a specific commercial reverse proxy;
- solving every provider-specific account-management feature here.
Acceptance criteria
Foundations
- Non-loopback hosting is possible.
- Data-plane admission authentication exists.
- ChatGPT forward-auth can preserve caller credentials.
- Additional OpenAI-compatible gateways can be routed centrally.
- Remote model discovery through
/v1/modelsexists. - Catalog refresh can be managed centrally.
Remaining multi-user contract
- A trustworthy tenant identity contract exists.
- Tenant admission and forwarded provider credentials are independently represented.
- Per-tenant model/provider authorization exists.
- Tenant credential/account state cannot cross identities.
- Request history is safely tenant-attributed.
- Usage is safely tenant-attributed.
- Normal tenants cannot access management-only APIs.
- Tenant-visible model/catalog projections agree with authorization.
- OAuth/account-pool affinity is safe across tenants.
- Concurrency and resource behavior is explicitly bounded and tested.
- Trusted-proxy / forwarded-identity semantics are documented.
- Multi-user negative security tests prove cross-tenant isolation.
- A supported centrally hosted deployment is documented.
- Accepted implementation is merged into
dev.
Close condition
Close this roadmap issue only when centrally hosted multi-user OpenCodex is a supported architecture with tested tenant isolation and authorization.
Individual focused issues such as #809 may close earlier without closing this tracker.
Activity
lidge-jun commentedon Jul 11, 2026
Hey @rafalkwol, thanks for the kind words and the clear question.
Short answer: the architecture already supports most of what you described, but opencodex is currently designed as a personal proxy (single-user, loopback). Multi-user hosting is on the roadmap — I'm planning to patch toward that direction and will keep this issue open to track it.
What already works today
authMode: "forward", which relays the caller'sAuthorizationandchatgpt-account-idheaders verbatim to upstream. The proxy never stores or manages ChatGPT credentials; each user's own OAuth token flows through untouched.openai-chatadapter provider in the opencodex config. All non-ChatGPT model requests route there."hostname": "0.0.0.0"andOPENCODEX_API_AUTH_TOKENto expose the proxy on the network. The token protects both management and data-plane endpoints./v1/modelsendpoint serves the merged catalog (native gpt + routed LiteLLM models) over HTTP, so remote Codex clients see the expanded picker.The gap: admission auth vs. ChatGPT auth
When bound to a non-loopback address, opencodex requires an admission token (
x-opencodex-api-keyheader orAuthorization: Bearer <token>) on every request. The problem is that Codex Desktop sends the user's ChatGPT OAuth token in theAuthorizationheader — there's no built-in mechanism in codex-rs to send both the admission token and the ChatGPT token simultaneously without modifying the Codex client.Recommended workaround (no codex-rs modification): put a reverse proxy (nginx, Caddy, etc.) in front of opencodex that:
x-opencodex-api-keyheader from a shared secret or per-user auth layer.Authorizationheader untouched so ChatGPT passthrough still works.This keeps codex-rs completely unmodified on each user's machine — they only need
openai_base_urlpointing at your reverse proxy, plus amodel_catalog_jsonpointing at a catalog file you distribute (or they can fetch it from the proxy's/v1/models).Caveats to be aware of
encrypted_content. If your users rely on sub-agents hitting LiteLLM models, this is a blocker until patched.opencodex-catalog.json(ormodel_catalog_jsonin config.toml) for the model picker to show routed models. You can automate this by having your reverse proxy serve it, or by distributing the file alongside the config.If you want to implement this now with AI assistance
If you'd like to fork and customize opencodex for your multi-user setup, here's a detailed prompt you can give to an AI agent to implement the changes. This approach keeps codex-rs completely unmodified — all changes are proxy-side:
AI implementation prompt
What I'd like from you
Could you describe your desired environment more concretely? Specifically:
This will help me scope the multi-user patch properly. I'll keep this issue open and use it to track the work.
rafalkwol commentedon Jul 11, 2026
Thank you for a very detailed answer.
From what I see it basically has nearly everything I need. Currently the issue with dashboard being single user is not a problem as I'm not planning to use it as I only need the proxy functionallity and proper routing of requests between models and ChatGPT subscrption with handling of image-gen (which opencodex seems to already handle). The blocked you mentioned is something I guess will probably be fixed sooner or later so also not a big issue.
The catalog distribution via file is the most "annoying" thing as I'm planning to change the models server by LiteLLM pretty frequently and would prefer users to not have to do anything at all to get the changes after initial config of the "openai_base_url" which I can set for them from the domain level probably.
The whole idea of this is like this:
Now to answer your questions:
Again, thanks for your work. This is already a really good solution and if push comes to shove I will try to tinker with it myself if I will miss something that won't be planned by the roadmap.
Best regards.
Wibias commentedon Jul 12, 2026
@rafalkwol
I tested the suggested nginx workaround locally on Windows and can confirm that it works in practice.
Test environment:
Configuration:
x-opencodex-api-keyAuthorizationheader was preservedchatgpt-account-idwas forwarded unchangedSuccessfully tested:
GET /v1/modelsPOST /v1/responsesgpt-5.4POST /v1/images/generationsusinggpt-image-2All tested requests returned HTTP 200.
Example nginx log entries:
This confirms that the proposed request flow works in a real single-user Windows setup:
Limitations of this test:
So this does not validate the full multi-user production scenario yet, but it does confirm that the nginx admission-auth workaround works with native OpenAI models, routed providers, model switching, and image generation.
Wibias commentedon Jul 13, 2026
I also tested how provider and model catalog changes propagate from Ollama through OpenCodex into Codex Desktop.
Test setup
http://127.0.0.1:11434Adding a new Ollama model
Ollama immediately exposed the newly added model through its OpenAI-compatible endpoint:
However, the model did not appear immediately in the OpenCodex catalog:
I then monitored the OpenCodex catalog without restarting OpenCodex and without running another sync.
The new model appeared automatically after:
This suggests that OpenCodex refreshes provider model catalogs in the background, likely on an interval of approximately five minutes.
ocx syncbehaviourDuring the test, running:
did not cause the newly added Ollama model to appear immediately in
/v1/models.The model appeared later through the background refresh.
So
ocx syncappears to write the model catalog currently known to OpenCodex into the local Codex catalog, but it does not necessarily force an immediate synchronous provider rediscovery first.Codex Desktop model picker
Once the model appeared in OpenCodex
/v1/models, the running Codex Desktop application still did not update its model picker.The new model appeared only after fully restarting Codex Desktop.
The same behaviour was observed when removing a model:
The same restart requirement also applied when adding the Ollama provider itself.
Observed refresh flow
Practical impact
This means centralized model changes are possible without manually editing or redistributing a new catalog file for every individual model change.
However, the experience is not fully dynamic from the end user's perspective:
For the multi-user scenario discussed in this issue, this is still workable, but users would need to be told to restart Codex Desktop after model catalog changes.
rafalkwol commentedon Jul 13, 2026
@Wibias Thanks for the tests you made. From their conclusion it seems to me the only things left to validate for MVP scenario is concurrent multi-user access while hosting opencodex on remote server and also the possibility of extracting some user identifying information from their ChatGPT auth token.
Wibias commentedon Jul 13, 2026
Agreed. I can validate concurrent request handling in a controlled local setup using multiple Codex Desktop/CLI clients against the same nginx and OpenCodex endpoint.
That would cover concurrent native, routed and image-generation requests, but it would still use a single ChatGPT identity. A true multi-user validation would require at least two separate ChatGPT accounts and isolated Codex client profiles, so the original company environment may be better suited for that final test.
For user identification, I think it would be safer to investigate the forwarded
chatgpt-account-idheader before attempting to parse the OAuth bearer token itself.The questions I would validate are:
chatgpt-account-idis present on forwarded ChatGPT requestsI would avoid logging or storing the raw
Authorizationheader. Ifchatgpt-account-idis not sufficiently trustworthy or stable, the cleaner production design would probably be to establish user identity at nginx through company SSO or VPN authentication and pass a trusted internal user header to OpenCodex/LiteLLM.rafalkwol commentedon Jul 13, 2026
Thanks for the summary. I was thinking about the
chatgpt-account-idalso, but i'm not sure it will be easy to map to user identities without some manual work. I'm also thinking about injecting the salted hash into the link ofopenai_base_urlwhich I would populate for each user from domain so I would be able to embedd their identity into the link which I would than extract on the proxy level. OAuth token parsing may indeed not be a good idea to go for as it's probably not realiable and may not have any easly identifiable information.I will try out those ideas when I get to it in our internal environment. It's still some time to go as right now we are working on hosting the local model on the provisioned hardware, but after we get the LiteLLM gateway fully running we will go for opencodex next. Just like to plan ahead of time. :) Thanks the time spent on this.
lidge-jun commentedon Jul 22, 2026
Status: this remains on the long-term roadmap. Admission auth and catalog refresh already work today (as covered earlier in this thread); the remaining gap is the genuinely hard part — tenant identity/isolation, per-user authorization and policy, trustworthy user attribution, and concurrency/load validation. That's project-scale design work rather than a patch, and the single-user assumptions run deep enough that we'd rather design it properly than ship a mode that leaks state across users. Keeping open as the tracking issue.
Wibias commentedon Aug 8, 2026
Follow-up tracking
The original multi-machine hosting use case now has focused implementation issues.
Current follow-up:
github-actions commentedon Aug 8, 2026
Automated translation bookkeeping — detected language: English.
[-]Hosting opencodex as a proxy for multiple users with ChatGPT subscription passthrough and LiteLLM integration[/-][+][Roadmap] Centrally hosted multi-user OpenCodex with tenant isolation[/+]lidge-jun commentedon Aug 19, 2026
리뷰 · 우선순위 54 / 80
현재
dev는 중앙 호스팅의 조각은 있습니다. 비루프백 바인드, 데이터 플레인 인증, ChatGPT 자격 증명 전달, 라우트 프로바이더, 원격/v1/models, 카탈로그 새로고침, 앞단 TLS 프록시가 그것입니다. 서버는 여전히 단일 사용자 프록시입니다.src/server/index.ts는 원격 클라이언트가 공유/멀티테넌트 호스트용이 아니라고 경고하고, 설정 주석도 기본 끔·멀티테넌트 아님을 반복합니다. 테넌트 신원·인가·자격 증명 소유권·사용 귀속·관리 평면 격리의 1급 모델은 없습니다.이슈가 원하는 것은 여러 Codex 클라이언트가 한 OpenCodex를 쓰되 자격 증명, 사용량, 요청 상태, 라우팅 정책, 관리 능력이 새지 않는 것입니다. 사용자는 자기 ChatGPT를 가져오고 운영자는 LiteLLM 같은 라우트 모델을 중앙 관리할 수 있습니다. 남은 문제는 도달 가능성이 아니라 테넌트 경계입니다.
네이티브 신뢰성과 위생이 점수입니다. 호출자가 넣는 정보 헤더만으로 인가 신원을 만들면 안 됩니다. 계정 풀과 Usage는 지금 프로세스 전역이라 테넌트 필터 없이 열면 옆 사용자 사용량이 보입니다. Windows/서비스 동시성(#820)과 데이터 플레인 카탈로그(#809)는 자식이지, 이 우산을 한 PR로 닫지 않습니다.
이미 있는 nginx
x-opencodex-api-key우회는 MVP 도달은 되지만 테넌트 격리는 아닙니다. 메인테이너가 말한 대로 프로젝트 규모 설계이고 단일 사용자 가정이 코드 곳곳에 있습니다. 해시된 URL 신원이나 chatgpt-account-id 매핑은 설계 메모이지 구현 허가가 아닙니다.해결방안은 테넌트 신원 계약과 관리/데이터 평면 분리를 먼저 고정하고, #809 같은 최소 권한 슬라이스만 지금 구현하는 것입니다. 전역 계정 풀과 Usage를 테넌트화하기 전에 멀티유저 UI를 열지 마십시오. 패치는 추측하지 않으며, 현재 트리를 멀티테넌트 아키텍처로 보지 않습니다.
이 댓글은 grok-bot이 작성했습니다
parkjs101 commentedon Aug 21, 2026
Code-grounded status: attribution exists, but nothing consumes it
I walked the current
devtree against this issue's remaining checklist and had anindependent reviewer verify every reference below. Posting the code-level detail because
it narrows where the tenant contract has to be introduced.
1. A server-derived key identity already exists for configured keys
apiKeysis a multi-entry array with stable per-key ids, and admission already names thematched key instead of collapsing to a boolean:
src/types/config.ts:217-222—OcxApiKeyEntry { id; name; key; createdAt }src/server/auth-cors.ts:338-349—resolveDataPlaneAdmissionSecret()compares withsecretEquals/timingSafeEqualand returns{ kind: "configured", keyId: k.id }src/server/auth-cors.ts:357-370—admissionFields()projects it to{ admissionKind, apiKeyId }This matters for §1's constraint: for configured keys the principal comes from a
server-held secret comparison, not a caller-asserted header, so no
chatgpt-account-idtrust and no OAuth introspection is required to name the caller.
Two limits worth stating plainly. Environment-token and loopback admissions carry no
key id at all (
src/server/auth-cors.ts:345, and theloopback/environmentvariants at:324-327), so they cannot be tenants as-is. And this is identification only — there isno policy contract anywhere.
Attribution already reaches storage:
src/server/request-log.ts:55,133,373,944,src/usage/log.ts:77,442, indexed asapi_key_idinsrc/routing/history/indexer.ts:62,164with a filter at
:518.2.
apiKeyIdis recorded after the fact, not carried through the requestOcxApiKeyEntry(src/types/config.ts:217-222) has noallowedModels/allowedProviders.src/server/management/request-history-routes.ts:95-101readsapiKeyIdfrom the URLquery (line 100) and passes it to the SQL filter at
src/routing/history/indexer.ts:518.The cited code shows no authenticated-scope override, so the parameter constrains the
query rather than the caller. That is correct for an operator surface; it is the piece
that would need inverting before any tenant-facing history read.
The shape this suggests is an immutable
TenantContextderived at admission and threadedthrough authorization, routing, catalog projection, history/usage, pool state, and
concurrency — rather than reusing
apiKeyIdas a log field.3. Process-global routing state, and where it would need tenant keying
Each row below is a verified global declaration. The failure column is the consequence
if that map's keying stays tenant-blind, not a demonstrated cross-tenant defect today
— the proxy is single-user, so these are correct as written.
src/codex/routing.ts:160-164,:832-835threadId+ quota scope, no tenant dimensionsrc/codex/pool-rotation.ts:13-14src/codex/quota.ts:88src/oauth/anthropic-routing.ts:65-66src/providers/key-failover.ts:38src/providers/request-pacing.ts:68I verified the declarations; I did not trace every key-use site, so treat the right
column as the design obligation rather than a proven leak.
Prior art exists in-tree:
src/adapters/cursor.ts:84-86namespaces thread scope byauthenticated tenant so "shared-proxy tenants ... cannot collide". Codex-side routing has
no equivalent dimension.
4. Sequencing corrections
Catalog projection should not ship ahead of enforcement. Filtering
/v1/modelsfirstlooks low-risk because it has no denial path, but hiding a model while the call still
succeeds establishes that invisible means blocked when it does not. Landing visibility and
admission together from one policy is also what makes §8's "catalog filtering and request
admission must agree" hold by construction.
A single shared resolver is not currently callable.
/v1/modelsresolves admission atsrc/server/index.ts:898-905, then assembles models, entitlements, native rows, combos andaccount-qualified selectors in later branches. Threading context through those call sites
is prerequisite work, not a side effect of adding one function.
5. Two invariants any design has to preserve
includes
GET /v1/models(src/server/index.ts:610-634) while the remote path requiresadmission (
:898-905). Those must stay distinct.AUTH_MATRIX(
src/server/auth-cors.ts:401) accepts a bearer that is one of the proxy's own secrets,resolved at
:461-472; it is safe because pool/main credentials are substituted ratherthan forwarded (
src/codex/auth-context.ts:585-605, noting the final branch returnsselected headers for passthrough). Adding per-tenant keys must not blur that boundary.
6. Suggested slice order
TenantContext+ explicit policy contractscoping, credential non-forwarding, cross-tenant affinity
operator-only management reads kept distinct from any tenant self-service read
On defaults: "absent policy means unrestricted" would contradict the fail-closed stance
above, so it should be scoped as an explicit legacy exception — unrestricted only for
existing loopback/single-user operation, while tenant mode rejects a key with no policy.
Notes for adjacent issues
Optional centrally managed pools. Out of scope already rules out OpenCodex centrally
owning every user's ChatGPT credential, and the premise is that users forward their own.
What still needs a decision for slice 6 is the optional operator-managed pool that #695
covers: whether a centrally held account pool may coexist with forwarded per-user
credentials, since tenant-keying the pool differs depending on that answer.
For #809. Codex app-server reads the on-disk catalog once at startup and does not
reread it (
src/codex/app-server-restart-service.ts:3-7), so a single shared catalog filecannot express per-tenant visibility. A read-only data-plane endpoint looks like the only
carrier that can.
Ingwannu commentedon Aug 21, 2026
Thanks for the code-grounded follow-up. This is a useful narrowing of the roadmap.
The key distinction is exactly the one you called out: the admission-derived
apiKeyIdgives us attribution, not authorization. It is currently a logging/query dimension and must not be treated as a tenant principal or policy decision by itself.The right first implementation slice is an immutable
TenantContextderived only from trusted admission state, paired with one explicit policy contract. The negative tests should come first and cover at least model denial, catalog/admission agreement, history scoping, credential non-forwarding, and cross-tenant affinity isolation. Catalog filtering and request enforcement should land from the same policy boundary rather than as separate promises.This does not approve a specific implementation yet, and the broader multi-user roadmap remains open: the global routing/pool/usage/concurrency state still needs a complete ownership and isolation design before the proxy can be described as multi-tenant. But this analysis gives us a much better boundary for a future focused proposal.