GCP.Storage reference
Bucket
Section titled “Bucket”Source:
src/GCP/Storage/Bucket.ts
A Google Cloud Storage bucket.
Bucket: Creating a Bucket
Section titled “Bucket: Creating a Bucket”Generated name
const bucket = yield* GCP.Storage.Bucket("assets", { forceDestroy: true,});Explicit name, location, and labels
const bucket = yield* GCP.Storage.Bucket("assets", { bucketName: "my-app-assets", location: "US-CENTRAL1", storageClass: "STANDARD", versioning: true, labels: { env: "prod" }, forceDestroy: true,});Hierarchical namespace (folders)
const bucket = yield* GCP.Storage.Bucket("tree", { hierarchicalNamespace: true, uniformBucketLevelAccess: true, forceDestroy: true,});Static website bucket with public read
const site = yield* GCP.Storage.Bucket("site", { uniformBucketLevelAccess: true, website: { mainPageSuffix: "index.html", notFoundPage: "404.html" }, forceDestroy: true,});yield* GCP.IAM.Member("PublicRead", { kind: "storage.bucket", name: site.bucketName, role: "roles/storage.objectViewer", member: "allUsers",});Bucket: Updating a Bucket
Section titled “Bucket: Updating a Bucket”const bucket = yield* GCP.Storage.Bucket("assets", { forceDestroy: true, labels: { env: "prod" },});Bucket: Binding from a Function
Section titled “Bucket: Binding from a Function”export class Api extends GCP.Function<Api>()( "Api", { main: import.meta.url }, Effect.gen(function* () { const bucket = yield* GCP.Storage.Bucket("assets", { forceDestroy: true }); const putObject = yield* GCP.Storage.PutObject(bucket); return { fetch: Effect.gen(function* () { yield* putObject({ name: "hello.txt", body: "Hello!" }).pipe( Effect.orDie, ); return HttpServerResponse.text("ok"); }), }; }).pipe(Effect.provide([GCP.Storage.PutObjectHttp])),) {}Bucket: Destroying a Bucket
Section titled “Bucket: Destroying a Bucket”const bucket = yield* GCP.Storage.Bucket("assets", { forceDestroy: true,});forceDestroy: true deletes objects before the bucket. alchemy destroy waits until the bucket is gone.
BucketAccessControl
Section titled “BucketAccessControl”Source:
src/GCP/Storage/BucketAccessControl.ts
A Cloud Storage bucket ACL entry.
Fine-grained ACLs are identity (bucket, entity). Role is mutable;
changing bucketName or entity replaces the entry. Entries have no
labels field, so list / pnpm nuke:gcp discover them by enumerating
alchemy-labeled buckets and skipping project-team defaults.
Uniform bucket-level access buckets do not support bucket ACLs.
BucketAccessControl: Creating a Bucket ACL
Section titled “BucketAccessControl: Creating a Bucket ACL”Grant a service account writer access
const bucket = yield* GCP.Storage.Bucket("assets", { forceDestroy: true,});const acl = yield* GCP.Storage.BucketAccessControl("writer", { bucketName: bucket.bucketName, entity: "user-app@project.iam.gserviceaccount.com", role: "WRITER",});Update the role
const acl = yield* GCP.Storage.BucketAccessControl("writer", { bucketName: bucket.bucketName, entity: "user-app@project.iam.gserviceaccount.com", role: "READER",});BucketEventSource
Section titled “BucketEventSource”Source:
src/GCP/Storage/BucketEventSource.ts
Event source streaming a Cloud Storage Bucket’s object changes
(created, deleted, archived, metadata updated) into the hosting compute.
GCP.Storage.BucketEventSourceLive provisions a Pub/Sub topic owned by
the host plus a JSON_API_V1 bucket notification on it (filtered by
event type and object prefix), then delegates delivery to whichever
GCP.PubSub.TopicEventSource implementation is provided alongside it:
GCP.Run.TopicEventSource (push, for GCP.Function /
GCP.CloudFunctions.Function) or GCP.Run.TopicPullEventSource (pull,
for GCP.Run.Job / GCP.Run.WorkerPool).
Consume it through consumeBucketEvents.
BucketEventSource: Consuming Bucket Events
Section titled “BucketEventSource: Consuming Bucket Events”Process uploads on a Cloud Run service
export class Thumbnails extends GCP.Function<Thumbnails>()( "Thumbnails", { main: import.meta.url }, Effect.gen(function* () { const uploads = yield* GCP.Storage.Bucket("Uploads", {}); yield* GCP.Storage.consumeBucketEvents( uploads, { prefix: "incoming/" }, (events) => events.pipe( Stream.runForEach((event) => Effect.log(`${event.object} (${event.metadata.size} bytes)`), ), ), ); }).pipe( Effect.provide(GCP.Storage.BucketEventSourceLive), Effect.provide(GCP.Run.TopicEventSource), ),) {}Deletions on a worker pool
yield* GCP.Storage.consumeBucketEvents( uploads, { eventTypes: ["OBJECT_DELETE", "OBJECT_ARCHIVE"] }, (events) => events.pipe(Stream.runForEach(handle)),);// …provided with GCP.Storage.BucketEventSourceLive// and GCP.Run.TopicPullEventSourceBucketEventSourceLive
Section titled “BucketEventSourceLive”Source:
src/GCP/Storage/BucketEventSourceLive.tsKind: Layer · Provides:GCP.Storage.BucketEventSource
Implementation of GCP.Storage.BucketEventSource over Pub/Sub.
Deploy-time: creates a Pub/Sub topic owned by the host
({bucket}-BucketEvents) and a JSON_API_V1 bucket notification that
publishes the requested event types (and object prefix) to it; the
notification grants the Cloud Storage service agent
roles/pubsub.publisher on the topic. Delivery is delegated to the
provided GCP.PubSub.TopicEventSource implementation, so provide one
alongside this layer: GCP.Run.TopicEventSource (push, HTTP hosts) or
GCP.Run.TopicPullEventSource (pull, Jobs and WorkerPools). Runtime:
each message is parsed into a BucketEvent.
BucketEventSourceLive: Choosing the delivery
Section titled “BucketEventSourceLive: Choosing the delivery”Push to a Cloud Run service
Effect.gen(function* () { yield* GCP.Storage.consumeBucketEvents(bucket, (events) => events.pipe(Stream.runForEach((event) => Effect.log(event.object))), );}).pipe( Effect.provide(GCP.Storage.BucketEventSourceLive), Effect.provide(GCP.Run.TopicEventSource),);Pull on a worker pool
Effect.gen(function* () { yield* GCP.Storage.consumeBucketEvents(bucket, (events) => events.pipe(Stream.runForEach((event) => Effect.log(event.object))), ); return { run: Effect.never };}).pipe( Effect.provide(GCP.Storage.BucketEventSourceLive), Effect.provide(GCP.Run.TopicPullEventSource),);DefaultObjectAccessControl
Section titled “DefaultObjectAccessControl”Source:
src/GCP/Storage/DefaultObjectAccessControl.ts
A Cloud Storage default object ACL entry on a bucket.
Default object ACLs are copied onto objects created without an explicit
ACL. Identity is (bucket, entity). Role is mutable; changing
bucketName or entity replaces the entry. Entries have no labels
field, so list / pnpm nuke:gcp discover them by enumerating
alchemy-labeled buckets and skipping project-team defaults.
Uniform bucket-level access buckets do not support default object ACLs.
DefaultObjectAccessControl: Creating a Default Object ACL
Section titled “DefaultObjectAccessControl: Creating a Default Object ACL”Grant a service account reader access on new objects
const bucket = yield* GCP.Storage.Bucket("assets", { forceDestroy: true,});const acl = yield* GCP.Storage.DefaultObjectAccessControl("reader", { bucketName: bucket.bucketName, entity: "user-app@project.iam.gserviceaccount.com", role: "READER",});Update the role
const acl = yield* GCP.Storage.DefaultObjectAccessControl("reader", { bucketName: bucket.bucketName, entity: "user-app@project.iam.gserviceaccount.com", role: "OWNER",});DeleteObject
Section titled “DeleteObject”Source:
src/GCP/Storage/DeleteObject.ts
Runtime binding for Cloud Storage objects.delete.
Bind this operation to a Bucket in a Function/Action init phase.
Provide DeleteObjectHttp.
DeleteObject: Deleting Objects
Section titled “DeleteObject: Deleting Objects”const deleteObject = yield* GCP.Storage.DeleteObject(bucket);yield* deleteObject({ object: "hello.txt" });DeleteObjectHttp
Section titled “DeleteObjectHttp”Source:
src/GCP/Storage/DeleteObjectHttp.tsKind: Layer · Provides:GCP.Storage.DeleteObject
HTTP implementation of DeleteObject.
Source:
src/GCP/Storage/Files.ts
Upload a local directory into a Cloud Storage bucket: one
Object per file, keyed by its POSIX path relative to path,
with Content-Type inferred from the extension. Files removed from the
directory are deleted from the bucket on the next deploy (their
Object resources become orphans).
Files: Uploading a site
Section titled “Files: Uploading a site”const bucket = yield* GCP.Storage.Bucket("Site", { forceDestroy: true });const objects = yield* GCP.Storage.Files("SiteFiles", { bucketName: bucket.bucketName, path: "./dist", cacheControl: (key) => key.endsWith(".html") ? "no-cache" : "public, max-age=31536000, immutable",});Folder
Section titled “Folder”Source:
src/GCP/Storage/Folder.ts
A Cloud Storage folder in a hierarchical-namespace bucket.
Folders exist only on buckets created with hierarchicalNamespace: true (which also requires uniform bucket-level access). Identity is
(bucket, folder name). Folders have no mutable fields and no labels,
so list / pnpm nuke:gcp discover them by enumerating
alchemy-labeled buckets.
Folder: Creating a Folder
Section titled “Folder: Creating a Folder”Generated name
const bucket = yield* GCP.Storage.Bucket("tree", { hierarchicalNamespace: true, uniformBucketLevelAccess: true, forceDestroy: true,});const folder = yield* GCP.Storage.Folder("uploads", { bucketName: bucket.bucketName,});Explicit path
const folder = yield* GCP.Storage.Folder("uploads", { bucketName: bucket.bucketName, folderName: "uploads/incoming/",});GetObject
Section titled “GetObject”Source:
src/GCP/Storage/GetObject.ts
Runtime binding that downloads an object’s content from a Cloud Storage
Bucket (objects.get with alt=media). Grants
roles/storage.objectViewer on the bucket.
Bind this operation to a Bucket in a Function/Action init phase.
Provide GetObjectHttp.
GetObject: Reading Objects
Section titled “GetObject: Reading Objects”const getObject = yield* GCP.Storage.GetObject(bucket);const { body } = yield* getObject({ object: "hello.txt" });const text = new TextDecoder().decode(body);GetObjectHttp
Section titled “GetObjectHttp”Source:
src/GCP/Storage/GetObjectHttp.tsKind: Layer · Provides:GCP.Storage.GetObject
HTTP implementation of GetObject.
HmacKey
Section titled “HmacKey”Source:
src/GCP/Storage/HmacKey.ts
A Cloud Storage HMAC key for a service account.
HMAC keys authenticate S3-compatible interoperable requests. Cloud
Storage returns the secret once at create time; Alchemy stores it
redacted and never re-reads it. Keys have no labels field, so list
enumerates every HMAC key in the project (excluding DELETED) for
pnpm nuke:gcp. A service account may have at most ten keys in
ACTIVE or INACTIVE state. Changing serviceAccountEmail replaces
the key. Destroy deactivates the key, then deletes it.
HmacKey: Creating an HMAC Key
Section titled “HmacKey: Creating an HMAC Key”Active key for a service account
const key = yield* GCP.Storage.HmacKey("interop", { serviceAccountEmail: "app@project.iam.gserviceaccount.com",});Inactive key
const key = yield* GCP.Storage.HmacKey("interop", { serviceAccountEmail: "app@project.iam.gserviceaccount.com", state: "INACTIVE",});Managed
Section titled “Managed”Source:
src/GCP/Storage/Managed.ts
A Cloud Storage managed folder.
Managed folders provide IAM on an object-name prefix in a bucket with
uniform bucket-level access. Identity is (bucket, folder name).
Managed folders have no mutable fields and no labels, so list /
pnpm nuke:gcp discover them by enumerating alchemy-labeled buckets.
Managed: Creating a Managed Folder
Section titled “Managed: Creating a Managed Folder”Generated name
const bucket = yield* GCP.Storage.Bucket("assets", { uniformBucketLevelAccess: true, forceDestroy: true,});const folder = yield* GCP.Storage.Managed("team", { bucketName: bucket.bucketName,});Explicit path
const folder = yield* GCP.Storage.Managed("team", { bucketName: bucket.bucketName, managedFolderName: "teams/payments/",});Notification
Section titled “Notification”Source:
src/GCP/Storage/Notification.ts
A Cloud Storage Pub/Sub notification configuration on a bucket.
GCS has no update API for notifications — every user-facing field is
immutable and changing it replaces the config. The Cloud Storage
service account is granted roles/pubsub.publisher on the topic
during reconcile so events can actually be published.
Notifications carry no labels, and customAttributes are delivered on
every message, so Alchemy does not stamp ownership markers. read finds
the config by its recorded id; without one, an identical config on the
bucket is reported as unowned.
Notification: Creating a Notification
Section titled “Notification: Creating a Notification”Notify on every object event
const bucket = yield* GCP.Storage.Bucket("assets", { forceDestroy: true,});const topic = yield* GCP.PubSub.Topic("events", {});const notification = yield* GCP.Storage.Notification("object-events", { bucketName: bucket.bucketName, topic: topic.name,});Filtered events, prefix, and custom attributes
const notification = yield* GCP.Storage.Notification("uploads", { bucketName: bucket.bucketName, topic: topic.name, payloadFormat: "JSON_API_V1", eventTypes: ["OBJECT_FINALIZE"], objectNamePrefix: "uploads/", customAttributes: { env: "prod" },});Object
Section titled “Object”Source:
src/GCP/Storage/Object.ts
A single object in a Cloud Storage bucket whose content is declared as
infrastructure — inline content or a local file.
Reconcile observes the live object and uploads only when its MD5,
Content-Type, Cache-Control, or ownership metadata differ from the
desired state. Use Files to upload a whole directory.
Object: Uploading content
Section titled “Object: Uploading content”Inline content
const bucket = yield* GCP.Storage.Bucket("assets", { forceDestroy: true });yield* GCP.Storage.Object("Robots", { bucketName: bucket.bucketName, key: "robots.txt", content: "User-agent: *\nAllow: /\n",});A local file with caching
yield* GCP.Storage.Object("Logo", { bucketName: bucket.bucketName, key: "logo.svg", file: "./public/logo.svg", cacheControl: "public, max-age=31536000, immutable",});ObjectAccessControl
Section titled “ObjectAccessControl”Source:
src/GCP/Storage/ObjectAccessControl.ts
A Cloud Storage object ACL entry.
Fine-grained object ACLs are identity (bucket, object, entity). Role
is mutable; changing bucketName, object, or entity replaces the
entry. Entries have no labels field, so list / pnpm nuke:gcp
discover them by enumerating objects in alchemy-labeled buckets and
skipping project-team and numeric-owner defaults.
Uniform bucket-level access buckets do not support object ACLs.
ObjectAccessControl: Creating an Object ACL
Section titled “ObjectAccessControl: Creating an Object ACL”Grant a service account reader access
const bucket = yield* GCP.Storage.Bucket("assets", { forceDestroy: true,});const acl = yield* GCP.Storage.ObjectAccessControl("reader", { bucketName: bucket.bucketName, object: "hello.txt", entity: "user-app@project.iam.gserviceaccount.com", role: "READER",});Update the role
const acl = yield* GCP.Storage.ObjectAccessControl("reader", { bucketName: bucket.bucketName, object: "hello.txt", entity: "user-app@project.iam.gserviceaccount.com", role: "OWNER",});PutObject
Section titled “PutObject”Source:
src/GCP/Storage/PutObject.ts
Runtime binding that uploads an object’s content to a Cloud Storage
Bucket (multipart objects.insert), overwriting any live object
with the same name. Grants roles/storage.objectUser on the bucket
(overwrite needs delete permission, which objectCreator lacks).
Bind this operation to a Bucket in a Function/Action init phase.
Provide PutObjectHttp.
PutObject: Writing Objects
Section titled “PutObject: Writing Objects”Write a text object
const putObject = yield* GCP.Storage.PutObject(bucket);yield* putObject({ name: "hello.txt", body: "Hello, World!" });Write bytes with metadata
yield* putObject({ name: "report.json", body: new TextEncoder().encode(JSON.stringify(report)), contentType: "application/json", metadata: { source: "nightly" },});PutObjectHttp
Section titled “PutObjectHttp”Source:
src/GCP/Storage/PutObjectHttp.tsKind: Layer · Provides:GCP.Storage.PutObject
HTTP implementation of PutObject.
ReadBucket
Section titled “ReadBucket”Source:
src/GCP/Storage/ReadBucket.ts
Read access to a Cloud Storage Bucket: head, get, list.
Grants roles/storage.objectViewer on the bucket only.
ReadBucket: Reading a bucket
Section titled “ReadBucket: Reading a bucket”const uploads = yield* GCP.Storage.ReadBucket(bucket);const object = yield* uploads.get("hello.txt");const text = object && new TextDecoder().decode(object.body);const { objects } = yield* uploads.list({ prefix: "images/" });// …provided with Effect.provide(GCP.Storage.ReadBucketHttp)ReadBucketHttp
Section titled “ReadBucketHttp”Source:
src/GCP/Storage/ReadBucketHttp.tsKind: Layer · Provides:GCP.Storage.ReadBucket
HTTP implementation of ReadBucket over the Cloud Storage JSON API.
ReadWriteBucket
Section titled “ReadWriteBucket”Source:
src/GCP/Storage/ReadWriteBucket.ts
Read and write access to a Cloud Storage Bucket. Grants
roles/storage.objectUser on the bucket only.
ReadWriteBucket: Reading and writing
Section titled “ReadWriteBucket: Reading and writing”const files = yield* GCP.Storage.ReadWriteBucket(bucket);const source = yield* files.get("in.txt");if (source) yield* files.put("out.txt", source.body);// …provided with Effect.provide(GCP.Storage.ReadWriteBucketHttp)ReadWriteBucketHttp
Section titled “ReadWriteBucketHttp”Source:
src/GCP/Storage/ReadWriteBucketHttp.tsKind: Layer · Provides:GCP.Storage.ReadWriteBucket
HTTP implementation of ReadWriteBucket over the Cloud Storage JSON API.
SignGetObjectUrl
Section titled “SignGetObjectUrl”Source:
src/GCP/Storage/SignGetObjectUrl.ts
Mint V4 signed download (GET) URLs for objects in a Cloud Storage
Bucket, so a browser can fetch an object without Google
credentials.
The URL is signed with the host’s own runtime service account through
the IAM Credentials signBlob API (no key file). The binding grants the
host roles/storage.objectViewer on the bucket (a signed URL carries the
signer’s permissions) and roles/iam.serviceAccountTokenCreator on its
own service account only. See the signed URL
guide.
SignGetObjectUrl: Signing Download URLs
Section titled “SignGetObjectUrl: Signing Download URLs”Mint a signed GET URL
const signGetObjectUrl = yield* GCP.Storage.SignGetObjectUrl(bucket);const url = yield* signGetObjectUrl({ object: "reports/2026.pdf" });// hand `url` to a browser — it downloads the object without credentials// …provided with Effect.provide(GCP.Storage.SignGetObjectUrlHttp)Custom expiry and response Content-Type
const url = yield* signGetObjectUrl({ object: "reports/2026.pdf", expiresIn: 3600, contentType: "application/pdf",});SignGetObjectUrlHttp
Section titled “SignGetObjectUrlHttp”Source:
src/GCP/Storage/SignGetObjectUrlHttp.tsKind: Layer · Provides:GCP.Storage.SignGetObjectUrl
HTTP implementation of SignGetObjectUrl: V4 signing where the
signature comes from IAM Credentials signBlob as the runtime’s own
service account (read from the metadata server).
WriteBucket
Section titled “WriteBucket”Source:
src/GCP/Storage/WriteBucket.ts
Write access to a Cloud Storage Bucket: put, delete.
Grants roles/storage.objectUser on the bucket only (overwriting needs
delete, which objectCreator lacks).
WriteBucket: Writing to a bucket
Section titled “WriteBucket: Writing to a bucket”const uploads = yield* GCP.Storage.WriteBucket(bucket);yield* uploads.put("hello.txt", "Hello!", { contentType: "text/plain" });yield* uploads.delete("old.txt");// …provided with Effect.provide(GCP.Storage.WriteBucketHttp)WriteBucketHttp
Section titled “WriteBucketHttp”Source:
src/GCP/Storage/WriteBucketHttp.tsKind: Layer · Provides:GCP.Storage.WriteBucket
HTTP implementation of WriteBucket over the Cloud Storage JSON API.