Athena Contracts
The canonical contract model for Athena client identity, capabilities, connection, routing, and backend dispatch.
Athena is strongest when the top level owns the truth.
That means Athena, not PostgreSQL, Scylla, Supabase, or S3, defines:
- what a client is
- what operations a client is allowed to perform
- how a request is resolved
- what response and error envelopes look like
- what observability metadata is emitted
This page documents the contract model that keeps Athena in charge even when multiple backends sit underneath it.
Use this page as the architecture contract source of truth when introducing new backends, refactoring route handlers, or deciding whether a feature belongs to Athena core or to a backend-specific driver.
Mental model
The important boundary is simple: Athena decides identity, policy, dispatch, and envelope shape. Drivers only implement backend work beneath that decision.
Contract stack
Athena should be read as a stack of contracts, from most authoritative to least:
- Client identity contract: what an Athena client is.
- Lifecycle contract: whether that client is active, frozen, or inactive.
- Capability contract: what that client is allowed to do.
- Connection contract: what engine-specific connection data exists.
- Resolver contract: how Athena turns a client name into a runtime backend.
- Dispatch contract: how routes call the resolved backend.
- Driver contract: what a backend driver must implement.
- Backend extension contracts: what remains intentionally engine-specific.
1. Client identity contract
An Athena client is a logical backend binding, not a Postgres connection string with optional metadata.
Canonical shape:
pub struct AthenaClientDefinition {
pub client_name: String,
pub engine: AthenaEngine,
pub lifecycle: AthenaClientLifecycle,
pub capabilities: AthenaCapabilities,
pub connection: AthenaConnection,
pub metadata: serde_json::Value,
}Rules:
client_nameis the stable Athena-facing identifier used byX-Athena-Client.engineis explicit and never inferred from unrelated fields when a first-class engine tag exists.metadatais supplementary. It may enrich a client definition, but it must not be the only place where Athena learns the client's core identity.- Route handlers should consume
AthenaClientDefinition, not raw catalog rows.
2. Lifecycle contract
Lifecycle is Athena-owned state, not backend-owned health.
Suggested shape:
pub enum AthenaClientLifecycle {
Active,
Frozen,
Inactive,
}Semantics:
Active: Athena may resolve and dispatch requests.Frozen: Athena knows the client but intentionally blocks execution.Inactive: Athena keeps the definition but does not treat it as runnable.
Important distinction:
- Lifecycle is a policy decision.
- Runtime connectivity is an operational fact.
A client can be Active but currently unavailable because its backend cannot be reached. Athena should keep those ideas separate.
3. Capability contract
Capabilities are the most important contract after identity because they keep Athena honest about what “compatible” means.
Suggested shape:
pub struct AthenaCapabilities {
pub gateway_query: bool,
pub raw_sql: bool,
pub raw_cql: bool,
pub schema_read: bool,
pub migrations_read: bool,
pub provisioning: bool,
pub deferred_query: bool,
pub cdc: bool,
pub object_read: bool,
pub object_write: bool,
}Rules:
- Routes check capabilities before touching backend-specific code.
- Unsupported operations fail as unsupported Athena capabilities, not as accidental backend errors.
- New engines become safe to add because Athena can say “this client exists, but it does not support this route family.”
4. Connection contract
Connection data must be a tagged union, not a Postgres-first row with sidecar metadata for everything else.
Suggested shape:
pub enum AthenaConnection {
Postgres(PostgresConnectionInfo),
Scylla(ScyllaConnectionInfo),
S3(S3ConnectionInfo),
}Rules:
- Athena chooses the connection variant based on the client definition.
- Route handlers never pattern-match on ad hoc metadata blobs.
- Connection structs are backend-specific, but the enum is Athena-owned.
5. Resolver contract
Athena needs two resolution stages:
- Resolve the logical client definition.
- Resolve the runtime backend handle.
Suggested shape:
pub enum AthenaResolvedBackend {
Postgres(PostgresBackendHandle),
Scylla(ScyllaBackendHandle),
S3(S3BackendHandle),
}
pub trait AthenaBackendResolver {
async fn resolve_client(
&self,
state: &AppState,
client_name: &str,
) -> Result<AthenaClientDefinition, AthenaResolveError>;
async fn resolve_backend(
&self,
state: &AppState,
client: &AthenaClientDefinition,
) -> Result<AthenaResolvedBackend, AthenaResolveError>;
}Rules:
X-Athena-Clientresolves to a client definition first, never directly to aPgPool.- Runtime registries are implementation details of backend resolution.
- Missing clients, frozen clients, and unreachable backends should produce different Athena errors.
6. Dispatch contract
Every route family should follow the same top-level flow:
auth -> resolve client -> require capability -> resolve backend -> execute -> normalize responseThat means route handlers should depend on Athena contracts like:
AthenaClientDefinition- capability checks
AthenaResolvedBackend- shared response and error builders
They should not depend directly on:
PgPoolClientConnectionTargetScyllaConnectionInfo- backend-specific catalog parsing
7. Driver contract
Drivers are Athena’s backend adapters. They implement execution and backend-specific metadata access, but they do not define Athena identity.
Suggested execution-facing traits:
#[async_trait]
pub trait AthenaQueryBackend {
async fn execute_raw_query(
&self,
request: AthenaRawQueryRequest,
) -> Result<AthenaQueryResult, AthenaBackendError>;
}
#[async_trait]
pub trait AthenaSchemaBackend {
async fn list_tables(&self) -> Result<Vec<AthenaTableInfo>, AthenaBackendError>;
}
#[async_trait]
pub trait AthenaProvisioningBackend {
async fn provision(
&self,
request: AthenaProvisionRequest,
) -> Result<AthenaProvisionResult, AthenaBackendError>;
}Rules:
- Drivers implement Athena contracts, not the other way around.
- A backend only implements the traits it truly supports.
- Athena route handlers remain stable even when driver implementations change.
8. Backend extension contracts
Not every feature should be universal. Athena should explicitly model backend-specific surfaces.
| Backend | Athena-owned contract | Backend-owned extension |
|---|---|---|
| PostgreSQL | client identity, capabilities, dispatch, error shape | pool lifecycle, SQL execution, information_schema, provisioning |
| Scylla | client identity, capabilities, dispatch, error shape | CQL execution, keyspace/session behavior, cluster node topology |
| S3 | client identity, capabilities, dispatch, error shape | bucket/object semantics, signed URL generation, storage metadata |
Rule:
- Athena owns the stable platform surface.
- Backend extensions are allowed only below that surface.
Route family contracts
Each route family should advertise the capability it requires and whether it is intended to be engine-neutral or engine-specific.
| Route family | Required capability | Intended scope |
|---|---|---|
/gateway/query | gateway_query | Athena-level query execution contract |
/query/sql and /gateway/sql | raw_sql or raw_cql depending on request language | Engine-aware raw query contract |
/query/count | query capability plus count support | Athena-level, but only where the engine can support it honestly |
/schema/* | schema_read | Backend-specific introspection behind an Athena contract |
/schema/migrations | migrations_read | Usually Postgres/Supabase-specific |
/management/* provisioning | provisioning | Explicitly capability-gated, not universal by default |
/storage/* | object_read / object_write | Storage-family contract |
/pipelines | pipeline execution plus source/sink capability checks | Athena orchestration contract |
Current drift to remove
Athena’s current implementation still contains several Postgres-shaped assumptions that this contract model is meant to remove.
Catalog drift
- Postgres connection fields are first-class in catalog storage.
- Non-Postgres backends are still partially inferred from metadata.
Desired end state:
- engine is explicit
- connection is typed
- metadata is supplementary only
Runtime drift
- some resolution paths still ask for a Postgres pool first
- some route helpers still talk in terms of “Postgres client” rather than “Athena client”
Desired end state:
- route helpers resolve Athena clients first
- backend handles are resolved afterward
Capability drift
- some routes are implicitly Postgres-only without saying so
- some backend differences appear as implementation quirks rather than explicit feature flags
Desired end state:
- unsupported behavior is reported through Athena capability checks
Current route status
This is the practical contract state Athena should communicate today while the dominance refactor is still underway.
| Concern | Current practical state |
|---|---|
| Client identity | still partially Postgres-shaped in catalog and runtime helpers |
| Raw Scylla execution | available for the Scylla path, but not yet a fully equal first-class platform contract |
Deferred /gateway/query | not yet universal across backends |
| Schema introspection | still effectively Postgres/Supabase oriented |
| Provisioning and management | still heavily Postgres-oriented |
| Storage operations | separate storage-family contract, not part of the SQL/CQL contract |
Response and error contract
Even when execution diverges by backend, Athena should preserve a stable outer contract:
- consistent auth failure envelope
- consistent not-found / frozen / inactive distinctions
- stable request correlation and trace metadata
- stable metrics and operation logs
- backend name recorded as metadata, not leaked as the sole contract shape
Rule:
- Athena normalizes the caller-facing envelope.
- Drivers contribute detail, but do not own the public response contract.
Observability contract
Every routed operation should emit Athena-level telemetry:
- request id
- logical client name
- resolved backend engine
- route family
- operation outcome
- latency and availability signals
Backend-specific diagnostics may be attached, but the top-level telemetry dimensions must remain Athena-native.
SDK contract
The SDKs should mirror Athena’s contract hierarchy:
- client selects an Athena logical client
- Athena resolves the engine internally
- SDK APIs expose capability-aware operations
- engine-specific escape hatches are explicit, not accidental
Rule:
- the SDK should not force callers to think in backend-native routing terms unless they intentionally opt into a backend-specific escape hatch
Anti-contracts
These are the invariants Athena should reject:
- “an Athena client is a
pg_uri” - “route handlers may resolve directly to
PgPool” - “metadata is where backend identity lives forever”
- “backend-specific implementation failures are the same thing as unsupported Athena features”
- “compatibility means pretending every backend supports the same semantics”
Recommended implementation order
- Introduce Athena-owned client, connection, lifecycle, and capability types.
- Add a catalog adapter that can produce those types from current storage.
- Add a resolver that returns
AthenaResolvedBackend. - Move
queryandgatewayroute families onto that resolver. - Convert schema, provisioning, storage, and pipeline families into explicit capability-checked contracts.
- Replace the remaining Postgres-shaped catalog assumptions with engine-neutral storage.