siwc-proxy
A local OpenAI-compatible API backed by
Sign in with ChatGPT token sharing.
Sign in once; the proxy keeps the ChatGPT plan credential fresh and serves
/v1/chat/completions, /v1/responses, and /v1/models to any OpenAI SDK,
framework, or tool that lets you set a base URL.
- Sign-in and re-auth handled for you. The proxy hosts its own OAuth callback, registers its client on first use, and reuses that registration afterwards. Authlib does PKCE, state, nonce, and ID-token checks.
- Safe token refresh. Rotating refresh tokens are refreshed once, even across processes (file lock). Refreshed tokens are saved before they are checked, so a failed check never loses the new refresh token. A temporary outage never deletes credentials. An upstream 401 triggers one refresh and retry.
- Works with ordinary client code.
responses.create(input="hi")withoutstream/storeworks: the proxy streams upstream and aggregates the result. Chat Completions is translated to Responses. Options that can't be honored, such asuserandseed, are dropped and listed in a response header. - Errors in OpenAI's shape and status. For example, a plan usage limit becomes
a 429
RateLimitError, so normal client backoff works.
Quick start
Install the siwc-proxy command with uv.
uv gets a suitable Python (3.12+) for it if you don't have one:
uv tool install siwc-proxy
Start the proxy:
siwc-proxy serve
If you are not signed in yet, serve prints a sign-in link. Open it in a browser
on the same computer, or run this in a second terminal:
siwc-proxy login
On a remote server or any machine without a browser, run siwc-proxy login --remote
there instead. Open the printed link on your own computer and sign in. Then copy the
http://127.0.0.1:... address the browser ends up on (the page fails to load;
that is expected) and paste it back into the terminal.
Then point any OpenAI client at the proxy:
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8787/v1", api_key="<local key>")
model = client.models.list().data[0].id
print(client.responses.create(model=model, input="Say hello.").output_text)
The local key is generated on first run and shown by:
siwc-proxy status --show-key
Or choose your own key with SIWC_PROXY_API_KEY. The key only protects the
local proxy; it is never sent to OpenAI.
More ways to run it
-
Without installing:
uvx siwc-proxy serve. Putuvxin front of everysiwc-proxycommand in this README. -
Upgrade:
uv tool upgrade siwc-proxy. -
Not on your
PATHafteruv tool install? Runuv tool update-shelland open a new terminal. -
pip or pipx:
pipx install siwc-proxy, orpip install siwc-proxyin a virtual environment.python -m siwc_proxyworks like thesiwc-proxycommand. -
One-file scripts with uv: declare
openaiinline and run the file withuv run hello.py:# /// script # dependencies = ["openai"] # /// import os from openai import OpenAI client = OpenAI(base_url="http://127.0.0.1:8787/v1", api_key=os.environ["SIWC_PROXY_API_KEY"]) model = client.models.list().data[0].id print(client.responses.create(model=model, input="Say hello.").output_text)
Commands
| Command | What it does |
|---|---|
siwc-proxy serve |
Run the proxy (default 127.0.0.1:8787). Starts even when signed out. |
siwc-proxy login [--consent] [--remote] |
Sign in or re-authorize. Uses the running proxy if there is one; otherwise hosts the callback on a temporary port. --remote signs in with a browser on another computer by pasting back the address it lands on. --consent shows ChatGPT's permission screen again, in case you didn't allow plan usage before. |
siwc-proxy logout [--forget] |
Revoke and clear tokens. --forget also drops the registration (needed to switch accounts). |
siwc-proxy status [--json] [--show-key] |
Account, state, scopes, expiry. Never prints tokens. |
siwc-proxy token |
Print a fresh access token to stdout, for scripts. |
Every command accepts --data-dir, before or after the command name (or SIWC_PROXY_DATA_DIR),
to choose a credential directory.
Use one directory per account.
serve --host can bind a non-loopback interface for Docker or LAN use, but the
proxy speaks plain HTTP: the key and your prompts then cross the network
unencrypted. See the warning in docs/proxy.md.
See docs/proxy.md for configuration, routes, the Chat translation rules, error mapping, and Docker/LAN access.
Development
git clone https://github.com/d-nyamkhuu/siwc-proxy.git
cd siwc-proxy
uv sync
uv run pytest -q
uv run siwc-proxy serve # run the checkout
The tests run the real openai SDK against the app. OpenAI's OAuth and API are
mocked: real RSA-signed ID tokens, rotating refresh tokens, and multi-process refresh races.
No live account is used.
Releasing
The version lives only in pyproject.toml. Bump it, then push a matching tag:
uv version --bump minor # or patch / major
git commit -am "Release $(uv version --short)"
git tag "v$(uv version --short)"
git push origin main --tags
The release workflow runs the tests, builds the
wheel and sdist, smoke-tests both, and publishes to PyPI with
Trusted Publishing, so no API token is stored.
Before the first release, add a pending publisher on PyPI for project siwc-proxy,
repository d-nyamkhuu/siwc-proxy, workflow release.yml, environment pypi.
References
Metadata
Release files for siwc-proxy 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| siwc_proxy-0.2.0.tar.gz | 61.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| siwc_proxy-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 100.1 kB
Release files / siwc_proxy-0.2.0.tar.gz
| Download URL | siwc_proxy-0.2.0.tar.gz |
|---|---|
| Size | 61.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3cae38bf05c8d8615154844bb6cc2a8f3deebfa19a2040df93e9af215b3a4c79
|
|
BLAKE2b-256 checksum How to use checksums |
ab8ab033804fff233ff6cea2e34e92aabc5d1fbf78266e1dbe90fa01207590ee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / siwc_proxy-0.2.0-py3-none-any.whl
| Download URL | siwc_proxy-0.2.0-py3-none-any.whl |
|---|---|
| Size | 38.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aa6ab655eb06cd778910061500b8bc185706153833362c0ee3a53aa73df779cb
|
|
BLAKE2b-256 checksum How to use checksums |
9483c2bf05ceff5d41203011127da062f7317e21a856ef686c6735770b22fd9c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|