Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Admin Operations

Administrative endpoints for garbage collection, background tasks, cron scheduling, metrics, health checks, backup/restore, and user/group management. Most admin endpoints require root access.

Endpoint Summary

Garbage Collection

MethodPathDescriptionRoot Required
POST/admin/gcRun synchronous garbage collectionYes

Background Tasks

MethodPathDescriptionRoot Required
POST/admin/tasks/reindexTrigger a reindex taskYes
POST/admin/tasks/gcTrigger a background GC taskYes
GET/admin/tasksList all tasks with progressYes
GET/admin/tasks/{id}Get a single taskYes
DELETE/admin/tasks/{id}Cancel a taskYes

Cron Scheduling

MethodPathDescriptionRoot Required
GET/admin/cronList cron schedulesYes
POST/admin/cronCreate a cron scheduleYes
PATCH/admin/cron/{id}Update a cron scheduleYes
DELETE/admin/cron/{id}Delete a cron scheduleYes

Backup & Restore

MethodPathDescriptionRoot Required
POST/admin/exportExport database as .aeordbYes
POST/admin/diffCreate patch between versionsYes
POST/admin/importImport a backup or patchYes
POST/admin/promotePromote a version hash to HEADYes

Monitoring

MethodPathDescriptionRoot Required
GET/admin/metricsPrometheus metricsYes (auth required)
GET/admin/healthHealth checkNo (public)

API Key Management

MethodPathDescriptionRoot Required
POST/admin/api-keysCreate an API keyYes
GET/admin/api-keysList all API keysYes
DELETE/admin/api-keys/{key_id}Revoke an API keyYes

User Management

MethodPathDescriptionRoot Required
POST/admin/usersCreate a userYes
GET/admin/usersList all usersYes
GET/admin/users/{user_id}Get a userYes
PATCH/admin/users/{user_id}Update a userYes
DELETE/admin/users/{user_id}Deactivate a user (soft delete)Yes

Group Management

MethodPathDescriptionRoot Required
POST/admin/groupsCreate a groupYes
GET/admin/groupsList all groupsYes
GET/admin/groups/{name}Get a groupYes
PATCH/admin/groups/{name}Update a groupYes
DELETE/admin/groups/{name}Delete a groupYes

Garbage Collection

POST /admin/gc

Run garbage collection synchronously. Identifies and removes orphaned entries not reachable from the current HEAD.

Query Parameters:

ParameterTypeDefaultDescription
dry_runbooleanfalseIf true, report what would be collected without deleting

Response: 200 OK

The response contains GC statistics (entries scanned, reclaimed bytes, etc.).

Example:

# Dry run
curl -X POST "http://localhost:3000/admin/gc?dry_run=true" \
  -H "Authorization: Bearer $TOKEN"

# Actual GC
curl -X POST http://localhost:3000/admin/gc \
  -H "Authorization: Bearer $TOKEN"

Error Responses:

StatusCondition
403Non-root user
500GC failure

Background Tasks

POST /admin/tasks/reindex

Enqueue a reindex task for a directory path. Re-scans all files and rebuilds index entries.

Request Body:

{
  "path": "/data/"
}

Response: 200 OK

{
  "id": "task-uuid-here",
  "task_type": "reindex",
  "status": "pending"
}

Example:

curl -X POST http://localhost:3000/admin/tasks/reindex \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"path": "/data/"}'

POST /admin/tasks/gc

Enqueue a background GC task (non-blocking).

Request Body:

{
  "dry_run": false
}

Response: 200 OK

{
  "id": "task-uuid-here",
  "task_type": "gc",
  "status": "pending"
}

Example:

curl -X POST http://localhost:3000/admin/tasks/gc \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": false}'

GET /admin/tasks

List all tasks with their current progress.

Response: 200 OK

[
  {
    "id": "task-uuid-here",
    "task_type": "reindex",
    "status": "running",
    "args": {"path": "/data/"},
    "progress": 0.45,
    "eta_ms": 1775968500000
  }
]

Each task includes progress (0.0-1.0) and eta_ms (estimated completion timestamp) if available.

Example:

curl http://localhost:3000/admin/tasks \
  -H "Authorization: Bearer $TOKEN"

GET /admin/tasks/

Get a single task by ID.

Response: 200 OK

{
  "id": "task-uuid-here",
  "task_type": "reindex",
  "status": "running",
  "args": {"path": "/data/"},
  "progress": 0.45,
  "eta_ms": 1775968500000
}

Error Responses:

StatusCondition
404Task not found

DELETE /admin/tasks/

Cancel a task.

Response: 200 OK

{
  "id": "task-uuid-here",
  "status": "cancelled"
}

Example:

curl -X DELETE http://localhost:3000/admin/tasks/task-uuid-here \
  -H "Authorization: Bearer $TOKEN"

Cron Scheduling

