Beta . Ortha CMS is in public beta. Every plan is free until it ends.
Features Ortha CMS on GitHub

Google Cloud Storage

Media plugin @orthacms/media-server@orthacms/media-admin

Native auth, Workload Identity, and when you do not need this package at all.

Documents 0.13.1 Updated Edit this page Report a problem

On this page

Native Google authentication for GCS: service-account keys, Application Default Credentials and Workload Identity.

  • id: gcs
  • Capabilities: { directUrl: <computed>, contentTypeMetadata: true, streamingPut: true }

You may not need this package

With an HMAC key, the S3 adapter reaches GCS already. Use this one when HMAC keys are forbidden, or for Workload Identity.

Install

npm install @orthacms/media-provider-gcs

Configure

Example language
MEDIA_GCS_BUCKET=acme-cms-media
MEDIA_GCS_PROJECT_ID=acme-prod
MEDIA_GCS_KEY_FILE=/secrets/gcs-media.json
MEDIA_DIRECT_SERVE=signed-url
MEDIA_GCS_BUCKET=acme-cms-media
# no key file: Application Default Credentials / Workload Identity
# sign URLs through IAM (needs iam.serviceAccounts.signBlob)
MEDIA_GCS_SIGN_WITH_IAM=true
MEDIA_DIRECT_SERVE=signed-url
MEDIA_GCS_BUCKET=acme-cms-media
# no key, no IAM signing: downloads stream through the app
MEDIA_DIRECT_SERVE=off
VariableDefaultNotes
MEDIA_GCS_BUCKETrequired
MEDIA_GCS_PROJECT_IDADC’s project
MEDIA_GCS_KEY_FILEunsetService-account JSON key path
MEDIA_GCS_SIGN_WITH_IAMfalseSign via IAM signBlob instead of a private key

Read by a generated app’s config/media-storage.ts (the reference server ships with local disk):

import type { GcsStorageConfig } from '@orthacms/media-provider-gcs';
import { defined, readEnv, readFlag, requireEnv } from '@orthacms/utils-server';

export function mediaStorage(): GcsStorageConfig {
    return defined({
        bucket: requireEnv('MEDIA_GCS_BUCKET'),
        projectId: readEnv('MEDIA_GCS_PROJECT_ID'),
        keyFilename: readEnv('MEDIA_GCS_KEY_FILE'),
        signWithIam: readFlag('MEDIA_GCS_SIGN_WITH_IAM', false)
    });
}

All options

GcsStorageConfig: bucket, projectId?, keyFilename?, credentials? ({ client_email, private_key }), keyPrefix?, signWithIam?, bucketClient? (a pre-built Bucket).

How it authenticatesdirectUrl
keyFilename or inline credentialstrue
signWithIam: truetrue
Bare Application Default Credentialsfalse — proxied

Register

// apps/server/src/plugins.ts
import { createGcsStorageProvider } from '@orthacms/media-provider-gcs';

MediaServerPlugin({
    provider: createGcsStorageProvider(config.plugins.media.storage),
    config: config.plugins.media
});

Verify

verify() reads the bucket’s metadata at boot. A wrong bucket or a credential that cannot see it refuses the start.

Grant the runtime service account access, then upload a file (curl example) and list it:

gcloud storage buckets add-iam-policy-binding gs://acme-cms-media \
  --member=serviceAccount:cms@acme-prod.iam.gserviceaccount.com \
  --role=roles/storage.objectAdmin

gcloud storage ls --recursive gs://acme-cms-media/
# gs://acme-cms-media/<workspaceId>/<assetId>/kingfisher.jpg

With signed-url, the download’s Location is a V4 signed URL carrying response-content-disposition and response-content-type.

Behaviour worth knowing

  • get reads metadata before opening the stream, so a missing object is a 404 rather than a broken streaming 200.
  • A failed upload deletes the object.
  • signWithIam is opt-in because it needs iam.serviceAccounts.signBlob.