Athena

Rights and Client Binding

How Athena matches granted rights, bound clients, and resource-specific permissions.

After Athena proves that a key is real, it still has to prove that the key is allowed to do the requested work. That logic lives in right_matches, missing_required_rights, read_right_for_resource, write_right_for_resource, delete_right_for_resource, and rpc_right.

Client binding comes before route execution

If an ApiKeyRecord has a non-empty client_name, the request must present the same X-Athena-Client value or Athena rejects it with a 403.

This is how Athena keeps one key scoped to one logical client even when the server can reach many client pools.

Right matching rules

Athena uses a permissive wildcard matcher in right_matches.

Granted rightWhat it satisfies
users.readonly users.read
users.*every action on users
*.readread access on any resource
gateway.readread requirements on any resource-style gateway operation
gateway.*any gateway-style right
*every right

That means Athena can support both narrow rights and operational wildcard keys.

Common right shapes

Resource-derived gateway rights

For fetch, update, insert, and delete style calls, Athena can derive rights from the resource name:

  • users.read
  • users.write
  • users.delete

When the resource is missing or includes a dotted schema name like public.users, Athena falls back to the broader gateway forms:

  • gateway.read
  • gateway.write
  • gateway.delete

Fixed operation rights

Other routes use explicit rights, for example:

  • gateway.query
  • gateway.rpc.execute
  • gateway.storage_proxy
  • management.read
  • management.tables.write
  • management.indexes.drop

See the generated Athena OpenAPI reference and the management route pages for route-specific requirements.

Rights must exist before a key can use them

create_api_key and save_api_key both call ensure_rights_exist. If you attach a right name that is not present in api_key_rights, Athena rejects the mutation instead of storing a dangling grant.

Bootstrap a right

curl -X POST "http://localhost:4052/admin/api-key-rights" \
  -H "X-Athena-Admin-Key: $ATHENA_KEY_12" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "gateway.query",
    "description": "Run /gateway/query"
  }'

Create a client-bound key with that right

curl -X POST "http://localhost:4052/admin/api-keys" \
  -H "X-Athena-Admin-Key: $ATHENA_KEY_12" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "analytics-query-runner",
    "client_name": "analytics",
    "rights": ["gateway.query"]
  }'

Full runtime example

curl -X POST "http://localhost:4052/gateway/query" \
  -H "X-Athena-Client: analytics" \
  -H "X-Athena-Key: $ANALYTICS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "select now() as executed_at"
  }'

This succeeds only when:

  • the key is valid
  • the key is either unbound or bound to analytics
  • the key has gateway.query or a wildcard that satisfies it
  • the IP policy allows the request

Status codes from this stage

FailureStatus
Missing or invalid key when enforcement is on401
Client mismatch403
Missing rights403
IP policy denial403