Policies
For: Platform operators and policy authors configuring policy bundles on Axemere Gateway.
Configuration Overview | Credentials | Workloads | Policies
Policies are organized into a bundle that controls evaluation order and expiry, and rule files that contain the actual governance rules.
Table of Contents
- 3.1 Bundle file
- 3.2 Rule files
- 3.3 Rule fields
- 3.4 Condition language
- 3.5 Effect language
- 3.6 Per-layer rule files
- 3.7 Additional bundle files
- 3.8 Complete examples
- 3.9 Policy Add-Ons
- 4. Pushing policies at runtime
- 5. Policy file locations by installation method
- See also
3.1 Bundle file
Location: configs/policies/bundle.yaml (or pushed via the admin API)
The bundle is the root of the policy configuration. It sets global defaults and controls which layers are evaluated, in what order.
Default bundle (skeleton + add-ons)
The default bundle.yaml shipped with the gateway is a skeleton -- it contains no rules of its own. Instead, rules are loaded automatically from the addons/ subdirectory under MVGC_POLICIES_DIR. This is the add-on system described in section 3.9.
schema: mvgc.policy_bundle.v1 bundle_id: default version: "1.0.0" issued_at: "2026-03-15T00:00:00Z" expires_at: "2028-03-15T00:00:00Z" defaults: decision: deny trace_level: matched_rules evaluation: order: - identity - connectors stop_on: - deny merge_strategy: first_match
On startup, the gateway detects that the bundle has no inline_rules and no files: list. It then scans <MVGC_POLICIES_DIR>/addons/ and loads every .yaml file found there (symlinks are followed automatically). This means enabling or disabling a provider is as simple as placing or removing a file in addons/ and calling POST /v1/admin/policies/reload -- no restart required.
Advanced: file-referenced or inline bundles
For production governance with explicit per-layer rule files, use mvp_policy.yaml (see section 3.7) or write your own bundle. Three loading modes are supported:
# Mode 1: skeleton (default) -- loads add-ons from addons/ automatically schema: mvgc.policy_bundle.v1 # ... no files: or inline_rules: ... # Mode 2: file-referenced -- loads named rule files from MVGC_POLICIES_DIR files: - targets.yaml - credential-rules.yaml - budgets.yaml - risk.yaml # transforms.yaml is optional; the binary embeds a default and uses it when absent # Mode 3: inline -- embeds rules directly (used for admin API pushes and tests) inline_rules: identity: - id: identity.allow.proxy priority: 100 when: field: context.connection_type equals: connect_proxy effect: decision: allow
inline_rules takes precedence over files. Both take precedence over the add-on loader. A bundle with a non-empty files: or inline_rules: section never loads from addons/.
Bundle signature
signature: alg: ed25519 kid: "kid_cp_policy_1" sig: "base64url..."
Populated by the CP when MVGC_BUNDLE_SIGN=true. See Signed Policy Bundles.
Bundle field reference
| Field | Type | Required | Description |
|---|---|---|---|
schema | string | yes | Must be mvgc.policy_bundle.v1. |
bundle_id | string | yes | Unique bundle identifier. |
version | string | yes | Version string (any format). |
issued_at | RFC3339 | no | When the bundle was issued. |
expires_at | RFC3339 | no | The gateway rejects the bundle after this time. |
defaults.decision | string | yes | Decision applied when no rule matches. Typically deny. See decision valid values. |
defaults.trace_level | string | no | How much detail to include in policy traces. See defaults.trace_level valid values. |
defaults.require_receipt | bool | no | When true, every execution must be recorded in the ledger. |
evaluation.order | []string | yes | Layer evaluation order. |
evaluation.stop_on | []string | no | Decisions that halt evaluation early. See decision valid values. |
evaluation.merge_strategy | string | no | How multiple matching rules are combined. See evaluation.merge_strategy valid values. |
signature | object | no | Ed25519 signature for bundle integrity. Verified when MVGC_BUNDLE_VERIFY_SIGNATURES=true. |
files | []string | no | Relative paths to policy rule files (used with filesystem bundles). |
inline_rules | map | no | Rules embedded directly in the bundle. Keyed by layer name. Takes precedence over files. |
proxy.managed_domains | []string | no | Per-bundle HTTPS domains to intercept (merged with gateway-level MVGC_MANAGED_DOMAINS). |
proxy.bypass_domains | []string | no | Domains to explicitly pass through even if they match managed_domains. |
defaults.trace_level valid values
| Value | Description |
|---|---|
none | No trace included in the execution record. |
summary | Final decision and matched layer names only. |
matched_rules | Includes the IDs of all rules that matched. |
full | Full evaluation detail including all context fields and rule outcomes. |
evaluation.merge_strategy valid values
| Value | Description |
|---|---|
first_match | Stop at the first rule that matches within a layer. Recommended. |
most_specific | Apply the most specific matching rule (by priority). |
priority | Evaluate all rules; apply the one with the lowest priority number. |
3.2 Rule files
Each policy layer has its own YAML file. Rules in each file share the same schema and layer tag.
Rule file structure
schema: mvgc.policy_rules.v1 layer: identity rules: - id: identity.allow.all.explicit description: "Allow all explicit action requests" priority: 100 when: field: context.connection_type equals: direct_api effect: decision: allow reason: "explicit action request allowed"
Rule file header fields
| Field | Type | Description |
|---|---|---|
schema | string | Must be mvgc.policy_rules.v1 or mvgc.rule_file.v1. |
layer | string | The evaluation layer this file belongs to. |
rules | []Rule | The list of policy rules. |
3.3 Rule fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique rule identifier. Included in policy traces. Convention: <layer>.<effect>.<target>. |
description | string | no | Human-readable explanation of the rule's intent. |
priority | int | yes | Lower numbers are evaluated first within a layer. Ties are resolved by file order. |
when | Condition | no | Match expression. When omitted, the rule always matches. |
effect | Effect | yes | The outcome when the rule matches. |
3.4 Condition language
Conditions are YAML objects that evaluate fields from the request context.
Logical combinators
# All sub-conditions must match when: all: - field: context.connection_type equals: direct_api - field: context.attribution.project_id exists: true # Any sub-condition must match when: any: - field: action.target_host equals: api.openai.com - field: action.target_host equals: api.anthropic.com
Combinators can be nested to any depth.
Leaf operators
| Operator | Type | Description | Example |
|---|---|---|---|
equals | string | Exact string match | equals: api.openai.com |
in | []string | Value is in the list | in: ["api.openai.com", "api.anthropic.com"] |
not_in | []string | Value is not in the list | not_in: ["api.evil.com"] |
exists | bool | Field is present and non-empty | exists: true |
regex | string | RE2-compatible regex match | regex: "^proj-[0-9]+" |
prefix | string | String starts with value | prefix: "proj-" |
suffix | string | String ends with value | suffix: ".openai.azure.com" |
lt | decimal string | Numeric less-than | lt: "1.00" |
lte | decimal string | Numeric less-than-or-equal | lte: "0.00" |
gt | decimal string | Numeric greater-than | gt: "2.00" |
gte | decimal string | Numeric greater-than-or-equal | gte: "0.50" |
Numeric operators accept decimal strings to avoid floating-point imprecision. Use quoted values like "1.00", "0.75".
Context field reference
Fields are addressed as dotted paths. All paths under context.* or action.* are valid.
Identity and ingress:
| Field | Description |
|---|---|
context.org_id | Organization ID of the caller |
context.workload_id | Workload ID of the calling service |
context.connection_type | direct_api, sdk_redirect, or connect_proxy |
context.delegation_id | Delegation token ID, if present |
context.principal_id | Principal identity |
Action:
| Field | Description |
|---|---|
context.action.type | Action type: ai.infer, http.request, etc. |
context.action.method | HTTP method: GET, POST, etc. |
context.action.target_host | Target hostname (e.g. api.openai.com) |
context.action.target_path | Target URL path |
context.action.params.<key> | Action parameter by key (e.g. context.action.params.model) |
context.action.headers_present | Comma-separated list of present header names |
context.action.header.<name> | Value of a specific request header |
action.target_host | Shorthand for context.action.target_host (usable in inline rules) |
Attribution:
| Field | Description |
|---|---|
context.attribution.customer_id | Customer identifier |
context.attribution.account_id | Billing account identifier |
context.attribution.project_id | Project identifier |
context.attribution.labels.<key> | Label value by key |
Cost estimates:
| Field | Description |
|---|---|
context.estimates.tokens_in | Estimated input tokens |
context.estimates.tokens_out_max | Estimated maximum output tokens |
context.estimates.cost_usd_max | Estimated maximum cost as decimal string |
Budget state:
| Field | Description |
|---|---|
context.budgets.org_usd_remaining | Remaining org budget as decimal string |
context.budgets.project_usd_remaining | Remaining project budget as decimal string |
Risk signals:
| Field | Description |
|---|---|
context.risk.score | Composite risk score (0.0 to 1.0) |
context.risk.request_rate | Recent request rate (requests/minute) |
context.risk.cost_rate | Recent cost rate (USD/minute, decimal string) |
context.risk.signals | Active signal names (e.g. rate_spike, cost_anomaly) |
Delegation:
| Field | Description |
|---|---|
context.delegation_id | Token ID from the delegation token |
delegation.actions_allow | Actions allowed by the delegation scope |
delegation.targets_allow | Targets allowed by the delegation scope |
Connector candidates:
| Field | Description |
|---|---|
context.candidates.connectors | List of eligible connector IDs |
context.candidates.credentials | List of eligible credential IDs |
3.5 Effect language
The effect block defines what happens when a rule matches. Effects can produce a decision, modify the request, or select infrastructure.
decision valid values
| Value | HTTP Status | Description |
|---|---|---|
allow | 200 | Proceed with the request. |
deny | 403 | Reject the request. |
downgrade | 200 | Allow but mutate the request (e.g. swap model). Use with mutate. |
require_approval | 202 | Pause and create an approval request. Returns approval_id. |
rate_limit | 429 | Throttle the caller. Returns Retry-After header. Use with rate_limit. |
quarantine | 403 | Reject and create a quarantine record for review. Use with quarantine. |
require_attribution | 403 | Reject with an attribution-specific reason. |
Credential selection
Credential IDs referenced here must be registered -- see Credentials.
effect: # Select by ID (preferred): select_credential: cred-openai # Or with explicit struct: select_credential: credential_id: cred-openai # Or select by attributes (policy engine picks the first match): select_credential: provider: openai mode: alias billing_owner: customer
| Field | Type | Description |
|---|---|---|
credential_id | string | Exact credential ID to use. |
provider | string | Filter by provider name. |
mode | string | Filter by credential mode. See Credentials -- mode valid values. |
billing_owner | string | Filter by billing owner. See Credentials -- billing_owner valid values. |
Connector selection
effect: select_connector: connector_id: openai
Valid connector IDs match the provider values in the credentials provider table: openai, anthropic, gemini, azure_openai, cohere, generic_http.
Attribution enforcement
effect: enforce_attribution: require: - project_id - account_id allow_overrides: - project_id - labels
| Field | Type | Description |
|---|---|---|
require | []string | Attribution fields that must be present. Request is denied if any are missing. Valid values: customer_id, account_id, project_id, labels. |
allow_overrides | []string | Attribution fields the caller may override. |
Default attribution
effect: default_attribution: customer_id: cust-default account_id: acct-default project_id: proj-default labels: env: prod
Applies before enforce_attribution. Fields are only set if not already present in the request.
Budget enforcement
effect: enforce_budget: limit_key: "project:proj-123" max_usd_per_day: "200.00" max_usd_per_request: "2.00"
| Field | Type | Description |
|---|---|---|
limit_key | string | Budget scope key (e.g. project:proj-123, org:org-001). |
max_usd_per_day | decimal string | Daily spend cap. |
max_usd_per_request | decimal string | Per-request cost cap. |
Request mutation
Use mutate with decision: downgrade to rewrite action parameters:
effect: decision: downgrade mutate: action.params.model: "gpt-4o-mini" action.headers.X-Cost-Tier: "economy" action.target_path: "/v1/chat/completions" action.method: "POST" reason: "model downgraded due to cost threshold"
Valid mutate key prefixes:
| Prefix | Description |
|---|---|
action.params.<key> | Set an action parameter |
action.headers.<name> | Set a request header |
action.target_host | Override the target hostname |
action.target_path | Override the target path |
action.method | Override the HTTP method |
Rate limiting
effect: decision: rate_limit rate_limit: key: "org" limit: 100 window: "1m"
| Field | Type | Description |
|---|---|---|
key | string | Rate limit bucket key. "org" uses the org ID; any string is allowed. |
limit | number | Maximum requests per window. |
window | duration | Time window (e.g. "1m", "5m", "1h"). |
Quarantine
effect: decision: quarantine quarantine: reason: "critical risk score exceeded threshold"
| Field | Type | Description |
|---|---|---|
reason | string | Human-readable reason stored in the quarantine record. |
Require receipt
effect: require_receipt: true
When true, overrides the bundle default and requires this execution to be recorded in the ledger.
3.6 Per-layer rule files
Default configuration uses add-ons. The per-layer files described in this section are used with
mvp_policy.yamland thefiles:loading mode for production governance. The out-of-the-boxbundle.yamlloads rules from theaddons/directory instead. See section 3.9 for the add-on system.
The mvp_policy.yaml bundle evaluates five rule-file layers. Identity filtering and delegation token checks are handled by gateway-level configuration settings (see allowed_connection_types and the connector settings below) rather than policy rules.
Rules within a layer are evaluated in ascending priority order; the lowest priority number runs first. With merge_strategy: first_match, evaluation stops at the first matching rule in each layer.
Gateway-level enforcement settings
Two behaviours that were formerly expressed as policy rules are now controlled by gateway config settings, keeping the policy files focused on per-request governance logic:
| Setting | Config key | Env var | Default | Description |
|---|---|---|---|---|
| Allowed connection types | gateway.allowed_connection_types | MVGC_ALLOWED_CONNECTION_TYPES | (all) | Comma-separated list of permitted connection types (direct_api, sdk_redirect, connect_proxy). Empty = all permitted. |
| Allow generic HTTP connector | connectors.allow_generic_http | MVGC_ALLOW_GENERIC_HTTP | true | When false, the generic HTTP fallback connector is disabled; only named AI-provider connectors are available. |
# configs/mvgc.yaml gateway: allowed_connection_types: - direct_api - sdk_redirect - connect_proxy # remove to disable HTTPS proxy interception gateway-wide connectors: allow_generic_http: true # set to false to restrict to AI providers only
3.6.1 targets.yaml
Path: configs/policies/targets.yaml
Controls which upstream hostnames the gateway will forward requests to. This is the primary safeguard against requests being routed to unauthorized external services. Rules match on context.action.target_host, which is the hostname extracted from the request (explicit action requests use action.target_host; proxy mode uses the HTTP Host header).
Default rules:
schema: mvgc.policy_rules.v1 layer: targets rules: - id: targets.allow.openai priority: 100 when: all: - field: context.action.target_host in: ["api.openai.com"] effect: decision: allow reason: "openai target allowed" - id: targets.allow.anthropic priority: 100 when: all: - field: context.action.target_host in: ["api.anthropic.com"] effect: decision: allow reason: "anthropic target allowed" - id: targets.allow.gemini priority: 100 when: all: - field: context.action.target_host in: ["generativelanguage.googleapis.com"] effect: decision: allow reason: "gemini target allowed" - id: targets.allow.azure_openai priority: 100 when: any: - field: context.action.target_host suffix: "openai.azure.com" - field: context.action.target_host suffix: "cognitiveservices.azure.com" effect: decision: allow reason: "azure openai target allowed" - id: targets.deny.unknown priority: 1000 when: all: - field: context.action.target_host not_in: ["api.openai.com", "api.anthropic.com", "generativelanguage.googleapis.com"] effect: decision: deny reason: "target host not in allowlist"
Note on the deny rule:
targets.deny.unknownuses anot_inlist that intentionally omits Azure hostnames -- those are caught bytargets.allow.azure_openaiat priority 100 before the deny rule at priority 1000 is reached. If you add providers, add both an allow rule for them and include their hostnames in thenot_inlist of the deny rule (or replace the catch-all approach entirely with a single deny rule at the bottom that matches everything).
Common customizations:
# Add a private or on-premise LLM endpoint: - id: targets.allow.private-llm priority: 100 when: field: context.action.target_host equals: llm.internal.corp.example effect: decision: allow reason: "internal LLM endpoint allowed" # Restrict a workload to a single provider: - id: targets.deny.non-openai.billing-app priority: 150 when: all: - field: context.workload_id equals: wl-billing-app - field: context.action.target_host not_in: ["api.openai.com"] effect: decision: deny reason: "billing-app may only reach OpenAI"
3.6.2 credential-rules.yaml
Path: configs/policies/credential-rules.yaml
Selects which credential to use for each request and enforces attribution requirements. Credential selection rules match on the target host (and optionally on workload, ingress mode, or attribution fields) and set both select_connector and select_credential together. The catch-all deny at the bottom rejects any request that does not match a credential rule -- typically because project_id is missing.
Default rules:
schema: mvgc.policy_rules.v1 layer: credentials rules: - id: creds.select.openai.prod priority: 100 when: all: - field: context.action.target_host equals: api.openai.com - field: context.attribution.project_id exists: true effect: select_connector: connector_id: openai select_credential: credential_id: cred-openai provider: openai mode: alias billing_owner: customer enforce_attribution: require: - project_id decision: allow reason: "openai credential selected" # Identical pattern for creds.select.anthropic.prod, creds.select.gemini.prod, # creds.select.azure_openai.prod -- each matches on target_host + project_id # and selects the corresponding credential. - id: creds.deny.missing.project priority: 500 when: all: - field: context.attribution.project_id exists: false effect: decision: deny reason: "project_id is required for attribution"
Every credential selection rule requires project_id to be present in the attribution block. The select_connector and select_credential effects work together: select_connector routes to the provider-specific connector, select_credential tells the connector which API key to use.
Common customizations:
# Add a second OpenAI credential for a different team: - id: creds.select.openai.team-b priority: 90 when: all: - field: context.action.target_host equals: api.openai.com - field: context.workload_id prefix: "wl-team-b-" - field: context.attribution.project_id exists: true effect: select_connector: connector_id: openai select_credential: credential_id: cred-openai-team-b enforce_attribution: require: - project_id decision: allow # Apply default attribution before the attribution check: - id: creds.default.attribution priority: 60 effect: default_attribution: project_id: proj-unattributed
3.6.3 budgets.yaml
Path: configs/policies/budgets.yaml
Enforces cost caps at both the per-request and per-project-per-day levels. Cost estimates are computed by the gateway from the request body before this layer runs and are available as context.estimates.cost_usd_max. Daily spend state is tracked in Postgres and available as context.budgets.project_usd_remaining.
Default rules:
schema: mvgc.policy_rules.v1 layer: budgets rules: # Hard-deny if a single request's estimated cost exceeds $2.00. - id: budgets.deny.request.cap priority: 100 when: all: - field: context.estimates.cost_usd_max gt: "2.00" effect: decision: deny reason: "per-request cost cap exceeded (max $2.00)" # Hard-deny if the project has exhausted its daily budget. - id: budgets.deny.project.daily.exhausted priority: 50 when: all: - field: context.budgets.project_usd_remaining lte: "0.00" effect: decision: deny reason: "project daily budget exhausted" # Downgrade the model when estimated cost is high but under the hard cap. - id: budgets.downgrade.high.cost priority: 200 when: all: - field: context.action.type equals: ai.infer - field: context.estimates.cost_usd_max gt: "1.00" effect: decision: downgrade mutate: action.params.model: "gpt-4o-mini" reason: "cost estimate exceeds threshold; downgrading model"
Rule priority determines the order: exhausted daily budget (priority 50) is caught before the per-request cap (100), which is caught before the downgrade (200). A request with an estimated cost over $2 is denied outright; one between $1-$2 has its model swapped to gpt-4o-mini instead.
Estimates vs actuals:
context.estimates.cost_usd_maxis a pre-execution estimate based on input token count and model pricing. It may be conservative.context.budgets.project_usd_remainingreflects actual committed spend from previous requests. Use both together for effective budget governance.
3.6.4 risk.yaml

