Skip to main content

Access policies

An access policy decides who may call a model's public HTTPS endpoint. The gateway on the model's cluster checks each caller's credential before the request reaches the model and refuses everything else, so a refused request never uses accelerator time. A policy holds one mechanism: API keys that AstroPulse keeps and you hand out one per caller, or JWTs from an OpenID Connect issuer you already run. It belongs to the organization and is reused by name across models and clusters.

Before you start​

In the console​

  1. Open Integrations > Access Policies. Under Policies, select API keys or Identity provider (OIDC).

  2. Enter the Policy name, for example partner-keys, and select Create. An identity-provider policy also needs its issuer and audiences: see Let your services call with tokens.

    Open zoom view

    Integrations > Access Policies with the New API-key policy form and the policy list

    Integrations > Access Policies with the New API-key policy form and the policy listIntegrations > Access Policies with the New API-key policy form and the policy list
  3. For an API-key policy, select Keys on its row and create one key per caller: Give callers API keys. For an identity-provider policy, select Test a token to check a real token: Test a token.

  4. Open Workloads > Models > Deploy model. On the Access step, pick Serve on <domain> under External access, then the policy under Who may call it.

    Open zoom view

    The Access step of Deploy model with a domain picked and an access policy under Who may call it

    The Access step of Deploy model with a domain picked and an access policy under Who may call itThe Access step of Deploy model with a domain picked and an access policy under Who may call it
  5. Select Review, then Deploy.

Access policies: how every call is checked

You should see​

The policy's row reads 1 application under Used by. On the model's Overview, the Endpoint section shows Ready with what callers must send, for example "Callers must send an API key from access policy qwen-keys.", the public URL, and the policy and its revision under Who may call it.

Open zoom view

A model's Endpoint section: Ready, the public URL, and Who may call it naming the policy at revision 1

A model's Endpoint section: Ready, the public URL, and Who may call it naming the policy at revision 1A model's Endpoint section: Ready, the public URL, and Who may call it naming the policy at revision 1

What a policy protects​

  • An exposed model must name a policy. externalAccess.accessPolicyRef is required on a model with externalAccess; there is no silent open default.
  • Serving with no authentication is an explicit choice. In the console it is Anyone (no authentication) under Who may call it; in a manifest, accessPolicyRef.none: UnauthenticatedPublicInference. It needs organization-owner authority and is recorded in the audit log.
  • A model that is not exposed has no public address, and an accessPolicyRef on it is refused.
MechanismYou bringThe caller sends
apiKeysNothing: AstroPulse keeps the keys and you issue one per callerAuthorization: Bearer <key>, or x-api-key: <key> if the policy sets header: x-api-key
jwtAn OpenID Connect issuer that publishes a JWKSAuthorization: Bearer <token>

A SAML or social identity provider reaches a model the way every API does: by issuing JWTs, which jwt validates.

API keys or JWT​

API keys (apiKeys)JWT (jwt)
Who callsA partner, a customer, a script: anyone you can hand a secret toYour own workloads that already have an identity: a pod's service account, a CI job, a service registered with your identity provider
SetupThe policy, then one key per callerAn issuer reachable over HTTPS, and a policy naming its issuer, audiences and optional require claims
What the caller holdsA long-lived key, valid until its expiresAt or until you revoke itA short-lived token from your issuer
RotationCreate a new key, hand it over, revoke the old oneYour issuer issues new tokens and rotates its signing keys; the gateway reads the JWKS
RevocationRevoke the key: every endpoint on the policy refuses it within about a minuteStop issuing. Tokens already issued work until exp, plus a minute of clock skew
Secrets AstroPulse storesThe keys, in its vault; each value is shown onceNone
Check a credential firstCall the endpointTest a token, or astroctl access-policy test

Use JWT for traffic between your own systems: nothing long-lived to leak, and rotation is your issuer's job. Use API keys for callers who cannot get a token from your issuer, and for a first test. AstroPulse issues no tokens and runs no identity provider; for jwt it only validates your issuer's tokens.

Revisions​

