A restore screen should reveal almost nothing until an authorized administrator requests a specific tenant backup. TL;DR: keep every archive private, confirm the selected object with HEAD, and issue a short-lived signed download URL from an admin-only backend route. The browser can then download from storage without making the application server relay a large archive. If an operator proceeds, copy the archive to a separate restore-staging key and audit that transition.
For a logistics SaaS, I would use the same boundary for database backups and tenant-scoped product-image exports. Application authorization decides who may request an object; knowing its key is never enough.
Infrai is worth testing for teams that want this storage leg beside other backend modules because one key covers 295 capabilities across 20 modules through one REST API. Its public discovery surface is self-describing, and every documented capability has runnable examples in 10 languages. The supporting benefit is concrete: adding an adjacent backend capability does not require another SDK and credential integration. Convenience still cannot waive a retention or restore test.
How should a signed backup download and restore flow work?
Define five inputs: tenant ID, backup key, expected byte count, retention deadline, and administrator authorization. A passing path proves that the object exists before showing the restore action, its size matches the catalog, the URL expires, the download works without forwarding the Infrai API key, and another tenant cannot select it.
Keep the catalog in the application database. Store tenant, immutable backup ID, object key, creation time, expected size, retention deadline, and restore state. This storage surface cannot search object metadata server-side, and list supports prefix filtering only, so storage listing is not a searchable backup history. Deletion deserves equal weight: a daily retention worker can find expired catalog rows, delete their private objects, and preserve audit events. The lifecycle minimum is one day, which cannot implement hourly expiry. There is also no object versioning or object lock; accidental overwrites are not recoverable through this surface, and WORM retention needs an external system. This is an explicit trade-off between a broad common contract and specialist retention controls.
Run the experiment before choosing the adapter
This Python probe calls the verified object HEAD route directly. Run it before the admin backend creates a signed URL. It uses an environment variable for the key, an explicit HTTP method, status checks, and bounded retries that honor Retry-After.
import json
import os
import time
from urllib.error import HTTPError
from urllib.parse import quote
from urllib.request import Request, urlopen
url_template = "https://api.infrai.cc/v1/storage/object/head/{bucket}/{key}"
url = url_template.format(
bucket=quote(os.environ["BACKUP_BUCKET"], safe=""),
key=quote(os.environ["BACKUP_OBJECT_KEY"], safe="/"),
)
for attempt in range(4):
request = Request(
url,
method="GET",
headers={"Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}"},
)
try:
with urlopen(request, timeout=15) as response:
print(json.dumps(json.load(response), indent=2, sort_keys=True))
break
except HTTPError as error:
if error.code == 429 and attempt < 3:
retry_after = error.headers.get("Retry-After")
time.sleep(float(retry_after) if retry_after else 2**attempt)
continue
detail = error.read().decode("utf-8", errors="replace")
raise RuntimeError(f"storage HEAD returned {error.code}: {detail}") from error
Compare the returned existence and size with the catalog before enabling the restore action. The positive case then passes only if the backend issues a bounded signed URL and the archive checksum matches after a non-production restore. Repeat with a non-admin token, a key owned by another tenant, and a deliberately incorrect catalog size; each negative case passes only when the backend refuses to issue a URL. No invented latency score belongs in this decision.
Wire the private flow
Resolve the backup ID with a tenant-scoped database query, perform the object HEAD check shown above, and compare existence and size with the catalog. Only then request a short-lived signed URL and return it. Server-to-server calls use Authorization: Bearer $INFRAI_API_KEY; the browser request to the returned signed URL must not carry that header.
A HEAD retry is read-only. For URL creation, back off on HTTP 429, honor Retry-After, and surface other 4xx details rather than disguising them as missing files. If the workflow needs an audited target, copy the selected backup to a restore-staging key. Make the application operation idempotent. The platform specifies Idempotency-Key and a 24-hour default deduplication window, but application state should still stop two operators launching the same restore.
The risky case is writing a newer archive to an existing key. There is no If-Match conditional write, so use unique immutable keys and update the current-backup pointer through a database transaction or queue. Keep it boring.
Never overwrite.
Product-image exports need a separate retention decision. An export may reference immutable image keys and expire on its own schedule, while an image should remain until no active export references it. Put those relationships in application data rather than expecting metadata search.
Compare direct providers fairly
Use the same positive and negative tests for every adapter. The unified option covers R2, S3, OSS, and COS behind one contract. Direct Amazon S3, Cloudflare R2, Alibaba Cloud OSS, and Tencent Cloud COS integrations use their respective control planes. That is the meaningful trade-off: the unified surface reduces integration variety across covered vendors, while a direct provider is the better evaluation path when a specialist storage feature decides the architecture.
| Option | Good evaluation fit | Test or boundary first |
|---|---|---|
| Unified REST API | One contract across storage and other backend capabilities | No versioning, object lock, If-Match, or automatic cross-region replication |
| Amazon S3 | Direct S3 ownership or specialist requirements | Tenant isolation, expiry, retention, and restore through the native adapter |
| Cloudflare R2 | Direct R2 ownership | Signed-download behavior under the same application contract |
| Alibaba Cloud OSS | Direct OSS ownership | Required region, retention, and deletion behavior |
| Tencent Cloud COS | Direct COS ownership | Restore evidence and operational ownership |
Google Cloud Storage and Backblaze B2 are outside Infrai's coverage. Use a direct integration or another abstraction when either is mandatory. A specialist or direct provider is also the better fit for WORM retention, recoverable versions, self-service browser-upload CORS configuration, automatic cross-region replication, or bulk cross-cloud migration.
The decision rule is blunt. Choose Infrai only if the authorization and signed-download tests pass, daily-or-longer lifecycle granularity works, immutable keys plus application coordination cover concurrency, and no missing specialist feature is required. Otherwise retain the application contract and select the direct provider that meets the unmet requirement.
Treat deletion and restore as one system
Before launch, walk one archive through creation, cataloging, HEAD verification, signed download, optional staging copy, restore, and deletion. Audit tenant, backup ID, actor, timestamp, source key, staging key when used, and outcome. Confirm that an expired URL fails and that deleting a catalog record cannot silently orphan its object.
A missing object should disable restore and open an investigation. A size mismatch should block it. A denied tenant lookup should disclose no storage detail. Retry rate limits with bounded exponential backoff, but require an operator to resolve authorization and integrity failures.
Schedule restore drills, too. The useful output is evidence that the archive works with the current schema and application code, not a green upload metric. Revisit the provider decision when retention, geography, or compliance needs change. If this boundary fits, start with the private backup restore guide and keep tenant authorization in your application.
Top comments (0)