Skip to content

Admin REST API ​

A JSON API for operating the server, mounted at /rest/api/v1. It powers the web console and is equally usable from scripts.

Authentication ​

Plain headers carrying an access key the server knows (root or any active IAM user):

bash
curl -H "X-API-Key: admin" -H "X-API-Secret: admin-secret" \
  http://localhost:8000/rest/api/v1/server/info

Endpoints ​

Server ​

EndpointMethodReturns
/server/infoGETname, version, uptime, processors, memory, and the running edition label (community for a cluster of ≤2 nodes, enterprise when license.enterprise: true is set for a larger cluster) with its serverPools / rackAwareness capability flags (both always true — single edition, no feature gating)
/server/statsGETbucket and object counts

Bucket quotas ​

Hard per-bucket size limits (bytes). A PUT, multipart complete, or copy that would push the bucket over its quota is refused with HTTP 507 QuotaExceeded; no quota (0) means unlimited.

Enforcement is best-effort: each node checks against a short-lived (≈5s) per-node usage cache, so in a multi-node cluster concurrent writes may overshoot the quota slightly within that window, and a delete frees headroom only after the cache refreshes. It is a guardrail against runaway growth, not a byte-exact accounting boundary.

EndpointMethodPurpose
/buckets/{bucket}/quotaGETcurrent {quota, usage} in bytes
/buckets/{bucket}/quotaPUTset the quota — body {"quota": <bytes>}
/buckets/{bucket}/quotaDELETEclear the quota (unlimited)

Custom response headers (TinyS3 extension) ​

Not an S3 feature. Real S3 has no per-bucket response-header configuration — this is a TinyS3 convenience, additive and harmless to standard S3 clients.

Configure a set of extra HTTP headers that TinyS3 adds to successful object GET/HEAD responses for the bucket. The driving use case is serving assets into a cross-origin-isolated page, e.g. Cross-Origin-Resource-Policy: cross-origin and Cross-Origin-Embedder-Policy: require-corp.

The config is a JSON object of header-name → value. A header is only added when the response does not already carry it (existing headers are never overridden or duplicated).

Guardrails (enforced on PUT with a 400, and again at apply time by silently skipping the bad entry so a misconfiguration never fails an object read):

  • Header name must match ^[A-Za-z0-9-]{1,64}$.
  • No CR/LF or other control characters in name or value; value capped at 1024 chars.
  • Reserved names are rejected (case-insensitive): content-length, content-type, content-range, content-encoding, transfer-encoding, connection, keep-alive, etag, last-modified, accept-ranges, date, host, authorization, www-authenticate, and any name starting with x-amz-.
  • At most 32 headers; total JSON capped at 16 KiB.
EndpointMethodPurpose
/buckets/{bucket}/response-headersGETcurrent header map ({} if none)
/buckets/{bucket}/response-headersPUTset the headers — body is a JSON object, e.g. {"Cross-Origin-Resource-Policy":"cross-origin"}
/buckets/{bucket}/response-headersDELETEclear all custom headers

Remote endpoints ​

Saved remote S3 endpoints (name + URL + credentials) reused by transfer jobs and bucket-replication rules. Managed at /endpoints (GET list, POST add, DELETE by id). Endpoints are persisted across restarts with encrypted credentials; responses never include the secret.

Replication ​

EndpointMethodPurpose
/buckets/{bucket}/replication/resyncPOSTre-queue the bucket's source objects against its replication rules, skipping objects already matching the destination (DR resync); returns the count enqueued

Root-only. See the bucket replication guide.

Metrics ​

EndpointMethodReturns
/metricsGETin-memory counters since start: requests, bytes up/down, disk I/O, auth failures by reason, requests by operation/status, latency per operation, and 15-minute time series

Prometheus text format is served separately at /tinys3/metrics.

Audit logging ​

Root-only. Turn per-request audit logging on/off and change its targets at runtime (persisted, no restart). See the audit configuration.

EndpointMethodPurpose
/auditGETstatus: {enabled, file, bucket, bucketPrefix, retentionDays, deliveryIntervalSeconds} — the retention/interval fields report the effective values the sink runs at
/auditPUTset {enabled, file?, bucket?, bucketPrefix?, retentionDays?, deliveryIntervalSeconds?}; a bucket target must already have versioning + object lock enabled or the call is rejected
/audit/tailGETlive tail of the most recent audit entries (in-memory ring); ?since=<seq>&limit=<n> returns {enabled, cursor, entries[]} — poll with the returned cursor as the next since
/audit/archiveGETpaginated listing of the WORM archive bucket's objects; ?startAfter=<key>&maxKeys=<n> → {bucket, prefix, objects[], nextStartAfter, truncated} (objects marked manifest are seal objects)
/audit/archive/objectGET?key=<key> returns one archived object's parsed records {key, records[]}; the key must be within the configured audit prefix

