Athena

Security, auth ordering, and rate limits

How Athena enforces authentication on sensitive routes, orders auth before body parsing, and applies optional inbound and outbound rate limits.

This page summarizes product-facing behavior for the API hardening features: who can call which routes, 401 vs 400 semantics, gateway rights, optional rate limits, and stats / fetch typing. For day-to-day integration, pair this with API Overview and the OpenAPI reference.

Storage API (/storage/*)

Authentication

  • Every /storage/* request must satisfy require_admin_or_gateway:
    • Static admin: ATHENA_KEY_12 set, and the same value sent as X-Athena-Key or X-Athena-Admin-Key, or
    • Gateway API key: a valid ath_* key in X-Athena-Key with the gateway.storage_proxy right (storage proxy for user-supplied S3-compatible credentials).

Auth before JSON (401 vs 400)

  • Storage uses service middleware so authentication runs before the Json body extractor.
  • Callers without valid credentials get 401 Unauthorized even if the body is missing or not valid JSON.
  • After a successful auth, malformed JSON or validation failures can still return 400 Bad Request (for example invalid bucket / endpoint shape).

Validation (after auth)

  • Bucket names must satisfy S3 DNS-style rules (length, charset, no adjacent dots).
  • Endpoint must be a URL with host only (no path, query, fragment, or userinfo). HTTPS is required unless ATHENA_STORAGE_ALLOW_HTTP=1 is set for non-TLS dev endpoints.
  • Optional scanner-oriented checks may return 400 for obvious automation substrings in fields.

Inbound rate limiting

  • When enabled, the storage group applies after auth (per-IP, optional X-Forwarded-For trust). See Inbound rate limits below.

See also Storage API.

Schema, introspection, registry, and OpenAPI

These surfaces require admin key or gateway key with read-appropriate rights (same require_admin_or_gateway pattern as other protected metadata):

  • GET /schema/* (clients, tables, columns, migrations, etc.)
  • GET /clients
  • GET /router/registry
  • GET /registry, GET /registry/{id}
  • GET /openapi.yaml, GET /openapi-wss.yaml

Callers still use X-Athena-Client where the handler resolves a logical client for Postgres-backed schema reads. Health-style endpoints (for example /ping, cluster health) stay public for load balancers.

See Schema and Introspection.

Raw SQL (/query/sql, /gateway/sql)

  • require_admin_or_gateway with gateway.query (and the request’s client binding via X-Athena-Client where applicable) runs before pool resolution.
  • driver is restricted to an allowlist (for example athena, postgresql / postgres, supabase) with a short maximum length; unsupported values return 400 after auth.

Backup admin (/admin/backups/*)

  • Protected by ATHENA_KEY_12 (same header rules as the static admin key) plus optional inbound rate limit group backup_admin.
  • Handlers authenticate before logging user-controlled query or body content at INFO; list filters use redacted summaries where appropriate.

Inbound rate limits

Optional per-route-group throttles (token-bucket via governor) keyed primarily by client IP, with optional use of the first hop of X-Forwarded-For when rate_limit_trust_x_forwarded_for is enabled under gateway in config (or ATHENA_RATE_LIMIT_TRUST_X_FORWARDED_FOR).

When a limit trips, Athena responds with 429 Too Many Requests and may include Retry-After.

Groups (each has _enabled, _per_second, _burst):

GroupTypical routes
storage/storage/* (after auth)
schema/schema/*, related introspection
raw_sql/query/sql, /gateway/sql
backup_admin/admin/backups/*

Config keys (under the gateway: section of config.yaml, names as loaded by the server):

  • rate_limit_inbound_{group}_enabled
  • rate_limit_inbound_{group}_per_second
  • rate_limit_inbound_{group}_burst
  • rate_limit_trust_x_forwarded_for

Environment overrides follow the pattern ATHENA_RATE_LIMIT_INBOUND_{GROUP}_ENABLED, ATHENA_RATE_LIMIT_INBOUND_{GROUP}_PER_SECOND, ATHENA_RATE_LIMIT_INBOUND_{GROUP}_BURST (group in uppercase, e.g. STORAGE, RAW_SQL).

Defaults keep limits off unless _enabled is set, so normal deployments are not throttled until you opt in.

Outbound Supabase rate limits

To cap egress HTTP from Athena to Supabase (for example under abuse or retry storms), configure under gateway::

  • rate_limit_outbound_supabase_enabled
  • rate_limit_outbound_supabase_per_second
  • rate_limit_outbound_supabase_burst

When the bucket is exhausted, gateway paths that call Supabase may short-circuit with a service unavailable–style response (see server logs at debug for throttle reasons). Defaults are conservative (off or high) so legitimate bulk work is not blocked until you tune them.

Gateway fetch and stats tables

For client_statistics and client_table_statistics, equality filters on numeric and timestamp columns must not be sent to Postgres as untyped text (which can produce 42883).

Athena merges information_schema column types from your pool with a static fallback descriptor map for these tables when live metadata is empty or incomplete. That keeps string JSON filter values compatible with bigint and double precision columns.

See Gateway API for fetch usage.

Stats rollup and database indexes

Heavy INSERT … SELECT rollups from gateway_request_log / gateway_operation_log into stats tables benefit from appropriate indexes. Apply DBA-reviewed migrations such as:

  • sql/migrations/add_gateway_operation_log_stats_index.sql — partial index on (client, table_name, operation) for client_table_statistics rollups.

Operations checklist

  • gateway.api_key_fail_mode: use fail_closed in production so API key enforcement cannot silently allow traffic when the key store is unavailable.
  • OpenAPI / docs: after changing auth or status codes on routes, regenerate or sync openapi.yaml and publish docs so clients see 401 / 429 where applicable.
  • Invariant: no S3 SDK calls, s3cmd, or other storage I/O for user-supplied bucket/endpoint/credentials until auth has succeeded for that request.