GET /admin/cron

List all cron schedules.

Response: 200 OK

[
  {
    "id": "nightly-gc",
    "schedule": "0 2 * * *",
    "task_type": "gc",
    "args": {"dry_run": false},
    "enabled": true
  }
]

POST /admin/cron

Create a new cron schedule.

Request Body:

{
  "id": "nightly-gc",
  "schedule": "0 2 * * *",
  "task_type": "gc",
  "args": {"dry_run": false},
  "enabled": true
}
FieldTypeRequiredDescription
idstringYesUnique schedule identifier
schedulestringYesCron expression
task_typestringYesTask type to enqueue ("gc", "reindex")
argsobjectYesArguments passed to the task
enabledbooleanYesWhether the schedule is active

Response: 201 Created

Example:

curl -X POST http://localhost:3000/admin/cron \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "nightly-gc",
    "schedule": "0 2 * * *",
    "task_type": "gc",
    "args": {"dry_run": false},
    "enabled": true
  }'

Error Responses:

StatusCondition
400Invalid cron expression
409Schedule with this ID already exists

PATCH /admin/cron/

Update a cron schedule. All fields are optional – only provided fields are changed.

Request Body:

{
  "enabled": false,
  "schedule": "0 3 * * *"
}
FieldTypeDescription
enabledbooleanEnable or disable the schedule
schedulestringNew cron expression
task_typestringNew task type
argsobjectNew task arguments

Response: 200 OK

Returns the updated schedule.

Error Responses:

StatusCondition
400Invalid cron expression
404Schedule not found

DELETE /admin/cron/

Delete a cron schedule.

Response: 200 OK

{
  "id": "nightly-gc",
  "deleted": true
}

Error Responses:

StatusCondition
404Schedule not found

Backup & Restore

POST /admin/export

Export the database (or a specific version) as an .aeordb archive file.

Query Parameters:

ParameterTypeDescription
snapshotstringExport a named snapshot (default: HEAD)
hashstringExport a specific version by hex hash

Response: 200 OK

  • Content-Type: application/octet-stream
  • Content-Disposition: attachment; filename="export-{hash_prefix}.aeordb"
  • Body: binary archive data

Example:

# Export HEAD
curl -X POST http://localhost:3000/admin/export \
  -H "Authorization: Bearer $TOKEN" \
  -o backup.aeordb

# Export a specific snapshot
curl -X POST "http://localhost:3000/admin/export?snapshot=v1.0" \
  -H "Authorization: Bearer $TOKEN" \
  -o backup-v1.aeordb

# Export by hash
curl -X POST "http://localhost:3000/admin/export?hash=a1b2c3d4..." \
  -H "Authorization: Bearer $TOKEN" \
  -o backup.aeordb

POST /admin/diff

Create a patch file representing the difference between two versions.

Query Parameters:

ParameterTypeRequiredDescription
fromstringYesSource snapshot name or hex hash
tostringNoTarget snapshot name or hex hash (default: HEAD)

Response: 200 OK

  • Content-Type: application/octet-stream
  • Content-Disposition: attachment; filename="patch-{hash_prefix}.aeordb"
  • Body: binary patch data

Example:

curl -X POST "http://localhost:3000/admin/diff?from=v1.0&to=v2.0" \
  -H "Authorization: Bearer $TOKEN" \
  -o patch-v1-v2.aeordb

POST /admin/import

Import a backup or patch file. Body limit: 10 MB.

Query Parameters:

ParameterTypeDefaultDescription
forcebooleanfalseForce import even if conflicts exist
promotebooleanfalsePromote the imported version to HEAD

Request:

  • Headers:
    • Authorization: Bearer <token> (required)
  • Body: raw .aeordb file bytes

Response: 200 OK

{
  "status": "success",
  "backup_type": "export",
  "entries_imported": 1500,
  "chunks_imported": 3200,
  "files_imported": 450,
  "directories_imported": 30,
  "deletions_applied": 5,
  "version_hash": "a1b2c3d4e5f6...",
  "head_promoted": true
}

Example:

curl -X POST "http://localhost:3000/admin/import?promote=true" \
  -H "Authorization: Bearer $TOKEN" \
  --data-binary @backup.aeordb

Error Responses:

StatusCondition
400Invalid or corrupt backup file
403Non-root user

POST /admin/promote

Promote an arbitrary version hash to HEAD.

Query Parameters:

ParameterTypeRequiredDescription
hashstringYesHex-encoded version hash to promote

Response: 200 OK

{
  "status": "success",
  "head": "a1b2c3d4e5f6..."
}

Example:

curl -X POST "http://localhost:3000/admin/promote?hash=a1b2c3d4e5f6..." \
  -H "Authorization: Bearer $TOKEN"

Error Responses:

StatusCondition
400Invalid hash format
404Version hash not found in storage

Monitoring

GET /admin/health

