claude-codex-proxy 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +227 -0
  3. package/bin/cli.js +113 -0
  4. package/docs/ACCOUNTS.md +202 -0
  5. package/docs/API.md +244 -0
  6. package/docs/ARCHITECTURE.md +119 -0
  7. package/docs/CLAUDE_INTEGRATION.md +163 -0
  8. package/docs/OAUTH.md +83 -0
  9. package/docs/OPENCLAW.md +33 -0
  10. package/docs/legal.md +9 -0
  11. package/images/demo-screenshot.png +0 -0
  12. package/images/f757093f-507b-4453-994e-f8275f8b07a9.png +0 -0
  13. package/package.json +56 -0
  14. package/public/css/style.css +791 -0
  15. package/public/index.html +838 -0
  16. package/public/js/app.js +619 -0
  17. package/src/account-manager.js +526 -0
  18. package/src/account-rotation/index.js +93 -0
  19. package/src/account-rotation/rate-limits.js +293 -0
  20. package/src/account-rotation/strategies/base-strategy.js +48 -0
  21. package/src/account-rotation/strategies/index.js +31 -0
  22. package/src/account-rotation/strategies/round-robin-strategy.js +42 -0
  23. package/src/account-rotation/strategies/sticky-strategy.js +97 -0
  24. package/src/claude-config.js +154 -0
  25. package/src/cli/accounts.js +551 -0
  26. package/src/direct-api.js +214 -0
  27. package/src/format-converter.js +563 -0
  28. package/src/index.js +45 -0
  29. package/src/middleware/credentials.js +116 -0
  30. package/src/middleware/sse.js +130 -0
  31. package/src/model-api.js +189 -0
  32. package/src/model-mapper.js +88 -0
  33. package/src/oauth.js +623 -0
  34. package/src/response-streamer.js +469 -0
  35. package/src/routes/accounts-route.js +296 -0
  36. package/src/routes/api-routes.js +102 -0
  37. package/src/routes/chat-route.js +239 -0
  38. package/src/routes/claude-config-route.js +114 -0
  39. package/src/routes/logs-route.js +43 -0
  40. package/src/routes/messages-route.js +164 -0
  41. package/src/routes/models-route.js +115 -0
  42. package/src/routes/settings-route.js +154 -0
  43. package/src/server-settings.js +70 -0
  44. package/src/server.js +57 -0
  45. package/src/signature-cache.js +106 -0
  46. package/src/thinking-utils.js +312 -0
  47. package/src/utils/logger.js +170 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,227 @@
