TRENDING
A mountain gorilla looks at the camera from green forest undergrowth
October 29, 2026
How to Compress Time-Series Metrics in Python With Gorilla Delta-of-Delta and XOR Encoding
A bronze-framed hourglass with white sand beside a small Ferris wheel ornament on a wooden table against a teal wall
October 29, 2026
NIST’s Final Post-Quantum Transition Report Puts Every Quantum-Vulnerable Public-Key Scheme on One 2030 and 2035 Clock, and Three Federal Documents Word the Deadline Three Ways
Six weathered stone columns of the Temple of Apollo at Delphi, Greece, standing on a stone platform above a valley of olive groves
October 29, 2026
Honeypots Recorded Probes Against Oracle E-Business Suite CVE-2026-101767 Seven Days After Its Patch, and the Finder’s Write-Up Is Dated Patch Day
Vintage silver stopwatch with a chain on a black background, its dial showing seconds from 5 to 60
October 29, 2026
How to Find a Slow Query in PostgreSQL 18 With pg_stat_statements, Read Its EXPLAIN ANALYZE Plan, and Fix It With the Right Index
A wooden Tower of Hanoi puzzle on a market table, with three pegs and stacked discs of different sizes
October 29, 2026
Two Labs’ Claimed Refutations of the Exponential Time Hypothesis Turn Verification Into a Statement-Reading Problem, and Their Lean Theorems Promise Only That a Constant Exists
29 Oct 2026
SXZ.io SXZ.io
  • Home
Search the Site
Popular Searches:
Technology Amazon AI
Recent Posts
A flock of snow geese in flight, seen as dark silhouettes against an orange and red sunset sky with layered clouds
How to Run Zero-Downtime Schema Migrations on PostgreSQL 18 and Block Unsafe ALTER TABLE Statements With a Python Lint Gate
October 28, 2026
An old green steel truss bridge resting on temporary supports beside a new arch bridge under completion on a river in Portland, Oregon
The Angular 2 Rewrite Turns a Migration Lesson Into a Security Bill: AngularJS Still Draws 533,480 Downloads a Week, and 10 Advisories Have No Open-Source Fix
October 28, 2026
Hundreds of yellow rubber ducks floating in a river enclosure during a duck race
WordPress 7.1.4 Closes an Unauthenticated Race in Its Own Update Routine, but 19 Older Branches Had No Backport Hours After Release
October 28, 2026
SXZ.io SXZ.io
  • Home

Categories

Articles 270 Posts
News 273 Posts
Learning Hub 243 Posts
Home/Learning Hub/How to Configure Token Quotas and Model Access Policies for MaaS on OpenShift AI
Learning Hub

How to Configure Token Quotas and Model Access Policies for MaaS on OpenShift AI

A beginner's guide to Red Hat OpenShift AI's MaaS governance: how to configure a MaaSSubscription for token quotas and a MaaSAuthPolicy for model access control, step by step.

July 21, 2026 9 Min Read
78

Getting a large language model running on a cluster is only the first half of turning it into a shared service. The second half is governance: deciding who is allowed to call the model at all, and how much of it they can use before one noisy team burns through the capacity everyone else needs. Red Hat OpenShift AI’s Models-as-a-Service (MaaS) capability handles that second half with two purpose-built Kubernetes custom resources, MaaSSubscription for token quotas and MaaSAuthPolicy for access control. This tutorial teaches you how both work and how to configure them yourself, field by field, on a cluster that already has MaaS installed.

Table Of Content

  • Prerequisites
  • Understanding the two gates: subscriptions and policies
  • Step 1: Confirm your MaaS installation and find a model to govern
  • Step 2: Create a MaaSSubscription to set a token quota
  • What the fields mean
  • Write the manifest
  • Apply it and check the result
  • Step 3: Create a MaaSAuthPolicy to control access
  • What the fields mean
  • Write the manifest
  • Apply it and check the result
  • Step 4: Understand what happens at request time
  • Verify everything end to end
  • What’s next

