Local OpenAI-compatible proxy using ChatGPT Plus via OAuth
Project description
LAGP — LLM Auth Gateway Proxy
A local OpenAI-compatible proxy powered by your ChatGPT Plus subscription — no API key required.
What is LAGP?
LAGP runs a local HTTP server on port 11434 that speaks the OpenAI API. Any tool that accepts a custom base URL — Cline, Cursor, Continue, Open WebUI, or your own scripts — can point to http://localhost:11434 and use your existing ChatGPT Plus account as the backend, at no extra cost.
Authentication happens once through a standard browser OAuth flow. The token is stored locally at ~/.lagp/auth.json and refreshed automatically.
Your tool (Cline, Cursor…)
│ OpenAI API calls
▼
http://localhost:11434 ← LAGP
│ ChatGPT OAuth
▼
chatgpt.com backend
Requirements
- Python 3.9+
- An active ChatGPT Plus (or higher) subscription
- A modern browser (for the one-time OAuth login)
Installation
From PyPI
pip install lagp
From source
git clone https://github.com/helvecioneto/lagp.git
cd lagp
pip install .
Usage
Start the server:
lagp
On the first run, a browser window opens automatically for login. Sign in with your OpenAI account and the token is saved. The server starts immediately after authentication.
On subsequent runs, the saved token is loaded and the server starts right away.
Configure your tool
Point any OpenAI-compatible client to the local server:
| Setting | Value |
|---|---|
| Base URL | http://localhost:11434/v1 |
| API Key | any non-empty string (e.g. lagp) — or your configured key if using --api-key |
API key protection
By default LAGP accepts any value (or no value) in the Authorization header — convenient for local, single-user setups.
When you expose LAGP on a network (LAN, VPS, etc.) you can restrict access to a fixed set of keys:
# Single key
lagp --api-key my-secret-key
# Multiple keys (repeat the flag)
lagp --api-key key-for-alice --api-key key-for-bob
Once at least one --api-key is passed, every request to the proxy must include a matching key:
Authorization: Bearer <your-api-key>
Requests with a missing or wrong key are rejected with HTTP 401.
Endpoints affected
| Endpoint | Protected |
|---|---|
POST /v1/chat/completions |
✓ |
POST /api/chat |
✓ |
GET /v1/models |
✓ |
GET /api/tags |
✓ |
GET /login, /auth/* |
— (always open) |
Tip: generate a strong key with
python -c "import secrets; print(secrets.token_urlsafe(32))".
Available models
LAGP queries the ChatGPT backend at startup and lists the models your account can access. To see the live list at any time:
curl http://localhost:11434/v1/models
Typical models available with ChatGPT Plus:
| Model | Notes |
|---|---|
gpt-5.3-codex |
Latest, recommended |
gpt-5.2-codex |
Previous generation |
gpt-5.1-codex-max |
High-capacity variant |
gpt-5.1-codex-mini |
Faster, lighter |
gpt-5.2 |
General purpose |
The list is fetched live from the API — new models appear automatically as OpenAI releases them.
Endpoints
| Method | Path | Description |
|---|---|---|
GET |
/login |
Start OAuth login flow |
GET |
/v1/models |
List available models (OpenAI format) |
POST |
/v1/chat/completions |
OpenAI-compatible chat completions |
POST |
/api/chat |
Ollama-compatible chat (NDJSON) |
GET |
/api/tags |
Ollama model discovery |
Quick test
Check available models:
curl http://localhost:11434/v1/models
Send a chat message (open access — no --api-key configured):
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "gpt-5.3-codex", "messages": [{"role": "user", "content": "Hello!"}]}'
Send a chat message (with --api-key configured):
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer my-secret-key" \
-d '{"model": "gpt-5.3-codex", "messages": [{"role": "user", "content": "Hello!"}]}'
Use with the OpenAI Python SDK:
from openai import OpenAI
# Open access (no --api-key configured)
client = OpenAI(base_url="http://localhost:11434/v1", api_key="lagp")
# With --api-key configured — pass the real key
client = OpenAI(base_url="http://localhost:11434/v1", api_key="my-secret-key")
response = client.chat.completions.create(
model="gpt-5.3-codex",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)
Running on a remote server
LAGP works great on headless remote servers (VPS, cloud instances, etc.). Because the OAuth provider only allows localhost as a valid redirect URI, you cannot authenticate directly on a remote machine through its public IP. There are three supported approaches.
Method 1 — Token sync (recommended)
Authenticate on your local machine and push the tokens automatically to the remote server over HTTP.
On the remote server, start LAGP in no-browser mode with a shared secret:
lagp --no-browser --sync-secret <your-secret>
On your local machine, run:
lagp --sync-to http://<server-ip>:11434 --sync-secret <your-secret>
A browser opens, you log in with your OpenAI account, and the tokens are pushed to the remote server automatically. From that point the remote server is fully authenticated.
Network requirement: port 11434 must be open on the remote server.
Note:
--sync-secretis strongly recommended. Without it, anyone who can reach port11434could push arbitrary tokens to your server.
Method 2 — SSH tunnel
No configuration needed beyond SSH access. The tunnel makes the remote server's ports appear as local ports, so localhost redirect URIs work transparently.
Open two tunnels from your local machine:
ssh -L 11434:localhost:11434 -L 1455:localhost:1455 user@<server>
On the remote server (in another terminal or tmux):
lagp --no-browser
On your local machine, open in a browser:
http://localhost:11434/login
OAuth redirects to localhost:1455, which the tunnel forwards to the remote server. Tokens are saved on the server. Close the tunnel when done — lagp runs independently from that point.
Network requirement: only SSH (port 22) needs to be open.
Method 3 — Copy auth.json manually
If you have already authenticated locally, just copy the token file to the remote server:
scp ~/.lagp/auth.json user@<server>:~/.lagp/auth.json
Then start LAGP on the remote server:
lagp --no-browser
It will load the existing token and start immediately. Tokens refresh automatically, so this file stays valid until you explicitly log out.
Comparison
| Method 1 (sync) | Method 2 (SSH tunnel) | Method 3 (copy) | |
|---|---|---|---|
| Requires open port | 11434 | 22 only | 22 only |
| Fully automated | ✓ | — | — |
| No extra tooling | ✓ | needs SSH tunnel | needs SCP |
| Works without local LAGP | — | — | ✓ |
Re-authentication
Tokens expire eventually. To re-authenticate on a remote server, run either Method 1 or Method 2 again. The new token overwrites the old one at ~/.lagp/auth.json.
CLI reference
| Flag | Default | Description |
|---|---|---|
--port PORT |
11434 |
Port for the proxy server |
--callback-port PORT |
1455 |
Port for the OAuth callback listener |
--no-browser |
off | Do not open a browser automatically |
--api-key KEY |
— | Accepted client API key (repeat for multiple keys). When set, all requests must include Authorization: Bearer <key> |
--sync-to URL |
— | Push tokens to a remote LAGP after login |
--sync-secret SECRET |
— | Shared secret protecting the /auth/sync endpoint |
--log-level LEVEL |
INFO |
Log verbosity: DEBUG, INFO, WARNING, ERROR |
Token storage
The auth token is stored at ~/.lagp/auth.json on all platforms. To log out, delete this file and restart lagp.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lagp-1.0.2.tar.gz.
File metadata
- Download URL: lagp-1.0.2.tar.gz
- Upload date:
- Size: 23.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f3e334b7042bee4cb08604f485fad7657018cd2822548d09e4b4db2ac29fcac
|
|
| MD5 |
e7bac09ec0f6fb3c26d37613c8c7acd3
|
|
| BLAKE2b-256 |
ccc579d2aa951e09100d9ccff9fc9b09119c30a6207f1bc6617846eeaff8154e
|
Provenance
The following attestation bundles were made for lagp-1.0.2.tar.gz:
Publisher:
python-publish.yml on helvecioneto/lagp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lagp-1.0.2.tar.gz -
Subject digest:
9f3e334b7042bee4cb08604f485fad7657018cd2822548d09e4b4db2ac29fcac - Sigstore transparency entry: 970770682
- Sigstore integration time:
-
Permalink:
helvecioneto/lagp@5465e7765fe87be6a6284ddfc97806fa94489fd2 -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/helvecioneto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@5465e7765fe87be6a6284ddfc97806fa94489fd2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file lagp-1.0.2-py3-none-any.whl.
File metadata
- Download URL: lagp-1.0.2-py3-none-any.whl
- Upload date:
- Size: 20.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fed33b1020b625ec8116c11ba248067df04d416fd335dca404344c64a276aa57
|
|
| MD5 |
c23f5164dbced1d304ca51acda7dbc0d
|
|
| BLAKE2b-256 |
4a7d838387dfbb4c729011e5ac6be7b8cd017497e4f48f7d84bce2c2270ac380
|
Provenance
The following attestation bundles were made for lagp-1.0.2-py3-none-any.whl:
Publisher:
python-publish.yml on helvecioneto/lagp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lagp-1.0.2-py3-none-any.whl -
Subject digest:
fed33b1020b625ec8116c11ba248067df04d416fd335dca404344c64a276aa57 - Sigstore transparency entry: 970770730
- Sigstore integration time:
-
Permalink:
helvecioneto/lagp@5465e7765fe87be6a6284ddfc97806fa94489fd2 -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/helvecioneto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@5465e7765fe87be6a6284ddfc97806fa94489fd2 -
Trigger Event:
release
-
Statement type: