Offsite: S3 remotes and catalog

A remote is an S3-compatible bucket and its credentials. Exports land under the remote’s prefix in a self-describing layout, and the catalog is the appliance’s cached index of what the bucket holds. The bucket alone is enough to rebuild the catalog and recover a library.

<prefix>/<system>/<library-serial>/<cart-label>/<generation>/chunk-00000.tar.zst
<prefix>/<system>/<library-serial>/<cart-label>/<generation>/manifest.json
<prefix>/<system>/<library-serial>/topology.json
<prefix>/<system>/.openvtl-system.json

Remotes

The remote object never includes the secret key. has_secret reports whether one is stored.

{
  "id": 1,
  "name": "minio-lab",
  "endpoint": "minio.lab:9000",
  "region": "us-east-1",
  "bucket": "openvtl",
  "prefix": "",
  "access_key": "AKIA…",
  "use_ssl": false,
  "path_style": true,
  "created_at": "2026-08-31T00:10:00Z",
  "last_test_at": "2026-08-31T00:10:30Z",
  "last_test_ok": true,
  "last_test_detail": "write/read/delete OK",
  "has_secret": true
}

GET /api/remotes

read-only

Lists every remote.

POST /api/remotes

admin

Body field Type Notes
name string Required.
bucket string Required.
access_key string Required.
secret_key string Required on create. Write-only.
endpoint string Host or host:port. Defaults to s3.amazonaws.com.
region string
prefix string Key prefix inside the bucket.
use_ssl bool Default true.
path_style bool Default false. Typically true for non-AWS endpoints.
curl -sk -X POST https://appliance:8443/api/remotes \
  -H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
  -d '{"name":"minio-lab","endpoint":"minio.lab:9000","bucket":"openvtl","access_key":"AKIA…","secret_key":"…","use_ssl":false,"path_style":true}'

Returns 201 with the remote.

PUT /api/remotes/{id}

admin

Same fields as create. An empty or omitted secret_key keeps the stored one.

DELETE /api/remotes/{id}

admin Destructive

Removes the remote configuration and its cached catalog. Objects in the bucket are not touched. Deleting the last remote brings the dashboard’s no-offsite warning back. Returns {"ok": true}.

POST /api/remotes/{id}/test

admin

Writes, reads, and deletes a probe object in the bucket, and records the result on the remote.

{"ok": true, "detail": "write/read/delete OK"}

A failed test is still a 200 with ok: false and the error in detail.

GET /api/remotes/{id}/objects

read-only

Every object under the remote’s prefix, keyed relative to it. The raw bucket browser.

{"objects": [{"key": "demo01/OVTL280164/OVB001L5/20260831T001507Z/manifest.json", "size": 2311}, {"key": "demo01/OVTL280164/OVB001L5/20260831T001507Z/chunk-00000.tar.zst", "size": 573741824}]}

DELETE /api/remotes/{id}/objects

admin Destructive

Deletes every object under a folder prefix in the bucket. The prefix must end with /, which structurally prevents deleting a single chunk or manifest. This removes offsite copies; the catalog will no longer offer those generations after a rebuild.

curl -sk -X DELETE https://appliance:8443/api/remotes/1/objects \
  -H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
  -d '{"prefix":"demo01/OVTL280164/OVB009L5/"}'
{"deleted": 4}

Catalog

GET /api/catalog

read-only

The cached catalog for one remote: one entry per exported generation.

Query Notes
remote_id Required.
[
  {"remote_id": 1, "system_name": "demo01", "library_serial": "OVTL280164", "cart_label": "OVB001L5", "generation": "20260831T001507Z", "logical_bytes": 267421859840, "stored_bytes": 137438953472, "chunk_count": 25, "exported_at": "2026-08-31T00:19:40Z", "synced_at": "2026-08-31T00:19:41Z"}
]

POST /api/catalog/rebuild

admin

Clears the cached catalog for the remote and rebuilds it from a bucket listing, fetching every manifest. This is how a fresh appliance learns what an existing bucket holds. Nothing in the bucket is modified.

curl -sk -X POST https://appliance:8443/api/catalog/rebuild \
  -H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
  -d '{"remote_id":1}'
{"complete": 41, "incomplete": [{"system": "demo01", "library": "OVTL280164", "label": "OVB007L5", "generation": "20260830T231100Z"}]}

incomplete lists generation folders with no manifest: exports that were interrupted before the completion marker landed. They are not importable; retry the export or delete the folder.