Reference
Available storages
Blob storages
Blobscan can be configured to use any of the following blob storages:
- S3
- Google Cloud Storage
- Ethereum Swarm
- Load Network (prev. WeaveVM) (currently supports blob reading only)
- PostgreSQL
- File system
By default all storages are disabled and you must enable at least one in order to run Blobscan. This is done using environment variables.
Note that the database size can grow quickly. For this reason, it is not recommended to choose PostgreSQL in production.
Google Cloud Storage signed URLs
When GOOGLE_STORAGE_SIGNED_URLS_ENABLED is set to true, the API will return signed URLs for blobs stored in Google Cloud Storage instead of public URLs. Signed URLs provide time-limited access to private objects without requiring users to make the bucket publicly accessible.
If signing fails for a given object (for example, missing IAM permission), the API logs an error and falls back to the original URL — the request itself does not fail.
Required service account permissions
The required permissions depend on what the Blobscan instance does. The same service account is used both for normal storage operations and for generating signed URLs.
Permissions matrix
| Permission | Where granted | Read-only API instance | Indexer / propagator (writes blobs) |
|---|---|---|---|
storage.objects.get | bucket | ✅ — required to read blobs and to make signed URLs valid for clients | ✅ |
storage.objects.create | bucket | ✅ — required to upload blobs | |
storage.objects.delete | bucket | ✅ — required to remove blobs | |
storage.buckets.list | project | ✅ — required by the startup health check | ✅ |
The
storage.buckets.listrequirement comes from the bucket health check that runs at startup. It must be granted at project level (no bucket-scoped variant exists). If you cannot grant it project-wide, define a custom role with only that permission rather than using a broader predefined role.
Recommended predefined roles
- Read-only API instance (the one returning signed URLs):
roles/storage.objectVieweron the bucket (coversobjects.get).- A custom project-level role with
storage.buckets.list, orroles/storage.adminat project level (broader; only if you accept the tradeoff).
- Indexer / propagator instance (writes and deletes blobs):
roles/storage.objectUseron the bucket (coversobjects.create,get,delete,list).- Same project-level
storage.buckets.listrequirement as above.
Granting via gcloud
# Bucket-level read access (read-only API)
gcloud storage buckets add-iam-policy-binding gs://YOUR_BUCKET_NAME \
--member="serviceAccount:YOUR_SA@YOUR_PROJECT.iam.gserviceaccount.com" \
--role="roles/storage.objectViewer"
# Bucket-level read + write (indexer / propagator)
gcloud storage buckets add-iam-policy-binding gs://YOUR_BUCKET_NAME \
--member="serviceAccount:YOUR_SA@YOUR_PROJECT.iam.gserviceaccount.com" \
--role="roles/storage.objectUser"
# Project-level custom role for the health check (least-privilege)
gcloud iam roles create blobscanBucketLister --project=YOUR_PROJECT \
--title="Blobscan bucket lister" \
--permissions=storage.buckets.list
gcloud projects add-iam-policy-binding YOUR_PROJECT \
--member="serviceAccount:YOUR_SA@YOUR_PROJECT.iam.gserviceaccount.com" \
--role="projects/YOUR_PROJECT/roles/blobscanBucketLister"
Granting via Cloud Console
- Bucket-scoped role: Cloud Storage > Buckets > select bucket > Permissions > Grant access > pick Storage Object Viewer (read-only) or Storage Object User (read/write).
- Project-scoped bucket lister: IAM & Admin > Roles > Create role with only
storage.buckets.list. Then IAM & Admin > IAM > Grant access > assign the custom role to the service account.
How signing works
Blobscan signs URLs with the v4 algorithm. To sign without an extra API call, the runtime needs the service account's RSA private key. Blobscan reads it from the base64-encoded JSON key in GOOGLE_SERVICE_KEY, so on any environment where you provide that env var (Docker, Kubernetes, bare metal) signing is local and self-contained — no additional GCP API call is made at sign time. The IAM grants the SA still needs are the ones in the matrix above (objects.get so the signed URL is honored when clients dereference it, plus the project-level buckets.list for the health check).
If you ever switch to credentials without an embedded private key (for example, Workload Identity Federation, GKE Workload Identity, or gcloud user creds), the GCS client falls back to the IAM signBlob API. In that case the identity also needs roles/iam.serviceAccountTokenCreator on itself (or the target service account) so it can call iam.serviceAccounts.signBlob. See Google's signed URL docs for details.
Running on DigitalOcean Kubernetes (DOKS)
DOKS is not GKE, so there is no Workload Identity bridge to GCP. The simplest and recommended path is to keep using a service account JSON key mounted as an env var — local signing keeps working unchanged.
Create a service account in the GCP project and grant
roles/storage.objectVieweron the bucket (see above).Create a JSON key for that service account and base64-encode it:
gcloud iam service-accounts keys create key.json \ --iam-account=YOUR_SERVICE_ACCOUNT@YOUR_PROJECT.iam.gserviceaccount.com base64 -w 0 key.json > key.b64Store it as a Kubernetes secret:
kubectl create secret generic blobscan-gcs \ --from-file=GOOGLE_SERVICE_KEY=key.b64Reference it from the API deployment:
env: - name: GOOGLE_STORAGE_ENABLED value: 'true' - name: GOOGLE_STORAGE_BUCKET_NAME value: your-bucket-name - name: GOOGLE_STORAGE_PROJECT_ID value: your-project-id - name: GOOGLE_STORAGE_SIGNED_URLS_ENABLED value: 'true' - name: GOOGLE_SERVICE_KEY valueFrom: secretKeyRef: name: blobscan-gcs key: GOOGLE_SERVICE_KEYRotate the key periodically (
gcloud iam service-accounts keys delete+ create new) and re-apply the secret. Treat the JSON key as a long-lived credential and never commitkey.jsonto git.
If you prefer not to manage a long-lived key, you can use Workload Identity Federation with an OIDC provider on DOKS — but the cluster's pod identity has no private key, so signing falls through to the IAM signBlob API and you must additionally grant roles/iam.serviceAccountTokenCreator on the impersonated service account. This costs one extra API call per signed URL.
URL expiration
Signed URLs are valid for 1 hour (3600 seconds). After expiration, the URL will no longer grant access to the blob; clients should re-fetch the API response to get a fresh URL.