Auth & Security
API keys, bearer tokens, JWT/OIDC, fail-closed production mode, and the production checklist.
Krishiv supports four layers of authentication. Pick the right one for your topology, then verify that production mode (KRISHIV_PRODUCTION=1) is happy with the rest of your config.
1. API keys (Flight SQL and SQL API)
Static keys for short-lived clients and CI. Configure server-side:
# Comma-separated key=user pairs
export KRISHIV_API_KEYS="key1=alice,key2=ci-bot,key3="
Client-side:
krishiv sql --api-key key1 --query "SELECT 1"
import krishiv as ks
session = ks.Session.connect("http://coord:50051")
session.sql_as("key1", "SELECT 1")
API keys are intended for service-to-service auth, not for end users.
2. Bearer tokens (gRPC and HTTP management)
The default for production. Tokens are validated on every gRPC call and every /api/v1 HTTP call.
| Variable | Purpose |
|---|---|
KRISHIV_COORDINATOR_BEARER_TOKEN | Single static token. Set on the coordinator and on every client. |
KRISHIV_COORDINATOR_BEARER_TOKEN_FILE | File path. Hot-reloaded when the file changes. Use for Kubernetes secrets mounted as files. |
KRISHIV_COORDINATOR_BEARER_TOKENS | Comma/newline-separated list of accepted tokens. Use for rotation windows. |
KRISHIV_COORDINATOR_BEARER_TOKENS_FILE | File with one token per line. Hot-reloaded. |
KRISHIV_COORDINATOR_AUTH_RELOAD_INTERVAL_SECS | How often to re-read the file. Default: 60 s. |
KRISHIV_EXECUTOR_TASK_BEARER_TOKEN | Token the executor presents to the coordinator's task-control gRPC. |
Rotation
- Set
KRISHIV_COORDINATOR_BEARER_TOKENS=<new>,<old>on the coordinator. Both are accepted. - Roll clients to use the new token.
- Remove the old token from the list.
3. JWT / OIDC (SSO, end-user auth)
For end-user auth, plug in an OIDC provider. Krishiv validates the bearer token against a JWKS endpoint.
| Variable | Purpose |
|---|---|
KRISHIV_OIDC_JWKS_URI | OIDC JWKS URL. Set on the coordinator. |
KRISHIV_OIDC_AUDIENCE | Required in production. The aud claim must match. |
Programmatic:
use krishiv_api::{Session, JwtAuthProvider};
let provider = Arc::new(JwtAuthProvider::new(jwks_url, audience));
let session = Session::connect("http://coord:50051")
.with_auth(provider);
Roles: validate_grpc_auth / validate_grpc_auth_for_role enforce role-based access for management endpoints. Standard roles: admin, writer, reader.
4. UI bearer token
Separate from gRPC. Set KRISHIV_UI_TOKEN on the coordinator. The browser is prompted for the token; it is stored in localStorage and sent as Authorization: Bearer ... on every UI request.
/healthz always stays anonymous (so liveness probes work). All other UI routes require the token.
Production fail-closed
Set KRISHIV_PRODUCTION=1 (or any truthy value) to enable the production checks. The runtime will refuse to start, or refuse the offending command, if it detects:
| Check | What it rejects |
|---|---|
requires_http_auth | Coordinator or UI started without a bearer token in production. |
requires_file_backed_state | State backend that is not RocksDB on local disk or a checkpointed object store. |
allows_alpha_api | Calls to the alpha API surface (e.g. unbounded_memory_stream). |
allows_memory_checkpoint_uri | Checkpoint URI of memory:// or ephemeral://. |
allows_unbounded_shuffle_store | Shuffle store that has no upper bound (e.g. in-memory only). |
forbids_simulation_connectors | Connectors marked as test/simulation in production. |
profile_requires_durable_window_state | Stateful streaming operators with non-durable state. |
profile_requires_authenticated_flight | Flight SQL exposed without auth. |
profile_requires_authenticated_ui | UI exposed without auth. |
profile_requires_fail_closed_metadata | Metadata store without fencing / leases. |
profile_forbids_native_scalar_udfs | Native scalar UDFs (which have full process access). |
requires_manual_kafka_commit | Kafka sources where commits are not bound to checkpoints. |
allow_legacy_task_fragments | Pre-R5 task fragment format. |
allow_anonymous_http_override | Anonymous HTTP enabled by an override env (only via ALLOW_ANONYMOUS_HTTP_ENV). |
Policy hooks (table-level access control)
Beyond authentication, you can attach a PolicyHook to a session or to specific tables:
use krishiv_api::{Session, PolicyHook};
struct MyPolicy;
impl PolicyHook for MyPolicy {
fn on_query(&self, q: &ParsedQuery) -> Result<(), PolicyError> {
if q.references_table("pii") && !q.has_tag("allow_pii") { Err(PolicyError::Denied) }
}
}
let session = Session::builder().with_policy(Arc::new(MyPolicy)).build().await?;
Production checklist
-
KRISHIV_PRODUCTION=1 -
KRISHIV_COORDINATOR_BEARER_TOKENset (or OIDC configured) -
KRISHIV_OIDC_AUDIENCEset if using OIDC -
KRISHIV_UI_TOKENset -
KRISHIV_EXECUTOR_TASK_BEARER_TOKENset on every executor - Checkpoint storage is an object store, not
memory:// - Durability profile is
single-node-durableordistributed-durable - No anonymous HTTP overrides (
KRISHIV_ALLOW_ANONYMOUS_HTTP=1) - No manual Kafka commit (
requires_manual_kafka_commit) - OTLP tracing endpoint set (
OTEL_EXPORTER_OTLP_ENDPOINT)