# How to Manage API Key Rotation and Disabled Key Recovery in AxonHub

> Learn to manage AxonHub API key rotation and disabled key recovery. Securely rotate keys, switch channels, disable old ones, and recover accidentally disabled keys with ease.

- Repository: [Loop/axonhub](https://github.com/looplj/axonhub)
- Tags: how-to-guide
- Published: 2026-03-06

---

**AxonHub provides a complete lifecycle management system for API keys through its business logic layer in [`internal/server/biz/api_key.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/api_key.go), allowing you to rotate keys by creating new ones, switching channel references, and disabling old keys, with full recovery capabilities for accidentally disabled keys via the enable endpoint.**

API key rotation is a critical security practice for maintaining secure integrations, and the `looplj/axonhub` repository implements a robust ORM-based system for managing these credentials. The platform stores API keys in a SQLite or configured SQL database via the Ent ORM, with dedicated service layers handling validation, enablement, and quota tracking.

## Understanding the API Key Architecture in AxonHub

### Core Service Layer

The primary business logic resides in [`internal/server/biz/api_key.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/api_key.go), which exposes methods including `Create`, `Get`, `Update`, `Enable`, `Disable`, and `Delete`. This service enforces validation rules such as ensuring keys follow the format `<refreshToken>|<projectID>` and that associated projects exist before persistence.

Channel-specific validation occurs in [`internal/server/biz/channel_apikey.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/channel_apikey.go), which verifies that a channel has an enabled key before processing requests. When a disabled key is encountered, this layer returns explicit errors such as *"api key not enabled"* or *"failed to disable api key"* to prevent silent failures.

### Database Schema and Validation

The Ent schema defined in [`internal/ent/schema/api_key.go`](https://github.com/looplj/axonhub/blob/main/internal/ent/schema/api_key.go) structures the database table with fields including `Key`, `Name`, `Enabled` (boolean), `ProjectID`, and quota-related columns. This schema ensures that key lookups by project ID are indexed for performance, supporting high-throughput rotation scenarios.

## How to Rotate API Keys in AxonHub

### Step 1: Create a New API Key

Initiate rotation by generating a fresh key through the HTTP API or GraphQL mutation. The service validates that your key string contains the pipe-delimited format before storage.

```bash
curl -X POST https://<axonhub-host>/api/keys \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "production-rotation-2024",
    "key": "newRefreshToken|proj-12345"
  }'

```

### Step 2: Update Channel Configuration

Channels reference API keys by their database ID. Update your channel configuration to point to the newly created key ID before disabling the previous one to ensure zero-downtime rotation.

```bash
curl -X PUT https://<axonhub-host>/api/channels/<channel-id> \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "apiKeyId": "<new-key-id>"
  }'

```

### Step 3: Disable the Previous Key

Once traffic confirms the new key is active, disable the old key using the dedicated endpoint. This sets the `Enabled` field to `false` in the database without deleting the record.

```bash
curl -X POST https://<axonhub-host>/api/keys/<old-key-id>/disable \
  -H "Authorization: Bearer <admin-token>"

```

### Step 4: Verify Rotation via Quota Monitoring

Confirm that requests are no longer hitting the old key by querying quota statistics. The GraphQL layer defined in [`internal/server/gql/models_gen.go`](https://github.com/looplj/axonhub/blob/main/internal/server/gql/models_gen.go) exposes `apiKeyQuota` fields for this purpose.

```graphql
query {
  apiKeyQuota(keyId: "<old-key-id>") {
    limit
    used
    resetAt
  }
}

```

## How to Recover Disabled API Keys

### Locating Disabled Keys

Query for disabled keys using the filter parameter on the list endpoint or GraphQL query. This searches the `Enabled = false` records stored in the Ent-managed database.

```bash
curl https://<axonhub-host>/api/keys?enabled=false \
  -H "Authorization: Bearer <admin-token>"

```

```graphql
query {
  apiKeys(enabled: false) {
    id
    name
    projectId
    createdAt
  }
}