If you already followed our earlier MaaS API Keys guide, you have the other piece of this picture: how a user or service gets a credential in the first place. This guide picks up right after that, and covers what happens once someone actually presents that credential at the gateway: how much are they allowed to use, and which models can they reach.

Every field, type, and validation rule shown below comes directly from the opendatahub-io/models-as-a-service project on GitHub, the open source upstream that Red Hat OpenShift AI ships this feature from, cross-checked against Red Hat’s own MaaS governance announcement and the Kuadrant project’s own documentation for the policy engine MaaS builds on. Where this guide describes cluster behavior rather than quoting the source directly, that is called out explicitly.

Prerequisites

  • A Red Hat OpenShift cluster on version 4.19.9 or later, the minimum the MaaS project has tested against. Gateway API, the traffic-routing layer MaaS depends on, reached general availability in the OpenShift 4.19 release line.
  • Red Hat OpenShift AI 3.3 self-managed (or the upstream Open Data Hub operator) installed, with the aigateway and modelsAsAService components set to Managed in your cluster’s DataScienceCluster resource. If MaaS itself is not installed yet, the project’s own MaaS setup guide walks through the gateway, database, and controller installation this tutorial assumes is already done.
  • At least one model already deployed and registered with MaaS as a MaaSModelRef, showing status.phase: Ready. Step 1 below shows you how to check this; if nothing is Ready yet, deploy one of the project’s sample models first.
  • The oc CLI (or plain kubectl, since these are ordinary Kubernetes custom resources) installed and logged in with permission to create resources in the namespace where your models live.
  • Comfort reading and applying basic Kubernetes YAML. No prior knowledge of MaaS, Kuadrant, or Gateway API is assumed; every term is explained the first time it comes up.

Understanding the two gates: subscriptions and policies

A Custom Resource Definition (CRD) is how Kubernetes lets a project add its own object types alongside built-ins like Pod or Service, so you manage them the exact same way, with YAML files and oc apply. MaaS adds several, but two are the governance layer this guide focuses on:

  • MaaSSubscription is the quota gate. It says how many tokens a given owner (one or more OpenShift users or groups) can consume against specific models within a time window.
  • MaaSAuthPolicy is the access gate. It says which models a given set of subjects (again, users or groups) are allowed to reach at all, independent of how much quota they have.

Red Hat’s own announcement of this feature puts the relationship between the two plainly: “Both must pass before the request reaches the inference server.” They are deliberately separate resources rather than one combined object, because that lets them evolve independently. One team can share a generous token quota while still being restricted to a narrower set of models than another team with a smaller quota; changing who is allowed to reach a model does not require touching that team’s rate limits at all, and vice versa.

Neither resource enforces anything itself. Both are handled by the maas-controller, which generates and manages underlying Kuadrant policies at the Gateway API layer: a MaaSAuthPolicy compiles down to a Kuadrant AuthPolicy enforced by its Authorino component, and a MaaSSubscription‘s token limits feed Kuadrant’s Limitador rate-limiting component. You will see both of those names again in Step 4, when we look at what actually happens to a request that fails one of these checks.

Step 1: Confirm your MaaS installation and find a model to govern

You cannot sensibly set a quota or an access policy for a model that either does not exist yet or has not finished starting up. This step confirms both the current state and the exact name and namespace values you will reference in the next two steps, since those have to match exactly.

# Context: OpenShift AI 3.3 with MaaS installed, oc logged in, models deployed in the "llm" namespace (the default used by the project's own sample deployments).
# Purpose: list every registered model and its readiness in one line each.
oc get maasmodelref -n llm -o jsonpath='{range .items[*]}{.metadata.name}: phase={.status.phase} endpoint={.status.endpoint}{"\n"}{end}'

Expected output is one line per model, for example:

llama-3-8b-instruct: phase=Ready endpoint=https://llama-3-8b-instruct.apps.example.com

