Skip to content

Share defaults and service budgets

Apply policy fallbacks once, override selected fields for a service, and distinguish keyless RPS from shared token accounting.

Lab sequence and validation scope

Start with lab preparation and common API conventions. These commands belong to a multi-page lab, not standalone mutations. The Quickstart provides the canonical mutation lifecycle. Read the evidence limits before recording a result.

Before you start

Finish quotas and save every default and service-budget scope that will be changed.

Focus of this page: Compare inherited defaults with explicit scope values and a listener token budget.

Topology and execution locations

flowchart TD
    C["Client namespace l3h1<br/>192.0.2.1"] --> G["Gateway llb1<br/>VIP 192.0.2.254"]
    G --> L["Llama echo l3ep1<br/>198.51.100.1"]
    G --> M["Mistral echo l3ep2<br/>203.0.113.1"]
    K["Keycloak kc-aigw<br/>Docker bridge"] -.->|"JWT issuer / JWKS"| G
    G -.-> P["Policy store pg-jwtauth<br/>Docker bridge"]
    O["Operator on Linux host"] -.->|"REST via l3h1 / port 11111"| G
    O -.->|"independent fixture observations"| L
    style G fill:#e1f5fe,stroke:#0288d1,color:#1f2937
    style L fill:#e8f5e9,stroke:#43a047,color:#1f2937
    style M fill:#e8f5e9,stroke:#43a047,color:#1f2937

These are Linux containers/namespaces on one disposable host. The model names identify synthetic echoes, not loaded GPU models. The baseline listener is VIP 192.0.2.254:2040; TLS, capacity and failure exercises add the explicit listener ports shown in their steps. Keycloak/JWKS and PostgreSQL use inspected Docker-bridge addresses.

Where What happens here
Host operator Prepare private LAB files, issue credentials and run namespace commands.
l3h1 Send management calls and client traffic into llb1.
llb1 Enforce identity, quota and capacity before choosing a backend.
l3ep1 / l3ep2 Observe independent Llama/Mistral receipt and hold/release fixtures.
kc-aigw / pg-jwtauth Supply identity and policy state; they are not on the inference response path.

Walkthrough

Prerequisites, configuration and expected precedence

Use q4/q7/t1 credentials and bootstrap ports 2059–2062. Save every pre-existing scope row before changing it. Each positive field resolves explicit → rule → global → no limit; zero falls through. POST is a scope-row upsert: include all fields to preserve. An all-zero defaults row is rejected; use DELETE.

Defaults input Required / example / unit Meaning and impact
scope Required global or rule Select the policy scope
rule_ident Required for rule, 192.0.2.254:2059 VIP:port; not the LB deletion full key
default_user_rps, default_tenant_rps Optional positive requests/second Fallback for the named identity scope
default_user_tpm, default_tenant_tpm Optional positive tokens/minute Per-field fallback token budget
vip_shared_rps Optional 100 requests/second here Keyless traffic on the service shares this RPS; not authenticated-tenant aggregate RPS
vip_shared_tpm Optional 10 tokens/minute here Shared actual usage from credentialed/keyless token-metered responses; keyless has no pre-reservation

After POST/GET, send three quick q7 requests to 2059: rule user RPS9 wins over global RPS1. Send q4 twice to 2060: its zero user field falls through to global RPS1, so the second is denied. On 2061 q4 usage12 spends shared TPM10; immediately send t1 to the same port and expect shared-budget 429/receipt0, then after two seconds send t1 to 2040 and require 200. Avoid attributing a user-RPS refusal to shared TPM. Check typed errors and vip token quota metrics.

The blocks include scoped DELETE and GET404, global restoration, then VIP defaults delete→two-tenant recovery→restore. Only POST a saved JSON body when its original GET was 200; never replay a 404 error body. Preserve bootstrap-owned VIP defaults after the experiment.

sudo ip netns exec l3h1 curl -sS -i "$API/config/ai/ratelimit/defaults/global" > "$LAB/results/defaults-before.txt"
sudo ip netns exec l3h1 curl -sS -i -X POST "$API/config/ai/ratelimit/defaults" \
  -H 'Content-Type: application/json' -d '{"scope":"global","default_user_rps":1}'
sudo ip netns exec l3h1 curl -sS -i "$API/config/ai/ratelimit/defaults/global"
sudo ip netns exec l3h1 curl -sS -i -X POST "$API/config/ai/ratelimit/defaults" \
  -H 'Content-Type: application/json' \
  -d '{"scope":"rule","rule_ident":"192.0.2.254:2059","default_user_rps":9}'
sudo ip netns exec l3h1 curl -sS -i "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2059"
sudo ip netns exec l3h1 curl -sS -i -X POST "$API/config/ai/ratelimit/defaults" \
  -H 'Content-Type: application/json' \
  -d '{"scope":"rule","rule_ident":"192.0.2.254:2060","default_tenant_tpm":100000}'
sudo ip netns exec l3h1 curl -sS -i "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2060"
sudo ip netns exec l3h1 curl -sS \
  "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2060" | jq .

Verify before removing defaults

Run the pairs together so refill does not obscure the intended refusal.

sudo ip netns exec l3h1 curl -sS -i --max-time 15 --config "$LAB/q7.curl" \
  -H 'Content-Type: application/json' -H "X-Test-Nonce: $RUN-rule-1" \
  --data-binary @"$LAB/examples/llama-request.json" "http://$VIP:2059/v1/chat/completions"
sudo ip netns exec l3h1 curl -sS -i --max-time 15 --config "$LAB/q7.curl" \
  -H 'Content-Type: application/json' -H "X-Test-Nonce: $RUN-rule-2" \
  --data-binary @"$LAB/examples/llama-request.json" "http://$VIP:2059/v1/chat/completions"
