Add persistent context files and cleanup stale references
- Add CONTEXT.md: Full infrastructure documentation with architecture diagram, routing table, network details, common operations, and important limitations - Add CLAUDE.md: Project-level context for AI agents working in this codebase - Add global-claude-md/CLAUDE.md: User-level global context for ~/.claude/ - Delete nginx-ai-proxy.conf: Unused, Caddy replaced nginx on big-server - Update deploy.sh: Remove stale nginx setup step, update domain to ai.ein-softworks.com - Update README.md: Remove nginx-ai-proxy.conf from files list, update domain, and remove leaked auth token from example Context files provide persistent reference for AI coding sessions across all projects and the ein-ai-proxy codebase specifically.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# Ein AI Proxy - Project Context
|
||||
|
||||
Anthropic-compatible proxy that routes AI requests to multiple providers based on model name: sonnet→GLM-4.7 (Z.ai), opus→Anthropic, haiku→DeepSeek.
|
||||
|
||||
## File Structure
|
||||
- `proxy.mjs` - Main proxy server (zero dependencies, Node.js built-ins only)
|
||||
- `config.json` - API keys and auth token (gitignored)
|
||||
- `config.example.json` - Template config
|
||||
- `ein-ai-proxy.service` - Systemd unit file
|
||||
- `deploy.sh` - Deployment and testing script
|
||||
|
||||
## Development
|
||||
Run locally: `node proxy.mjs` (requires `config.json`)
|
||||
Test health endpoint: `curl http://localhost:3100/health`
|
||||
Zero npm dependencies — do not add any.
|
||||
|
||||
## Deployment
|
||||
On mini-server (ssh mini-server): `cd ~/ai-proxy && git pull && sudo systemctl restart ein-ai-proxy`
|
||||
View logs: `sudo journalctl -u ein-ai-proxy -f`
|
||||
|
||||
## See Also
|
||||
`CONTEXT.md` - Full infrastructure details (network, servers, Caddy, routing table)
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
# Ein AI Proxy - Full System Context
|
||||
|
||||
This document provides complete context for working on the ein-ai-proxy project and the surrounding infrastructure.
|
||||
|
||||
## Architecture Diagram
|
||||
|
||||
```
|
||||
Work Laptop (u229331)
|
||||
┌────────────────────┐
|
||||
│ Claude Code CLI │
|
||||
│ OR Roo Code │
|
||||
└────────┬───────────┘
|
||||
│ HTTPS (SSE streaming)
|
||||
│ ANTHROPIC_BASE_URL=
|
||||
│ https://ai.ein-softworks.com
|
||||
▼
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Internet │
|
||||
│ DNS: ai.ein-softworks.com → 198.98.201.213│
|
||||
└─────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ big-server │ Gateway (198.98.201.213)
|
||||
│ 192.168.1.61 │ ┌──────────────────────┐
|
||||
│ Caddy (Docker) │ │ HTTPS (443) routing: │
|
||||
│ iptables rules │ │ ai.ein-softworks.com │
|
||||
│ │ │ → 192.168.1.140:3100 │
|
||||
│ TCP Forwarding: │ └──────────────────────┘
|
||||
│ 41922 → mini:22 │
|
||||
│ 3022 → mini:3022 │
|
||||
└─────────┬──────────┘
|
||||
│ Internal LAN (192.168.1.0/24)
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ mini-server │ Home AI Server
|
||||
│ 192.168.1.140 │ ┌──────────────────────┐
|
||||
│ │ │ ein-ai-proxy (3100) │
|
||||
│ Services: │ │ systemd: ein-ai- │
|
||||
│ - ein-ai-proxy │ │ proxy │
|
||||
│ - Gitea (3000) │ │ source: ~/ai-proxy/ │
|
||||
│ - Caddy (local) │ └──────────────────────┘
|
||||
└─────────┬──────────┘
|
||||
│ Routes by model name:
|
||||
┌────────────────────┼────────────────────┬──────────────────┐
|
||||
│ │ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
┌──────────┐ ┌────────────┐ ┌───────────────┐ ┌────────────┐
|
||||
│Anthropic │ │ Z.ai │ │ DeepSeek │ │ Default │
|
||||
│ API │ │ GLM-4.7 │ │ V3.2 │ │ (fallback)│
|
||||
└──────────┘ └────────────┘ └───────────────┘ └────────────┘
|
||||
(pay-per-t) (flat $10/mo) (cheap per token) (GLM-4.7)
|
||||
```
|
||||
|
||||
## Model Routing Table
|
||||
|
||||
| Requested Model | Provider | Rewritten To | Auth Style | Use Case |
|
||||
|-----------------|----------|--------------|------------|----------|
|
||||
| `claude-opus-4-6` | Anthropic | unchanged | x-api-key | Complex tasks |
|
||||
| `claude-opus-4-5` | Anthropic | unchanged | x-api-key | Complex tasks |
|
||||
| `claude-opus-4-5-20251101` | Anthropic | unchanged | x-api-key | Complex tasks |
|
||||
| `claude-sonnet-4-6` | Z.ai | glm-4.7 | Bearer | Daily workhorse |
|
||||
| `claude-sonnet-4-5` | Z.ai | glm-4.7 | Bearer | Daily workhorse |
|
||||
| `claude-sonnet-4-5-20250929` | Z.ai | glm-4.7 | Bearer | Daily workhorse |
|
||||
| `claude-haiku-4-5-20251001` | DeepSeek | deepseek-chat | Bearer | Trivial tasks |
|
||||
| `claude-haiku-4-5` | DeepSeek | deepseek-chat | Bearer | Trivial tasks |
|
||||
| `glm-4.7` | Z.ai | unchanged | Bearer | Direct GLM request |
|
||||
| `glm-4.5-air` | Z.ai | unchanged | Bearer | Direct GLM request |
|
||||
| `deepseek-chat` | DeepSeek | unchanged | Bearer | Direct DeepSeek |
|
||||
| *unknown* | Z.ai | glm-4.7 | Bearer | Default fallback |
|
||||
|
||||
## Network Architecture
|
||||
|
||||
### DNS Records
|
||||
All resolve to `198.98.201.213` (big-server's public IP):
|
||||
- `ai.ein-softworks.com` — Direct A record
|
||||
- `git.ein-softworks.com` — CNAME → maybezone.duckdns.org
|
||||
- `ssh.ein-softworks.com` — CNAME → maybezone.duckdns.org
|
||||
- `ssh.maybe.zone` — CNAME → maybezone.duckdns.org
|
||||
|
||||
### Servers
|
||||
|
||||
#### big-server (192.168.1.61)
|
||||
- **SSH**: `ssh big-server` (host: ssh.ein-softworks.com, port: 30141, user: harrison, key: ~/.ssh/id_personal)
|
||||
- **OS**: Linux
|
||||
- **Caddy**: Docker container with network_mode: host
|
||||
- Config inside container: `/etc/caddy/Caddyfile`
|
||||
- Edit: `sudo su`, `cd /opt/reverse-proxy`, edit Caddyfile, `docker restart caddy`
|
||||
- Logs: `docker logs caddy --tail 20`
|
||||
- **SSE streaming config**: `flush_interval -1`, timeouts 600s
|
||||
- **TCP forwarding via iptables**:
|
||||
- Port 41922 → mini-server:22 (SSH)
|
||||
- Port 3022 → 192.168.1.140:3022 (Gitea SSH)
|
||||
- Port 30141 → big-server SSH directly
|
||||
|
||||
#### mini-server (192.168.1.140)
|
||||
- **SSH**: `ssh mini-server` (host: ssh.maybe.zone, port: 41922, user: harrison, key: ~/.ssh/id_personal)
|
||||
- **OS**: Debian Linux, zsh shell
|
||||
- **Node**: v24 via nvm (scripts must `source $NVM_DIR/nvm.sh` before using npm/node)
|
||||
- **Docker**: Requires sudo; user harrison does not have passwordless sudo
|
||||
- **Services (Docker)**:
|
||||
- Gitea: `/opt/gitea/docker-compose.yml`, ports 3000:3000 (web), 3022:2222 (SSH)
|
||||
- Caddy: `/opt/reverse-proxy/docker-compose.yml`, network_mode: host (local reverse proxy)
|
||||
- **AI proxy**:
|
||||
- Systemd unit: `ein-ai-proxy`
|
||||
- Source: `~/ai-proxy/`
|
||||
- Port: 3100, bound 0.0.0.0
|
||||
- Config: `~/ai-proxy/config.json` (gitignored, contains API keys and auth token)
|
||||
- **Gitea**: https://git.ein-softworks.com
|
||||
- **Shell aliases** (~/.zshrc.local):
|
||||
- `ai` → GLM-4.7 (Z.ai)
|
||||
- `ai-opus` → Claude Opus (Anthropic)
|
||||
- `ai-deep` → DeepSeek V3.2
|
||||
- These set env vars per-provider and call `claude` directly (no proxy needed locally)
|
||||
- **Dotfiles**: Public on GitHub; secrets only in ~/.zshrc.local (sourced via `[[ -f ~/.zshrc.local ]] && source ~/.zshrc.local`)
|
||||
|
||||
### Traffic Routing
|
||||
|
||||
**HTTPS (ports 80/443)** — Handled by Caddy on big-server:
|
||||
- `git.ein-softworks.com` → 192.168.1.140:3000 (Gitea web UI)
|
||||
- `ai.ein-softworks.com` → 192.168.1.140:3100 (AI proxy)
|
||||
- `maybe.zone` / `matrix.maybe.zone` / etc. → local Matrix services
|
||||
- Media stack, UniFi Controller, VPN container → local services
|
||||
|
||||
**TCP port forwarding** — Handled by iptables on big-server:
|
||||
- Port 41922 → mini-server:22 (SSH)
|
||||
- Port 3022 → 192.168.1.140:3022 (Gitea SSH)
|
||||
|
||||
## Work Laptop Configuration
|
||||
|
||||
**OS**: macOS
|
||||
**User**: u229331
|
||||
|
||||
### SSH Config
|
||||
```
|
||||
Host big-server
|
||||
HostName ssh.ein-softworks.com
|
||||
Port 30141
|
||||
User harrison
|
||||
IdentityFile ~/.ssh/id_personal
|
||||
|
||||
Host mini-server
|
||||
HostName ssh.maybe.zone
|
||||
Port 41922
|
||||
User harrison
|
||||
IdentityFile ~/.ssh/id_personal
|
||||
```
|
||||
|
||||
### Claude Code CLI
|
||||
- Location: `~/.local/bin/claude`
|
||||
- Config: `~/.claude/settings.json`
|
||||
- Environment variables:
|
||||
- `ANTHROPIC_BASE_URL`: https://ai.ein-softworks.com
|
||||
- `ANTHROPIC_AUTH_TOKEN`: (proxy auth token from config.json)
|
||||
|
||||
### Roo Code (VS Code Extension)
|
||||
Three profiles configured:
|
||||
| Profile | Model | Routes To | Reasoning | Context |
|
||||
|---------|-------|-----------|-----------|---------|
|
||||
| "default" | claude-sonnet-4-5 | GLM-4.7 (Z.ai) | ON | Standard |
|
||||
| "opus" | claude-opus-4-6 | Anthropic Opus | ON | 1M context enabled |
|
||||
| "cheap" | claude-haiku-4-5 | DeepSeek V3.2 | OFF | Fast/cheap tasks |
|
||||
|
||||
## Proxy Internals
|
||||
|
||||
### File Structure
|
||||
```
|
||||
~/ai-proxy/
|
||||
├── proxy.mjs # Main proxy server (zero dependencies)
|
||||
├── config.json # API keys + auth token (gitignored)
|
||||
├── config.example.json # Template config
|
||||
├── ein-ai-proxy.service # Systemd unit file
|
||||
├── deploy.sh # Deployment script
|
||||
├── package.json # Package metadata
|
||||
├── .gitignore # Excludes config.json
|
||||
├── CONTEXT.md # This file
|
||||
├── CLAUDE.md # Project-level context
|
||||
└── README.md # Public documentation
|
||||
```
|
||||
|
||||
### Provider Configuration
|
||||
```javascript
|
||||
const PROVIDERS = {
|
||||
anthropic: {
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
authStyle: "api-key" // x-api-key header
|
||||
},
|
||||
zai: {
|
||||
baseUrl: "https://api.z.ai/api/anthropic",
|
||||
authStyle: "bearer" // Authorization: Bearer token
|
||||
},
|
||||
deepseek: {
|
||||
baseUrl: "https://api.deepseek.com/anthropic",
|
||||
authStyle: "bearer" // Authorization: Bearer token
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### SSE Streaming
|
||||
- Streaming responses (`stream: true`) are piped through without buffering
|
||||
- Caddy on big-server uses `flush_interval -1` for immediate SSE delivery
|
||||
- Timeouts set to 600s to accommodate long reasoning responses
|
||||
|
||||
### Authentication
|
||||
Proxy authenticates clients via:
|
||||
1. `Authorization: Bearer <token>` header, OR
|
||||
2. `x-api-key: <token>` header
|
||||
|
||||
Token must match `config.json.authToken`.
|
||||
|
||||
## Common Operations
|
||||
|
||||
### On mini-server (where proxy runs)
|
||||
```bash
|
||||
# Restart proxy
|
||||
sudo systemctl restart ein-ai-proxy
|
||||
|
||||
# View live logs
|
||||
sudo journalctl -u ein-ai-proxy -f --no-pager
|
||||
|
||||
# Check proxy health
|
||||
curl https://ai.ein-softworks.com/health
|
||||
|
||||
# Pull and deploy changes
|
||||
cd ~/ai-proxy && git pull
|
||||
sudo systemctl restart ein-ai-proxy
|
||||
```
|
||||
|
||||
### On big-server (where Caddy runs)
|
||||
```bash
|
||||
# Restart Caddy
|
||||
sudo su
|
||||
cd /opt/reverse-proxy
|
||||
docker restart caddy
|
||||
|
||||
# View Caddy logs
|
||||
docker logs caddy --tail 20
|
||||
```
|
||||
|
||||
## Important Limitations
|
||||
|
||||
### MCP Tools Execute Client-Side
|
||||
MCP tools run on the work laptop (where the AI client is), NOT on the proxy server. This means:
|
||||
- Company-internal MCP servers work fine because the client is on the company network
|
||||
- The proxy only routes LLM API calls — it doesn't execute MCP tools
|
||||
|
||||
### Thinking-Block Provider Switching
|
||||
**You cannot switch between providers mid-conversation when reasoning/thinking is enabled.**
|
||||
|
||||
GLM-4.7's thinking block signatures are different from Anthropic's and will be rejected by the Anthropic API. If you start a conversation with Sonnet (GLM-4.7) and then switch to Opus (Anthropic API), the provider will reject the request.
|
||||
|
||||
**Solution**: Start a fresh session with `/clear` before switching to a different provider.
|
||||
|
||||
### Zero Dependencies
|
||||
The proxy has zero npm dependencies — it uses only Node.js built-ins (`node:http`, `node:fs`, etc.). Do not add dependencies unless absolutely necessary.
|
||||
|
||||
## Key Reference URLs
|
||||
- **Proxy API**: https://ai.ein-softworks.com
|
||||
- **Gitea**: https://git.ein-softworks.com
|
||||
- **Git remote**: `ssh://git@git.ein-softworks.com:3022/harrison/ein-ai-proxy.git`
|
||||
@@ -39,7 +39,7 @@ Add to `~/.claude/settings.json`:
|
||||
{
|
||||
"env": {
|
||||
"ANTHROPIC_BASE_URL": "https://ai.ein-softworks.com",
|
||||
"ANTHROPIC_AUTH_TOKEN": "2d6aca1161e8ad8451c3c1247edf037b736dd7054fc2fab9e35adee313fdba5c",
|
||||
"ANTHROPIC_AUTH_TOKEN": "your-proxy-auth-token",
|
||||
"API_TIMEOUT_MS": "3000000"
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ Add to `~/.claude/settings.json`:
|
||||
2. Set API Provider to **Anthropic**
|
||||
3. Enter your proxy auth token as the API Key
|
||||
4. Check **Use custom base URL**
|
||||
5. Enter `https://ai.maybe.zone`
|
||||
5. Enter `https://ai.ein-softworks.com`
|
||||
|
||||
## Switching models
|
||||
|
||||
@@ -63,8 +63,9 @@ From Claude Code, the default Sonnet model routes to GLM-4.7. To use Opus for a
|
||||
- `config.json` — Your API keys and auth token (gitignored)
|
||||
- `config.example.json` — Template config
|
||||
- `ein-ai-proxy.service` — Systemd unit file
|
||||
- `nginx-ai-proxy.conf` — Nginx reverse proxy config
|
||||
- `deploy.sh` — Deployment script
|
||||
- `CLAUDE.md` — Project-level context for AI agents
|
||||
- `CONTEXT.md` — Full infrastructure documentation
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -81,32 +81,19 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Step 5: Nginx setup reminder ---
|
||||
echo "[5/5] Nginx setup:"
|
||||
echo ""
|
||||
echo " 1. Create DNS A record: ai.maybe.zone -> $(curl -s ifconfig.me 2>/dev/null || echo 'YOUR_IP')"
|
||||
echo ""
|
||||
echo " 2. Install the nginx config:"
|
||||
echo " sudo cp $PROXY_DIR/nginx-ai-proxy.conf /etc/nginx/sites-available/ai-proxy"
|
||||
echo " sudo ln -sf /etc/nginx/sites-available/ai-proxy /etc/nginx/sites-enabled/"
|
||||
echo " sudo nginx -t && sudo systemctl reload nginx"
|
||||
echo ""
|
||||
echo " 3. Get SSL cert:"
|
||||
echo " sudo certbot --nginx -d ai.maybe.zone"
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " Deployment complete!"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
echo " Proxy running at: http://127.0.0.1:3100"
|
||||
echo " After nginx+SSL: https://ai.maybe.zone"
|
||||
echo " Public URL: https://ai.ein-softworks.com"
|
||||
echo ""
|
||||
echo " --- Client Setup ---"
|
||||
echo ""
|
||||
echo " Claude Code CLI (~/.claude/settings.json):"
|
||||
echo ' {'
|
||||
echo ' "env": {'
|
||||
echo " \"ANTHROPIC_BASE_URL\": \"https://ai.maybe.zone\","
|
||||
echo " \"ANTHROPIC_BASE_URL\": \"https://ai.ein-softworks.com\","
|
||||
echo " \"ANTHROPIC_AUTH_TOKEN\": \"$AUTH_TOKEN\","
|
||||
echo ' "API_TIMEOUT_MS": "3000000"'
|
||||
echo ' }'
|
||||
@@ -115,7 +102,7 @@ echo ""
|
||||
echo " Roo Code (VS Code):"
|
||||
echo " Provider: Anthropic"
|
||||
echo " API Key: $AUTH_TOKEN"
|
||||
echo " Custom Base URL: https://ai.maybe.zone"
|
||||
echo " Custom Base URL: https://ai.ein-softworks.com"
|
||||
echo ""
|
||||
echo " Model routing:"
|
||||
echo " Sonnet requests -> GLM-4.7 (Z.ai)"
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
AI Proxy: https://ai.ein-softworks.com | Routes: sonnet→GLM-4.7, opus→Anthropic, haiku→DeepSeek
|
||||
SSH: big-server (ssh.ein-softworks.com:30141), mini-server (ssh.maybe.zone:41922) via ~/.ssh/id_personal
|
||||
Gitea: https://git.ein-softworks.com | mini-server: Debian/zsh, Node v24 via nvm (source before use)
|
||||
WARNING: /clear before switching providers mid-conversation when thinking enabled (GLM-4.7 thinking blocks incompatible with Anthropic API)
|
||||
@@ -1,46 +0,0 @@
|
||||
# Ein AI Proxy - Add this as a new server block or include in your existing nginx config
|
||||
# Place at: /etc/nginx/sites-available/ai-proxy
|
||||
# Then: sudo ln -s /etc/nginx/sites-available/ai-proxy /etc/nginx/sites-enabled/
|
||||
# Then: sudo nginx -t && sudo systemctl reload nginx
|
||||
#
|
||||
# Prerequisites:
|
||||
# - DNS A record for ai.maybe.zone pointing to your server IP
|
||||
# - SSL cert (use certbot: sudo certbot --nginx -d ai.maybe.zone)
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name ai.maybe.zone;
|
||||
|
||||
# SSL certs - certbot will fill these in, or point to your existing certs
|
||||
# ssl_certificate /etc/letsencrypt/live/ai.maybe.zone/fullchain.pem;
|
||||
# ssl_certificate_key /etc/letsencrypt/live/ai.maybe.zone/privkey.pem;
|
||||
|
||||
# SSE streaming support - critical for Claude Code / Roo Code
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
|
||||
# Max body size for large prompts with long context
|
||||
client_max_body_size 50m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3100;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# SSE streaming headers
|
||||
proxy_set_header Connection '';
|
||||
chunked_transfer_encoding on;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTP -> HTTPS redirect
|
||||
server {
|
||||
listen 80;
|
||||
server_name ai.maybe.zone;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
Reference in New Issue
Block a user