```

### Re-enabling Keys Safely

Restore a disabled key by calling the enable endpoint. The service in [`internal/server/biz/api_key.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/api_key.go) validates that the key still conforms to the `<refreshToken>|<projectID>` format and that the associated project exists before flipping the `Enabled` bit back to `true`.

```bash
curl -X POST https://<axonhub-host>/api/keys/<key-id>/enable \
  -H "Authorization: Bearer <admin-token>"

```

If the key fails validation—for example, if the project was deleted during the disablement period—the service returns a `failed to enable api key: <error>` message, preventing accidental re-enablement of broken credentials.

## Best Practices for API Key Lifecycle Management

- **Automate rotation schedules**: Implement cron jobs or CI/CD pipelines that call the create/disable endpoints every 30–90 days according to your compliance policy.
- **Maintain audit trails**: All enable, disable, and create actions are logged via the standard logger in [`internal/server/biz/api_key.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/api_key.go), providing non-repudiation records.
- **Graceful switchover**: Always update channel configurations to reference new key IDs before disabling old keys to prevent service interruption.
- **Monitor quota usage**: Regularly query the `apiKeyQuota` GraphQL field to detect anomalous usage patterns that might indicate a compromised key.
- **Validate before recovery**: When re-enabling old keys, verify that the project ID and token format are still valid to avoid runtime errors in [`channel_apikey.go`](https://github.com/looplj/axonhub/blob/main/channel_apikey.go).

## Summary

- AxonHub stores API keys in a SQL database via the Ent ORM, with core logic in [`internal/server/biz/api_key.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/api_key.go) and validation in [`internal/server/biz/channel_apikey.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/channel_apikey.go).
- **Rotation** involves creating a new key via `POST /api/keys`, updating channel references, disabling the old key via `POST /api/keys/:id/disable`, and verifying via quota queries.
- **Recovery** of disabled keys is achieved by querying `enabled=false` records and calling `POST /api/keys/:id/enable`, with automatic validation of the `<refreshToken>|<projectID>` format.
- The system enforces non-empty keys, project existence checks, and comprehensive logging for audit trails.

## Frequently Asked Questions

### Can I re-enable an API key that was disabled months ago?

Yes, provided the key record still exists in the database and passes validation. The `Enable` method in [`internal/server/biz/api_key.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/api_key.go) checks that the key conforms to the expected `<refreshToken>|<projectID>` format and that the associated project is still active. If these checks pass, the `Enabled` field is set to `true` regardless of how long the key was disabled.

### What happens to channel requests when an API key is disabled?

Channels validate API key status before processing requests through the logic in [`internal/server/biz/channel_apikey.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/channel_apikey.go). When a disabled key is encountered, the system returns explicit errors such as *"api key not enabled"* or *"failed to disable api key"*, causing the request to fail immediately. This prevents silent usage of revoked credentials and ensures clients receive clear feedback to update their configurations.

### How does AxonHub validate API key format during rotation?

The service layer enforces that all API keys follow the format `<refreshToken>|<projectID>`. During creation in [`internal/server/biz/api_key.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/api_key.go), the code validates that the key is non-empty and contains the pipe delimiter. The project ID portion is checked against existing projects to ensure validity. This validation runs both during initial creation and when re-enabling disabled keys, ensuring only properly formatted credentials enter the system.

### Is there an audit trail for API key enable and disable operations?

Yes, all lifecycle operations are logged through the standard logging framework implemented in [`internal/server/biz/api_key.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/api_key.go). The service methods emit `log.Info` or `log.Debug` statements when keys are created, enabled, disabled, or deleted. Additionally, the quota tracking system in [`internal/server/gql/models_gen.go`](https://github.com/looplj/axonhub/blob/main/internal/server/gql/models_gen.go) maintains usage statistics per key, providing a secondary record of when keys were active. These logs should be collected and retained according to your organization's compliance requirements.