Report vulnerabilities privately through GitHub's private vulnerability reporting on the
wingsmith-dev/wingsmith repository
(the Security tab, then Report a vulnerability). Please do not open a public issue, pull
request or discussion for a security problem.
Include what you can of: the component and commit, the configuration involved, steps to reproduce, and what an attacker gains. A maintainer will acknowledge the report, keep you updated in the advisory thread, and credit you in the advisory unless you ask not to be.
Wingsmith is pre-release. There are no tagged releases yet, so only the main branch receives
security fixes. Once releases exist, this section will say which ones are supported.
| Component | Runs | Trusts |
|---|---|---|
plane api |
central, behind your ingress | callers presenting a valid admin session, tenant credential or gateway service token |
plane hub |
central, behind your ingress | operators presenting a per-cluster enrolment token |
plane reactor |
central, no listener | the database |
| Postgres | central | the plane processes only; it holds all declared state and every credential hash |
gateway |
central, in the request path | see "Gateway listeners" below |
operator, agent |
one each per cluster | the plane and gateway they are configured to dial |
Clusters only dial out: the operator to the hub over a WebSocket, the agent to the gateway over a TLS tunnel. Neither the plane nor the gateway ever opens a connection into a cluster or holds a kubeconfig.
The plane resolves every request to one of four principal kinds. There is no roles or scopes system beyond these.
- admin: the single human operator of the installation, using the console or
wings. Signs in with a password (POST /v1/auth/login) and receives a short-lived signed session token plus a server-side refresh token. Wingsmith assumes one operator, or a small team sharing that role; it has no per-user accounts, SSO or audit trail by person. Admin auth is off only underWINGSMITH_APP_ENV=local, for a laptop, where every HTTP request is an admin. Outsidelocalit is always on, and the plane refuses to start withWINGSMITH_ADMIN_AUTH=disabled. - tenant: a tenant's own console credential, giving a read-only view of what that tenant is served and nothing else.
- operator: a cluster's operator, authenticated by that cluster's enrolment token. Each token is
minted per cluster (
POST /v1/clusters/{ref}/enrolment-token) and binds the session to that one cluster: an operator can only write observed state for the cluster its token names. Operator tokens are accepted by the hub only and refused on the HTTP API. There is no shared fleet-wide operator token. - gateway: a service principal for the gateway, configured as
WINGSMITH_GATEWAY_SERVICE_TOKENon both the plane and the gateway. It is accepted on exactly three routes,GET /v1/gateway/tenants,GET /v1/endpoints/resolvedandPOST /v1/clusters/tunnel-auth, and has no admin rights. Outsidelocal, an unset token means no gateway principal resolves at all.
Outside WINGSMITH_APP_ENV=local, the plane refuses to start without the session signing secret and
the admin password hash, so a misconfigured deployment fails closed.
The gateway decides trust by which listener accepted a connection (ADR 0016):
- The internal listener has no authentication by design. Reaching it is the proof of trust: it resolves no tenant and checks no key, so a caller there can reach every enabled tenant's models, including tenants that require an API key on the public listener. It must never be exposed outside your own network. Run it as a ClusterIP Service with no ingress, and restrict which pods can reach it with a NetworkPolicy.
- The public listener (off unless
WINGSMITH_GATEWAY_PUBLIC_LISTENis set) identifies the tenant from theHostheader and, for tenants that declareauth: api_key, requires one of that tenant's keys. A tenant that declaresauth: nonegets401there unlessWINGSMITH_GATEWAY_PUBLIC_ALLOW_NO_AUTH=true. Another tenant's model reads as404. - The tunnel listener accepts agent dials. Each agent presents its cluster's enrolment token, which the gateway verifies with the plane before carrying any traffic.
The gateway does not keep request or response bodies in its logs. See
libs/gateway/README.md for what it records.
These are known and documented but not yet fixed. Take them into account when you deploy.
- The metrics
clusterlabel is not authenticated. Clusters push metrics withremote_write, and a cluster holding valid write credentials can write series under anyclusterlabel, including the gateway's. Consumers must not treat that label as an authenticated claim (ADR 0012). In practice an enrolled cluster could distort another cluster's dashboards or the gateway figures a tenant's service report is built from. A label-enforcing write proxy is on the roadmap. - The gateway's internal listener needs a NetworkPolicy. Without one, any pod in the cluster that can reach the Service can send unauthenticated inference requests to every enabled tenant's models, whatever auth those tenants declare.
compose.ymlis for local development only. It runs withWINGSMITH_APP_ENV=local, which turns admin auth off and relaxes the credentials the plane requires at boot. Never expose it to a network.- Human access is a single admin role. Anyone with the admin password can do everything. Roles, scopes and SSO are on the roadmap, not built.
- One enrolment token serves three uses. A cluster's token authenticates its operator to the
hub, its agent to the gateway and, unless the chart is given other metrics credentials, its
metrics agent to
remote_write. Whoever reads it from any of those pods can open a hub session for that cluster. Per-purpose credentials are on the roadmap. - The console's refresh token is readable by page script. It is kept in session storage, so
script injected into the console could read it and mint sessions until it is revoked. An
HttpOnlycookie is on the roadmap. - The plane's
/metricsneeds no credential. It is served on the api port beside the API. Refuse it at the ingress and scrape the pods directly, as the deployment guide says.