gateway/ is a separate module that exposes skyl over HTTP: one REST + SSE
surface that fans out to any configured provider. Use it when non-Go services
need model access, or when you want API keys held in exactly one place.
Calling it from another language#
The gateway speaks ordinary JSON over HTTP, so there is no SDK to install in any language — the wire format is the interface.
curl -sS "$SKYL_URL/v1/chat" \
-H "Authorization: Bearer $SKYL_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"model":"gpt-5.6","max_tokens":256,
"messages":[{"role":"user","text":"Hello"}]}'curl -sS "$SKYL_URL/v1/chat" \
-H "Authorization: Bearer $SKYL_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"model":"gpt-5.6","max_tokens":256,
"messages":[{"role":"user","text":"Hello"}]}'import httpx
r = httpx.post(f"{BASE}/v1/chat", headers=HEADERS, timeout=120.0, json={
"model": "gpt-5.6",
"max_tokens": 256,
"messages": [{"role": "user", "text": "Hello"}],
})
print(r.json()["text"])import httpx
r = httpx.post(f"{BASE}/v1/chat", headers=HEADERS, timeout=120.0, json={
"model": "gpt-5.6",
"max_tokens": 256,
"messages": [{"role": "user", "text": "Hello"}],
})
print(r.json()["text"])const res = await fetch(`${BASE}/v1/chat`, {
method: 'POST',
headers: HEADERS,
body: JSON.stringify({
model: 'gpt-5.6',
max_tokens: 256,
messages: [{ role: 'user', text: 'Hello' }],
}),
});
console.log((await res.json()).text);const res = await fetch(`${BASE}/v1/chat`, {
method: 'POST',
headers: HEADERS,
body: JSON.stringify({
model: 'gpt-5.6',
max_tokens: 256,
messages: [{ role: 'user', text: 'Hello' }],
}),
});
console.log((await res.json()).text);Complete clients — streaming, the tool-calling loop and error handling — are on Calling the gateway from another language.
It is a separate module#
go get github.com/BAGOMBEKA-JOB-DEV/skyl/gatewaygithub.com/BAGOMBEKA-JOB-DEV/skyl ← core library, zero dependencies
github.com/BAGOMBEKA-JOB-DEV/skyl/gateway ← this, own go.mod, uses chigithub.com/BAGOMBEKA-JOB-DEV/skyl ← core library, zero dependencies
github.com/BAGOMBEKA-JOB-DEV/skyl/gateway ← this, own go.mod, uses chigo get on the core library never pulls in chi. That is the whole point of
the split, recorded in
ADR-0003.
The core library is an HTTP client; chi routes inbound requests. They solve
opposite problems, so putting chi in the core module would tax every library
user with a router they never call. It requires Go 1.25+, inherited from the
OpenTelemetry SDK by way of skyl/otel.
When you want it#
- Non-Go services need models. A Python worker and a TypeScript frontend can both call one endpoint instead of each integrating four vendor SDKs.
- Keys live in one place. Application code holds a gateway token, not provider credentials. Rotation happens once.
- One audited egress point. Every model call in the estate flows through a single service you can log, meter and rate-limit.
- Swap providers without redeploying callers. Change gateway config; clients do not move.
If you are a Go service calling a model, skip the gateway and import the library — an extra network hop buys you nothing.
Endpoints#
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET | /healthz | None | Liveness. Returns 200 as long as the process is running. |
GET | /readyz | None | Readiness. Distinct from liveness: it reports whether the server is accepting traffic, so a draining instance can be pulled from a load balancer before it stops. |
GET | /metrics | None | Prometheus metrics. Served only when SKYL_METRICS is enabled. |
GET | /v1/providers | Bearer | The names of every registered provider, so a client can discover what it may ask for. |
GET | /v1/models | Bearer | The live model list from a provider, queried upstream rather than served from a compiled-in table. Takes ?provider=NAME. |
POST | /v1/chat | Bearer | A completion. The response carries the assistant turn in the same shape a request takes. |
POST | /v1/chat/stream | Bearer | A completion, streamed as Server-Sent Events. Flushed per event, with keep-alive frames, and the upstream request is cancelled as soon as the client disconnects. |
Security posture#
The gateway proxies paid APIs, so the failure mode of a misconfiguration is someone else spending your money.
- Auth is mandatory. No
SKYL_AUTH_TOKEN, no start. There is no flag to disable it. - Tokens are compared with
subtle.ConstantTimeCompare. - Provider keys are never logged and never returned in an error body.
- Run it on a private network. It is an internal service.
Full detail: Security and deployment.