---
title: "Model access"
---

# Model access

Connect AI model providers to Neural Inverse Govern, add models to the catalog, assign them to the organisation, teams and projects, and set the classification and region rules that decide which model may serve which data.

Model access decides which AI models exist for whom. Nothing is usable by default: a model reaches a person only when its provider connection is enabled, the model is in the catalog and enabled, it is assigned at a scope that applies to the person, and it is allowed for the request's data classification.

## 1. Connect a provider

Open **Models > Providers and keys** and add a connection, or use the API:

```bash
curl -sS -X POST "$KEYSTONE_URL/api/v1/models/providers" \
  -H "Authorization: Bearer $ADMIN_TOKEN" -H 'Content-Type: application/json' \
  -d '{"name": "Anthropic production", "kind": "anthropic", "base_url": "https://api.anthropic.com", "region": "us"}'

curl -sS -X PUT "$KEYSTONE_URL/api/v1/models/providers/$PROVIDER_ID/key" \
  -H "Authorization: Bearer $KEY_ADMIN_TOKEN" -H 'Content-Type: application/json' \
  -d '{"key": "<provider API key>"}'
```

Connection kinds are `anthropic`, `openai`, `google`, `azure_openai`, `bedrock`, `vertex`, `openai_compatible` and `ollama`. Azure takes `config.azure_api_version`; Vertex takes `config.vertex_project` and `config.vertex_location`.

| Kind | Key format |
|---|---|
| `anthropic`, `openai`, `google`, `azure_openai` | The provider's API key |
| `bedrock` | A JSON document `{"access_key_id", "secret_access_key", "session_token"}` (session token optional). Requests are signed with AWS Signature Version 4. |
| `vertex` | A Google service account JSON. Govern mints short-lived access tokens from it. |
| `openai_compatible`, `ollama` | Optional |

### Keys are write-only

Keys are stored in Govern Vault and never returned. Responses show only `set`, the last four characters (`hint`), a `version` and the rotation time. Setting, rotating and deleting a key needs `models:keys`, separate from configuring the connection (`models:admin`), so the person who handles keys need not manage policy.

Changing a connection's base URL, region or settings drops its stored key (audited `models.provider.key.removed`, reason `endpoint_changed`), so nobody can redirect a key to a host they control. Key changes are refused while the vault is sealed.

### Outbound connections

Every upstream request goes through Govern's outbound guard: loopback, private, link-local, carrier-grade NAT and cloud metadata addresses are refused at connect time, and no redirect is followed. The operator of a self-hosted installation can open private networks for model servers with `NI_AUTH_MODELS_PRIVATE_NETWORKS` (comma-separated CIDR prefixes or addresses; refused on the hosted service). With it, `openai_compatible` and `ollama` connections are confined to those networks and their models count as self-hosted. Without it they reach public addresses only and do not count as self-hosted.

### Provider health

`POST /api/v1/models/providers/{id}/health/check` (`models:admin`) probes a connection now; `GET /api/v1/models/providers/{id}/health` (`models:view`) shows the last 20 probes and the circuit state of its models. The console page is **Models > Provider health**. A scheduled probe runs only when the operator sets `NI_AUTH_MODELS_PROVIDER_HEALTH_SCHEDULE` (for example `@every 15m`).

## 2. Add models to the catalog

Open **Models > Model catalog**, or `POST /api/v1/models/catalog` (`models:write`). Govern does not discover models from providers: you enter each model and its prices, which keeps the catalog air-gap safe and prices under your control.

| Field | Meaning |
|---|---|
| `provider_id` | The connection |
| `model_key` | The name clients use; unique among model keys and route keys |
| `upstream_model` | The provider's model id |
| `display_name` | Shown in the apps |
| `uses` | Any of `chat`, `agent`, `completion`, `embedding`, `rerank`, `image`, `audio` |
| `region`, `residency` | Where the model runs |
| `max_classification` | The strictest classification the model may serve |
| `zero_retention` | The provider keeps no data for this model |
| `input_usd_per_mtok`, `output_usd_per_mtok` | Prices per million tokens; budgets charge with them |
| `usd_per_image`, `audio_usd_per_mchar` | Required above 0 for image and audio models |
| `cache_read_usd_per_mtok`, `cache_write_usd_per_mtok` | Prompt cache prices (optional) |
| `context_window`, `max_output_tokens` | Limits |
| `enabled` | Default on |

