Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,9 @@ The changes:
- Opt-in token authentication, with the gateway's variables:
`SIE_AUTH_MODE` (`none`, `token`, `auto`) and `SIE_AUTH_TOKENS` or
`SIE_AUTH_TOKEN`. A configured token turns it on. Clients send
`Authorization: Bearer <token>` or `x-api-key: <token>`; when a request
sends both, `Authorization` is used and `x-api-key` is ignored. `/healthz`,
`/livez` and `/readyz` stay open.
`Authorization: Bearer <token>` or `x-api-key: <token>`; when both
carry a token, `Authorization` is used and `x-api-key` is ignored.
`/healthz`, `/livez` and `/readyz` stay open.
- In token mode without a configured token, the server generates one and
saves it with mode `0600` in `SIE_DATA_DIR`. It prints the token once when
stderr is a terminal, and otherwise only the file's path. A container with
Expand All @@ -248,15 +248,18 @@ The changes:
- Rust/Candle worker: logs a notice when it binds `0.0.0.0` only because
`SIE_SERVER_HOST` is unset.
- Gateway:
- Accepts `x-api-key` alongside `Authorization`. When a request sends both,
`Authorization` is used and `x-api-key` is ignored, so a request that
passes today still passes, including one where a proxy in front of the
gateway adds an `x-api-key` of its own. Repeated `x-api-key` values that
carry different tokens get `401`.
- A repeated `Authorization` header still uses its first value. The gateway
logs a warning, once per process, the first time a request that passes
- Accepts `x-api-key` alongside `Authorization`. The first
`Authorization` value decides when it carries a token, and `x-api-key` is
then ignored, so a request that passes today still passes, including one
where a proxy in front of the gateway adds an `x-api-key` of its own.
When that value is empty or blank, `x-api-key` decides. An unreadable
first value is refused, and `Bearer` alone counts as the token `Bearer`.
Repeated `x-api-key` values that carry different tokens get `401`.
- Later `Authorization` values are still ignored. The gateway logs a
warning, once per process, the first time a request that passes
authentication repeats `Authorization` with values that carry different
tokens or with a value it cannot read. A refused request never logs it.
tokens, with a value it cannot read, or with a token only after a first
value that carries none. A refused request never logs it.
- `SIE_WORKER_AUTH_TOKEN` is the token the gateway presents to workers.
- Logs a warning when `SIE_AUTH_MODE` is unset on a non-loopback bind.
- Telemetry: `SIE_TELEMETRY_ENABLED=1` opts in explicitly. The default stays on,
Expand All @@ -273,7 +276,7 @@ The changes:
| `sie-server` beyond loopback | No authentication | A token is required. Without `SIE_AUTH_TOKEN`, one is generated, and printed once on a terminal | `SIE_AUTH_MODE=none` |
| `sie-server` on loopback without authentication | A request whose `Host` does not name this machine is answered, with a warning | It gets `400`. So does a request whose `Origin` names another site, at least for WebSocket upgrades and methods other than `GET` | Token authentication, or a setting for more allowed `Host` names, designed together with this change |
| Rust/Candle worker bind | `0.0.0.0` | `127.0.0.1` | `SIE_SERVER_HOST=0.0.0.0` (the image sets it) |
| Gateway in token mode, repeated `Authorization` values that carry different tokens | The first value is used, with a warning | `401` | Send one `Authorization` header |
| Gateway in token mode, a repeated `Authorization` header whose values carry different tokens, include one it cannot read, or carry a token only after the first value | The later values are ignored; a request that passes authentication logs a warning, once per process | `401` | Send one `Authorization` header |
| Gateway binary beyond loopback with `SIE_AUTH_MODE` unset | Starts without authentication | Refuses to start | `SIE_AUTH_MODE=none`, or `token` with `SIE_AUTH_TOKENS` |
| Telemetry | On unless disabled | Off unless `SIE_TELEMETRY_ENABLED=1` | `SIE_TELEMETRY_ENABLED=1` |
| Helm `gateway.auth.mode` | `none` | `token`, with a generated Secret (needs the chart change above) | `gateway.auth.mode=none` |
Expand Down
15 changes: 10 additions & 5 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ What a default install exposes, and how to close it.
with `SIE_AUTH_TOKEN` (or comma-separated `SIE_AUTH_TOKENS`). Without a
configured token, the server generates one and keeps it in `SIE_DATA_DIR`.
Clients send it as `Authorization: Bearer <token>` or `x-api-key: <token>`;
when both are sent, `Authorization` is used. `/healthz`, `/livez` and
when both carry a token, `Authorization` is used. `/healthz`, `/livez` and
`/readyz` stay open. Token authentication covers the HTTP port; the IPC
socket and the engines' ports listen on this machine only.
- Keep it on this machine with `--host 127.0.0.1`. In Docker, publish the port
Expand All @@ -64,10 +64,15 @@ What a default install exposes, and how to close it.
`SIE_AUTH_MODE=token` and `SIE_AUTH_TOKENS`. Mutations of pools, admin and
config routes also need `SIE_ADMIN_TOKEN`.
- Clients send a token as `Authorization: Bearer <token>` or
`x-api-key: <token>`. When both are sent, `Authorization` is used and
`x-api-key` is ignored. Until 0.13.0, a repeated `Authorization` header uses
its first value and logs a warning; from 0.13.0, repeated values that carry
different tokens are refused.
`x-api-key: <token>`. The first `Authorization` value decides when it
carries a token, and `x-api-key` is then ignored. When that value is empty
or blank, `x-api-key` decides. An unreadable first value is refused, and
`Bearer` alone counts as the token `Bearer`. Later `Authorization` values
are ignored. Until 0.13.0, a request that passes authentication with a
repeated `Authorization` header whose values carry different tokens, include
one the gateway cannot read, or carry a token only after the first value logs
a warning, once per process; a refused request never logs it. From 0.13.0
such a request is refused.
- `/healthz`, `/readyz`, `/openapi.json` and `/docs` are always reachable.
`/`, `/health` and `/ws/*` require a token unless
`SIE_AUTH_EXEMPT_OPERATIONAL=true`.
Expand Down
17 changes: 11 additions & 6 deletions deploy/helm/sie-cluster/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1563,12 +1563,17 @@ An Ingress or a `LoadBalancer`/`NodePort` gateway Service also needs TLS; see
With `gateway.auth.mode` set to `token` (or `static`, which the gateway treats
the same), the gateway requires a token on every request except its probes and
API description. Clients send it as `Authorization: Bearer <token>` or
`x-api-key: <token>`. When a request sends both, the gateway uses
`Authorization` and ignores `x-api-key`; until 0.13.0, a repeated
`Authorization` header uses its first value. SDK clients pass the token as
`api_key`/`apiKey`, or read it from `SIE_API_KEY` when their base URL comes
from `SIE_BASE_URL`. The tokens come from one Secret, generated or managed by
the operator:
`x-api-key: <token>`. The first `Authorization` value decides when it
carries a token, and the gateway then ignores `x-api-key`. When that value is
empty or blank, `x-api-key` decides. An unreadable first value is refused, and
`Bearer` alone counts as the token `Bearer`. Later `Authorization` values are
ignored. Until 0.13.0, a request that passes authentication with a repeated
`Authorization` header whose values carry different tokens, include one the
gateway cannot read, or carry a token only after the first value logs a
warning, once per process; a refused request never logs it. From 0.13.0 such a
request gets `401`. SDK clients pass the token as `api_key`/`apiKey`, or read
it from `SIE_API_KEY` when their base URL comes from `SIE_BASE_URL`. The tokens
come from one Secret, generated or managed by the operator:

