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.
Publishing waits for approval of the pypi deployment on the workflow run. After it publishes,
the workflow creates the GitHub release with generated notes and the built files attached.
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.2
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.2.tar.gz | 62.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| siwc_proxy-0.2.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 101.8 kB
Release files / siwc_proxy-0.2.2.tar.gz
| Download URL | siwc_proxy-0.2.2.tar.gz |
|---|---|
| Size | 62.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cb579f2f573543584958c16f8077f9620631b43cd505843d8ecddfe88d2f57ad
|
|
BLAKE2b-256 checksum How to use checksums |
d2986ac20e9d590ab805a6a9adfa72d61e82d6e916d767f0214aba565b18de20
|
| 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.2-py3-none-any.whl
| Download URL | siwc_proxy-0.2.2-py3-none-any.whl |
|---|---|
| Size | 39.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
36eb340caaa4db49bbde068cbec5918c3ee744ea8c45c3fa831949e1d34eb0d4
|
|
BLAKE2b-256 checksum How to use checksums |
dd4d85db7ab03527ea77b08a14097a6651464a48b09cc7f61e71a0f949c5ca2c
|
| 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}
|