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 right | What it satisfies |
|---|---|
users.read | only users.read |
users.* | every action on users |
*.read | read access on any resource |
gateway.read | read 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.readusers.writeusers.delete
When the resource is missing or includes a dotted schema name like
public.users, Athena falls back to the broader gateway forms:
gateway.readgateway.writegateway.delete
Fixed operation rights
Other routes use explicit rights, for example:
gateway.querygateway.rpc.executegateway.storage_proxymanagement.readmanagement.tables.writemanagement.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.queryor a wildcard that satisfies it - the IP policy allows the request
Status codes from this stage
| Failure | Status |
|---|---|
| Missing or invalid key when enforcement is on | 401 |
| Client mismatch | 403 |
| Missing rights | 403 |
| IP policy denial | 403 |