IP access control ​

Root-only front-door lists enforced on the resolved client IP (socket peer unless the request comes from a configured security.trustedProxies hop) before routing or auth, for both S3 and admin requests. A block (deny) always wins over an allow; loopback always passes so a local operator can never be locked out. See self-lockout recovery.

EndpointMethodPurpose
/blocklistGET{ entries[], callerIp } — active bans (single IPs or CIDRs) plus your resolved IP
/blocklistPOSTban { cidr, reason?, ttlSeconds?, force? }; force required to ban 0.0.0.0/0
/blocklistDELETE?cidr=<cidr> removes a ban
/allowlistGET{ enabled, entries[], callerIp } — enabled is the enforcement flag (default-deny when true)
/allowlistPOSTallow { cidr, reason?, ttlSeconds? }
/allowlistDELETE?cidr=<cidr> removes an entry
/allowlistPUT{ enabled, force? } toggle enforcement — refused unless your own resolved IP is covered (and the list is non-empty) or force=true, so you can't lock yourself out

Kill-switches: TINY_S3_BLOCKLIST_DISABLED / TINY_S3_ALLOWLIST_DISABLED (or security.blocklistDisabled / security.allowlistDisabled).

Erasure & healing ​

EndpointMethodPurpose
/erasure/healthGETdisks healthy/total, shard scheme, object health counts
/erasure/disksGETper-disk status
/erasure/resilienceGETtolerated-failure summary
/erasure/healing/statsGETscans run, objects healed/unrecoverable, last scan
/erasure/healing/scanPOSTtrigger a healing scan now

Transfer jobs ​

One API for every data transfer — a one-off copy and a continuous sync are the same job with a different intervalSeconds (0 = run once, N = repeat every N seconds).

EndpointMethodPurpose
/jobsGET / POSTlist / create (create starts the job immediately)
/jobs/{id}GET / PUT / DELETEinspect / edit / remove
/jobs/{id}/start · /cancelPOSTcontrol (start re-runs a cancelled/paused job)
/jobs/{id}/pause · /resumePOSTcontrol (scheduled jobs only)
/jobs/{id}/reconcilePOSTverify destination holds the whole source (PULL and PUSH)
/jobs/test-connectionPOSTvalidate a remote endpoint + credentials

A job has a direction (PULL or PUSH), one remote endpoint, and one of two scopes: a mapping (sourceBucket/destBucket + optional prefixes) or a mirror (buckets list or allBuckets, with optional deleteOrphans). Responses carry per-cycle progress (objects/bytes, failures, throughput, ETA) and cumulative stats (cycles, copied/skipped/deleted totals) for scheduled jobs.

Completion is verified. A one-off transfer is not declared COMPLETED from a clean copy pass alone — on finishing, the engine reconciles the destination against the source (object count, per-object size, and single-part ETag). If gaps remain it runs one more copy pass to fill them and re-verifies; the job is COMPLETED only when the source fully reconciles, otherwise COMPLETED_WITH_ERRORS. The result is returned as a verification report on the job (inSync, sourceObjects, matched, missing, sizeMismatch, sample discrepancies), so a migration that silently dropped an object is caught rather than reported as done.

Restart-safe. Jobs are persisted; a transfer interrupted by a restart is re-queued and resumed on boot (already-copied objects are skipped by the size/ETag check, so re-driving is safe).

Credentials & users ​

EndpointMethodPurpose
/endpointsGET / POSTsaved remote endpoints for transfer jobs
/usersGETlocal users (console pickers)

Notes ​

  • Endpoints that only exist in a given mode (e.g. /erasure/* in simple mode) return 404; the console degrades gracefully.
  • All responses are JSON. Errors carry { "error": ... } or { "message": ... }.

TinyS3 is AGPLv3 licensed. Cloudflare and Cloudflare Workers are trademarks of Cloudflare, Inc. Amazon S3 and AWS are trademarks of Amazon.com, Inc. or its affiliates. MinIO is a trademark of MinIO, Inc. They are named only to describe compatibility; totis.ai is not affiliated with, endorsed by, or sponsored by any of them.