/v1/* endpoints.
Hivenet Router hashes every submitted key with SHA-256 and compares the result with a stored hash. The router does not need to retain the raw client key.
Two key-management modes are available:
Administration keys are separate from client API keys. Client keys do not grant access to
/admin/*.Choose a key mode
Use static mode when:- keys change infrequently
- operators manage configuration files directly
- file-based review and version control fit your workflow
- keys should be restored automatically after a router restart
- another service owns the key lifecycle
- keys need to be created, disabled, or revoked at runtime
- the router must receive full-registry reconciliation from a control service
- you do not want static client-key entries in
auth.yaml
Key format
The built-in key generator creates keys with:
A preview resembles:
Generate a static key
Run:- the raw key
- its SHA-256 hash
- its masked preview
- an
auth.yamlentry
Configure a static key
Create an auth file such as:Static key fields
Duplicate
key_hash values are rejected when static configuration is loaded.
Use the exact lowercase SHA-256 output produced by hivenet-router keygen or sha256sum. Do not edit a hash manually.
metadata.name and metadata.owner must not be empty.
Static key expiration
Static keys use:401 unauthorized response.
Use a client key
Send the raw key with the standard bearer header:Bearer prefix, but use the standard format for applications, proxies, and SDKs.
OpenAI Python client
Restrict models
A static key can declare an exact model allowlist:403:
GET /v1/models returns only models that the key may use.
See Model restrictions for multi-tenant examples and discovery behavior.
Configure flat quotas
The flat quota shape applies one request and token budget to every model the owner may call.
The input and output rate fields apply only when the effective model policy uses
mode: serverless. They cannot be combined with quota.per_model.
A serverless key can also declare max_occupancy_share at the key level, alongside quota:
share × admit_budget_tokens × healthy_replicas. It deliberately does not apply HIVENET_ROUTER_ADMIT_FRACTION; the global occupancy gate remains the pool-safety limit.
For every reachable serverless model, a nonzero input_tokens_per_minute must be at least the policy’s max_input_tokens. The router validates this and the occupancy-share range at startup, during reloads, and on dynamic admin key writes.
The auth.DeriveKeyDefaults helper can calculate suggested values from certified model limits. The current runtime does not load router_limits.yaml or apply those defaults automatically.
The flat quota bucket is keyed by:
HIVENET_ROUTER_RPM_BURST_SECONDS to a value from 1 through 59 to reduce the burst window; 0 preserves the full-minute capacity.
The daily token counter resets at midnight UTC.
Configure per-model quotas
Per-model quotas define separate budgets for each model:0 explicitly when one limit should be unlimited.
For example:
Per-model quotas are also an allowlist
Whenquota.per_model exists, its model names become the key’s effective model allowlist.
For example:
models contains other names.
quota.per_model takes precedence so quota configuration and access control cannot disagree.
A request for a model without a quota entry is rejected rather than falling through to an unlimited default.
Do not mix quota shapes
This is invalid:- the flat shape
- the per-model shape
How token budgets work
Daily token limits currently apply to language-model chat requests. At admission, Hivenet Router:- estimates prompt tokens
- reads
max_completion_tokens, or falls back tomax_tokens - checks whether the estimated prompt and requested maximum output fit in the remaining budget
- charges the prompt estimate when the request is admitted
429. The previously charged prompt estimate remains counted.
For streaming responses, the bytes may already have reached the client before the final completion-token total is known. Hivenet Router records the output usage after the stream finishes, but it cannot retract an already delivered stream.
Embedding and reranking requests currently participate in request-rate quotas but do not charge their input against
tokens_per_day.Rate-limit headers
Finite quotas may produce:
Unlimited values do not produce a remaining-value header.
Request-rate exhaustion returns HTTP
429 with:
- a preflight rejection can return a positive
X-RateLimit-Remaining-Tokensvalue when the remaining budget exists but cannot admit the request’s estimated input plus requested maximum output - a post-response token rejection returns
X-RateLimit-Remaining-Tokens: 0 - unlimited token budgets omit the header
error.code and the remaining headers rather than matching message text.
Choose quota persistence
Request-rate state is always kept in memory. Daily token usage can use either:
The default is:
Reload static keys
After editingauth.yaml, send:
- parses and validates the new file
- builds replacement authentication providers
- swaps them atomically
- rebuilds the in-memory quota limiter state
- keeps the previous configuration when reload validation fails
Enable dynamic mode
Dynamic mode keeps client keys in an in-memory registry managed through/admin/api-keys/*.
Enable it with:
auth.yaml.
Administration authentication is mandatory in dynamic mode. The router refuses to start without:
Create a hash for a dynamic key
The dynamic API accepts the SHA-256 hash, not the raw bearer key. You can use the built-in key generator and take the printed hash, or hash a key created by your control service:key_hash must contain exactly 64 hexadecimal characters.
Hashes are normalized to lowercase.
Bootstrap the dynamic registry
Replace the complete registry:Dynamic key fields
Dynamic expiration uses RFC 3339:
Dynamic registry versions
Every mutation includes an opaque version string. Hivenet Router compares versions lexicographically. Use values whose string ordering matches time ordering:- single-key upserts
- deletions
- full-registry replacement
rev_10 before rev_9.
Disable or revoke a dynamic key
To disable a key without removing its metadata, update it with:401 unauthorized response as an unknown or expired key.
To remove the entry, call:
Dynamic registry visibility
Operators can list entries through:Dynamic restart behavior
Dynamic key state is memory-only. After every router restart:- the registry is empty
- every previous client key fails authentication
- the external control service must push a current snapshot
- the service can confirm state through
/admin/api-keys/version
Admin keys are separate
Admin keys come from:api.keys. The hivenet-router keygen output is designed for hashed client-key entries, while administrator keys remain raw environment secrets.
Use an admin key:
HIVENET_ROUTER_ALLOW_INSECURE_ADMIN=true is set explicitly. Use that override only for isolated development and testing.
Security guidance
- Store raw keys in a secret manager.
- Never commit raw keys or
.envfiles. - Give each application or integration its own key.
- Use meaningful owners and names.
- Keep model access as narrow as practical.
- Set quotas that match the intended workload.
- Use expiration for temporary credentials.
- Use separate client and administration credentials.
- Send keys only over HTTPS outside a trusted network.
- Do not place keys in query strings.
- Enable audit logging before production use.
- Rotate keys without waiting for a suspected compromise.
Troubleshooting
Every request returns 401 unauthorized
Check that:
- the router is using
api-keyordynamicmode - the application sends the raw key rather than the hash
- the
Authorizationheader reaches the router - a static hash matches the exact raw key
- the static key has not expired
- the dynamic key is enabled
- the dynamic registry has been repopulated after restart
A static key does not load
Check the router logs. Common causes include:- empty
key_hash - empty
metadata.name - empty
metadata.owner - duplicate hashes
- invalid
expires_at - invalid quota configuration
- empty
api.keyswhile mode isapi-key
A key can see the wrong models
Check whether the key uses:quota.per_model exists, its keys define the effective allowlist and take precedence over models.
Several keys consume one quota unexpectedly
Check their owner values. Keys with the same:The effective per-model RPM is unexpected
The rate is:Daily usage resets after restart
The quota backend is probably using:A dynamic update returns 409 stale version
Read the current registry version:
A dynamic update rejects the hash
Check that it is exactly 64 hexadecimal characters:key_hash.
Next steps
auth.yaml reference
Review the full static authentication and quota schema.
Model restrictions
Design model access for applications, tenants, and workload types.
Key rotation
Rotate static, dynamic, administration, agent, and provider credentials safely.

