synckey 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,799 @@
1
+ Metadata-Version: 2.4
2
+ Name: synckey
3
+ Version: 0.1.0
4
+ Summary: One API key for every AI provider. Automatic failover, tier-based fallback, and zero rate-limit headaches.
5
+ Project-URL: Homepage, https://github.com/pmdnawaz1/synckey
6
+ Project-URL: Documentation, https://github.com/pmdnawaz1/synckey#readme
7
+ Project-URL: Repository, https://github.com/pmdnawaz1/synckey
8
+ Project-URL: Issues, https://github.com/pmdnawaz1/synckey/issues
9
+ Project-URL: Changelog, https://github.com/pmdnawaz1/synckey/releases
10
+ Author-email: synckey <synckey@example.com>
11
+ License: MIT
12
+ Keywords: ai,api-key,cerebras,cohere,gateway,gemini,groq,llm,nvidia,openai,proxy,routing
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: cryptography>=42.0
24
+ Requires-Dist: fastapi>=0.110
25
+ Requires-Dist: httpx>=0.27
26
+ Requires-Dist: orjson>=3.9
27
+ Requires-Dist: rich>=13.7
28
+ Requires-Dist: typer>=0.12
29
+ Requires-Dist: uvicorn[standard]>=0.29
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
32
+ Requires-Dist: pytest>=8.0; extra == 'dev'
33
+ Requires-Dist: respx>=0.21; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # synckey
37
+
38
+ <h1 align="center">
39
+ <img src="https://img.shields.io/github/stars/pmdnawaz1/synckey?style=flat&color=20MDFF" alt="Stars"/>
40
+ <img src="https://img.shields.io/pypi/v/synckey?style=flat&color=20MDFF" alt="PyPI"/>
41
+ <img src="https://img.shields.io/pypi/l/synckey?style=flat&color=20MDFF" alt="License"/>
42
+ <br/>
43
+ One API key. Every AI model. Zero headaches.
44
+ </h1>
45
+
46
+ **synckey** lets your app use Gemini, Groq, OpenAI, Anthropic, Cerebras, Mistral, DeepSeek, xAI, and 40+ other providers — through a single OpenAI-compatible API key. No code changes. No rate limit nightmares. Just one endpoint.
47
+
48
+ ```python
49
+ from openai import OpenAI
50
+
51
+ client = OpenAI(
52
+ base_url="http://localhost:8787/v1",
53
+ api_key="sk-synckey-abc123..." # ← one key for everything
54
+ )
55
+
56
+ # Just works. synckey routes to whichever provider has the model.
57
+ client.chat.completions.create(
58
+ model="claude-sonnet-4-6",
59
+ messages=[{"role": "user", "content": "Write me a poem"}]
60
+ )
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Why synckey?
66
+
67
+ | Problem | synckey Solution |
68
+ |---------|------------------|
69
+ | Managing 10 different API keys | **One key** for all providers |
70
+ | Rate limits breaking production | **Automatic failover** to the next available provider |
71
+ | Expensive models during peak | **Tier-based fallback** → uses cheaper models when others are throttled |
72
+ | Switching providers mid-code | **Zero code changes** — just change the model name |
73
+ | Key rotation nightmares | **One command** to rotate: `synckey renew` |
74
+
75
+ ---
76
+
77
+ ## Quick Start
78
+
79
+ ```bash
80
+ # 1. Install
81
+ pip install synckey
82
+
83
+ # 2. Guided setup (30 seconds)
84
+ synckey setup
85
+
86
+ # 3. Start the gateway
87
+ synckey serve
88
+ ```
89
+
90
+ Your app now uses `http://localhost:8787/v1` with the unified key from setup.
91
+
92
+ **That's it.** Your OpenAI SDK, LangChain agent, or curl command now routes to 40+ models across all providers automatically.
93
+
94
+ ---
95
+
96
+ ## How to Connect Your App
97
+
98
+ ### Python OpenAI SDK
99
+
100
+ ```python
101
+ from openai import OpenAI
102
+
103
+ client = OpenAI(
104
+ base_url="http://localhost:8787/v1",
105
+ api_key="sk-synckey-xxx"
106
+ )
107
+
108
+ # Use default model (set during setup)
109
+ client.chat.completions.create(
110
+ messages=[{"role": "user", "content": "Hello!"}]
111
+ )
112
+
113
+ # Specify a model - synckey routes automatically
114
+ client.chat.completions.create(
115
+ model="gpt-4o",
116
+ messages=[{"role": "user", "content": "Hello!"}]
117
+ )
118
+
119
+ # Streaming responses
120
+ stream = client.chat.completions.create(
121
+ model="gpt-4o",
122
+ messages=[{"role": "user", "content": "Tell me a story"}],
123
+ stream=True
124
+ )
125
+ for chunk in stream:
126
+ print(chunk.choices[0].delta.content, end="")
127
+ ```
128
+
129
+ ### Node.js
130
+
131
+ ```javascript
132
+ import OpenAI from 'openai';
133
+
134
+ const client = new OpenAI({
135
+ baseURL: 'http://localhost:8787/v1',
136
+ apiKey: 'sk-synckey-xxx'
137
+ });
138
+
139
+ const response = await client.chat.completions.create({
140
+ model: 'gpt-4o',
141
+ messages: [{ role: 'user', content: 'Hello!' }]
142
+ });
143
+ ```
144
+
145
+ ### Go
146
+
147
+ ```go
148
+ package main
149
+
150
+ import (
151
+ "github.com/sashabaranov/go-openai"
152
+ )
153
+
154
+ func main() {
155
+ client := openai.NewClient("sk-synckey-xxx")
156
+ client.BaseURL = "http://localhost:8787/v1"
157
+
158
+ resp, err := client.CreateChatCompletion(
159
+ context.Background(),
160
+ openai.ChatCompletionRequest{
161
+ Model: "gpt-4o",
162
+ Messages: []openai.ChatMessage{
163
+ {Role: "user", Content: "Hello!"},
164
+ },
165
+ },
166
+ )
167
+ }
168
+ ```
169
+
170
+ ### curl
171
+
172
+ ```bash
173
+ # Basic request
174
+ curl http://localhost:8787/v1/chat/completions \
175
+ -H "Authorization: Bearer sk-synckey-xxx" \
176
+ -H "Content-Type: application/json" \
177
+ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'
178
+
179
+ # Streaming
180
+ curl http://localhost:8787/v1/chat/completions \
181
+ -H "Authorization: Bearer sk-synckey-xxx" \
182
+ -H "Content-Type: application/json" \
183
+ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}], "stream": true}'
184
+
185
+ # Use default model (omit model)
186
+ curl http://localhost:8787/v1/chat/completions \
187
+ -H "Authorization: Bearer sk-synckey-xxx" \
188
+ -H "Content-Type: application/json" \
189
+ -d '{"messages": [{"role": "user", "content": "Hello"}]}'
190
+
191
+ # Force specific provider
192
+ curl http://localhost:8787/v1/chat/completions \
193
+ -H "Authorization: Bearer sk-synckey-xxx" \
194
+ -H "X-Provider: groq" \
195
+ -H "Content-Type: application/json" \
196
+ -d '{"model": "llama-3.3-70b-versatile", "messages": [{"role": "user", "content": "Hello"}]}'
197
+ ```
198
+
199
+ ### LangChain
200
+
201
+ ```python
202
+ from langchain_openai import ChatOpenAI
203
+
204
+ llm = ChatOpenAI(
205
+ base_url="http://localhost:8787/v1",
206
+ api_key="sk-synckey-xxx",
207
+ model="gpt-4o"
208
+ )
209
+
210
+ response = llm.invoke("Hello!")
211
+ ```
212
+
213
+ ---
214
+
215
+ ## API Reference
216
+
217
+ ### Endpoints
218
+
219
+ | Endpoint | Method | Auth | Description |
220
+ |----------|--------|------|-------------|
221
+ | `/v1/chat/completions` | POST | Yes | Chat completions (streaming supported) |
222
+ | `/v1/embeddings` | POST | Yes | Embeddings |
223
+ | `/v1/completions` | POST | Yes | Legacy completions |
224
+ | `/v1/models` | GET | Yes | List available models |
225
+ | `/v1/usage` | GET | Yes | Token usage totals |
226
+ | `/v1/events` | GET | Yes | Recent routing events |
227
+ | `/v1/requests/{id}` | GET | Yes | Poll deferred request status |
228
+ | `/healthz` | GET | No | Health check |
229
+
230
+ ### Headers
231
+
232
+ | Header | Values | Description |
233
+ |--------|--------|-------------|
234
+ | `Authorization` | `Bearer sk-synckey-xxx` | **Required.** Your unified API key |
235
+ | `X-Quality-Floor` | `frontier`, `high`, `mid`, `low` | Minimum tier to use (never drop below this) |
236
+ | `X-Provider` | Provider ID (e.g., `groq`, `gemini`) | Hint to prefer this provider first |
237
+
238
+ ### POST /v1/chat/completions
239
+
240
+ **Request:**
241
+ ```json
242
+ {
243
+ "model": "gpt-4o",
244
+ "messages": [
245
+ {"role": "system", "content": "You are a helpful assistant."},
246
+ {"role": "user", "content": "Hello!"}
247
+ ],
248
+ "temperature": 0.7,
249
+ "max_tokens": 1000,
250
+ "stream": false
251
+ }
252
+ ```
253
+
254
+ **Response (non-streaming):**
255
+ ```json
256
+ {
257
+ "id": "chatcmpl-xxx",
258
+ "object": "chat.completion",
259
+ "created": 1700000000,
260
+ "model": "gpt-4o",
261
+ "choices": [{
262
+ "index": 0,
263
+ "message": {
264
+ "role": "assistant",
265
+ "content": "Hello! How can I help you today?"
266
+ },
267
+ "finish_reason": "stop"
268
+ }],
269
+ "usage": {
270
+ "prompt_tokens": 20,
271
+ "completion_tokens": 15,
272
+ "total_tokens": 35
273
+ }
274
+ }
275
+ ```
276
+
277
+ **Response (deferred/queued - 202):**
278
+ ```json
279
+ {
280
+ "id": "defer_abc123",
281
+ "object": "chat.deferred",
282
+ "status": "queued",
283
+ "result_url": "/v1/requests/defer_abc123",
284
+ "retry_after": 28
285
+ }
286
+ ```
287
+
288
+ ### GET /v1/requests/{id}
289
+
290
+ Poll for deferred request completion:
291
+
292
+ ```bash
293
+ curl http://localhost:8787/v1/requests/defer_abc123 \
294
+ -H "Authorization: Bearer sk-synckey-xxx"
295
+ ```
296
+
297
+ **Queued response:**
298
+ ```json
299
+ {"id": "defer_abc123", "status": "queued", "retry_after": 15}
300
+ ```
301
+
302
+ **Running response:**
303
+ ```json
304
+ {"id": "defer_abc123", "status": "running"}
305
+ ```
306
+
307
+ **Done response:**
308
+ ```json
309
+ {
310
+ "id": "defer_abc123",
311
+ "status": "done",
312
+ "response": { /* full completion response */ }
313
+ }
314
+ ```
315
+
316
+ ---
317
+
318
+ ## Architecture
319
+
320
+ ```
321
+ ┌────────────────────────────────────────────────────────────────────────┐
322
+ │ Your Application │
323
+ │ (OpenAI SDK, LangChain, curl, any) │
324
+ └─────────────────────────────────────┬──────────────────────────────────┘
325
+ │
326
+ ▼
327
+ ┌─────────────────────────────────┐
328
+ │ synckey Gateway │
329
+ │ http://localhost:8787/v1 │
330
+ │ │
331
+ │ ┌─────────────────────────┐ │
332
+ │ │ 1. Auth Middleware │ │
333
+ │ │ (unified key) │ │
334
+ │ └─────────────────────────┘ │
335
+ │ │ │
336
+ │ ▼ │
337
+ │ ┌─────────────────────────┐ │
338
+ │ │ 2. Router │ │
339
+ │ │ - resolve model │ │
340
+ │ │ - check tier floor │ │
341
+ │ │ - priority ordering │ │
342
+ │ └─────────────────────────┘ │
343
+ │ │ │
344
+ │ ▼ │
345
+ │ ┌─────────────────────────┐ │
346
+ │ │ 3. Key Pool │ │
347
+ │ │ - per-key buckets │ │
348
+ │ │ - RPM/TPM tracking │ │
349
+ │ │ - health states │ │
350
+ │ └─────────────────────────┘ │
351
+ │ │ │
352
+ │ ▼ │
353
+ │ ┌─────────────────────────┐ │
354
+ │ │ 4. Proxy Layer │ │
355
+ │ │ - forward to upstream │ │
356
+ │ │ - stream passthrough │ │
357
+ │ │ - log usage/cost │ │
358
+ │ └─────────────────────────┘ │
359
+ │ │ │
360
+ │ ▼ │
361
+ │ ┌─────────────────────────┐ │
362
+ │ │ 5. Deferred Queue │ │
363
+ │ │ - 202 + poll │ │
364
+ │ │ - auto-retry │ │
365
+ │ └─────────────────────────┘ │
366
+ └──────────────────────┬──────────┘
367
+ │
368
+ ┌───────────────────────────┼───────────────────────────┐
369
+ ▼ ▼ ▼
370
+ ┌─────────┐ ┌─────────┐ ┌─────────┐
371
+ │ Groq │ │ Gemini │ │Anthropic│
372
+ └─────────┘ └─────────┘ └─────────┘
373
+ ```
374
+
375
+ ### Internal Components
376
+
377
+ | Component | Purpose | Resources |
378
+ |-----------|---------|-----------|
379
+ | **Auth Middleware** | Validates unified key (SHA-256 hash) | ~1MB RAM |
380
+ | **Router** | Resolves model → provider, enforces tier floor | ~5MB RAM |
381
+ | **Key Pool** | Tracks per-key state (LIVE/COOLING/DEAD), RPM/TPM buckets | ~10KB per key |
382
+ | **Proxy Layer** | Forwards requests, handles streaming, logs responses | Scales with concurrency |
383
+ | **Deferred Queue** | SQLite-backed queue for when all keys are cooling | ~1MB RAM + disk |
384
+
385
+ ### Resource Usage
386
+
387
+ | Metric | Typical | Under Load |
388
+ |--------|---------|------------|
389
+ | **RAM** | 50-100MB | 200MB max |
390
+ | **CPU** | <1% idle | Scales with concurrent requests |
391
+ | **Disk** | SQLite DB (~1MB per 10K requests) | Configurable TTL |
392
+ | **Latency Overhead** | 2-5ms | Adds ~10ms at 100 concurrent |
393
+
394
+ synckey is designed for **minimal footprint**. The gateway is single-threaded async by default (uvicorn with 1 worker). For higher throughput, increase workers:
395
+
396
+ ```bash
397
+ synckey serve --workers 4 # 4 async workers
398
+ ```
399
+
400
+ ---
401
+
402
+ ## Smart Routing
403
+
404
+ synckey doesn't just blindly forward requests. It intelligently manages the entire lifecycle:
405
+
406
+ ### Automatic Failover
407
+
408
+ ```python
409
+ # You request: claude-opus-4-8
410
+ # Groq has it and is fast → routes there
411
+ # Groq hits rate limit → automatically tries Anthropic
412
+ # Anthropic succeeds → returns response, logs the fallback
413
+ ```
414
+
415
+ ### Tier-Based Fallback
416
+
417
+ Every model is classified into a quality tier. If your preferred model is unavailable, synckey finds alternatives at the **same tier first** — never drops you to a cheaper tier unless explicitly configured.
418
+
419
+ ```
420
+ FRONTIER: claude-opus-4-8, gpt-5, gemini-2.5-pro
421
+ HIGH: claude-sonnet-4-6, gpt-4o, gemini-1.5-pro
422
+ MID: gpt-4o-mini, llama-3.3-70b, gemini-2.0-flash
423
+ LOW: gemma-2b, llama-3.2-1b
424
+ ```
425
+
426
+ ### Deferred Requests (Queue)
427
+
428
+ When **every** key is rate-limited, synckey queues the request instead of failing:
429
+
430
+ ```json
431
+ {
432
+ "id": "defer_abc123",
433
+ "status": "queued",
434
+ "result_url": "/v1/requests/defer_abc123",
435
+ "retry_after": 28
436
+ }
437
+ ```
438
+
439
+ Poll the result URL or wait — synckey runs it automatically when a key frees up. No code changes needed. Works with streaming too.
440
+
441
+ ---
442
+
443
+ ## Use Cases
444
+
445
+ ### Load Balance Across Providers
446
+
447
+ ```python
448
+ # All these route intelligently based on your configured priority
449
+ client.chat.completions.create(model="gpt-4o") # Groq → Gemini → OpenAI
450
+ client.chat.completions.create(model="claude-sonnet-4-6") # Anthropic → Groq → Gemini
451
+ client.chat.completions.create(model="llama-3.3-70b") # Groq → Cerebras → Together
452
+ ```
453
+
454
+ ### Cost Optimization
455
+
456
+ ```bash
457
+ # Set your default to a tier, not a specific model
458
+ synckey config set-default mid
459
+
460
+ # synckey picks the best MID-tier model that's available and affordable
461
+ # Falls back to HIGH tier only if MID is completely unavailable
462
+ ```
463
+
464
+ ### Zero-Downtime Deployments
465
+
466
+ ```python
467
+ # When OpenAI has an outage, your app keeps working
468
+ # synckey automatically routes to the next available provider
469
+ # Your users never see an error
470
+ ```
471
+
472
+ ---
473
+
474
+ ## Live Dashboard
475
+
476
+ ```bash
477
+ synckey dash
478
+ ```
479
+
480
+ ```
481
+ ┌──────────────────────────────────────────────────────────────┐
482
+ │ synckey dashboard [1]overview │
483
+ ├──────────────────────────────────────────────────────────────┤
484
+ │ │
485
+ │ Keys (3) Requests Today Events │
486
+ │ ┌──────────┐ 1,247 12 rate limits │
487
+ │ │ ● Groq │ Avg: 142ms 2 tier fallbacks │
488
+ │ │ ○ Gemini │ Errors: 0.2% 0 key deaths │
489
+ │ │ ● Cohere │ │
490
+ │ └──────────┘ Last Request │
491
+ │ Model: gpt-4o-mini │
492
+ │ Provider: Groq │
493
+ │ Latency: 89ms │
494
+ │ │
495
+ │ [1]overview [2]keys [3]events [4]routing [q]quit │
496
+ └──────────────────────────────────────────────────────────────┘
497
+ ```
498
+
499
+ ---
500
+
501
+ ## Supported Providers
502
+
503
+ 40+ providers supported out of the box:
504
+
505
+ | Category | Providers |
506
+ |----------|-----------|
507
+ | **Fast/Cheap** | Groq, Cerebras, GitHub Models, DeepSeek |
508
+ | **Frontier** | OpenAI, Anthropic, Google Gemini, xAI |
509
+ | **Open Source** | Ollama, LM Studio, LocalAI, Fireworks |
510
+ | **Aggregators** | OpenRouter, Together AI, SambaNova |
511
+
512
+ Run `synckey providers` to see the full list.
513
+
514
+ ---
515
+
516
+ ## CLI Reference
517
+
518
+ ```bash
519
+ # First time setup
520
+ synckey setup # guided interactive setup
521
+ synckey setup --force # regenerate unified key
522
+
523
+ # Gateway
524
+ synckey serve # start (runs in background)
525
+ synckey serve --port 9000 # custom port
526
+
527
+ # Key management
528
+ synckey keys # list all keys
529
+ synckey keys --rm <id> # remove a key
530
+ synckey keys --disable <id> # temporarily disable
531
+ synckey keys --enable <id> # re-enable a disabled key
532
+
533
+ # Testing
534
+ synckey test # health check all keys
535
+ synckey test groq llama-3.3-70b-versatile 5 # test a route
536
+
537
+ # Routing
538
+ synckey routing priority # show provider order
539
+ synckey routing priority groq gemini # set preference
540
+ synckey routing preset reliable # never drop tier
541
+ synckey routing preset cheap # prefer cost over quality
542
+
543
+ # Config
544
+ synckey config set-default gemini-2.0-flash # set default model
545
+ synckey config provider-default groq llama-3.3-70b-versatile # per-provider
546
+ synckey config alias fast mid # create alias
547
+
548
+ # Tiers
549
+ synckey tier set my-model frontier # override tier
550
+ synckey tier list # show all overrides
551
+
552
+ # Monitoring
553
+ synckey dash # live dashboard
554
+ synckey status # quick overview
555
+ synckey usage # totals
556
+ synckey usage --spend # cost breakdown
557
+ synckey usage --recent # last 25 requests
558
+ synckey events # rate limits, fallbacks, deaths
559
+ synckey deferred list # queued requests
560
+
561
+ # Key rotation
562
+ synckey renew # regenerate unified key
563
+ ```
564
+
565
+ ---
566
+
567
+ ## Environment Variables
568
+
569
+ | Variable | Default | Description |
570
+ |----------|---------|-------------|
571
+ | `SYNCKEY_HOME` | `~/.synckey/` | Config and state directory |
572
+ | `SYNCKEY_DB` | `{SYNCKEY_HOME}/synckey.db` | SQLite database path |
573
+ | Provider env vars | — | `GROQ_API_KEY`, `ANTHROPIC_API_KEY`, etc. |
574
+
575
+ ---
576
+
577
+ ## Configuration
578
+
579
+ Config file: `~/.synckey/config.toml`
580
+
581
+ ```toml
582
+ [gateway]
583
+ host = "127.0.0.1"
584
+ port = 8787
585
+ request_timeout = 120
586
+
587
+ [routing]
588
+ priority = ["groq", "gemini"]
589
+ tier_fallback = true
590
+ max_retries = 6
591
+ default_cooldown = 20
592
+ deep_cooling_threshold = 60
593
+
594
+ [deferred]
595
+ enabled = true
596
+ ttl = 3600
597
+ poll = 5
598
+ max_queue = 1000
599
+ ```
600
+
601
+ ---
602
+
603
+ ## Production Deployment
604
+
605
+ ### PM2 (Recommended for all platforms)
606
+
607
+ PM2 keeps synckey running 24/7, auto-restarts on crash, and survives server reboots.
608
+
609
+ ```bash
610
+ # Install PM2
611
+ npm install -g pm2
612
+
613
+ # Start synckey (runs in background, daemonized)
614
+ pm2 start examples/ecosystem.config.js
615
+
616
+ # Save current process list (persists across reboots)
617
+ pm2 save
618
+
619
+ # Generate startup script for your OS
620
+ pm2 startup
621
+
622
+ # Other useful commands
623
+ pm2 logs synckey # view logs
624
+ pm2 restart synckey # restart
625
+ pm2 stop synckey # stop
626
+ pm2 monit # real-time dashboard
627
+ ```
628
+
629
+ **ecosystem.config.js** (included in `examples/`):
630
+ ```javascript
631
+ module.exports = {
632
+ apps: [{
633
+ name: 'synckey',
634
+ script: 'python.exe',
635
+ args: '-m synckey serve',
636
+ cwd: 'C:\\path\\to\\synckey',
637
+ interpreter: 'none',
638
+ watch: false,
639
+ autorestart: true,
640
+ restart_delay: 10000,
641
+ max_restarts: 10,
642
+ min_uptime: 5000,
643
+ }]
644
+ };
645
+ ```
646
+
647
+ ### systemd (Linux/WSL)
648
+
649
+ For servers running systemd:
650
+
651
+ ```bash
652
+ # 1. Install synckey
653
+ pip install synckey
654
+
655
+ # 2. Create dedicated user (optional but recommended)
656
+ sudo useradd -r -s /bin/false synckey
657
+ sudo mkdir /var/lib/synckey /run/synckey
658
+ sudo chown synckey:synckey /var/lib/synckey /run/synckey
659
+
660
+ # 3. Install service file
661
+ sudo cp examples/synckey.service /etc/systemd/system/
662
+ sudo systemctl daemon-reload
663
+
664
+ # 4. Enable and start
665
+ sudo systemctl enable synckey
666
+ sudo systemctl start synckey
667
+ sudo systemctl status synckey
668
+ ```
669
+
670
+ **synckey.service** (included in `examples/`):
671
+ ```ini
672
+ [Unit]
673
+ Description=synckey unified AI gateway
674
+ Documentation=https://github.com/pmdnawaz1/synckey
675
+ After=network-online.target
676
+ Wants=network-online.target
677
+
678
+ [Service]
679
+ Type=simple
680
+ User=synckey
681
+ Group=synckey
682
+ WorkingDirectory=/opt/synckey
683
+ ExecStart=/usr/bin/python3 -m synckey serve
684
+ ExecReload=/bin/kill -HUP $MAINPID
685
+ Restart=on-failure
686
+ RestartSec=10
687
+ StandardOutput=journal
688
+ StandardError=journal
689
+ SyslogIdentifier=synckey
690
+
691
+ # Security hardening
692
+ NoNewPrivileges=true
693
+ PrivateTmp=true
694
+ ProtectSystem=strict
695
+ ProtectHome=true
696
+ ReadWritePaths=/var/lib/synckey /run/synckey
697
+ Environment=PYTHONUNBUFFERED=1
698
+
699
+ [Install]
700
+ WantedBy=multi-user.target
701
+ ```
702
+
703
+ ### Windows Service (NSSM)
704
+
705
+ ```bash
706
+ nssm install synckey "C:\path\to\python.exe" "-m synckey serve"
707
+ nssm set synckey AppDirectory "C:\path\to\synckey"
708
+ nssm set synckey Start SERVICE_AUTO_START
709
+ nssm start synckey
710
+ ```
711
+
712
+ ### Auto-Restart Behavior
713
+
714
+ | Event | Action |
715
+ |-------|--------|
716
+ | Server reboot | PM2/systemd auto-starts synckey |
717
+ | Crash (OOM, segfault) | Auto-restart after 10s delay |
718
+ | All keys cooling | synckey returns 202 + poll URL (no crash) |
719
+
720
+ ### Expose on Domain
721
+
722
+ ```bash
723
+ synckey serve --host 0.0.0.0 --port 8787
724
+ ```
725
+
726
+ **nginx:**
727
+ ```nginx
728
+ server {
729
+ listen 443 ssl;
730
+ server_name ai.example.com;
731
+ ssl_certificate /etc/ssl/certs/ai.example.com.crt;
732
+ ssl_certificate_key /etc/ssl/private/ai.example.com.key;
733
+
734
+ location / {
735
+ proxy_pass http://127.0.0.1:8787;
736
+ proxy_set_header Host $host;
737
+ proxy_buffering off;
738
+ proxy_read_timeout 300s;
739
+ }
740
+ }
741
+ ```
742
+
743
+ **Caddy:**
744
+ ```caddyfile
745
+ ai.example.com {
746
+ reverse_proxy localhost:8787 {
747
+ flush_interval -1
748
+ }
749
+ }
750
+ ```
751
+
752
+ ---
753
+
754
+ ## Security
755
+
756
+ - Provider secrets are **Fernet-encrypted** at rest
757
+ - Unified key stored as **SHA-256 hash** (not reversible)
758
+ - Gateway binds to `localhost` by default
759
+ - Secrets directory is `0700` permissions
760
+
761
+ ---
762
+
763
+ ## Troubleshooting
764
+
765
+ ### 401 Unauthorized
766
+ - Verify your unified key is correct: `synckey status`
767
+ - Regenerate if needed: `synckey renew`
768
+
769
+ ### Connection refused
770
+ - Ensure synckey is running: `synckey serve`
771
+ - Check if port is in use: `netstat -an | grep 8787`
772
+
773
+ ### All requests returning 202 queued
774
+ - Keys may be in cooldown: `synckey status`
775
+ - Check events: `synckey events`
776
+
777
+ ### Model not found (404)
778
+ - Model may not be in index: `synckey models --refresh`
779
+ - Provider may not have the model
780
+
781
+ ---
782
+
783
+ ## Compare
784
+
785
+ | Feature | synckey | LiteLLM | Portkey | Direct |
786
+ |---------|---------|---------|---------|--------|
787
+ | One API key | ✅ | ✅ | ✅ | ❌ |
788
+ | Auto-failover | ✅ | ✅ | ✅ | ❌ |
789
+ | Tier fallback | ✅ | ❌ | ❌ | ❌ |
790
+ | Deferred queue | ✅ | ❌ | ❌ | ❌ |
791
+ | No DB needed | ✅ | ❌ | ❌ | ✅ |
792
+ | Local gateway | ✅ | ❌ | ❌ | ✅ |
793
+ | Zero config | ✅ | ❌ | ❌ | ✅ |
794
+
795
+ ---
796
+
797
+ ## License
798
+
799
+ MIT