`PUT /api/v1/models/catalog/{id}/fallbacks` sets a model's fallback chain (up to 20). Each fallback is decided again from scratch, so a fallback never reaches a model the caller may not use.

## 3. Assign models

Assignments allow or deny a model at a scope. Open the model in the catalog, or use `PUT /api/v1/models/assignments` (`models:write`) with `scope_kind` (`org`, `team` or `project`), `scope_id`, `model_id` and `effect` (`allow` or `deny`).

- A model is usable when it is allowed at a scope that applies to the caller and denied at none. A deny at any applicable scope wins.
- Applicable scopes are the organisation, every team the caller belongs to, and the request's project when the caller is a member of it.
- **Leaving out the project never loosens a project's rules.** A request without a project gets the denies of every project the caller belongs to, and is classified at the strictest scope the caller works in.
- To give one project a model the organisation does not want generally, allow it on that project only.

### Defaults

`PUT /api/v1/models/defaults` sets a default model per use (`chat`, `agent`, `completion`, `embedding`) at a scope. The most specific scope wins: project, then team, then organisation. A default the caller may not use is skipped.

## 4. Set the routing policy

**Models > Routing** edits the organisation's policy (`GET/PUT /api/v1/models/policy`; `models:admin` to change):

| Field | Meaning |
|---|---|
| `classification_rules` | Per classification, `require` (`any`, `zero_retention`, `self_hosted`, `self_hosted_or_zero_retention`) and optional `regions` |
| `allowed_regions` | When set, a model whose region is not listed is not eligible |
| `require_project` | `regulated` (default), `always` or `never` |
| `user_keys_enabled` | Let people bring their own provider key |
| `user_key_max_classification` | The strictest classification a personal key may serve (default `internal`) |
| `offline_grace_seconds` | How long the apps' signed model manifest stays valid (default 24 hours, at most 7 days) |

### Classification

Each request is classified from its project: the strictest of project, team, department and organisation. A client may raise the classification with `X-Keystone-Classification`, never lower it. A project with compliance frameworks set is regulated. Then:

1. The model's `max_classification` must be at least the request's classification.
2. The organisation's rule for that classification must hold.

| Classification | Default rule |
|---|---|
| public, internal, confidential | `any` |
| restricted | `self_hosted_or_zero_retention` |

`require_project: regulated` refuses a request without a project when the caller's strictest classification is confidential or restricted, or when the caller belongs to a regulated project. `always` refuses every request without one. An API key belongs to no team or project, so it is decided at the organisation root's classification.

### Personal keys

With `user_keys_enabled`, people may store their own Anthropic, OpenAI or Google key with `PUT /api/v1/models/me/keys/{provider}`. The gateway uses it for catalog models on connections of that kind, only up to `user_key_max_classification`, only for models the organisation allowed, and only through the organisation's configured endpoint. Spend on a personal key is not counted against spend budgets (tokens are).

## Permissions

| Permission | Grants |
|---|---|
| `models:read` | Using models: the signed manifest, the gateway and one's own keys. Held by Member. |
| `models:view` | Reading the configuration: connections (never keys), catalog, assignments, defaults, policy, budgets with live usage. Not held by Member, because base URLs can name internal hosts and budgets show what people spend. |
| `models:write` | Catalog entries, fallbacks, assignments, defaults, budgets, routes |
| `models:admin` | Connections (not keys), the routing policy, gateway settings, guardrails, rate limits, request logs |
| `models:keys` | Setting, rotating and deleting a connection's key |

Model connections are also shareable resources: a Viewer of one connection can read its configuration, a User can call models through it. See [Roles and permissions](https://neuralinverse.com/docs/govern/roles/#resource-roles).

## Limits

50 provider connections, 500 catalog models, 20 fallbacks per model, 2,000 assignments, 2,000 defaults and 500 budgets per organisation.

## Not supported

Bedrock models other than Anthropic models (the Converse API) and Gemini's native API are not built. Google and Vertex Gemini models are reached through their OpenAI-compatible endpoints.

## Audit

Every change is audited in its own transaction. Key changes write two records, the change and a key record with the new version and hint, never the key.