1
+ # Codex Claude Proxy
2
+
3
+ ![Architecture banner](./images/f757093f-507b-4453-994e-f8275f8b07a9.png)
4
+
5
+ [![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/)
6
+ [![Node.js Version](https://img.shields.io/badge/Node.js-18%2B-blue.svg)](https://nodejs.org/)
7
+ [![GitHub stars](https://img.shields.io/github/stars/zzy-life/codex-claude-proxy?style=social)](https://github.com/zzy-life/codex-claude-proxy)
8
+
9
+ > **Use Claude Code CLI with the power of ChatGPT Codex models.**
10
+ > A local proxy that translates Anthropic API requests into ChatGPT Codex calls, enabling you to use the `claude` CLI tool with your ChatGPT Free/Plus/Pro subscription.
11
+
12
+ ---
13
+
14
+ ## 🚀 Features
15
+
16
+ - **Seamless Translation**: Translates Anthropic Messages API calls to ChatGPT Codex format.
17
+ - **Model Mapping**: automatically maps `claude-sonnet` and `claude-opus` to their Codex equivalents.
18
+ - **Multi-Account Support**: Manage multiple ChatGPT accounts with easy switching and auto-refresh.
19
+ - **Web Dashboard**: Built-in UI (`http://localhost:8081`) for managing accounts, viewing logs, and testing prompts.
20
+ - **Raw Proxy Payload Logs**: Optional debug logging for Claude Code requests, upstream Codex payloads, completed responses, and upstream errors.
21
+ - **Streaming Support**: Full Server-Sent Events (SSE) support for real-time responses.
22
+ - **Native Tool Calling**: Supports Claude's tool use capabilities by translating them to Codex function calls.
23
+
24
+ ---
25
+
26
+ ## � Security & Privacy
27
+
28
+ **Is this a malicious proxy? No.**
29
+
30
+ - **Local Execution**: This server runs entirely on your local machine (`localhost`).
31
+ - **Direct Communication**: It connects *directly* to OpenAI/ChatGPT endpoints. No data is sent to any third-party server.
32
+ - **Open Source**: The full source code is available here for you to audit.
33
+ - **No Data Collection**: We do not track your prompts, keys, or personal data.
34
+
35
+ ---
36
+
37
+ ## ⚙️ How it works
38
+
39
+ This tool acts as a "translation layer" between the Claude CLI and ChatGPT's Codex backend.
40
+
41
+ 1. **Intercept**: Claude Code CLI sends a request to `localhost:8081` (thinking it's Anthropic's API).
42
+ 2. **Translate**: The proxy converts the Anthropic-format JSON into the specific payload format required by ChatGPT's internal Codex API.
43
+ 3. **Forward**: The request is sent securely to ChatGPT using your own authenticated session.
44
+ 4. **Stream**: The response from ChatGPT is converted back into Anthropic's Server-Sent Events (SSE) format and streamed to your terminal.
45
+
46
+ ```
47
+ ┌──────────────────┐ ┌─────────────────────┐ ┌────────────────────────────┐
48
+ │ Claude Code │────▶│ This Proxy Server │────▶│ ChatGPT Codex Backend API │
49
+ │ (Anthropic API) │ │ (Anthropic ⇄ OpenAI)│ │ (codex/responses) │
50
+ └──────────────────┘ └─────────────────────┘ └────────────────────────────┘
51
+ ```
52
+
53
+ ---
54
+
55
+ ## �📦 Installation
56
+
57
+ You don't need to install anything if you just want to run it:
58
+
59
+ ```bash
60
+ # Run directly with npx
61
+ npx claude-codex-proxy@latest start
62
+ ```
63
+
64
+ Or install globally to use the CLI commands anywhere:
65
+
66
+ ```bash
67
+ npm install -g claude-codex-proxy
68
+ claude-codex-proxy start
69
+ ```
70
+
71
+ ---
72
+
73
+ ## 🚦 Quick Start
74
+
75
+ ### 1. Start the Proxy
76
+
77
+ ```bash
78
+ npx claude-codex-proxy@latest start
79
+ ```
80
+ The server will start at `http://localhost:8081`.
81
+
82
+ ### 2. Add Your Account
83
+
84
+ #### **Option A: Web Dashboard (Local Desktop)**
85
+
86
+ 1. Open the dashboard at **[http://localhost:8081](http://localhost:8081)**
87
+ 2. Go to the **Accounts** tab
88
+ 3. Click **Add Account** and login with your ChatGPT account
89
+
90
+ #### **Option B: CLI (Desktop or Headless/VM)**
91
+
92
+ ```bash
93
+ # Desktop (opens browser)
94
+ claude-codex-proxy accounts add
95
+
96
+ # Headless/VM server (manual code input)
97
+ claude-codex-proxy accounts add --no-browser
98
+ ```
99
+
100
+ For **headless/VM servers** without a browser:
101
+ 1. Run the command with `--no-browser`
102
+ 2. It will print a URL - copy and open it on a device with a browser
103
+ 3. Complete login on that device
104
+ 4. After redirect, copy the callback URL (or just the code)
105
+ 5. Paste it back in the terminal
106
+
107
+ ### 3. Configure Claude Code
108
+ Run this command to automatically configure your `claude` CLI to use the proxy:
109
+ ```bash
110
+ curl -X POST http://localhost:8081/claude/config/proxy
111
+ ```
112
+
113
+ *Alternatively, set the environment variables manually:*
114
+ ```bash
115
+ export ANTHROPIC_BASE_URL=http://localhost:8081
116
+ export ANTHROPIC_API_KEY=dummy-key # The key is ignored but required by the CLI
117
+ ```
118
+
119
+ To skip Claude Code's login/onboarding prompt, edit `~/.claude.json` and add:
120
+ ```json
121
+ {
122
+ "hasCompletedOnboarding": true
123
+ }
124
+ ```
125
+
126
+ 4. **Run Claude**:
127
+ ```bash
128
+ claude
129
+ ```
130
+
131
+ ---
132
+
133
+ ## 🧠 Model Mapping
134
+
135
+ The proxy resolves model names dynamically, so Claude Code can request Claude-style names while the proxy routes them to Codex models through your ChatGPT account.
136
+
137
+ Default mapping:
138
+
139
+ | Requested Model | Upstream Model | Auth Required | Description |
140
+ | :--- | :--- | :---: | :--- |
141
+ | `claude-*sonnet*` | `gpt-5.5` | ✅ | Uses the configured Sonnet-family Codex model. |
142
+ | `claude-*opus*` | `gpt-5.5` | ✅ | Uses the configured Opus-family Codex model. |
143
+ | `claude-*haiku*` | `gpt-5.4-mini` | ✅ | Uses the configured Haiku-family Codex model by default. |
144
+ | `gpt-*` | unchanged | ✅ | Direct Codex model IDs pass through without remapping. |
145
+ | unknown model | `gpt-5.5` | ✅ | Falls back to the configured default Codex model. |
146
+
147
+ You can customize mappings from the Web Dashboard, go to the model settings, and choose the Codex model used for each Claude family.
148
+
149
+ ---
150
+
151
+ ## 🛠️ Configuration & API
152
+
153
+ ### Web Dashboard
154
+ Visit `http://localhost:8081` to:
155
+ - **Manage Accounts**: Add, remove, or switch active ChatGPT accounts.
156
+ - **View Logs**: See real-time request/response logs for debugging.
157
+ - **Test Models**: Run quick tests against the configured models.
158
+ - **Configure Raw Proxy Payload Logs**: Enable detailed debugging logs from the Settings tab when you need to inspect raw Claude Code requests and upstream Codex payloads.
159
+
160
+ ### Raw Proxy Payload Logs
161
+ Raw payload logging is disabled by default because it may include prompts, tool results, file contents, and upstream response bodies. When enabled, the Logs tab captures:
162
+
163
+ - `[Claude Request]`: the original Anthropic-compatible request body received from Claude Code.
164
+ - `[Client Request]`: the original OpenAI Chat Completions-compatible request body, when using `/v1/chat/completions`.
165
+ - `[OpenAI Request]`: the converted payload sent to the ChatGPT Codex Responses API.
166
+ - `[OpenAI Response Completed]`: the completed upstream response object.
167
+ - `[OpenAI Error]`: non-2xx upstream status and error body.
168
+
169
+ You can enable it from the Web Dashboard under **Settings → Raw Proxy Payload Logs**, or via API:
170
+
171
+ ```bash
172
+ curl -X POST http://localhost:8081/settings/raw-proxy-logs \
173
+ -H "Content-Type: application/json" \
174
+ -d '{"enabled":true}'
175
+ ```
176
+
177
+ Disable it again with:
178
+
179
+ ```bash
180
+ curl -X POST http://localhost:8081/settings/raw-proxy-logs \
181
+ -H "Content-Type: application/json" \
182
+ -d '{"enabled":false}'
183
+ ```
184
+
185
+ Check the current state:
186
+
187
+ ```bash
188
+ curl http://localhost:8081/settings/raw-proxy-logs
189
+ ```
190
+
191
+ Authentication headers and access tokens are not logged.
192
+
193
+ ### API Endpoints
194
+ - `GET /health`: Check server status.
195
+ - `GET /accounts`: List configured accounts.
196
+ - `POST /v1/messages`: Anthropic-compatible chat completion endpoint.
197
+
198
+ See [API Documentation](./docs/API.md) for full details.
199
+
200
+ ---
201
+
202
+ ## 🤝 Contributing
203
+
204
+ Contributions are welcome! Please feel free to submit a Pull Request.
205
+
206
+ 1. Fork the repository
207
+ 2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
208
+ 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
209
+ 4. Push to the branch (`git push origin feature/AmazingFeature`)
210
+ 5. Open a Pull Request
211
+
212
+ ---
213
+
214
+ ## 📄 License
215
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
216
+
217
+ ## ⚠️ Disclaimer
218
+ This project is an independent open-source tool and is not affiliated with, endorsed by, or sponsored by Anthropic or OpenAI. "Claude" is a trademark of Anthropic PBC. "ChatGPT" and "Codex" are trademarks of OpenAI. Use responsibly and in accordance with applicable Terms of Service.
219
+
220
+ ---
221
+
222
+ <div align="center">
223
+ <p>If you find this project useful, please give it a star! ⭐️</p>
224
+ <a href="https://github.com/zzy-life/codex-claude-proxy">
225
+ <img src="https://img.shields.io/github/stars/zzy-life/codex-claude-proxy?style=social" alt="Star on GitHub">
226
+ </a>
227
+ </div>
package/bin/cli.js ADDED
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname, join } from 'path';
5
+ import { readFileSync } from 'fs';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ const packageJson = JSON.parse(
11
+ readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')
12
+ );
13
+
14
+ const args = process.argv.slice(2);
15
+ const command = args[0];
16
+
17
+ function showHelp() {
18
+ console.log(`
19
+ codex-claude-proxy v${packageJson.version}
20
+
21
+ Proxy server for using ChatGPT Codex models with Claude Code CLI.
22
+
23
+ USAGE:
24
+ codex-claude-proxy <command> [options]
25
+
26
+ COMMANDS:
27
+ start Start the proxy server (default port: 8081)
28
+ accounts Manage ChatGPT accounts (interactive)
29
+ accounts add Add a new ChatGPT account via OAuth
30
+ accounts add --no-browser Add account manually (headless/VM)
31
+ accounts list List all configured accounts
32
+ accounts remove Remove accounts interactively
33
+ accounts verify Verify account tokens are valid
34
+ accounts clear Remove all accounts
35
+
36
+ OPTIONS:
37
+ --help, -h Show this help message
38
+ --version, -v Show version number
39
+
40
+ ENVIRONMENT:
41
+ PORT Server port (default: 8081)
42
+
43
+ EXAMPLES:
44
+ codex-claude-proxy start
45
+ PORT=3000 codex-claude-proxy start
46
+ codex-claude-proxy accounts add
47
+ codex-claude-proxy accounts add --no-browser
48
+ codex-claude-proxy accounts list
49
+ codex-claude-proxy accounts verify
50
+
51
+ HEADLESS/VM USAGE:
52
+ 1. Run: codex-claude-proxy accounts add --no-browser
53
+ 2. Copy the URL shown and open in browser on another device
54
+ 3. After login, paste the callback URL back in terminal
55
+
56
+ CONFIGURATION:
57
+ Claude Code CLI (~/.claude/settings.json):
58
+ {
59
+ "env": {
60
+ "ANTHROPIC_BASE_URL": "http://localhost:8081",
61
+ "ANTHROPIC_API_KEY": "dummy"
62
+ }
63
+ }
64
+ `);
65
+ }
66
+
67
+ function showVersion() {
68
+ console.log(packageJson.version);
69
+ }
70
+
71
+ async function main() {
72
+ if (args.includes('--help') || args.includes('-h')) {
73
+ showHelp();
74
+ process.exit(0);
75
+ }
76
+
77
+ if (args.includes('--version') || args.includes('-v')) {
78
+ showVersion();
79
+ process.exit(0);
80
+ }
81
+
82
+ switch (command) {
83
+ case 'start':
84
+ case undefined:
85
+ await import('../src/index.js');
86
+ break;
87
+
88
+ case 'accounts': {
89
+ const subCommand = args[1] || 'add';
90
+ process.argv = ['node', 'accounts-cli.js', subCommand, ...args.slice(2)];
91
+ await import('../src/cli/accounts.js');
92
+ break;
93
+ }
94
+
95
+ case 'help':
96
+ showHelp();
97
+ break;
98
+
99
+ case 'version':
100
+ showVersion();
101
+ break;
102
+
103
+ default:
104
+ console.error(`Unknown command: ${command}`);
105
+ console.error('Run "codex-claude-proxy --help" for usage information.');
106
+ process.exit(1);
107
+ }
108
+ }
109
+
110
+ main().catch((err) => {
111
+ console.error('Error:', err.message);
112
+ process.exit(1);
113
+ });
@@ -0,0 +1,202 @@
1
+ # Account Management
2
+
3
+ ## Storage Structure
4
+
5
+ ### Main Registry
6
+
7
+ **Location:** `~/.codex-claude-proxy/accounts.json`
8
+
9
+ ```json
10
+ {
11
+ "accounts": [
12
+ {
13
+ "email": "user@gmail.com",
14
+ "accountId": "d41e9636-16d8-42be-91da-7ea8773bfb7e",
15
+ "planType": "plus",
16
+ "accessToken": "eyJhbGciOiJSUzI1NiIs...",
17
+ "refreshToken": "rt_WpTMn1...",
18
+ "idToken": "eyJhbGciOiJSUzI1NiIs...",
19
+ "expiresAt": 1770886178000,
20
+ "addedAt": "2026-02-13T04:00:00.000Z",
21
+ "lastUsed": "2026-02-13T04:30:00.000Z",
22
+ "quota": {
23
+ "usage": {...},
24
+ "account": {...},
25
+ "lastChecked": "2026-02-14T10:00:00.000Z"
26
+ }
27
+ }
28
+ ],
29
+ "activeAccount": "user@gmail.com",
30
+ "version": 1
31
+ }
32
+ ```
33
+
34
+ ### Per-Account Tokens
35
+
36
+ **Location:** `~/.codex-claude-proxy/accounts/<email>/auth.json`
37
+
38
+ ```json
39
+ {
40
+ "auth_mode": "chatgpt",
41
+ "OPENAI_API_KEY": null,
42
+ "tokens": {
43
+ "id_token": "...",
44
+ "access_token": "...",
45
+ "refresh_token": "...",
46
+ "account_id": "..."
47
+ },
48
+ "last_refresh": "2026-02-14T10:00:00.000Z"
49
+ }
50
+ ```
51
+
52
+ ## Operations
53
+
54
+ ### Add Account (OAuth)
55
+
56
+ ```bash
57
+ curl -X POST http://localhost:8081/accounts/add
58
+
59
+ # Returns OAuth URL to open in browser
60
+ ```
61
+
62
+ ### Import from Codex App
63
+
64
+ ```bash
65
+ curl -X POST http://localhost:8081/accounts/import
66
+
67
+ # Imports from ~/.codex/auth.json
68
+ ```
69
+
70
+ ### List Accounts
71
+
72
+ ```bash
73
+ curl http://localhost:8081/accounts
74
+
75
+ # Response
76
+ {
77
+ "accounts": [
78
+ {
79
+ "email": "user@gmail.com",
80
+ "accountId": "...",
81
+ "planType": "plus",
82
+ "addedAt": "...",
83
+ "lastUsed": "...",
84
+ "isActive": true,
85
+ "tokenExpired": false,
86
+ "quota": {...}
87
+ }
88
+ ],
89
+ "activeAccount": "user@gmail.com",
90
+ "total": 1
91
+ }
92
+ ```
93
+
94
+ ### Switch Active Account
95
+
96
+ ```bash
97
+ curl -X POST http://localhost:8081/accounts/switch \
98
+ -H "Content-Type: application/json" \
99
+ -d '{"email":"other@gmail.com"}'
100
+ ```
101
+
102
+ Switching:
103
+ 1. Updates `activeAccount` in `accounts.json`
104
+ 2. Updates auth file for the account
105
+ 3. Next API calls use new account's credentials
106
+
107
+ ### Remove Account
108
+
109
+ ```bash
110
+ curl -X DELETE http://localhost:8081/accounts/user@gmail.com
111
+ ```
112
+
113
+ Removes:
114
+ - Account from registry
115
+ - Per-account token directory
116
+
117
+ ### Refresh Tokens
118
+
119
+ ```bash
120
+ # Active account
121
+ curl -X POST http://localhost:8081/accounts/refresh
122
+
123
+ # Specific account
124
+ curl -X POST http://localhost:8081/accounts/user@gmail.com/refresh
125
+
126
+ # All accounts
127
+ curl -X POST http://localhost:8081/accounts/refresh/all
128
+ ```
129
+
130
+ ## Token Lifecycle
131
+
132
+ ### Expiration
133
+
134
+ - Access tokens expire in ~1 hour (3600 seconds)
135
+ - Refresh tokens are long-lived (weeks/months)
136
+
137
+ ### Auto-Refresh
138
+
139
+ - Background refresh every **55 minutes**
140
+ - Startup refresh 2 seconds after server start
141
+ - Proactive refresh 5 minutes before expiry
142
+
143
+ ### Token Validation
144
+
145
+ Before each API call:
146
+ 1. Check if token is expired or expiring within 5 minutes
147
+ 2. If yes, refresh using refresh token
148
+ 3. Use new access token for the call
149
+
150
+ ## Quota Tracking
151
+
152
+ ### Fetch Quota
153
+
154
+ ```bash
155
+ curl http://localhost:8081/accounts/quota
156
+
157
+ # Response
158
+ {
159
+ "success": true,
160
+ "email": "user@gmail.com",
161
+ "quota": {
162
+ "usage": {
163
+ "totalTokenUsage": 15,
164
+ "limit": 100,
165
+ "remaining": 85,
166
+ "percentage": 15,
167
+ "resetAt": "..."
168
+ },
169
+ "account": {...}
170
+ },
171
+ "cached": false
172
+ }
173
+ ```
174
+
175
+ ### Web UI Quota Display Rules
176
+
177
+ - The Accounts table displays **remaining quota** as a percentage.
178
+ - Remaining percentage is normalized to `0-100` to avoid broken UI values.
179
+ - If `limitReached=true` or `allowed=false`, UI shows quota as exhausted even when percentage data is missing.
180
+ - If usage data is unavailable, UI shows `-` instead of rendering a broken bar.
181
+ - Reset window is shown using `usage.resetAt` (with fallback to `usage.raw.rate_limit.primary_window.reset_at`).
182
+ - UI also shows a relative countdown (e.g. `Resets in 6d 13h`) when reset data is available.
183
+
184
+ ### Refresh All Quotas
185
+
186
+ ```bash
187
+ curl http://localhost:8081/accounts/quota/all
188
+ ```
189
+
190
+ ## Account Persistence
191
+
192
+ On server startup:
193
+ 1. `ensureAccountsPersist()` loads accounts
194
+ 2. Restores active account's auth
195
+ 3. Starts auto-refresh timer
196
+
197
+ ## Security
198
+
199
+ - Tokens stored locally in `~/.codex-claude-proxy/`
200
+ - Directory permissions: user read/write only
201
+ - Never logged or exposed in API responses
202
+ - Per-account isolation via separate directories