Skip to content
skyl

The gateway

An optional HTTP service exposing skyl over the network.

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"}]}'

Complete clients — streaming, the tool-calling loop and error handling — are on Calling the gateway from another language.

It is a separate module#

Terminal
go get github.com/BAGOMBEKA-JOB-DEV/skyl/gateway
github.com/BAGOMBEKA-JOB-DEV/skyl          ← core library, zero dependencies
github.com/BAGOMBEKA-JOB-DEV/skyl/gateway  ← this, own go.mod, uses chi
github.com/BAGOMBEKA-JOB-DEV/skyl          ← core library, zero dependencies
github.com/BAGOMBEKA-JOB-DEV/skyl/gateway  ← this, own go.mod, uses chi

go 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#

MethodPathAuthPurpose
GET/healthzNoneLiveness. Returns 200 as long as the process is running.
GET/readyzNoneReadiness. 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/metricsNonePrometheus metrics. Served only when SKYL_METRICS is enabled.
GET/v1/providersBearerThe names of every registered provider, so a client can discover what it may ask for.
GET/v1/modelsBearerThe live model list from a provider, queried upstream rather than served from a compiled-in table. Takes ?provider=NAME.
POST/v1/chatBearerA completion. The response carries the assistant turn in the same shape a request takes.
POST/v1/chat/streamBearerA 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.

Pages#

Edit this page on GitHub