- **Generated.** With `gateway.auth.tokenSecretName` empty and
`gateway.auth.generateToken=true`, the chart creates the Secret
Expand Down
2 changes: 1 addition & 1 deletion examples/coding-agent-claude-code/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export API_TIMEOUT_MS=1800000
`/v1/messages`.
- `ANTHROPIC_AUTH_TOKEN` arrives as `Authorization: Bearer ...`, and
Claude Code sends `ANTHROPIC_API_KEY` as `x-api-key`. The gateway and
`sie-server` read either one; when a request carries both, they use
`sie-server` read either one; when both carry a token, they use
`Authorization` and ignore `x-api-key`.
- The model id is sent byte for byte, slash and colon included. The four
model variables keep every request, background tasks and subagents
Expand Down
2 changes: 1 addition & 1 deletion packages/sie_gateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Each `--flag` above has a matching `SIE_*` environment variable (see next sectio
| `SIE_NATS_USER` | | NATS user (Helm sets `sie-gateway`). Set together with `SIE_NATS_PASSWORD`; setting only one fails startup |
| `SIE_NATS_PASSWORD` | | NATS password of `SIE_NATS_USER`. Redacted in `Config` debug output |
| `SIE_AUTH_MODE` | `none` | Auth mode for inbound requests: `none` disables, `token` (alias `static`) enforces. Values are case-sensitive. An unknown value, including `sie-server`'s `auto`, refuses every request with `500` except the probes and the API description (`/healthz`, `/readyz`, `/openapi.json`, `/docs`), and `main` logs a startup error naming the bad value. While it is unset on a non-loopback bind and the configuration is otherwise valid, `main` logs a warning: from 0.13.0 the gateway refuses to start that way |
| `SIE_AUTH_TOKENS` | | CSV of valid tokens for inference and pool/config read endpoints, presented as `Authorization: Bearer <token>` or `x-api-key: <token>` (when both are sent, `Authorization` is used and `x-api-key` is ignored). A repeated `Authorization` header uses its first value, as before, and logs a warning once; from 0.13.0 repeated values that carry different tokens get `401`. Repeated `x-api-key` values that carry different tokens get `401`. If unset, the singular `SIE_AUTH_TOKEN` is used as a fallback. When auth is enabled and this list is empty, non-probe requests return `500` |
| `SIE_AUTH_TOKENS` | | CSV of valid tokens for inference and pool/config read endpoints, presented as `Authorization: Bearer <token>` or `x-api-key: <token>` (the first `Authorization` value decides when it carries a token, and `x-api-key` is then ignored; when that value is empty or blank, `x-api-key` decides; an unreadable first value is refused, and `Bearer` alone counts as the token `Bearer`). Later `Authorization` values are ignored, as before. A request that passes authentication with a repeated `Authorization` header whose values carry different tokens, include one the gateway cannot read, or carry a token only after the first value logs a warning once per process (a refused request never logs it); from 0.13.0 such a request gets `401`. Repeated `x-api-key` values that carry different tokens get `401`. If unset, the singular `SIE_AUTH_TOKEN` is used as a fallback. When auth is enabled and this list is empty, non-probe requests return `500` |
| `SIE_AUTH_TOKEN` | | Singular alias for `SIE_AUTH_TOKENS` (fallback only; prefer the plural form) |
| `SIE_ADMIN_TOKEN` | | Admin bearer token the gateway requires inbound for admin-gated mutations: `POST/PUT/DELETE` on `/v1/configs/*`, `/v1/admin/*`, `/v1/pools/*`. If empty and an inbound request targets one of those paths, the middleware fails closed with `403`. Sent to `sie-config` only when `SIE_CONFIG_SERVICE_TOKEN` is unset (deprecated fallback) |
| `SIE_CONFIG_SERVICE_TOKEN` | | Bearer token the gateway presents **as a client** on its `sie-config` reads (`/v1/configs/bundles`, `/export`, `/epoch`): `sie-config`'s read-scoped `SIE_CONFIG_READ_TOKEN`. When set, it decides and a blank value sends no token; when unset, the gateway falls back to `SIE_ADMIN_TOKEN` and logs a deprecation warning |
Expand Down
2 changes: 1 addition & 1 deletion packages/sie_gateway/docs/architecture-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ Gateway (`sie-gateway`):
- `SIE_CONFIG_SERVICE_TOKEN` — bearer token the gateway-as-client presents on its `sie-config` reads (`GET /v1/configs/bundles`, `/bundles/{id}`, `/export`, `/epoch`). It should be `sie-config`'s read-scoped token (`SIE_CONFIG_READ_TOKEN` there), which the chart generates and always wires in (empty when the chart runs no `sie-config`). When the variable is set it decides, and a blank value sends no `Authorization` header (so `sie-config` must either be unauthenticated or the gateway will fail with `401`/`403`). When it is unset, the gateway falls back to `SIE_ADMIN_TOKEN` and, when `SIE_CONFIG_SERVICE_URL` is set, logs a deprecation warning at startup; it logs the same warning when the two are equal. `sie-config` accepts one read token at a time and every component reads it at start, so a rotation restarts `sie-config` first and then the gateways and worker sidecars; until they restart, they receive `403` on config reads.
- `SIE_ADMIN_TOKEN` — the token that `AuthLayer` requires for admin-gated mutations on the gateway itself (`POST/PUT/DELETE` on `/v1/configs/*`, `/v1/admin/*`, `/v1/pools/*`). If empty and the matching inbound request targets an admin path, the middleware fails closed with `403`. It is not sent to `sie-config` unless `SIE_CONFIG_SERVICE_TOKEN` is unset (the deprecated fallback above). The chart wires it only from `gateway.auth.adminTokenSecretName`, never from the `sie-config` admin token.
- `SIE_AUTH_TOKEN` / `SIE_AUTH_TOKENS` — tokens accepted by the gateway's own inference API (`/v1/encode`, `/v1/score`, `/v1/extract`) and for read-side pool/config routes. Not used for outbound calls to `sie-config`. When `SIE_AUTH_MODE` enables auth but this list is empty, every non-probe request returns `500`.
- `SIE_AUTH_MODE` — `token` (alias: `static`) enforces auth; `none` (default) disables it. A typo refuses every request with `500` except the probes and the API description (`/healthz`, `/readyz`, `/openapi.json`, `/docs`), and `audit_auth` logs a startup error naming the bad value so operators see it in `kubectl logs`. Requests present a token as `Authorization: Bearer` or `x-api-key`; when both are sent, `Authorization` is used and `x-api-key` is ignored. Only the first `Authorization` value is read; repeated values that disagree log a warning once and are refused from 0.13.0. Repeated `x-api-key` values that disagree are refused.
- `SIE_AUTH_MODE` — `token` (alias: `static`) enforces auth; `none` (default) disables it. A typo refuses every request with `500` except the probes and the API description (`/healthz`, `/readyz`, `/openapi.json`, `/docs`), and `audit_auth` logs a startup error naming the bad value so operators see it in `kubectl logs`. Requests present a token as `Authorization: Bearer` or `x-api-key`. The first `Authorization` value decides when it carries a token, and `x-api-key` is then ignored. When that value is empty or blank, `x-api-key` decides. An unreadable first value is refused, and `Bearer` alone counts as the token `Bearer`. Later `Authorization` values are ignored; a request that passes authentication with a repeated `Authorization` header whose values carry different tokens, include one that cannot be read, or carry a token only after the first value logs a warning once per process (a refused request never logs it), and such a request is refused from 0.13.0. Repeated `x-api-key` values that disagree are refused.
- `SIE_AUTH_EXEMPT_OPERATIONAL` — when `true`, `/`, `/health`, and `/ws/*` are exempt from auth (they expose worker URLs, bundle assignments, queue depth, GPU inventory — treat as sensitive). Default `false`. **`/healthz` and `/readyz` are always exempt from auth** so kubelet probes never fail with **`401`/`403`** because of a missing bearer token. `/readyz` reports only process readiness and, when `SIE_CONFIG_SERVICE_URL` is set, first-bootstrap completion; worker health remains visible through `/health` and inference responses.
- `SIE_NATS_CONFIG_TRUSTED_PRODUCERS` — comma-separated producer allowlist for `sie.config.models._all`. Binary default is `sie-config`; Helm sets the release-scoped config Deployment name. Matching is exact OR K8s pod-name prefix (see §4.3). Untrusted notifications are dropped; the `config_poller` still closes the gap.
- `SIE_NATS_CONFIG_TRUST_ANY_PRODUCER` — `true` disables producer validation entirely. Intended for local/dev. `main.rs` emits a startup warning when on.
Expand Down
2 changes: 1 addition & 1 deletion packages/sie_gateway/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -4688,7 +4688,7 @@
},
"securitySchemes": {
"apiKeyAuth": {
"description": "The same SIE token as bearerAuth, sent in the x-api-key header. When a request sends both, the Authorization header is used and x-api-key is ignored.",
"description": "The same SIE token as bearerAuth, sent in the x-api-key header. When the first Authorization value carries a token, that token is used and x-api-key is ignored.",
"in": "header",
"name": "x-api-key",
"type": "apiKey"
Expand Down
Loading
Loading