Path: configs/policies/risk.yaml
Applies automated governance based on the composite risk score and real-time signal data computed by the gateway's risk scorer before policy evaluation. The risk scorer populates context.risk.score (0.0--1.0) and context.risk.signals using a sliding time window.
Risk score signals:
| Signal | Weight | Triggers when |
|---|---|---|
rate_spike | 0.40 | Requests in the last 1m > 3× the baseline avg req/min (over 5m window). Requires ≥5 baseline observations. |
cost_anomaly | 0.30 | USD spend in last 1m > 3× the baseline avg USD/min. Same minimum baseline guard. |
target_diversity | 0.15 | Distinct target hosts in last 1m > 3× the baseline rate from the 1m–5m prior window (the recent window is excluded from baseline to avoid it always subsuming itself). Requires ≥3 recent hosts. |
time_of_day | 0.15 | Current time falls outside MVGC_RISK_BUSINESS_HOURS (format: HH:MM-HH:MM,TZ). Only fires if the env var is set. |
Signals require at least five observations in the baseline window before activating. A newly started gateway or a workload with very few requests will not trigger risk rules even if the score appears elevated.
Default rules:
schema: mvgc.policy_rules.v1 layer: risk rules: # Application-tagged high-risk requests require approval. - id: risk.require_approval.high_cost priority: 100 when: field: context.action.params.risk_tier equals: "high" effect: decision: require_approval reason: "risk tier requires manual approval" # Automated quarantine for critical risk scores. - id: risk.quarantine.critical priority: 10 when: field: context.risk.score gt: "0.8000" effect: decision: quarantine quarantine: reason: "automated quarantine: risk score exceeds critical threshold" # Require approval for elevated but non-critical risk scores. - id: risk.approval.elevated priority: 20 when: field: context.risk.score gt: "0.5000" effect: decision: require_approval reason: "elevated risk score requires approval" # Rate limit callers with a request rate spike. - id: risk.ratelimit.burst priority: 30 when: field: context.risk.request_rate gt: "100.00" effect: decision: rate_limit rate_limit: key: "org" limit: 100 window: "1m"
Rules fire in priority order: critical quarantine (10) -> approval for elevated (20) -> rate limit burst (30) -> application-supplied risk_tier tag (100). Because the bundle's stop_on includes quarantine and rate_limit, hitting any of the first three rules stops further layer evaluation immediately.
Common customizations:
# Lower the quarantine threshold for a stricter posture: - id: risk.quarantine.strict priority: 10 when: field: context.risk.score gt: "0.6000" effect: decision: quarantine quarantine: reason: "strict mode: quarantine threshold 0.60" # Require approval specifically for off-hours requests: - id: risk.approval.off-hours priority: 25 when: field: context.risk.signals in: ["time_of_day"] effect: decision: require_approval reason: "off-hours request requires approval"
3.6.5 transforms.yaml
Path: configs/examples/transforms.yaml
The last layer evaluated before connector execution. Used for non-security mutations to the outgoing request: injecting headers, normalizing paths, or swapping models. Because this layer only runs after all deny/allow/rate-limit decisions have already been made, mutations here are guaranteed to apply only to requests that have fully passed governance.
Default rules:
schema: mvgc.policy_rules.v1 layer: transforms rules: # Inject an Axemere tracking header on every forwarded request. - id: transforms.header.request_id priority: 100 effect: mutate: "action.headers.X-MVGC-Request-ID": "injected" # Supply a default path when the caller omitted one. - id: transforms.path.api_version priority: 200 when: field: context.action.target_path exists: false effect: mutate: "action.target_path": "/v1/"
The X-MVGC-Request-ID rule has no when clause and therefore matches every request. The path normalization rule only fires when target_path is absent.
mutatewithoutdecision: When a transform rule omits thedecisionfield, it applies the mutation while leaving the existing decision unchanged. This is the correct pattern for header injection and path normalization. Usedecision: downgradeonly when the mutation itself represents a policy downgrade (e.g., swapping a premium model to an economy model).
Common customizations:
# Inject attribution as a header for downstream audit: - id: transforms.header.project priority: 100 when: field: context.attribution.project_id exists: true effect: mutate: "action.headers.X-Project-ID": "{{context.attribution.project_id}}" # Normalize gpt-4 to the current recommended version: - id: transforms.model.gpt4.normalize priority: 150 when: field: context.action.params.model equals: "gpt-4" effect: mutate: "action.params.model": "gpt-4o"
3.6.6 Access Rules (console)
The hosted console's Policies → Access Rules tab exposes four organization-wide toggles that wrap common credential and proxy behaviors without hand-editing YAML:
| Toggle | Effect |
|---|---|
| AWS Bedrock — Credential | Stores the org AWS Bedrock credential (SigV4 access/secret key pair, or a Bedrock API key). Required before enabling provider-bedrock. |
| Claude CLI Proxy | Allows Claude CLI companion traffic (claude.ai, statsig.anthropic.com, sentry.io) through a transparent MITM proxy; the console equivalent of the proxy: key described in proxy-mitm.yaml. |
| Deny Unknown Hosts | Blocks requests to any host not explicitly permitted by policy. Recommended for production; equivalent to the proxy.deny.unknown-host rule in proxy-credentials.yaml. |
| Google Vertex AI — Credential | Stores the org Google Vertex AI service-account credential. Required before enabling provider-vertex. |
The two credential toggles (AWS Bedrock, Google Vertex AI) stay disabled until the corresponding credential is configured; enabling them makes that provider selectable elsewhere in the console (Providers, Credentials).

3.7 Additional bundle files
Beyond the add-on system and the eight per-layer rule files, the configs/policies/ directory ships two standalone complete bundles. These are not loaded automatically -- they are templates and reference configurations intended to be pushed via the admin API.
mvp_policy.yaml
Path: configs/examples/mvp_policy.yaml
The full production-ready bundle. Unlike bundle.yaml (which loads add-ons from addons/ and evaluates the connectors layer only), mvp_policy.yaml loads named rule files for the targets, credentials, budgets, risk, and transforms layers and configures a strict stop_on list.
schema: mvgc.policy_bundle.v1 bundle_id: "bundle-mvp-001" version: "2026.03.06.1" issued_at: "2026-03-06T00:00:00Z" expires_at: "2027-03-06T00:00:00Z" defaults: decision: deny require_receipt: false trace_level: matched_rules evaluation: order: - targets - credentials - budgets - risk - transforms stop_on: - deny - require_approval - require_attribution - rate_limit - quarantine merge_strategy: first_match files: - targets.yaml - credential-rules.yaml - budgets.yaml - risk.yaml # transforms.yaml is optional; the binary embeds a default and uses it when absent
Comparison with bundle.yaml (the default startup bundle):
bundle.yaml | mvp_policy.yaml | |
|---|---|---|
| Rules location | Add-ons loaded from addons/ | External files via files: |
| Layers evaluated | connectors (from add-ons) | targets, credentials, budgets, risk, transforms |
| Attribution enforcement | None | require_attribution in stop_on |
stop_on | deny only | deny, require_approval, require_attribution, rate_limit, quarantine |
| Intended use | Quick-start, proxy mode | Full production governance |
To activate mvp_policy.yaml:
curl -X PUT http://localhost:7080/v1/admin/policies \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" \ -H "Content-Type: application/yaml" \ --data-binary @configs/examples/mvp_policy.yaml
After pushing, edit the individual layer files (targets.yaml, credential-rules.yaml, etc.) and push the bundle again to pick up changes. The file paths in files: are resolved relative to MVGC_POLICIES_DIR.
proxy-credentials.yaml
Path: configs/examples/proxy-credentials.yaml
A minimal single-layer bundle that maps AI provider hostnames to their credentials. It evaluates only the connectors layer and is intended as a customization template for transparent proxy credential routing.
schema: mvgc.policy_bundle.v1 bundle_id: proxy-credentials version: "1.0.0" ... evaluation: order: - connectors stop_on: - deny merge_strategy: first_match inline_rules: connectors: - id: proxy.allow.openai when: field: action.target_host equals: api.openai.com effect: decision: allow select_credential: cred-openai - id: proxy.allow.anthropic when: field: action.target_host equals: api.anthropic.com effect: decision: allow select_credential: cred-anthropic # ... gemini, azure_openai, cohere ... - id: proxy.deny.unknown-host priority: 500 when: field: action.target_host exists: true effect: decision: deny reason: "no credential configured for this target host"
This bundle is not loaded on startup and does not include identity, targets, budgets, risk, or transforms layers -- it performs only hostname-to-credential routing. Use it as a starting point when you need to:
- Override which credential maps to a specific provider
- Add custom Azure deployment hostnames (
<resource>.openai.azure.com) - Add non-standard or private LLM endpoints
- Restrict which hosts the transparent proxy will forward to
Push a customized copy to replace the active bundle:
cp configs/examples/proxy-credentials.yaml my-proxy-config.yaml # Edit my-proxy-config.yaml to add or change host mappings, then: curl -X PUT http://localhost:7080/v1/admin/policies \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" \ -H "Content-Type: application/yaml" \ --data-binary @my-proxy-config.yaml
proxy-mitm.yaml
Path: configs/examples/proxy-mitm.yaml
An example bundle showing the proxy: key, which controls per-workload SSL MITM domain
scope when MVGC_PROXY_MITM_ENABLED=true. This is the only bundle file that uses the
proxy: top-level key; it is otherwise a normal bundle and may include any policy rules.
schema: mvgc.policy_bundle.v1 bundle_id: proxy-mitm-example version: "1.0.0" issued_at: "2026-03-19T00:00:00Z" expires_at: "2027-03-19T00:00:00Z" # Per-bundle MITM domain scope. # managed_domains: intercept these hosts (merged with MVGC_MANAGED_DOMAINS). # bypass_domains: always pass through even if they match managed_domains. proxy: managed_domains: - api.openai.com - "*.anthropic.com" - generativelanguage.googleapis.com bypass_domains: - telemetry.anthropic.com defaults: decision: deny evaluation: order: - identity - connectors - targets stop_on: - deny merge_strategy: first_match
The proxy: key is validated at bundle load time. Empty strings in either list are
rejected. The bypass_domains list is checked first; a domain that matches both lists
is treated as bypassed.
When to use each bundle:
| Scenario | Approach |
|---|---|
| Getting started, transparent proxy with env var credentials | bundle.yaml (default, loads add-ons from addons/) |
| Production governance with all eight policy layers | Push mvp_policy.yaml via admin API |
| Customising only which credentials map to which hosts | proxy-credentials.yaml as a template |
| Configuring per-workload MITM domain scope | proxy-mitm.yaml as a template |
| Claude Code CLI proxy | mvgc-gateway addon enable claude-cli-proxy (see section 3.9) |
| Completely custom rules | Write your own bundle and push via admin API |
3.8 Complete examples
Allow all explicit requests, select credential by target host
# policies/identity.yaml schema: mvgc.policy_rules.v1 layer: identity rules: - id: identity.allow.explicit priority: 100 when: field: context.connection_type equals: direct_api effect: decision: allow
# policies/credential-rules.yaml schema: mvgc.policy_rules.v1 layer: credentials rules: - id: creds.select.openai priority: 100 when: all: - field: context.action.target_host equals: api.openai.com - field: context.attribution.project_id exists: true effect: select_connector: connector_id: openai select_credential: cred-openai enforce_attribution: require: - project_id decision: allow reason: "openai credential selected" - id: creds.deny.missing.project priority: 500 when: field: context.attribution.project_id exists: false effect: decision: deny reason: "project_id is required"
Budget enforcement with model downgrade
# policies/budgets.yaml schema: mvgc.policy_rules.v1 layer: budgets rules: - id: budgets.deny.request.cap priority: 100 when: field: context.estimates.cost_usd_max gt: "2.00" effect: decision: deny reason: "per-request cost cap exceeded ($2.00 max)" - id: budgets.downgrade.high.cost priority: 200 when: all: - field: context.action.type equals: ai.infer - field: context.estimates.cost_usd_max gt: "1.00" effect: decision: downgrade mutate: action.params.model: "gpt-4o-mini" reason: "cost estimate exceeds threshold; downgrading model"
Risk-based quarantine and rate limiting
# policies/risk.yaml schema: mvgc.policy_rules.v1 layer: risk rules: - id: risk.quarantine.critical priority: 10 when: field: context.risk.score gt: "0.8000" effect: decision: quarantine quarantine: reason: "automated quarantine: risk score exceeds critical threshold" - id: risk.approval.elevated priority: 20 when: field: context.risk.score gt: "0.5000" effect: decision: require_approval reason: "elevated risk score requires approval" - id: risk.ratelimit.burst priority: 30 when: field: context.risk.request_rate gt: "100.00" effect: decision: rate_limit rate_limit: key: "org" limit: 100 window: "1m"
Inline bundle (useful for the admin push API)
schema: mvgc.policy_bundle.v1 bundle_id: my-custom-bundle version: "1.0.0" issued_at: "2026-03-15T00:00:00Z" expires_at: "2027-03-15T00:00:00Z" defaults: decision: deny trace_level: matched_rules evaluation: order: - identity - connectors stop_on: - deny merge_strategy: first_match inline_rules: identity: - id: identity.allow.all priority: 100 effect: decision: allow connectors: - id: connectors.select.openai priority: 100 when: field: action.target_host equals: api.openai.com effect: decision: allow select_credential: cred-openai
3.9 Policy Add-Ons

The add-on system gives you a Unix-style drop-in mechanism for policy rules. Every rule file in the available/ catalog is inactive by default; placing a symlink (or copy) in addons/ activates it. The gateway loads all .yaml files from addons/ on startup, following symlinks automatically.
<MVGC_POLICIES_DIR>/
├── bundle.yaml <- skeleton: evaluation config, no inline rules
├── available/ <- catalog of all rule files (always present, inactive)
│ ├── core-identity.yaml
│ ├── provider-openai.yaml
│ ├── provider-anthropic.yaml
│ ├── provider-gemini.yaml
│ ├── provider-azure-openai.yaml
│ ├── provider-cohere.yaml
│ ├── deny-unknown-host.yaml
│ └── claude-cli-proxy.yaml
└── addons/ <- active add-ons (symlinks or copies from available/)
├── core-identity.yaml -> ../available/core-identity.yaml
├── provider-openai.yaml -> ../available/provider-openai.yaml
├── provider-anthropic.yaml -> ../available/provider-anthropic.yaml
├── provider-gemini.yaml -> ../available/provider-gemini.yaml
├── provider-azure-openai.yaml -> ../available/provider-azure-openai.yaml
├── provider-cohere.yaml -> ../available/provider-cohere.yaml
└── deny-unknown-host.yaml -> ../available/deny-unknown-host.yaml
claude-cli-proxy.yaml is in the catalog but not enabled by default. Users opt in.
Add-on catalog
| Add-on | Layer | What it does | Enabled by default |
|---|---|---|---|
core-identity | identity | Allows all three connection types (direct_api, sdk_redirect, connect_proxy) | Yes |
provider-openai | connectors | Routes api.openai.com -> cred-openai | Yes |
provider-anthropic | connectors | Routes api.anthropic.com -> cred-anthropic | Yes |
provider-gemini | connectors | Routes generativelanguage.googleapis.com -> cred-gemini | Yes |
provider-azure-openai | connectors | Routes *.openai.azure.com -> cred-azure-openai | Yes |
provider-cohere | connectors | Routes api.cohere.com -> cred-cohere | Yes |
deny-unknown-host | connectors | Catch-all deny (priority 500) for any unmatched target host | Yes |
claude-cli-proxy | connectors | Passthrough rules for claude.ai, statsig.anthropic.com, sentry.io -- used when proxying Claude Code CLI | No |
Enabling and disabling add-ons
Use the mvgc-gateway addon subcommand to manage add-ons:
# List all available add-ons and their enabled/disabled status mvgc-gateway addon list # Enable the Claude CLI proxy add-on mvgc-gateway addon enable claude-cli-proxy # Disable an add-on mvgc-gateway addon disable provider-cohere # Apply changes without restarting curl -s -X POST http://localhost:7080/v1/admin/policies/reload \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" | jq .
The addon enable command creates a symlink in addons/ pointing to available/<name>.yaml. The addon disable command removes the symlink. Call POST /v1/admin/policies/reload to pick up the change immediately -- no restart needed.
CP-connected gateways: syncing addon state from the control plane
When your gateway is connected to a control plane, the org admin enables and disables
add-ons from the console. Use addon sync to pull that state down to the local
filesystem so it survives offline periods:
# Reconcile local addon symlinks with the CP's enabled state for this org MVGC_CP_ADDR=cp.example.com:9090 \ MVGC_ORG_ID=org-abc123 \ MVGC_CP_TOKEN=$MVGC_CP_TOKEN \ mvgc-gateway addon sync
addon sync calls ListAddons on the CP and then enables or disables local symlinks
to match. Add-ons that are enabled in the CP but whose YAML is not installed locally
are skipped with a warning (upgrade the gateway package to get the latest catalog).
| Env var | Required | Description |
|---|---|---|
MVGC_CP_ADDR | Yes | Control plane gRPC address (e.g. cp.example.com:9090) |
MVGC_ORG_ID | Yes | Org ID whose addon state to sync |
MVGC_CP_TOKEN | No | Bearer token for CP authentication |
MVGC_CP_CA_CERT | No | Path to CA cert PEM for CP TLS verification |
MVGC_CP_SKIP_TLS_VERIFY | No | Set true to skip TLS cert verification (dev only) |
MVGC_CP_NO_TLS | No | Set true to disable TLS entirely (local/test only) |
The command is idempotent: safe to run on every deploy or from a cron job.
MVGC_POLICIES_DIR controls where the gateway looks for both available/ and addons/. The default is configs/policies/ relative to the working directory; installed packages set it to the installation-specific path.
Claude Code CLI proxy example:
Claude CLI contacts four hosts; only one is an AI API endpoint:
| Host | Traffic type | Gateway handling |
|---|---|---|
api.anthropic.com | AI API calls | Governed -- Anthropic connector, credential injection, metering |
claude.ai | Auth / web | Passthrough -- original headers forwarded unchanged |
statsig.anthropic.com | Feature flags / telemetry | Passthrough |
sentry.io | Error reporting | Passthrough |
# Enable the add-on and reload (no restart needed) mvgc-gateway addon enable claude-cli-proxy curl -s -X POST http://localhost:7080/v1/admin/policies/reload \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" | jq . # Set your Anthropic key and point Claude CLI at the gateway export ANTHROPIC_API_KEY=sk-ant-... export HTTPS_PROXY=http://localhost:7080 claude "Hello"
If the gateway terminates TLS with its own certificate, also set:
export NODE_EXTRA_CA_CERTS=/path/to/mvgc-ca.crt
How passthrough works: A rule with
decision: allowand noselect_credentialcauses the gateway to forward the request through the generic HTTP connector with all original client headers intact. No API key injection occurs.
How to create your own add-on
An add-on is a YAML rule file with the mvgc.rule_file.v1 schema and a single layer tag. It follows the same format as any other rule file (see section 3.2).
Step 1 -- Write the rule file
Place it in <MVGC_POLICIES_DIR>/available/. The filename (without .yaml) becomes the add-on name.
# available/provider-my-llm.yaml schema: mvgc.rule_file.v1 layer: connectors rules: - id: proxy.allow.my-llm priority: 100 when: field: action.target_host equals: api.my-llm-provider.example effect: decision: allow select_credential: cred-my-llm reason: "my-llm provider routed"
Step 2 -- Verify it appears in the catalog
mvgc-gateway addon list # ADDON LAYER RULES ENABLED # core-identity identity 2 true # deny-unknown-host connectors 1 true # ... # provider-my-llm connectors 1 false <- your new add-on
Step 3 -- Enable it
mvgc-gateway addon enable provider-my-llm # Restart the gateway to activate
Rule file schema reference:
| Field | Type | Description |
|---|---|---|
schema | string | Must be mvgc.rule_file.v1 |
layer | string | Which evaluation layer these rules belong to (identity, connectors, etc.) |
rules | []Rule | List of rules -- same format as any other rule file (see section 3.3) |
Guidelines for add-on authors:
- Use a consistent naming convention:
<category>-<name>.yaml(e.g.,provider-openai.yaml,core-identity.yaml) - Choose a priority that fits within the existing range. Provider add-ons use priority 100; the catch-all deny (
deny-unknown-host) uses priority 500 - Add-ons should be self-contained -- avoid rules that depend on variables set by another add-on
- Test with
mvgc-gateway addon listto verify the rule count and layer are parsed correctly before enabling
4. Pushing policies at runtime

Update the active policy bundle
curl -X PUT http://localhost:7080/v1/admin/policies \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" \ -H "Content-Type: application/yaml" \ --data-binary @configs/policies/bundle.yaml
Inspect active policy bundle
curl http://localhost:7080/v1/admin/policies \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN"
5. Policy file locations by installation method
The gateway loads policy files from the directory pointed to by MVGC_POLICIES_DIR. The default path depends on how the gateway was installed. Credentials are managed separately via the admin API and are not written to disk by the installer.
Quick reference
| Installation method | Policy files | Main config (mvgc.yaml) |
|---|---|---|
| Linux .deb / .rpm | /etc/mvgc/policies/ | /etc/mvgc/mvgc.yaml |
| macOS Homebrew | $(brew --prefix)/var/mvgc/configs/policies/ | $(brew --prefix)/etc/mvgc/mvgc.yaml |
| Docker Compose | /configs/policies/ (baked into image; no bind mount) | env vars in .env |
| Kubernetes / Helm | /configs/policies/ (from ConfigMap) | env vars + Kubernetes Secret |
| Tarball (manual) | configs/policies/ (relative to working directory) | mvgc.yaml (at tarball root) |
Linux packages (.deb / .rpm)
The installer places all policy files under /etc/mvgc/policies/ and marks them config|noreplace, meaning package upgrades will not overwrite files you have edited.
/etc/mvgc/mvgc.yaml <- main config (config|noreplace)
/etc/mvgc/policies/bundle.yaml <- skeleton bundle (loads from addons/ on startup)
/etc/mvgc/policies/available/ <- add-on catalog
/etc/mvgc/policies/available/core-identity.yaml
/etc/mvgc/policies/available/provider-openai.yaml
/etc/mvgc/policies/available/provider-anthropic.yaml
/etc/mvgc/policies/available/provider-gemini.yaml
/etc/mvgc/policies/available/provider-azure-openai.yaml
/etc/mvgc/policies/available/provider-cohere.yaml
/etc/mvgc/policies/available/deny-unknown-host.yaml
/etc/mvgc/policies/available/claude-cli-proxy.yaml
/etc/mvgc/policies/addons/ <- active add-ons (symlinks into available/)
/etc/mvgc/policies/addons/core-identity.yaml
/etc/mvgc/policies/addons/provider-openai.yaml
/etc/mvgc/policies/addons/provider-anthropic.yaml
/etc/mvgc/policies/addons/provider-gemini.yaml
/etc/mvgc/policies/addons/provider-azure-openai.yaml
/etc/mvgc/policies/addons/provider-cohere.yaml
/etc/mvgc/policies/addons/deny-unknown-host.yaml
/etc/mvgc/credentials/credentials.yaml <- credential seed (config|noreplace)
/etc/mvgc/workloads/workloads.yaml <- workload seed (config|noreplace)
/usr/bin/mvgc-gateway <- binary
/lib/systemd/system/mvgc-gateway.service
The systemd service reads MVGC_POLICIES_DIR from /etc/default/mvgc-gateway. To point the gateway at a different policy directory, edit that file and restart the service:
# Edit the default config sudo nano /etc/default/mvgc-gateway # MVGC_POLICIES_DIR=/etc/mvgc/policies <- change if needed # Restart sudo systemctl restart mvgc-gateway # Or push a new bundle live without restarting curl -X PUT http://localhost:7080/v1/admin/policies \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" \ -H "Content-Type: application/yaml" \ --data-binary @/etc/mvgc/policies/mvp_policy.yaml
macOS Homebrew
Homebrew installs the config under etc/ and policy files under var/ (following the convention that etc/ is for configuration and var/ is for mutable runtime data).
$(brew --prefix)/etc/mvgc/mvgc.yaml <- main config
$(brew --prefix)/var/mvgc/configs/policies/bundle.yaml <- skeleton bundle
$(brew --prefix)/var/mvgc/configs/policies/available/ <- add-on catalog
$(brew --prefix)/var/mvgc/configs/policies/available/core-identity.yaml
$(brew --prefix)/var/mvgc/configs/policies/available/provider-openai.yaml
$(brew --prefix)/var/mvgc/configs/policies/available/provider-anthropic.yaml
$(brew --prefix)/var/mvgc/configs/policies/available/provider-gemini.yaml
$(brew --prefix)/var/mvgc/configs/policies/available/provider-azure-openai.yaml
$(brew --prefix)/var/mvgc/configs/policies/available/provider-cohere.yaml
$(brew --prefix)/var/mvgc/configs/policies/available/deny-unknown-host.yaml
$(brew --prefix)/var/mvgc/configs/policies/available/claude-cli-proxy.yaml
$(brew --prefix)/var/mvgc/configs/policies/addons/ <- active add-ons
$(brew --prefix)/var/mvgc/configs/policies/addons/core-identity.yaml -> ../available/...
$(brew --prefix)/var/mvgc/configs/policies/addons/provider-openai.yaml -> ../available/...
$(brew --prefix)/var/mvgc/configs/policies/addons/provider-anthropic.yaml -> ../available/...
$(brew --prefix)/var/mvgc/configs/policies/addons/provider-gemini.yaml -> ../available/...
$(brew --prefix)/var/mvgc/configs/policies/addons/provider-azure-openai.yaml -> ../available/...
$(brew --prefix)/var/mvgc/configs/policies/addons/provider-cohere.yaml -> ../available/...
$(brew --prefix)/var/mvgc/configs/policies/addons/deny-unknown-host.yaml -> ../available/...
$(brew --prefix)/var/mvgc/configs/credentials/credentials.yaml <- credential seed
$(brew --prefix)/var/mvgc/configs/workloads/workloads.yaml <- workload seed
$(brew --prefix)/bin/mvgc-gateway <- binary
$(brew --prefix)/var/log/mvgc/gateway.log <- log output
On Apple Silicon the prefix is /opt/homebrew; on Intel it is /usr/local. To find your prefix: brew --prefix.
The launchd service sets MVGC_CONFIG to the installed mvgc.yaml. Edit that file to set your API key env vars and MVGC_ADMIN_TOKEN, then:
# Start (or restart) the service brew services restart mvgc-gateway # Or push a new bundle live without restarting curl -X PUT http://localhost:7080/v1/admin/policies \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" \ -H "Content-Type: application/yaml" \ --data-binary @"$(brew --prefix)/var/mvgc/configs/examples/mvp_policy.yaml"
Docker Compose
The customer-facing compose files (docker-compose.yaml and docker-compose.postgres.yaml) do not bind-mount a host configs/ directory. Policy files are baked into the image at /configs/policies/ and used as-is. Configuration is supplied entirely through environment variables in .env.
To push a policy bundle at runtime (no restart needed):
curl -X PUT http://localhost:7080/v1/admin/policies \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" \ -H "Content-Type: application/yaml" \ --data-binary @my-bundle.yaml
To supply custom policy files via a volume mount, use docker run directly (see the Docker and Podman guide for the three mount options).
To restart the gateway to pick up .env changes:
docker compose restart gateway
Kubernetes / Helm
The Helm chart stores policy files in a Kubernetes ConfigMap and mounts it at /configs/policies/ inside the gateway pod. MVGC_POLICIES_DIR is set to /configs/policies by default.
Kubernetes resource Mount path in pod
─────────────────────────────────── -> ────────────────────────────
ConfigMap: <release>-policies /configs/policies/
bundle.yaml, available/*.yaml,
addons/*.yaml (symlinks)
ConfigMap: <release>-credentials /configs/credentials/
credentials.yaml
ConfigMap: <release>-workloads /configs/workloads/
workloads.yaml
Secret: (user-managed) env vars injected via envFrom
DATABASE_URL, MVGC_ADMIN_TOKEN
To update policy files:
# Push a bundle live via admin API (no restart required) curl -X PUT http://localhost:7080/v1/admin/policies \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" \ -H "Content-Type: application/yaml" \ --data-binary @configs/examples/mvp_policy.yaml
Credentials are never stored in the ConfigMap. Inject them as environment variables via a Kubernetes Secret or an external secrets manager, referenced in values.yaml under adminToken.existingSecret and extraEnv.
Tarball (manual install)
The Linux release tarball extracts to a directory containing the binary, the main config, and the policy files:
mvgc-gateway <- binary
mvgc.yaml <- main config
configs/policies/bundle.yaml <- skeleton bundle
configs/policies/available/core-identity.yaml <- add-on catalog
configs/policies/available/provider-openai.yaml
configs/policies/available/provider-anthropic.yaml
configs/policies/available/provider-gemini.yaml
configs/policies/available/provider-azure-openai.yaml
configs/policies/available/provider-cohere.yaml
configs/policies/available/deny-unknown-host.yaml
configs/policies/available/claude-cli-proxy.yaml
configs/policies/addons/core-identity.yaml <- active add-ons (symlinks)
configs/policies/addons/provider-openai.yaml
configs/policies/addons/provider-anthropic.yaml
configs/policies/addons/provider-gemini.yaml
configs/policies/addons/provider-azure-openai.yaml
configs/policies/addons/provider-cohere.yaml
configs/policies/addons/deny-unknown-host.yaml
configs/credentials/credentials.yaml <- credential seed
configs/workloads/workloads.yaml <- workload seed
Set MVGC_POLICIES_DIR to the absolute path of the configs/policies/ directory before starting the gateway:
export MVGC_POLICIES_DIR=/opt/mvgc/configs/policies export MVGC_ADMIN_TOKEN=your-admin-token export DATABASE_URL=postgres://... ./mvgc-gateway
See also
- Configuration Overview -- how credentials, workloads, and policies interrelate
- Credentials -- credential reference: provider values, modes, secret resolution
- Workloads -- workload reference: default attribution, ingress modes