sudo ip netns exec l3h1 curl -sS -i --max-time 15 --config "$LAB/q7.curl" \
  -H 'Content-Type: application/json' -H "X-Test-Nonce: $RUN-rule-3" \
  --data-binary @"$LAB/examples/llama-request.json" "http://$VIP:2059/v1/chat/completions"
sudo ip netns exec l3h1 curl -sS -i --max-time 15 --config "$LAB/q4.curl" \
  -H 'Content-Type: application/json' -H "X-Test-Nonce: $RUN-global-1" \
  --data-binary @"$LAB/examples/llama-request.json" "http://$VIP:2060/v1/chat/completions"
sudo ip netns exec l3h1 curl -sS -i --max-time 15 --config "$LAB/q4.curl" \
  -H 'Content-Type: application/json' -H "X-Test-Nonce: $RUN-global-2" \
  --data-binary @"$LAB/examples/llama-request.json" "http://$VIP:2060/v1/chat/completions"
sleep 2
sudo ip netns exec l3h1 curl -sS -i --max-time 15 --config "$LAB/q4.curl" \
  -H 'Content-Type: application/json' -H "X-Test-Nonce: $RUN-vip-spend" \
  --data-binary @"$LAB/examples/llama-request.json" "http://$VIP:2061/v1/chat/completions"
sudo ip netns exec l3h1 curl -sS -i --max-time 15 --config "$LAB/t1.curl" \
  -H 'Content-Type: application/json' -H "X-Test-Nonce: $RUN-vip-denied" \
  --data-binary @"$LAB/examples/llama-request.json" "http://$VIP:2061/v1/chat/completions"
sleep 2
sudo ip netns exec l3h1 curl -sS -i --max-time 15 --config "$LAB/t1.curl" \
  -H 'Content-Type: application/json' -H "X-Test-Nonce: $RUN-vip-control" \
  --data-binary @"$LAB/examples/llama-request.json" "http://$VIP:2040/v1/chat/completions"
sudo ip netns exec l3h1 curl -sS "$API/metrics" | grep 'loxilb_ai_vip_token_quota'
sudo ip netns exec l3h1 curl -sS -i -X DELETE \
  "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2059"
sudo ip netns exec l3h1 curl -sS -i -X DELETE \
  "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2060"

Cleanup when the original global GET was 404

Run this branch only if the row did not exist.

sudo ip netns exec l3h1 curl -sS -i -X DELETE "$API/config/ai/ratelimit/defaults/global"
sudo ip netns exec l3h1 curl -sS -i "$API/config/ai/ratelimit/defaults/global"

Restore when the original global GET was 200

Run this alternative branch only for a saved successful response.

python3 -c 'import pathlib,sys;d=pathlib.Path(sys.argv[1]).read_bytes();pathlib.Path(sys.argv[2]).write_bytes(d.split(b"\r\n\r\n",1)[1])' "$LAB/results/defaults-before.txt" "$LAB/defaults-restore.json"
sudo ip netns exec l3h1 curl -sS -i -X POST "$API/config/ai/ratelimit/defaults" \
  -H 'Content-Type: application/json' --data-binary @"$LAB/defaults-restore.json"
sudo ip netns exec l3h1 curl -sS -i "$API/config/ai/ratelimit/defaults/global"
sudo ip netns exec l3h1 curl -sS -i "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2059"
sudo ip netns exec l3h1 curl -sS -i "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2060"
sudo ip netns exec l3h1 curl -sS -i "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2061"
sudo ip netns exec l3h1 curl -sS "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2061" > "$LAB/vip-default-before.json"
jq . "$LAB/vip-default-before.json"
sudo ip netns exec l3h1 curl -sS -i -X DELETE "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2061"
sudo ip netns exec l3h1 curl -sS -i "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2061"
sudo ip netns exec l3h1 curl -sS -i --config "$LAB/q4.curl" -H 'Content-Type: application/json' \
  -H "X-Test-Nonce: $RUN-vip-deleted-q4" --data-binary @"$LAB/examples/llama-request.json" "http://$VIP:2061/v1/chat/completions"
sudo ip netns exec l3h1 curl -sS -i --config "$LAB/t1.curl" -H 'Content-Type: application/json' \
  -H "X-Test-Nonce: $RUN-vip-deleted-t1" --data-binary @"$LAB/examples/llama-request.json" "http://$VIP:2061/v1/chat/completions"
sudo ip netns exec l3h1 curl -sS -i -X POST "$API/config/ai/ratelimit/defaults" \
  -H 'Content-Type: application/json' --data-binary @"$LAB/vip-default-before.json"
sudo ip netns exec l3h1 curl -sS "$API/config/ai/ratelimit/defaults/rule?rule_ident=192.0.2.254:2061" | jq .

Verification and cleanup

After each scope change read the exact scope path. A 200 from POST is not traffic proof. Require appropriate per-nonce receipts (1 for delivered, 0 for denied), isolated metric deltas, default GET absence or exact restored values, and the same users' normal traffic after cleanup. See common rollback semantics.

Troubleshooting and cleanup

Use diagnosis and scoped cleanup. Restore this chapter's changes before moving to another policy experiment.

Check your result

Read both the configured limit and the observed admission decision; restore complete original requests.

Compare API readback with the normal request, the deliberate denial, and an independent backend observation. Check the chapter's exact HTTP code and error body; HTTP 200 alone does not prove quota, identity, queue order, or resource return.

Next steps and cleanup

Restore the chapter's temporary policy before continuing in the same lab. Keep the original configuration and private evidence; use the final cleanup chapter when the entire lab is finished. Read the lab starting point if required values are missing. Use cleanup and recovery when ending the run.