Explanation: the jsonpath expression loops over every MaaSModelRef object in the namespace and prints just its name alongside two status fields: phase, the model’s current lifecycle state, and endpoint, the URL the gateway resolved for it. This is the same status shape the upstream install docs use to confirm a model is ready to serve traffic.

Common mistake: if phase shows anything other than Ready (commonly Pending while the underlying inference service is still starting), stop here and resolve that first. A subscription or auth policy that references a model before it is Ready will be accepted by the API server, since nothing here checks for that at creation time, but it will not do anything useful until the model itself catches up. Note the exact name and the namespace you queried (llm in the example above); you will reference both by name in the next two steps, and a typo here is silent, since Kubernetes will happily let you create a subscription or policy that points at a model name that does not exist.

Step 2: Create a MaaSSubscription to set a token quota

What the fields mean

A MaaSSubscription‘s spec has four parts, taken directly from the project’s Go type definitions:

  • owner: who this subscription belongs to, as a list of users (Kubernetes/OpenShift user names) and/or groups.
  • modelRefs: which models this subscription covers, each with its own name, namespace, and one or more tokenRateLimits entries.
  • tokenMetadata (optional): an organizationId, costCenter, and free-form labels you can attach for billing and usage attribution later.
  • priority: an integer, defaulting to 0, used to decide which subscription wins when a single user matches more than one. Higher numbers win.

Each entry in tokenRateLimits has exactly two fields, and both carry real validation rules enforced by the Kubernetes API server itself, not just documented conventions: limit must be an integer between 1 and 1,000,000,000, and window must match the pattern ^[1-9]\d{0,3}(s|m|h)$, meaning a number from 1 to 9999 immediately followed by s, m, or h. There is deliberately no d for days; express a daily limit as 24h instead.

Write the manifest

# team-alpha-quota.yaml
# Grants users alice and bob a shared 500,000-token budget per 24 hours
# against the llama-3-8b-instruct model confirmed Ready in Step 1.
apiVersion: maas.opendatahub.io/v1alpha1
kind: MaaSSubscription
metadata:
  name: team-alpha-quota
  namespace: llm
spec:
  owner:
    users:
      - alice
      - bob
  modelRefs:
    - name: llama-3-8b-instruct
      namespace: llm
      tokenRateLimits:
        - limit: 500000
          window: 24h
  tokenMetadata:
    organizationId: team-alpha
    costCenter: cc-4471
  priority: 10

Adjust metadata.namespace, the users list, and modelRefs[].name/namespace to match your own cluster and the model you confirmed in Step 1. The tokenMetadata block is optional; include it if you plan to feed usage back into a billing or cost-attribution system later, since those two fields exist specifically for that.

Apply it and check the result

# Context: manifest saved as team-alpha-quota.yaml.
# Purpose: create the subscription and confirm the API server accepted it.
oc apply -f team-alpha-quota.yaml

Expected output: maassubscription.maas.opendatahub.io/team-alpha-quota created.

# Context: right after creation.
# Purpose: check the subscription's own reconciliation status.
oc get maassubscription team-alpha-quota -n llm -o jsonpath='{.status.phase}{"\n"}'

Explanation: the MaaSSubscription‘s status also tracks modelRefStatuses and tokenRateLimitStatuses, reporting whether each referenced model and each generated rate limit actually reconciled. If phase stays anything other than Ready, inspect the full object with oc get maassubscription team-alpha-quota -n llm -o yaml and check those two status fields for which specific reference failed, most commonly a modelRefs[].name or namespace that does not match an existing MaaSModelRef.

Common mistakes: writing window: 24d instead of 24h will be rejected outright by the API server before the object is even stored, since the pattern is enforced at the OpenAPI schema level, not by the controller after the fact; expect an error citing the field path and the regular expression it failed. The other frequent mistake is pointing modelRefs[].namespace at the subscription’s own namespace out of habit, when the model actually lives somewhere else; the two namespaces do not have to match, and the API server will not warn you if they do not, since a cross-namespace reference is a normal, supported case.