Every save creates a new, immutable revision. An application keeps the revision it was deployed with, so a change reaches a model on its next deploy, never silently. API keys are not part of a revision: creating or revoking one needs no new apply.

A policy an application enforces cannot be deleted: Delete is disabled with "In use: delete the applications that enforce it first". Deleting the policy removes every revision, and recreating the name starts again at revision 1.

Check the endpoint​

Terminal
astroctl app endpoint get qwen-chat
YAML
application: qwen-chat
cluster: prod-inference
policy: partner-keys
policyRevision: 1
desiredState: Open
phase: Ready
url: https://qwen-chat.example.com/v1
message: Callers must send an API key from access policy partner-keys. …
probe:
method: GET
path: /
vantage: Cluster
result: Refused
policy, policyRevision

The policy and the revision the endpoint enforces.

desiredState

Open or Closed: what AstroPulse wants.

phase

What the cluster last reported: Pending (checks running, route unpublished), Ready, Closed, or Error (a check failed and the route is withdrawn; message says which).

probe

The last negative sample: an anonymous and an invalid-credential GET / through the gateway, and whether both were refused with 401 or 403 (result: Refused). Evidence for one path at one moment.

url

Shown only while the endpoint is open and the report is fresh and Ready.

message

While Ready, what callers must send and how far the address was checked; on Pending and Error, why.

Terminal
curl -s -o /dev/null -w '%{http_code}\n' https://qwen-chat.example.com/v1/models # 401
curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $AP_KEY" https://qwen-chat.example.com/v1/models # 200

The gateway strips the credential before the request reaches the model, so a model's logs never hold a key or a token.

No key: the endpoint refuses (401)
Try it with a policy key: the model answers

When a call is refused​

The gateway answers a refused call with a status code and a short plain-text body that does not say which check failed.

The caller seesAPI-key policyJWT policyNext step
401No key in the policy's header, a wrong key, or a revoked or expired keyNo token, or a token whose signature or iss fails, or whose exp or nbf is more than a minute offCheck the key, or test the token
403Not usedAnother audience (aud), or a require claim missing, of another type, or holding another valueTest the token: reason is AudienceMismatch, MissingClaim, ClaimType or ClaimValue
No answer, or another statusThe endpoint is not servingThe endpoint is not servingRead the endpoint

API key refused​

  1. The header. astroctl access-policy get <policy> -o yaml shows spec.apiKeys.header. With Authorization, or none, send Authorization: Bearer <key>. With x-api-key, send x-api-key: <key> and nothing else.
  2. The key exists and is unexpired. astroctl access-policy key list <policy> must list its label, with EXPIRES empty or in the future. A revoked key is gone and cannot be restored: create a new one.
  3. A new key. It reaches the gateway within about a minute. Wait, then call again.
  4. The right policy. astroctl app endpoint get <app> shows policy. A key works only on models attached to its policy.

Token refused​

Save the exact token the caller sent and run astroctl access-policy test <policy> --token-file <file>, or paste it into Test a token. Each reason:

reason: Malformed
Cause
The token is not a JWT, or is signed with a shared secret (HS256).
Fix
Send the token itself, signed by your issuer with an asymmetric algorithm.
reason: UnknownKey
Cause
The token's kid is not in the policy's key set.
Fix
Compare the token's iss with issuer; if they match, check jwksUri is the issuer's current key set.
reason: BadSignature
Cause
The token was altered, or signed by another key.
Fix
Get a fresh token from the issuer.
reason: IssuerMismatch
Cause
iss differs from issuer.
Fix
Set issuer to the token's iss exactly, trailing slash included.
reason: AudienceMismatch
Cause
The token was minted for another service (403).
Fix
Mint it with an audience in audiences.
reason: Expired
Cause
exp passed, beyond a minute of clock skew.
Fix
Get a new token; in a pod, read the token file again on each call.
reason: NotYetValid
Cause
nbf is in the future.
Fix
Check the caller's and the issuer's clocks.
reason: MissingClaim
Cause
A require claim is absent (403).
Fix
Have the issuer include it, or change require.
reason: ClaimType
Cause
A required claim is not the type the policy reads it as (403). A requirement reads scope and scp as a list and any other claim as a string, unless its type says otherwise.
Fix
Set the requirement's type to String or List, the way your issuer always sends the claim.
reason: ClaimValue
Cause
The claim holds another value (403).
Fix
Call with the expected identity, or add the value to require[].values.
reason: KeySetUnusable
Cause
The key set cannot be used.
Fix
Fix jwksUri, or leave it empty to take it from discovery.
reason: ProviderUnreachable
Cause
AstroPulse could not reach the key set.
Fix
Check the issuer is up and public; the cluster's gateway needs the same reach.

