Admin API¶
JSON-based administration API for managing the Arca server. All endpoints live under /admin/* on the same port as the S3 API (default 9000).
Authentication¶
All endpoints except /admin/health require AWS SigV4 authentication ā the same mechanism used for S3 requests. Access follows the credential's user, not the credential itself: a request is allowed when the user is root, or when the user's effective grants allow the arca:* action mapped from the admin path. Other credentials receive a 403 AccessDenied response but can still use the S3 API normally.
Authorization is controlled through grants (policy documents) attached to users and teams. Root users have unrestricted access to all endpoints. Non-root users need grants with the appropriate arca:* actions to access admin endpoints.
Error Responses¶
Admin API errors are returned as JSON (not S3 XML):
Common error codes:
| Code | HTTP Status | Description |
|---|---|---|
BadRequest |
400 | Invalid input (empty name, malformed JSON, etc.) |
NotFound |
404 | Entity not found |
Conflict |
409 | Constraint violation (duplicate name, cannot delete last admin, etc.) |
InternalError |
500 | Server-side error |
Health & Info¶
Health Check¶
Auth: None (designed for load balancer probes).
Query parameters:
| Parameter | Description |
|---|---|
writable |
Write-aware variant (?writable=1): additionally answers 503 read_only while the node's cluster write gate is closed. |
verbose |
Detailed view (?verbose=1): always answers 200 with the state and the cluster snapshot. |
Both flags accept 1, true or an empty value (?verbose).
Response 200 (the node is ready):
Response 503 (the node is not ready), with a Retry-After: 5 header:
The status value is resolved in this order of precedence:
| Status | When |
|---|---|
draining |
A graceful shutdown is in progress, or a maintenance-mode job is draining the S3 API on this node. Wins over every other state. |
syncing |
Clustered node only: it has not yet completed its first anti-entropy pass toward every eligible peer since startup, so it could still answer stale reads. A node with no eligible peers has nothing to sync from and reports ok. |
read_only |
Only with ?writable=1: the cluster write gate is closed (write quorum lost, or more eligible nodes than cluster_size). Without writable=1 a read-only node answers 200 ok, because it can still serve reads. Single-node deployments and available mode are always writable. |
ok |
None of the above. |
Point the default load balancer check at plain /admin/health, and the health check of a separate write pool at /admin/health?writable=1. See the High Availability guide for the operator view.
Response with ?verbose=1 200 (never 503, so an inspector always gets the detail; the state is in status):
{
"status": "ok",
"draining": false,
"cluster": {
"node_id": "node-a",
"local_endpoint": "https://10.0.0.1:9000",
"write_quorum": 2,
"has_write_quorum": true,
"live_node_count": 3,
"eligible_node_count": 3,
"size_exceeded": false,
"tombstone_gc_blocked": false,
"worker_leader": true,
"syncing": false,
"peers": [ "...one entry per known peer..." ]
}
}
cluster is null on a single-node deployment. Its fields mirror GET /admin/cluster (write_quorum is null in available mode); each peers entry carries node_id, endpoint, alive, last_seen, authenticated, config_ok, disk_total, disk_available and max_seq.
Cluster Topology¶
Auth: SigV4
Returns the cluster topology and consistency state as seen by the node serving the request (this is what the console topology card renders). This is the live view: arca cluster status on the command line only prints the node identity and the static configuration.
Response 200 (single-node deployment):
Response 200 (clustered):
{
"enabled": true,
"mode": "quorum",
"node_id": "node-a",
"write_quorum": 2,
"has_write_quorum": true,
"live_node_count": 3,
"eligible_node_count": 3,
"node_count": 3,
"config_aligned": true,
"size_exceeded": false,
"tombstone_gc_blocked": false,
"worker_leader": true,
"syncing": false,
"disk_total_bytes": 107374182400,
"disk_available_bytes": 53687091200,
"nodes": [
{
"node_id": "node-a",
"endpoint": "https://10.0.0.1:9000",
"alive": true,
"local": true,
"authenticated": true,
"config_ok": true
},
{
"node_id": "node-b",
"endpoint": "https://10.0.0.2:9000",
"alive": true,
"last_seen": "2026-06-24T10:02:11+00:00",
"local": false,
"authenticated": true,
"config_ok": true,
"sync": {
"hwm": 1532,
"lag": 0,
"last_reconcile": "2026-06-24T10:01:40+00:00",
"first_pass_done": true,
"skipped_entries": 0
}
}
]
}
| Field | Type | Description |
|---|---|---|
enabled |
boolean | false on a single-node deployment, in which case every other field is omitted |
mode |
string | quorum (a majority must hold a write) or available (always writable, best-effort replication) |
node_id |
string | Identity of the node serving the request |
write_quorum |
integer? | Durable copies required to acknowledge a write. Omitted in available mode. |
has_write_quorum |
boolean | Whether writes can currently be acknowledged |
live_node_count |
integer | Reachable nodes, including this one (unauthenticated and config-drifted nodes included) |
eligible_node_count |
integer | Nodes that count for replication and quorum (alive, authenticated and config-aligned, including this one) |
node_count |
integer | Total known nodes, including this one |
config_aligned |
boolean | false when any live peer's cluster-critical configuration differs from this node's |
size_exceeded |
boolean | true when more eligible nodes than cluster_size are live: the write gate is closed until the cluster is resized |
tombstone_gc_blocked |
boolean | true while tombstone GC is skipped because a known peer has been unreachable beyond the grace window |
worker_leader |
boolean | true when this node runs the cluster-singleton background work (exactly one node in a stable cluster) |
syncing |
boolean | true until this node completes its first anti-entropy pass toward every eligible peer (/admin/health answers 503 meanwhile) |
disk_total_bytes / disk_available_bytes |
integer? | Cluster-effective disk capacity and free space: the minimum across this node and the live peers. Omitted if unavailable. |
nodes |
array | This node first, then every known peer |
Each nodes entry:
| Field | Type | Description |
|---|---|---|
node_id |
string | Node identity |
endpoint |
string? | Peer base URL. On the local node, present only once it has learned its own advertised endpoint. |
alive |
boolean | Whether the most recent probe succeeded (always true for the local node) |
last_seen |
string? | Last successful contact (RFC 3339), kept on a dead peer. Omitted on the local node and on peers never reached. |
local |
boolean | true for the node serving the request |
authenticated |
boolean | Whether the node proved possession of the cluster secret on its last probe |
config_ok |
boolean | Whether the node's cluster-critical configuration matches this node's |
sync |
object? | Peers only: this node's anti-entropy pull state toward the peer, with hwm (highest peer sequence applied), lag (entries still to pull, omitted until the peer reports its cursor), last_reconcile (RFC 3339, omitted before the first full pass), first_pass_done and skipped_entries (entries skipped after persistent apply failures) |
See the High Availability guide for how to read these fields.
Server Info¶
Auth: SigV4
Response 200:
{
"version": "0.4.0",
"uptime_seconds": 3600,
"tls_enabled": true,
"encryption_enabled": true,
"kms_provider": "vault",
"kms_endpoint": "http://vault:8200"
}
| Field | Type | Description |
|---|---|---|
version |
string | Arca server version |
uptime_seconds |
integer | Seconds since server start |
tls_enabled |
boolean | Whether TLS is enabled on the main listener |
encryption_enabled |
boolean | Whether server-side encryption (SSE-S3) is enabled globally |
kms_provider |
string? | Key source: "local" (config file) or "vault" (Vault/OpenBAO). Omitted when no encryption. |
kms_endpoint |
string? | Vault/OpenBAO endpoint URL. Only present when kms_provider is "vault". |
Storage Stats¶
Auth: SigV4
Response 200:
{
"bucket_count": 5,
"object_count": 142,
"total_size_bytes": 1073741824,
"disk_total_bytes": 107374182400,
"disk_available_bytes": 53687091200
}
| Field | Type | Description |
|---|---|---|
bucket_count |
integer | Total number of buckets |
object_count |
integer | Total number of objects across all buckets |
total_size_bytes |
integer | Total size of all objects in bytes |
disk_total_bytes |
integer? | Total disk capacity in bytes. Omitted if unavailable. |
disk_available_bytes |
integer? | Available disk space in bytes. Omitted if unavailable. |
Current User Identity¶
Auth: SigV4
Returns the authenticated user's identity and effective permissions (from all grants, both direct and via teams).
Response 200:
{
"user": {
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "admin",
"is_root": true
},
"effective_actions": ["*"]
}
For non-root users, effective_actions lists the union of all Allow actions from all effective policy documents:
{
"user": {
"user_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"username": "alice",
"is_root": false
},
"effective_actions": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket",
"s3:ListAllMyBuckets"
]
}
Credentials¶
Credentials are access key / secret key pairs used for SigV4 authentication. Each credential belongs to a user. The legacy /admin/credentials endpoints operate on the calling user's credentials, while the /admin/users/{user_id}/credentials endpoints (in the Users section) allow managing credentials for any user.
List Credentials¶
Auth: SigV4
Returns all credentials. Secret keys are never included in list responses.
Response 200:
[
{
"access_key_id": "AKIA5B6BSHJA8CIHZSVG",
"description": "root credential",
"created_at": "2025-01-15T10:30:00+00:00",
"active": true,
"user_id": "root"
}
]
A credential carries no privileges of its own: it inherits them from the user
identified by user_id. Credentials on a root user have implicit full access,
including this API; credentials on any other user are authorized through that
user's grants.
Create Credential¶
Auth: SigV4
Creates a new credential for the calling user.
Request body:
description is optional and defaults to the empty string. The credential is
created for the calling user, and therefore has exactly the same privileges.
To create a credential for a different user, use
Create User Credential.
Response 201:
{
"access_key_id": "AKXYZ123456789ABCDEF",
"secret_access_key": "hQBDe6WnX9umjbSGCrld7YRUoYfaQUhUcJS/UAgv",
"description": "CI/CD pipeline",
"created_at": "2025-06-01T12:00:00+00:00",
"active": true,
"user_id": "root"
}
Warning
The secret_access_key is only returned once at creation time. Store it securely.
Update Credential¶
Auth: SigV4
Update a credential's active status and/or description. Both fields are optional; only provided fields are updated.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
active |
boolean | No | Enable or disable the credential |
description |
string | No | Update the credential description |
Response 204: Credential updated successfully (no body).
Response 404: Credential not found.
Response 409: Cannot deactivate the last active credential, or the last active credential belonging to a root user (prevents lockout).
Delete Credential¶
Auth: SigV4
Response 204: Credential deleted successfully (no body).
Response 404: Credential not found.
Response 409: Cannot delete the last active credential, or the last active credential belonging to a root user (prevents lockout).
Users¶
User management endpoints for the RBAC system. Users own credentials and can be members of teams. Grants (policy documents) can be attached directly to users or inherited through team membership.
List Users¶
Auth: SigV4
Response 200:
[
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "admin",
"description": "Root administrator",
"is_root": true,
"created_at": "2025-01-01T00:00:00+00:00",
"credential_count": 2,
"team_count": 0,
"grant_count": 1
},
{
"user_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"username": "alice",
"description": "Developer",
"is_root": false,
"created_at": "2025-06-01T12:00:00+00:00",
"credential_count": 1,
"team_count": 2,
"grant_count": 0
}
]
Create User¶
Auth: SigV4
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
username |
string | Yes | Unique username (must not be empty) |
description |
string | No | User description (defaults to empty string) |
Response 201:
{
"user_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"username": "alice",
"description": "Developer",
"is_root": false,
"created_at": "2025-06-01T12:00:00+00:00"
}
Response 400: Username is empty.
Response 409: Username already exists.
Note
New users are always created with is_root: false. The root flag cannot be set through the API.
Get User¶
Auth: SigV4
Returns detailed user information including counts of related entities.
Response 200:
{
"user_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"username": "alice",
"description": "Developer",
"is_root": false,
"created_at": "2025-06-01T12:00:00+00:00",
"credential_count": 1,
"team_count": 2,
"grant_count": 3
}
Response 404: User not found.
Update User¶
Auth: SigV4
Update a user's username and/or description. Both fields are optional; only provided fields are updated.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
username |
string | No | New unique username |
description |
string | No | New description |
Response 204: User updated successfully (no body).
Response 404: User not found.
Response 409: Cannot modify the root user, or new username already exists.
Delete User¶
Auth: SigV4
Response 204: User deleted successfully (no body).
Response 404: User not found.
Response 409: Cannot delete the root user, or user still has credentials (remove credentials first).
List User Credentials¶
Auth: SigV4
Returns all credentials belonging to the specified user. Secret keys are never included.
Response 200:
[
{
"access_key_id": "AKIA5B6BSHJA8CIHZSVG",
"description": "Main credential",
"created_at": "2025-06-01T12:00:00+00:00",
"active": true
}
]
Response 404: User not found.
Create User Credential¶
Auth: SigV4
Creates a new credential for the specified user. Access key and secret key can be auto-generated or provided explicitly.
Request body:
{
"description": "CI credential",
"access_key_id": "MYCUSTOMKEYID",
"secret_access_key": "myCustomSecretKey123"
}
| Field | Type | Required | Description |
|---|---|---|---|
description |
string | No | Credential description (defaults to empty string) |
access_key_id |
string | No | Custom access key ID. Auto-generated if omitted or empty. |
secret_access_key |
string | No | Custom secret access key. Auto-generated if omitted or empty. |
Response 201:
{
"access_key_id": "MYCUSTOMKEYID",
"secret_access_key": "myCustomSecretKey123",
"description": "CI credential",
"created_at": "2025-06-01T12:00:00+00:00",
"active": true,
"user_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
Response 404: User not found.
Response 409: Access key ID already exists.
Warning
The secret_access_key is only returned once at creation time. Store it securely.
List User Grants¶
Auth: SigV4
Returns grants directly attached to the user (not inherited from teams).
Response 200:
[
{
"grant_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "S3ReadOnly",
"description": "Read-only access to all buckets"
}
]
Response 404: User not found.
Attach Grant to User¶
Auth: SigV4
Attach a grant (policy) directly to a user. No request body is required.
Response 204: Grant attached successfully (no body).
Response 404: User or grant not found.
Detach Grant from User¶
Auth: SigV4
Remove a directly-attached grant from a user. No request body is required.
Response 204: Grant detached successfully (no body).
Response 404: Grant not attached to user.
List User Teams¶
Auth: SigV4
Returns the teams the user belongs to.
Response 200:
[
{
"team_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "developers",
"description": "Development team"
}
]
Response 404: User not found.
Effective User Grants¶
Auth: SigV4
Returns all effective grants for a user, combining direct grants and grants inherited from team membership. Each entry includes a source field indicating where the grant comes from.
Response 200:
[
{
"grant_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "S3ReadOnly",
"description": "Read-only access to all buckets",
"source": "direct"
},
{
"grant_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"name": "S3FullAccess",
"description": "Full S3 access",
"source": "team:developers"
}
]
| Source format | Description |
|---|---|
"direct" |
Grant is attached directly to the user |
"team:<name>" |
Grant is inherited from the named team |
Response 404: User not found.
Teams¶
Teams are groups of users. Grants attached to a team are inherited by all members, providing a convenient way to manage permissions for groups of users.
List Teams¶
Auth: SigV4
Response 200:
[
{
"team_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "developers",
"description": "Development team",
"created_at": "2025-06-01T12:00:00+00:00",
"member_count": 5,
"grant_count": 2
}
]
Create Team¶
Auth: SigV4
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Unique team name (must not be empty) |
description |
string | No | Team description (defaults to empty string) |
Response 201:
{
"team_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "developers",
"description": "Development team",
"created_at": "2025-06-01T12:00:00+00:00"
}
Response 400: Team name is empty.
Response 409: Team name already exists.
Get Team¶
Auth: SigV4
Returns detailed team information including counts of members and grants.
Response 200:
{
"team_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "developers",
"description": "Development team",
"created_at": "2025-06-01T12:00:00+00:00",
"member_count": 5,
"grant_count": 2
}
Response 404: Team not found.
Update Team¶
Auth: SigV4
Update a team's name and/or description. Both fields are optional; only provided fields are updated.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | No | New unique team name |
description |
string | No | New description |
Response 204: Team updated successfully (no body).
Response 404: Team not found.
Response 409: Team name already exists.
Delete Team¶
Auth: SigV4
Deletes the team and removes all member and grant associations.
Response 204: Team deleted successfully (no body).
Response 404: Team not found.
List Team Members¶
Auth: SigV4
Response 200:
[
{
"user_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"username": "alice",
"description": "Developer",
"is_root": false
}
]
Response 404: Team not found.
Add Team Member¶
Auth: SigV4
Add a user to the team. No request body is required.
Response 204: Member added successfully (no body).
Response 404: Team or user not found.
Remove Team Member¶
Auth: SigV4
Remove a user from the team. No request body is required.
Response 204: Member removed successfully (no body).
Response 404: Member not found in team.
List Team Grants¶
Auth: SigV4
Returns grants attached to the team.
Response 200:
[
{
"grant_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"name": "S3FullAccess",
"description": "Full S3 access"
}
]
Response 404: Team not found.
Attach Grant to Team¶
Auth: SigV4
Attach a grant (policy) to a team. All team members will inherit this grant. No request body is required.
Response 204: Grant attached successfully (no body).
Response 404: Team or grant not found.
Detach Grant from Team¶
Auth: SigV4
Remove a grant from a team. No request body is required.
Response 204: Grant detached successfully (no body).
Response 404: Grant not attached to team.
Grants¶
Grants are named, reusable IAM-compatible policy documents. They can be attached to users (directly) or teams (inherited by members). The policy evaluation engine uses AWS-style deny-overrides semantics: an explicit Deny in any grant always wins over Allow.
Policy Document Format¶
Grants contain an IAM-compatible policy document with this structure:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "OptionalStatementId",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"]
}
]
}
Supported actions:
| Action | Description |
|---|---|
* |
All actions (full wildcard) |
s3:* |
All S3 actions |
s3:CreateBucket |
Create bucket |
s3:DeleteBucket |
Delete bucket |
s3:ListBucket |
List objects in a bucket |
s3:ListAllMyBuckets |
List all buckets |
s3:GetBucketLocation |
Get bucket location |
s3:GetBucketEncryption |
Get bucket encryption configuration |
s3:PutBucketEncryption |
Set bucket encryption configuration |
s3:DeleteBucketEncryption |
Delete bucket encryption configuration |
s3:GetObject |
Get (download) an object |
s3:PutObject |
Put (upload) an object |
s3:DeleteObject |
Delete an object |
arca:* |
All Arca admin actions |
arca:ViewServerInfo |
View server info and stats |
arca:ManageUsers |
Manage users |
arca:ManageTeams |
Manage teams |
arca:ManageGrants |
Manage grants |
arca:ManageCredentials |
Manage credentials |
arca:CreatePresignedUrl |
Generate presigned URLs |
arca:CreateArchive |
Create archives |
Resource format: * (all resources) or S3 ARN patterns like arn:aws:s3:::bucket-name (bucket-level) and arn:aws:s3:::bucket-name/* (object-level). Wildcards * and ? are supported in ARN patterns.
List Grants¶
Auth: SigV4
Response 200:
[
{
"grant_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "S3ReadOnly",
"description": "Read-only access to all buckets",
"created_at": "2025-06-01T12:00:00+00:00"
}
]
Create Grant¶
Auth: SigV4
Request body:
{
"name": "S3ReadOnly",
"description": "Read-only access to all buckets",
"document": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowRead",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:ListAllMyBuckets",
"s3:GetBucketLocation"
],
"Resource": ["*"]
}
]
}
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Unique grant name (must not be empty) |
description |
string | No | Grant description (defaults to empty string) |
document |
object | Yes | IAM-compatible policy document (validated on creation) |
Response 201:
{
"grant_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "S3ReadOnly",
"description": "Read-only access to all buckets",
"document": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowRead",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:ListAllMyBuckets",
"s3:GetBucketLocation"
],
"Resource": ["*"]
}
]
},
"created_at": "2025-06-01T12:00:00+00:00",
"updated_at": "2025-06-01T12:00:00+00:00"
}
Response 400: Grant name is empty, policy document is invalid JSON, or policy validation fails (unknown action, invalid resource ARN, missing required fields, wrong version).
Response 409: Grant name already exists.
Get Grant¶
Auth: SigV4
Returns the full grant including its policy document.
Response 200:
{
"grant_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "S3ReadOnly",
"description": "Read-only access to all buckets",
"document": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": ["*"]
}
]
},
"created_at": "2025-06-01T12:00:00+00:00",
"updated_at": "2025-06-01T12:00:00+00:00"
}
Response 404: Grant not found.
Update Grant¶
Auth: SigV4
Update a grant's name, description, and/or document. All fields are optional; only provided fields are updated. When document is provided, it is validated like at creation time.
Request body:
{
"name": "S3ReadWrite",
"description": "Read-write access to all buckets",
"document": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
"Resource": ["*"]
}
]
}
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | No | New unique grant name |
description |
string | No | New description |
document |
object | No | New policy document (validated) |
Response 204: Grant updated successfully (no body).
Response 400: Policy document validation failed.
Response 404: Grant not found.
Delete Grant¶
Auth: SigV4
Deletes the grant and removes all user and team associations.
Response 204: Grant deleted successfully (no body).
Response 404: Grant not found.
Node-Local Logs¶
The audit log, the metrics history, the notification event log and the replication journal are strictly node-local: each node records its own entries and they are not replicated across a cluster. Their list endpoints accept a ?node= selector so that, behind a load balancer, an operator can still choose which node's data to read. See Per-node views in the High Availability guide for the operator view.
Node Selector (?node=)¶
Accepted by GET /admin/audit, GET /admin/metrics/history, GET /admin/notifications/events and GET /admin/replication/journal (not by the count, stats or clear endpoints).
| Value | Behaviour |
|---|---|
absent, empty, or this node's own node_id |
Answers from this node's data. On a clustered node the response carries a top-level "node": "<node_id>" naming the node that answered. |
a peer's node_id |
The serving node proxies the same query server-side to that peer over the signed inter-node transport. The response carries "node": "<peer node_id>". |
all |
Merged view: the query fans out to every eligible node in parallel and the rows are merged newest first. Every row gains a node field naming its source, the top-level total is the sum across nodes, the response carries "node": "all", and a sources array reports each node's total, or its error if that node failed (the other nodes' rows are still returned). Every node is asked for the same offset/limit window and the newest limit rows are kept, so deep pages are an approximation. |
Only eligible peers (alive, authenticated and config-aligned) can be targeted.
Response 400: ?node= was given but this node is not part of a cluster.
Response 404: no known peer has that node_id.
Response 503: the peer is known but not currently eligible (dead, unauthenticated, or config-drifted).
Response 502: the peer could not be reached, answered with a server error, or returned malformed JSON.
A peer's own 4xx error (for example audit logging disabled on that node) is forwarded with its original status. Peers running a version without the proxy routes answer 404.
List Audit Log Entries¶
Auth: SigV4
Query parameters: bucket, operation, user_id (exact-match filters), from / to (RFC 3339 timestamps), offset (default 0), limit (default 100, max 1000), node (node selector).
Response 200:
Response 400: audit logging is not enabled.
Related endpoints (this node only, no ?node=): GET /admin/audit/stats returns {"total_entries": N}; DELETE /admin/audit with body {"confirm": "CLEAR AUDIT LOG"} clears the log and returns {"deleted": N}.
Metrics History¶
Auth: SigV4
Query parameters: from / to (RFC 3339 timestamps), limit (default 500, max 5000), node (node selector).
Response 200:
Response 400: metrics collection is not enabled.
List Notification Events¶
Auth: SigV4
Query parameters: bucket, event_name, delivery_status (filters), offset (default 0), limit (default 100, max 1000), node (node selector).
Response 200:
Related endpoints (this node only, no ?node=): GET /admin/notifications/events/count takes the same filters and returns {"count": N}; DELETE /admin/notifications/events with body {"confirm": "CLEAR EVENTS"} clears the log and returns {"deleted": N}.
List Replication Journal Entries¶
Auth: SigV4
Each journal entry is recorded by the node that served the originating S3 write.
Query parameters: bucket, status, rule_id (filters), offset (default 0), limit (default 100), node (node selector).
Response 200:
Archive¶
Download Objects as Archive¶
Auth: SigV4
Stream a tar.gz archive containing the requested objects. The response is a streaming application/gzip download, no temporary files are created server-side.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
bucket |
string | Yes | Bucket name |
keys |
array | Yes | List of object keys to include (must not be empty) |
Response 200: Streaming application/gzip response with Content-Disposition: attachment; filename="my-bucket.tar.gz".
Response 400: Keys list is empty.
Response 404: Bucket not found, or any requested object key not found.
Note
All objects are resolved before streaming begins. If any key is missing, the request fails immediately with a 404 before any data is sent.
Presigned URLs¶
Generate Presigned URL¶
Auth: SigV4
Generate a presigned URL for downloading or uploading an object without requiring credentials.
Request body:
{
"bucket": "my-bucket",
"key": "path/to/file.txt",
"method": "GET",
"expires": 3600,
"endpoint": "https://arca.example.com:9443"
}
| Field | Type | Required | Description |
|---|---|---|---|
bucket |
string | Yes | Bucket name |
key |
string | Yes | Object key |
method |
string | No | HTTP method: "GET" (default), "PUT", "HEAD", or "DELETE" |
expires |
integer | No | Expiry in seconds (default: 3600, max: 604800 = 7 days) |
endpoint |
string | No | Base URL override (e.g. "https://arca.example.com:9443"). When provided, the presigned URL uses this host/scheme instead of the request's Host header. Useful when the client connects through a different endpoint than the public-facing URL. |
Response 200:
{
"url": "http://localhost:9000/my-bucket/path/to/file.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&...",
"expires_at": "2026-03-17T12:00:00Z"
}
| Field | Type | Description |
|---|---|---|
url |
string | Presigned URL with embedded SigV4 query parameters |
expires_at |
string | ISO 8601 expiration timestamp |
The generated URL can be used with curl, browsers, or any HTTP client without AWS credentials.
Maintenance Jobs¶
Long-running maintenance operations (re-encryption, metadata migration) are tracked as jobs and processed by a background worker. Only one job runs at a time, progress is persisted across restarts, and jobs can be paused, resumed and cancelled. In a cluster the worker is leader-gated. See the Migration & Maintenance guide for the full model (live vs maintenance mode, leader-gating, the re-encryption / migrate-db / migrate-topology operations).
Known job types: noop, encrypt, decrypt, migrate-db.
Job object¶
{
"id": "8f3cā¦",
"job_type": "encrypt",
"status": "running",
"mode": "live",
"params": { "bucket": "my-bucket", "rate_bytes_per_sec": 10485760 },
"total": 1280,
"done": 412,
"rate": 37.5,
"last_error": null,
"created_at": "2026-06-24T10:00:00Z",
"updated_at": "2026-06-24T10:02:11Z",
"started_at": "2026-06-24T10:00:01Z",
"finished_at": null
}
| Field | Type | Description |
|---|---|---|
id |
string | Job id (UUID) |
job_type |
string | noop | encrypt | decrypt | migrate-db |
status |
string | pending | running | paused | completed | failed | cancelled |
mode |
string | live (zero-downtime) | maintenance (drains the S3 API on the node) |
params |
object | Job-type-specific parameters |
total / done |
integer | Progress counters |
rate |
number | Current throughput (items/sec) |
last_error |
string | null | Error message if the job failed |
created_at / updated_at / started_at / finished_at |
string | null | ISO 8601 timestamps |
Create a Job¶
Auth: SigV4 (admin)
Request body:
{
"type": "encrypt",
"mode": "live",
"params": { "bucket": "my-bucket", "prefix": "logs/", "rate_bytes_per_sec": 10485760 }
}
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | One of the known job types |
mode |
string | No | live (default) or maintenance |
params |
object | No | Job-type parameters (see below) |
Job-type parameters:
encrypt/decrypt:bucket(optional, all buckets if omitted),prefix(optional, all keys if omitted),rate_bytes_per_sec(optional, live mode only,0= unlimited).migrate-db:target("sqlite"|"postgres", required),force(boolean, replace a non-empty destination).noop:n(steps),delay_ms(optional per-step sleep) ā a test job.
Response 201: the created job object.
Response 400: unknown job type or invalid mode.
Response 409: a maintenance job is already active.
List Jobs¶
Auth: SigV4 (admin)
Query parameters: limit ā history page size (default 50, max 500).
Response 200:
{
"active": { "...": "the in-flight job, or null" },
"jobs": [ "...recent history, newest first..." ]
}
Get a Job (with logs)¶
Auth: SigV4 (admin)
Response 200:
Response 404: no such job.
Pause / Resume / Cancel¶
POST /admin/maintenance/jobs/{id}/pause # running|pending -> paused
POST /admin/maintenance/jobs/{id}/resume # paused -> running
DELETE /admin/maintenance/jobs/{id} # any non-terminal -> cancelled
Auth: SigV4 (admin)
Each returns 200 with the updated job object.
Response 404: no such job.
Response 409: the job is not in a state from which the transition is allowed.