Step 3: Create a MaaSAuthPolicy to control access

What the fields mean

A MaaSAuthPolicy‘s spec, from the same source file, has three parts:

  • modelRefs: the same shape as before, but here it just names which models (by name and namespace) this policy grants access to. At least one is required.
  • subjects: who gets that access, as groups and/or users. A cluster-enforced validation rule requires at least one of the two to be non-empty; you cannot create a policy that grants access to nobody.
  • meteringMetadata (optional): the same kind of billing and tracking fields as a subscription’s tokenMetadata, kept as a separate type since access policies and quotas are metered independently.

Write the manifest

# team-alpha-access.yaml
# Grants users alice and bob access to llama-3-8b-instruct.
# Access and quota are independent: this does not set how much they can use, only that they may reach it at all.
apiVersion: maas.opendatahub.io/v1alpha1
kind: MaaSAuthPolicy
metadata:
  name: team-alpha-access
  namespace: llm
spec:
  modelRefs:
    - name: llama-3-8b-instruct
      namespace: llm
  subjects:
    users:
      - alice
      - bob
  meteringMetadata:
    organizationId: team-alpha
    costCenter: cc-4471

Apply it and check the result

# Context: manifest saved as team-alpha-access.yaml.
# Purpose: create the policy and inspect what it generated underneath.
oc apply -f team-alpha-access.yaml
oc get maasauthpolicy team-alpha-access -n llm -o yaml

Explanation: look at the object’s status block for three things: phase (should reach Ready), conditions (the usual Kubernetes condition list showing the reconciliation history), and authPolicies, a field unique to this resource that lists the underlying Kuadrant AuthPolicy objects the controller actually created to enforce this. That last field is your first stop for troubleshooting, since it tells you the real Kuadrant object name to inspect if access still is not behaving the way you expect.

Common mistakes: leaving both subjects.groups and subjects.users empty does not create a policy that denies everyone, it fails validation and is rejected immediately, since the CRD’s validation rule requires size(self.groups) > 0 || size(self.users) > 0. If you intend to deny access by default, do that by simply not creating a policy for those subjects at all, rather than trying to create an empty one. The second common mistake is assuming a MaaSAuthPolicy alone is enough; it is not, since it only answers “can this subject reach this model,” never “how much.” Without a matching MaaSSubscription, a user with access but no subscription has nothing defining their quota.

Step 4: Understand what happens at request time

With both resources in place, a request arriving at the MaaS gateway for a governed model is checked twice, in order, before it ever reaches the model itself:

  1. Authorino evaluates the compiled MaaSAuthPolicy first. If the caller’s identity is not covered by any policy’s subjects for the model being requested, Kuadrant’s documented behavior is to return HTTP 403 Forbidden, and the request never reaches Limitador or the model.
  2. Only if that check passes does Limitador evaluate the token rate limit derived from the matching MaaSSubscription. If the caller has already exhausted their tokenRateLimits budget for the current window, Kuadrant’s documented behavior is to return HTTP 429 Too Many Requests.

Both of those status codes are Kuadrant’s own documented behavior for AuthPolicy and RateLimitPolicy enforcement respectively, not something specific to MaaS. That is by design: MaaS is a thin, purpose-built layer that generates standard Kuadrant policies from a simpler, AI-specific schema, rather than reimplementing gateway enforcement itself. If you are debugging an unexpected 403 or 429, the underlying Kuadrant objects referenced in each resource’s status (shown in Step 3) are where to look next.

Verify everything end to end

Before considering this done, check all three resources together, since a working setup means all three agree:

# Context: MaaSSubscription and MaaSAuthPolicy both applied.
# Purpose: confirm the model, the quota, and the access policy are all Ready in one pass.
oc get maasmodelref llama-3-8b-instruct -n llm -o jsonpath='{.status.phase}{"\n"}'
oc get maassubscription team-alpha-quota -n llm -o jsonpath='{.status.phase}{"\n"}'
oc get maasauthpolicy team-alpha-access -n llm -o jsonpath='{.status.phase}{"\n"}'

Expected output: Ready three times. If any one of the three is not, that resource’s own status.conditions (visible with -o yaml in place of the jsonpath query) will name the specific reason, whether that is a missing model reference, a rejected field, or something still reconciling. Only once all three agree should you expect a real request from alice or bob to reach llama-3-8b-instruct, and only up to the 500,000-token, 24-hour budget the subscription defined.

What’s next

From here, a few natural directions to keep exploring: the project’s multi-tenant setup guide covers isolating separate business units with their own gateways and AITenant resources instead of sharing one; our earlier MaaS API Keys guide covers the credential side these policies check against; and Kuadrant’s own rate-limiting documentation is worth a read if you want to understand the policy engine underneath MaaS well enough to debug it directly, rather than only through the simplified MaaSSubscription and MaaSAuthPolicy layer this guide taught.

Tags:

AI GovernanceKuadrantKubernetesModels-as-a-ServiceOpenShift AI

Share

Rows of white server racks in a data center corridor, illustrative of the rack-scale infrastructure behind Nvidia's Vera Rubin platform
Previous Post

Nvidia’s Vera Rubin Platform Turns AI Infrastructure Into a Tokens-Per-Watt Contest

Close-up of dark data center server racks with blue status lights, representing the kind of production infrastructure involved in the OpenAI and Hugging Face security incident
Next Post

OpenAI’s Models Escaped Their Test Sandbox and Breached Hugging Face

No Comment! Be the first one.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Latest
28 Oct
How to Run Zero-Downtime Schema Migrations on PostgreSQL 18 and Block Unsafe ALTER TABLE Statements With a Python Lint Gate
28 Oct
The Angular 2 Rewrite Turns a Migration Lesson Into a Security Bill: AngularJS Still Draws 533,480 Downloads a Week, and 10 Advisories Have No Open-Source Fix
Trending
October 28, 2026
How to Run Zero-Downtime Schema Migrations on PostgreSQL 18 and Block Unsafe ALTER TABLE Statements With a Python Lint Gate
October 28, 2026
The Angular 2 Rewrite Turns a Migration Lesson Into a Security Bill: AngularJS Still Draws 533,480 Downloads a Week, and 10 Advisories Have No Open-Source Fix
October 28, 2026
WordPress 7.1.4 Closes an Unauthenticated Race in Its Own Update Routine, but 19 Older Branches Had No Backport Hours After Release
October 28, 2026
How to Compress Time-Series Metrics in Python With Gorilla Delta-of-Delta and XOR Encoding
October 28, 2026
NIST’s Final Post-Quantum Transition Report Puts Every Quantum-Vulnerable Public-Key Scheme on One 2030 and 2035 Clock, and Three Federal Documents Word the Deadline Three Ways
October 28, 2026
Honeypots Recorded Probes Against Oracle E-Business Suite CVE-2026-101767 Seven Days After Its Patch, and the Finder’s Write-Up Is Dated Patch Day

Related Posts

A laptop wrapped in a chain and padlock, illustrating least-privilege controls for AI agents.
Learning Hub

How to Secure Tool-Using AI Agents Before They Touch Production

June 8, 2026
Colorful sticky notes arranged on an office wall, symbolizing governance checklists and planning.
Learning Hub

AI Governance for Agentic Apps: A Practical Checklist for Builders

June 8, 2026
A technician connects green fiber optic cables at a data center, representing a private production inference endpoint.
Learning Hub

How to Deploy a Fine-Tuned LLM Behind a Private Production Inference Endpoint

June 8, 2026
Narrow aisle behind black supercomputer racks in a data center
Learning Hub

Kubernetes SELinux Volume Labeling: What Cluster Operators Should Audit Before v1.37

June 8, 2026
SXZ.io SXZ.io
  • [email protected]

Categories

Articles
Learning Hub
News

All Rights Reserved by SXZ.io ©2026