Accepted, but the call still fails: the model may be bound to an older revision. Compare policyRevision from astroctl app endpoint get <app> with the latest from astroctl access-policy get <policy>, and redeploy the model to take the latest.

The endpoint is not serving​

astroctl app endpoint get <app> reads:

desiredState: Closed
Cause
An owner closed the endpoint.
Fix
Reopen endpoint on the model's Overview, or astroctl app endpoint reopen <app>.
phase: Pending
Cause
Certificate, gateway or policy checks are running.
Fix
Wait; message says what is pending.
phase: Error
Cause
A check failed and the route is withdrawn. On an API-key policy, the policy often has no key yet, or its last key was revoked or expired.
Fix
Fix what message names, then reopen. On an API-key policy with no live key, create a key.
probe.result: UnexpectedResponse
Cause
A request without a valid credential was answered, so the route was withdrawn.
Fix
Read probe.message and message.

Close the endpoint​

On the model's Overview, select Close endpoint and confirm "Close this endpoint?". Callers can no longer reach the model at its address; the model keeps running and its policy stays. Reopen endpoint re-validates the policy, its keys and TLS, then serves again.

Open zoom view

Closing a model's endpoint from its Overview

Closing a model's endpoint from its OverviewClosing a model's endpoint from its Overview

Closing works whatever state your issuer or key set is in, and it lasts: a redeploy or a rollback does not reopen a closed endpoint. Deleting the application retires the endpoint and releases the policy.

What is guaranteed, and what is not​

  • An unauthenticated request is refused at the gateway and never costs accelerator time.
  • The endpoint is published only after the certificate, the gateway, the policy and the credential source are accepted, and withdrawn when any of them fails.
  • A cluster AstroPulse cannot reach keeps enforcing its last configuration. Nothing opens because AstroPulse went away.
  • Not guaranteed: callers inside the cluster still reach the model's own Service directly; a revoked key or rotated signing key takes effect when the gateway next reads it, not instantly; a JWT stays valid until its exp; keys are plaintext at rest on the cluster, and with your own Secret anyone with Secret read in the model's namespace can read them.

Limits in this release​

Model applications only; one endpoint per application; the Gateway add-on as the enforcement point; no per-caller quotas, rate limits or usage records; no projection of a JWT claim into an upstream header. Attaching a policy to another workload type does not gate its callers today.

Other ways in​

  • Nova: ask it, for example "create an API-key policy for qwen-chat's partners".
  • SDK and API: the same operations, in the API reference.

With astroctl​

partner-keys.yaml
apiVersion: platform.astropulse.io/v1
kind: AccessPolicy
metadata:
name: partner-keys
spec:
apiKeys:
managed: true
Terminal
astroctl access-policy apply -f partner-keys.yaml
astroctl access-policy get partner-keys -o yaml
# delete takes the current revision; it asks you to type the name, --yes skips that
astroctl access-policy delete partner-keys --revision 1
astroctl app endpoint close qwen-chat # asks you to type the application's name
astroctl app endpoint reopen qwen-chat

Attach it in the model's manifest:

YAML
spec:
externalAccess:
domain: example.com
accessPolicyRef:
name: partner-keys # or: none: UnauthenticatedPublicInference

Every flag: astroctl access-policy.

Settings​

An AccessPolicy is a control-plane object, not a Kubernetes resource: the same object through astroctl access-policy apply, the API or the console form.