Public health check endpoint. No authentication required.

Response: 200 OK

{
  "status": "ok"
}

Example:

curl http://localhost:3000/admin/health

GET /admin/metrics

Prometheus-format metrics endpoint. Requires authentication.

Response: 200 OK

  • Content-Type: text/plain; version=0.0.4; charset=utf-8
  • Body: Prometheus text exposition format

Example:

curl http://localhost:3000/admin/metrics \
  -H "Authorization: Bearer $TOKEN"

API Key Management

POST /admin/api-keys

Create a new API key. The plaintext key is returned only once – store it securely. Requires root.

Request Body:

{
  "user_id": "550e8400-e29b-41d4-a716-446655440000"
}
FieldTypeRequiredDescription
user_idstring (UUID)NoUser to create the key for (defaults to the calling user)

Response: 201 Created

{
  "key_id": "660e8400-e29b-41d4-a716-446655440001",
  "api_key": "aeor_660e8400_a1b2c3d4e5f6...",
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "created_at": "2026-04-13T10:00:00Z"
}

Example:

curl -X POST http://localhost:3000/admin/api-keys \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"user_id": "550e8400-e29b-41d4-a716-446655440000"}'

GET /admin/api-keys

List all API keys (metadata only – no secrets). Requires root.

Response: 200 OK

[
  {
    "key_id": "660e8400-e29b-41d4-a716-446655440001",
    "user_id": "550e8400-e29b-41d4-a716-446655440000",
    "created_at": "2026-04-13T10:00:00Z",
    "is_revoked": false
  }
]

DELETE /admin/api-keys/

Revoke an API key. Revoked keys cannot be used to obtain tokens. Requires root.

Response: 200 OK

{
  "revoked": true,
  "key_id": "660e8400-e29b-41d4-a716-446655440001"
}

Error Responses:

StatusCondition
400Invalid key ID format
404API key not found

User Management

POST /admin/users

Create a new user. Requires root.

Request Body:

{
  "username": "alice",
  "email": "[email protected]"
}
FieldTypeRequiredDescription
usernamestringYesUnique username
emailstringNoUser email address

Response: 201 Created

{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "username": "alice",
  "email": "[email protected]",
  "is_active": true,
  "created_at": 1775968398000,
  "updated_at": 1775968398000
}

GET /admin/users

List all users. Requires root.

Response: 200 OK

[
  {
    "user_id": "550e8400-e29b-41d4-a716-446655440000",
    "username": "alice",
    "email": "[email protected]",
    "is_active": true,
    "created_at": 1775968398000,
    "updated_at": 1775968398000
  }
]

GET /admin/users/

Get a single user. Requires root.

Response: 200 OK (same shape as the user object above)

Error Responses:

StatusCondition
400Invalid UUID
404User not found

PATCH /admin/users/

Update a user. All fields are optional. Requires root.

Request Body:

{
  "username": "alice_updated",
  "email": "[email protected]",
  "is_active": true
}

Response: 200 OK (returns the updated user)


DELETE /admin/users/

Deactivate a user (soft delete – sets is_active to false). Requires root.

Response: 200 OK

{
  "deactivated": true,
  "user_id": "550e8400-e29b-41d4-a716-446655440000"
}

Group Management

Groups define path-level access control rules using query-based membership.

POST /admin/groups

Create a new group. Requires root.

Request Body:

{
  "name": "editors",
  "default_allow": "/content/*",
  "default_deny": "/admin/*",
  "query_field": "role",
  "query_operator": "eq",
  "query_value": "editor"
}
FieldTypeRequiredDescription
namestringYesUnique group name
default_allowstringYesPath pattern for allowed access
default_denystringYesPath pattern for denied access
query_fieldstringYesUser field to query for membership (must be a safe field)
query_operatorstringYesComparison operator
query_valuestringYesValue to match against

Response: 201 Created

{
  "name": "editors",
  "default_allow": "/content/*",
  "default_deny": "/admin/*",
  "query_field": "role",
  "query_operator": "eq",
  "query_value": "editor",
  "created_at": 1775968398000,
  "updated_at": 1775968398000
}

GET /admin/groups

List all groups. Requires root.

Response: 200 OK (array of group objects)


GET /admin/groups/

Get a single group. Requires root.

Error Responses:

StatusCondition
404Group not found

PATCH /admin/groups/

Update a group. All fields are optional. Requires root.

Request Body:

{
  "default_allow": "/content/*",
  "query_value": "senior-editor"
}

The query_field value is validated against a whitelist of safe fields. Attempting to use an unsafe field returns a 400 error.

Error Responses:

StatusCondition
400Unsafe query field
404Group not found

DELETE /admin/groups/

Delete a group. Requires root.

Response: 200 OK

{
  "deleted": true,
  "name": "editors"
}

Error Responses:

StatusCondition
404Group not found