Skip to content

Authorization Scheme - #198

Draft
AryanAhadinia wants to merge 6 commits into
mainfrom
feat/model-authorization-rebased
Draft

Authorization Scheme#198
AryanAhadinia wants to merge 6 commits into
mainfrom
feat/model-authorization-rebased

Conversation

@AryanAhadinia

Copy link
Copy Markdown
Member

…mission

Companion to serving-api's label-based model authorization (ADR-0001). A launch now declares who the model is for, as an OpenTela peer label the gateway enforces:

sml ... --authorization public                        # anyone (default)
sml ... --authorization private                       # only the launcher
sml ... --authorization user1@epfl.ch,user2@ethz.ch   # only these users

"private" never reaches the mesh — the gateway cannot know who submitted a job, so the CLI resolves it to the launcher's own email via the Serving API's GET /v1/whoami before submission, and fails the launch rather than falling back to public if that call can't be made. LaunchArgs rejects an unresolved "private" as a guardrail for any path that forgets.

Values are canonicalized (strip, lowercase, dedupe) so two launches meaning the same policy emit the same label, and a served name whose replicas disagree on policy is refused by the gateway for everyone — so a launch that would collide with a running model under a different policy is refused up front instead of taking that model down with it.

Applied on every path that submits a launch: preconfigured, advanced, both loadtest launch paths, and the MCP server's launch tool.

@AryanAhadinia
AryanAhadinia force-pushed the feat/model-authorization-rebased branch from 9bcc4fd to 8e8950d Compare August 7, 2026 13:05
@robmsmt

robmsmt commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

I want to check we are not missing something from authentik here, I understand how you are implementing this but the auth isn't quite what we specified in the original requirements (won't link it here publicly)

but agree this is a useful feature

AryanAhadinia and others added 6 commits August 11, 2026 23:25
…mission

Companion to serving-api's label-based model authorization (ADR-0001). A
launch now declares who the model is for, as an OpenTela peer label the
gateway enforces:

    sml ... --authorization public                      # anyone (default)
    sml ... --authorization private                     # only the launcher
    sml ... --authorization user1@epfl.ch,user2@ethz.ch  # only these users

"private" never reaches the mesh — the gateway cannot know who submitted a
job, so the CLI resolves it to the launcher's own email via the Serving
API's GET /v1/whoami before submission, and fails the launch rather than
falling back to public if that call can't be made. LaunchArgs rejects an
unresolved "private" as a guardrail for any path that forgets.

Values are canonicalized (strip, lowercase, dedupe) so two launches meaning
the same policy emit the same label, and a served name whose replicas
disagree on policy is refused by the gateway for everyone — so a launch
that would collide with a running model under a different policy is refused
up front instead of taking that model down with it.

Applied on every path that submits a launch: `preconfigured`, `advanced`,
both `loadtest` launch paths, and the MCP server's launch tool.
SonarCloud's quality gate failed the PR on new-code coverage (65.7% vs the
80% threshold); every other condition passed. The gap was serving_api.py,
whose two HTTP calls had no tests at all, plus the prompt validator.

Covers whoami and served_model_authorizations against a stubbed httpx
client: success, 401, other non-2xx, transport error, timeout, and the
empty-payload cases. These decide who can reach a model, so the point of
most of them is that a failure surfaces as ServingApiError rather than a
silent empty answer a caller could read as "no restriction".

Also drives `sml advanced` end to end for the first time, asserting that
--authorization private becomes the launcher's email and that the conflict
check is asked about the namespaced served name.
Served names are <username>/<vendor>/<model>, but every example passed the
bare <vendor>/<model> and relied on SML prepending the account. Spell the
namespace out instead — 100 occurrences across 88 shell examples become
"$USER/<vendor>/<model>", and the four Python examples move from the old
"-<username>" suffix to a real namespace prefix.

SML still prepends a missing namespace, so existing user scripts keep
working; the examples just no longer depend on it.

$USER has to be the *cluster* account. It normally is, but running sml from
a laptop with a different local username now fails the namespace check
instead of silently publishing under the wrong name — documented in
usage-advanced, with the escape hatches (explicit name, or omit it).

Tests follow the same rule:
- the unit example test expands $USER the way a shell would before parsing,
  since shlex does not;
- the CLI example integration test runs each script with USER pinned to the
  account FirecREST reports, because a CI runner's $USER is not the cluster
  account, and asserts the launched name is namespaced under it;
- the launcher integration test now names the model explicitly rather than
  exercising the prepend path.

Also corrects served_name.py and the glossary, which claimed the gateway
cross-checks the namespace against launched_by. It does not — it only
requires three non-empty segments — so the check is ours and is a
collision guard, not an ownership guarantee.
Dropping the salt made a derived name a pure function of user + model, so
one user launching a model on sglang and on vllm advertised both under the
same id. OpenTela then load-balances across two different engines under one
name, and CI hits it head-on: the lightweight suite launches
Apertus-8B-Instruct-2509 on both frameworks, in parallel.

Names SML derives are now <username>/<vendor>/<model>-<framework>. Explicit
--served-model-name is untouched: if a user picks a name, it is theirs, even
if that means two frameworks share it. Two launches of one model on one
framework still share a name — those are replicas, which is the point.

derive_served_model_name is the single rule, used by preconfigured, the MCP
tool, and both launchers' fallback. The suffix keeps the id at three
segments, which is all the gateway will list.

The integration test names each launch explicitly and now includes the
topology too: the std matrix runs one model+framework in three
configurations concurrently, which the framework suffix alone would not
separate. All 15 CI launches get distinct names.
The previous runs failed in the FirecREST integration tests with cluster-side
errors on clariden (503 scheduler unhealthy, then 408 command timeout), not
with anything the branch changed. Re-running to see whether the cluster has
recovered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds an OpenTela peer label `sml_version`, so a running model on the mesh
says which SML produced it. That is the first thing worth knowing when a
launch misbehaves — whether it predates a fix — and until now it was only in
the telemetry payload, which the gateway never sees.

Resolved on the submitting machine and baked into the script as a literal:
SML is not installed on the compute node, so a $(...) would render empty,
and the version that matters is the one that built the launch.

The lookup moves into _sml_version(), shared with the telemetry payload so
the two cannot drift, and falls back to "unknown" rather than raising when
the package metadata is missing — an uninstalled source checkout should not
fail a launch over a provenance label.

No gateway change needed: /v1/models_detailed already passes peer labels
through verbatim, so the value shows up per model as labels.sml_version.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@AryanAhadinia
AryanAhadinia force-pushed the feat/model-authorization-rebased branch from 4416b3b to 7a9b948 Compare August 11, 2026 21:25
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants