> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rkat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Decisions

> Policy enforcement: auth rules, console access, runtime ops constraints, release validation, and BigQuery naming.

*Mirrored from Meerkat Meta main at [26f5ae9da1fd](https://github.com/lukacf/meerkat/commit/26f5ae9da1fd3caac378f1cd847ce50f9ec5ac92) on 2026-11-03; Meta version 0.9.9 at that commit.*

Meta's decision layer enforces operational policies through typed validation functions. Each function takes a policy or configuration input and returns a typed error on violation.

## Console access

`enforce_console_route_access` checks whether a request should be allowed to access console routes.

```rust theme={null}
enforce_console_route_access(
    &auth_policy,
    &console_policy,
    &ConsoleAccessRequest {
        provider: AuthProvider::GoogleOAuth,
        email: "admin@example.com".to_string(),
    },
)?;
```

### Decision rules

| # | Condition | Result |
| - | - | - |
| 1 | `require_app_auth` is `false` | Allow |
| 2 | Provider is `ServiceIdentity` | Validate `svc:` prefix and allowlist |
| 3 | Provider does not match `default_provider` | `AuthProviderMismatch` |
| 4 | Provider is `TestProvider` | `AuthProviderNotSupported` |
| 5 | Email not in `email_allowlist` | `EmailNotAllowlisted` |
| 6 | All checks pass | Allow |

***

## Runtime ops validation

`validate_runtime_ops_policy` enforces v0.1 operational constraints.

```rust theme={null}
validate_runtime_ops_policy(&RuntimeOpsPolicy {
    replica_count: 1,
    metrics: MetricsPolicy { enforce_slo_targets: false },
})?;
```

| Constraint | Required value | Error |
| - | - | - |
| `replica_count` | Must be `1` | `ReplicaCountMustBeOne` |
| `metrics.enforce_slo_targets` | Must be `false` | `SloTargetsNotSupportedV01` |

***

## BigQuery naming

`validate_bigquery_naming` ensures dataset and table names are safe.

```rust theme={null}
validate_bigquery_naming(&BigQueryNaming {
    dataset: "meta_sessions".into(),
    table: "session_rows".into(),
})?;
```

### Validation rules

* Dataset must be non-empty
* Table must be non-empty
* Both must contain only ASCII alphanumeric characters, underscores, and hyphens
* No dots, semicolons, spaces, or other special characters

***

## Trusted module loading

`load_trusted_meta_modules_from_toml` parses and validates a trust manifest.

```rust theme={null}
let modules = load_trusted_meta_modules_from_toml(r#"
[[modules]]
id = "router"
command = "meerkat-router"
"#)?;
```

### Validation rules

* Module `id` must be non-empty
* Module `command` must be non-empty
* TOML must parse correctly; an empty manifest (no `[[modules]]` table) declares no modules
* Restart policy defaults to `OnFailure` when omitted

***

## Release metadata

`validate_release_metadata` ensures release targets are complete and the support matrix is valid.

```rust theme={null}
validate_release_metadata(&ReleaseMetadata {
    targets: vec![
        "crates.io".into(),
        "npm".into(),
        "pypi".into(),
        "github-releases".into(),
    ],
    support_matrix: "same-as-meerkat".into(),
})?;
```

### Required targets

Every release must include all four targets:

| Target | Registry |
| - | - |
| `crates.io` | Rust crate |
| `npm` | TypeScript/React package |
| `pypi` | Python package |
| `github-releases` | Binary releases |

### Validation rules

* No duplicate targets
* All required targets must be present
* `support_matrix` must equal `"same-as-meerkat"`

***

## Runtime decision state

`build_runtime_decision_state` turns raw policy documents into the
`RuntimeDecisionState` the runtime consults while serving. It runs every check
below in order and returns the first refusal as a typed
`DecisionRuntimeError::Policy(DecisionPolicyError)`; nothing panics, so a host
that crashes at startup is unwrapping the result.

```rust theme={null}
let decisions = build_runtime_decision_state(RuntimeDecisionInputs {
    bigquery: BigQueryNaming { dataset: "meta".into(), table: "sessions".into() },
    trusted_meta_toml: String::new(),          // empty manifest: no trusted modules
    auth: AuthPolicy::default(),
    trusted_oidc: TrustedOidcRuntimeConfig {
        discovery_json: discovery_json,          // must carry issuer and jwks_uri
        jwks_json: jwks_json,                    // at least one key
        audience: "my-console".into(),
        require_verified_email: false,
    },
    console: ConsolePolicy::default(),           // require_app_auth: true
    ops: RuntimeOpsPolicy::default(),
    release_metadata_json: release_metadata_json,
})?;
```

| # | Check | Input | Runs when | Error |
| - | - | - | - | - |
| 1 | `validate_bigquery_naming` | `bigquery` | Always | `EmptyBigQueryDataset`, `EmptyBigQueryTable`, `InvalidBigQueryName` |
| 2 | `load_trusted_meta_modules_from_toml` | `trusted_meta_toml` | Always; an empty string is accepted and declares no modules | `TomlParse`, `MissingModuleId`, `MissingModuleCommand` |
| 3 | `validate_runtime_ops_policy` | `ops` | Always | `ReplicaCountMustBeOne`, `SloTargetsNotSupportedV01` |
| 4 | `parse_release_metadata_json` + `validate_release_metadata` | `release_metadata_json` | Always. Nothing reads the result at serve time; this is release governance carried in the build path | `TomlParse` (message names the release metadata JSON), `MissingReleaseTarget`, `DuplicateReleaseTarget`, `InvalidSupportMatrix` |
| 5 | `validate_trusted_oidc_runtime_config` | `trusted_oidc` | Only when `console.require_app_auth` is `true` | `InvalidTrustedAuthConfig` |

Check 5 requires a non-empty `audience`, a discovery document with `issuer`
and `jwks_uri`, and a JWKS with at least one key. It is skipped with
`require_app_auth: false` because an open console never verifies a token on
its own; the console's request-time path re-validates the same snapshot and
fails closed whenever a token actually has to be checked. `ConsolePolicy::default()`
has `require_app_auth: true`, so a host that keeps the default still needs a
complete snapshot.

One consequence of the conditional check: an open console with an ABAC
`AccessController` installed still resolves a token a caller volunteers, and a
broken snapshot then degrades that caller to anonymous instead of refusing at
startup. A host that wants the startup refusal on an open console calls
`validate_trusted_oidc_runtime_config(&inputs.trusted_oidc)?` itself before
building.

### Opting out of console auth

`RuntimeDecisionState::local_console(console, bigquery)` is the explicit
opt-out and the single owner of the keyless snapshot both gateway binaries
serve: a discovery document whose issuer resolves nowhere, a JWKS with no
keys, no trusted modules, default auth and ops policies, and the canonical
release metadata. It bypasses the validator because there is nothing to
validate about a snapshot that trusts nothing.

```rust theme={null}
// Local or host-protected console: no identity provider.
let decisions = RuntimeDecisionState::local_console(
    ConsolePolicy { require_app_auth: false, ..ConsolePolicy::default() },
    None, // session-store naming defaults to default_dataset / default_table
);
// A host that runs trusted modules adds them; the state starts with none.
let decisions = decisions.with_modules(trusted_modules);
```

At serve time the runtime reads `console`, the session-store naming and
`modules` (the trusted module set console ingress consults); `local_console`
takes the first two as parameters and `with_modules` covers the third.

The default stays fail-closed: `local_console(ConsolePolicy::default(), None)`
requires app auth and trusts no key, so it refuses every request. This is
exactly what `rpc_gateway` serves when an SDK launch passes neither
`runtime_options.auth_config` nor `console_require_app_auth: false`; it logs a
startup warning naming the condition.

`RuntimeDecisionState` has public fields, so a struct literal is also a
supported construction path; it skips every check above.

***

## Error types

All decision functions return `DecisionPolicyError`:

| Variant | Cause |
| - | - |
| `EmptyBigQueryDataset` | BigQuery dataset name is empty |
| `EmptyBigQueryTable` | BigQuery table name is empty |
| `InvalidBigQueryName` | Name contains disallowed characters |
| `TomlParse` | A policy document failed to parse: the trust manifest (TOML) or the release metadata (JSON). The message names the document and format; the variant name predates the JSON use and is kept because the enum is not `#[non_exhaustive]` |
| `MissingModuleId` | Module declaration has empty ID |
| `MissingModuleCommand` | Module declaration has empty command |
| `AuthProviderMismatch` | Request provider differs from configured |
| `AuthProviderNotSupported` | TestProvider used in production |
| `EmailNotAllowlisted` | Email not in allowlist |
| `InvalidServiceIdentity` | Service identity format invalid |
| `ServiceIdentityNotAllowlisted` | Service identity not in allowlist |
| `ReplicaCountMustBeOne` | Replica count is not 1 |
| `SloTargetsNotSupportedV01` | SLO targets enabled in v0.1 |
| `MissingReleaseTarget` | Required release target missing |
| `DuplicateReleaseTarget` | Same target listed twice |
| `InvalidSupportMatrix` | Support matrix is not "same-as-meerkat" |
| `InvalidTrustedAuthConfig` | Trusted OIDC snapshot invalid: empty audience, discovery without `issuer`/`jwks_uri`, or a JWKS with no keys. Checked at build time only when `console.require_app_auth` is `true` |

## See also

* [Authentication](https://docs.rkat.ai/meta/guides/authentication.md) -- auth policy usage
* [Configuration](https://docs.rkat.ai/meta/reference/configuration.md) -- all configuration types
* [Governance](https://docs.rkat.ai/meta/reference/governance.md) -- release candidate tracking


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.