Policy namemetadata.nameRequired

The name applications reference.

Accepts
A DNS label of at most 63 characters: lowercase letters, digits and -, starting and ending alphanumeric.
API keysspec.apiKeys.managed

true: AstroPulse keeps the keys; issue them. Exactly one of managed or secretRef.

spec.apiKeys.secretRef.name

Last resort: an Opaque Secret you keep in the application's namespace. See Your own Secret.

Accepts
A DNS label of at most 63 characters.
spec.apiKeys.header

The header callers send the key in. Set only with astroctl or the API.

Accepts
Authorization or x-api-key.
Default
Authorization
Issuerspec.jwt.issuerRequired

Matches the token's iss exactly.

Accepts
An HTTPS URL of at most 253 characters, with no credentials, query or fragment.
JWKS URI (optional)spec.jwt.jwksUri

Where the gateway reads the issuer's signing keys. It must be reachable from the cluster.

Accepts
An HTTPS URL of at most 2048 characters, with no credentials, query or fragment.
Default
The jwks_uri of the issuer's discovery document.
Audiencesspec.jwt.audiencesRequired

A token whose aud holds any one of them passes.

Accepts
1 to 8 strings, each at most 512 characters.
Required claimsspec.jwt.require

Claims every token must carry. All entries must hold; within an entry, any one of values passes. A dotted claim reaches a nested claim; an absent claim denies.

Accepts
Up to 16 entries. claim: at most 253 characters. values: 1 to 16 strings, each at most 512 characters, matched exactly.
revision

On an update, the revision get returned. A mismatch is refused rather than overwriting someone else's edit.

Default
Omitted or 0: create.
Who may call itexternalAccess.accessPolicyRef.name

On a model: the policy the endpoint enforces. Exactly one of name or none.

Anyone (no authentication)externalAccess.accessPolicyRef.none

UnauthenticatedPublicInference, that exact string. Organization-owner authority, and audited with the hostname it opens.

externalAccess.accessPolicyRef.revision

The policy revision the deployment binds to. Kept through deployment and rollback.

Default
The latest revision at apply.

Read-only fields on a policy: updatedAt, references (the applications referencing it; non-empty blocks delete) and openEndpoints (the applications whose open endpoint it protects now).

If it's refused​

"Exposed model needs an access decision"
Cause
The model has externalAccess but no accessPolicyRef.
Fix
Name a policy under Who may call it (accessPolicyRef.name), choose Anyone (no authentication), or remove externalAccess to keep the model inside the cluster.
"access policy is in use: it is enforced on qwen-chat; delete those applications first"
Cause
An application still enforces the policy.
Fix
Delete the applications it names first. Pointing an application at another policy does not release it: its earlier revisions still name this one, for rollback.
"access policy partner-keys is at revision 2, not 1: read it again, then apply with revision: 2"
Cause
Someone changed the policy since you read it.
Fix
get it again and apply your change on the current revision. A delete with an old revision reads "policy revision changed; fetch the policy and retry".
"… Retry once the provider is live, or save with allowUnverified=true to skip the check"
Cause
422 IdentityProviderUnreachable: AstroPulse could not reach the issuer or its key set, or it is not on the public internet.
Fix
Retry once it is up, or select Save without checking (apply --allow-unverified) with JWKS URI set.
"Access policy partner-own-keys does not keep its API keys in AstroPulse. Apply it with apiKeys.managed: true to create keys here."
Cause
A key operation on a policy that uses secretRef.
Fix
Apply the policy with managed: true, or manage the Secret yourself.
"an access policy holds at most 32 API keys; revoke one first"
Cause
The policy already holds 32 keys.
Fix
Revoke a key you no longer hand out, then create the new one.
"The stored API keys of this access policy can't be read. Revoke them under Credentials, then create new keys."
Cause
The policy's stored keys cannot be read.
Fix
Revoke them, then create new keys and hand them over.
"AstroPulse credential storage cannot be reached right now; try again in a minute"
Cause
Credential storage is briefly unavailable.
Fix
Try again in a minute.

Next​