From cd303607849a64dda7f03e436f4fc83849eb65e6 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Tue, 7 Apr 2026 13:44:49 -0400 Subject: [PATCH 1/3] [Artifacts] add docs scaffold --- .../2026-04-16-artifacts-now-in-beta.mdx | 11 ++++ src/content/directory/artifacts.yaml | 12 ++++ .../docs/artifacts/api/artifacts-fs.mdx | 11 ++++ .../api/authenticated-remote-urls.mdx | 11 ++++ .../docs/artifacts/api/git-support.mdx | 11 ++++ src/content/docs/artifacts/api/index.mdx | 15 +++++ src/content/docs/artifacts/api/rest-api.mdx | 11 ++++ .../docs/artifacts/api/workers-binding.mdx | 11 ++++ .../artifacts/concepts/best-practices.mdx | 11 ++++ .../concepts/how-artifacts-works.mdx | 11 ++++ src/content/docs/artifacts/concepts/index.mdx | 15 +++++ .../docs/artifacts/examples/git-client.mdx | 11 ++++ src/content/docs/artifacts/examples/index.mdx | 15 +++++ .../artifacts/examples/opencode-plugin.mdx | 11 ++++ .../examples/sandbox-sdk-artifacts.mdx | 11 ++++ .../docs/artifacts/get-started/index.mdx | 15 +++++ .../docs/artifacts/get-started/rest-api.mdx | 11 ++++ .../docs/artifacts/get-started/workers.mdx | 11 ++++ src/content/docs/artifacts/index.mdx | 58 +++++++++++++++++++ .../docs/artifacts/observability/index.mdx | 15 +++++ .../docs/artifacts/observability/metrics.mdx | 11 ++++ .../docs/artifacts/observability/queries.mdx | 11 ++++ .../docs/artifacts/platform/changelog.mdx | 13 +++++ src/content/docs/artifacts/platform/index.mdx | 15 +++++ .../docs/artifacts/platform/limits.mdx | 9 +++ .../docs/artifacts/platform/pricing.mdx | 9 +++ src/icons/artifacts.svg | 1 + 27 files changed, 357 insertions(+) create mode 100644 src/content/changelog/artifacts/2026-04-16-artifacts-now-in-beta.mdx create mode 100644 src/content/directory/artifacts.yaml create mode 100644 src/content/docs/artifacts/api/artifacts-fs.mdx create mode 100644 src/content/docs/artifacts/api/authenticated-remote-urls.mdx create mode 100644 src/content/docs/artifacts/api/git-support.mdx create mode 100644 src/content/docs/artifacts/api/index.mdx create mode 100644 src/content/docs/artifacts/api/rest-api.mdx create mode 100644 src/content/docs/artifacts/api/workers-binding.mdx create mode 100644 src/content/docs/artifacts/concepts/best-practices.mdx create mode 100644 src/content/docs/artifacts/concepts/how-artifacts-works.mdx create mode 100644 src/content/docs/artifacts/concepts/index.mdx create mode 100644 src/content/docs/artifacts/examples/git-client.mdx create mode 100644 src/content/docs/artifacts/examples/index.mdx create mode 100644 src/content/docs/artifacts/examples/opencode-plugin.mdx create mode 100644 src/content/docs/artifacts/examples/sandbox-sdk-artifacts.mdx create mode 100644 src/content/docs/artifacts/get-started/index.mdx create mode 100644 src/content/docs/artifacts/get-started/rest-api.mdx create mode 100644 src/content/docs/artifacts/get-started/workers.mdx create mode 100644 src/content/docs/artifacts/index.mdx create mode 100644 src/content/docs/artifacts/observability/index.mdx create mode 100644 src/content/docs/artifacts/observability/metrics.mdx create mode 100644 src/content/docs/artifacts/observability/queries.mdx create mode 100644 src/content/docs/artifacts/platform/changelog.mdx create mode 100644 src/content/docs/artifacts/platform/index.mdx create mode 100644 src/content/docs/artifacts/platform/limits.mdx create mode 100644 src/content/docs/artifacts/platform/pricing.mdx create mode 100644 src/icons/artifacts.svg diff --git a/src/content/changelog/artifacts/2026-04-16-artifacts-now-in-beta.mdx b/src/content/changelog/artifacts/2026-04-16-artifacts-now-in-beta.mdx new file mode 100644 index 000000000000000..251305fc973ae9e --- /dev/null +++ b/src/content/changelog/artifacts/2026-04-16-artifacts-now-in-beta.mdx @@ -0,0 +1,11 @@ +--- +title: "Artifacts now in beta: versioned filesystem that can speak git" +description: Artifacts is now available in beta as a versioned filesystem for Workers, APIs, and git-compatible workflows. +products: + - artifacts +date: 2026-04-16 +--- + +[Artifacts](/artifacts/) is now in beta. It provides a versioned filesystem for storing and exchanging file trees across Workers, the REST API, and git-compatible clients. + +Use Artifacts to publish build outputs, sync repositories, and move files between tools without inventing a new packaging format. For more information, refer to [Get started](/artifacts/get-started/) and [API](/artifacts/api/). diff --git a/src/content/directory/artifacts.yaml b/src/content/directory/artifacts.yaml new file mode 100644 index 000000000000000..ea258e432ea891a --- /dev/null +++ b/src/content/directory/artifacts.yaml @@ -0,0 +1,12 @@ +id: a9X2kP +name: Artifacts + +entry: + title: Artifacts + url: /artifacts/ + group: Developer platform + +meta: + title: Cloudflare Artifacts docs + description: Store, version, and share filesystem artifacts across Workers, APIs, and git-compatible tools + author: "@cloudflare" diff --git a/src/content/docs/artifacts/api/artifacts-fs.mdx b/src/content/docs/artifacts/api/artifacts-fs.mdx new file mode 100644 index 000000000000000..52bfb54127478b8 --- /dev/null +++ b/src/content/docs/artifacts/api/artifacts-fs.mdx @@ -0,0 +1,11 @@ +--- +title: ArtifactsFS +description: Reference the Artifacts filesystem interface. +pcx_content_type: reference +sidebar: + order: 4 +--- + +ArtifactsFS provides filesystem-oriented access to Artifact contents for code and tooling that expect file and directory semantics. + +This reference will document supported filesystem operations, path handling, and consistency expectations. diff --git a/src/content/docs/artifacts/api/authenticated-remote-urls.mdx b/src/content/docs/artifacts/api/authenticated-remote-urls.mdx new file mode 100644 index 000000000000000..1dbb97a78a11a8c --- /dev/null +++ b/src/content/docs/artifacts/api/authenticated-remote-urls.mdx @@ -0,0 +1,11 @@ +--- +title: Authenticated remote URLs +description: Reference authenticated remote URL support for Artifacts. +pcx_content_type: reference +sidebar: + order: 5 +--- + +Authenticated remote URLs let tools access Artifact-backed remotes using scoped credentials. + +This reference will document URL formats, authentication flows, and operational considerations for remote access. diff --git a/src/content/docs/artifacts/api/git-support.mdx b/src/content/docs/artifacts/api/git-support.mdx new file mode 100644 index 000000000000000..1a9b28affeebcda --- /dev/null +++ b/src/content/docs/artifacts/api/git-support.mdx @@ -0,0 +1,11 @@ +--- +title: Git support +description: Reference the git-compatible workflows supported by Artifacts. +pcx_content_type: reference +sidebar: + order: 3 +--- + +Artifacts can expose versioned filesystem content through git-compatible workflows. + +This reference will document supported operations, compatibility expectations, and integration constraints for git-based tooling. diff --git a/src/content/docs/artifacts/api/index.mdx b/src/content/docs/artifacts/api/index.mdx new file mode 100644 index 000000000000000..e8755c04fe5d158 --- /dev/null +++ b/src/content/docs/artifacts/api/index.mdx @@ -0,0 +1,15 @@ +--- +title: API +description: Reference the Artifacts Workers binding, REST API, and filesystem interfaces. +pcx_content_type: navigation +sidebar: + order: 4 + group: + hideIndex: true +--- + +import { DirectoryListing } from "~/components"; + +Use the Artifacts API reference to understand the interfaces exposed across Workers, HTTP, git-compatible workflows, and filesystem access. + + diff --git a/src/content/docs/artifacts/api/rest-api.mdx b/src/content/docs/artifacts/api/rest-api.mdx new file mode 100644 index 000000000000000..edd586481610470 --- /dev/null +++ b/src/content/docs/artifacts/api/rest-api.mdx @@ -0,0 +1,11 @@ +--- +title: REST API +description: Reference the Artifacts REST API. +pcx_content_type: reference +sidebar: + order: 2 +--- + +Use the REST API to manage Artifacts over HTTP from external systems, automation, and custom clients. + +This reference will document endpoints, request bodies, authentication requirements, and response formats. diff --git a/src/content/docs/artifacts/api/workers-binding.mdx b/src/content/docs/artifacts/api/workers-binding.mdx new file mode 100644 index 000000000000000..3c5fe7d51497349 --- /dev/null +++ b/src/content/docs/artifacts/api/workers-binding.mdx @@ -0,0 +1,11 @@ +--- +title: Workers binding +description: Reference the Artifacts binding available inside Cloudflare Workers. +pcx_content_type: reference +sidebar: + order: 1 +--- + +Use the Workers binding to create, inspect, and version Artifacts directly from a Worker. + +This reference will document the available binding methods, parameters, and return values. diff --git a/src/content/docs/artifacts/concepts/best-practices.mdx b/src/content/docs/artifacts/concepts/best-practices.mdx new file mode 100644 index 000000000000000..6fe0804841a397c --- /dev/null +++ b/src/content/docs/artifacts/concepts/best-practices.mdx @@ -0,0 +1,11 @@ +--- +title: Best practices for Artifacts +description: Follow recommended patterns for storing, organizing, and consuming Artifacts. +pcx_content_type: concept +sidebar: + order: 2 +--- + +Use consistent naming, version promotion, and lifecycle conventions so Artifact producers and consumers can share file trees safely. + +This page will document recommended patterns for structuring repositories, limiting large writes, handling retention, and integrating Artifacts into deployment or agent workflows. diff --git a/src/content/docs/artifacts/concepts/how-artifacts-works.mdx b/src/content/docs/artifacts/concepts/how-artifacts-works.mdx new file mode 100644 index 000000000000000..827be92c3d56c90 --- /dev/null +++ b/src/content/docs/artifacts/concepts/how-artifacts-works.mdx @@ -0,0 +1,11 @@ +--- +title: How Artifacts works +description: Understand the Artifacts data model and versioned filesystem behavior. +pcx_content_type: concept +sidebar: + order: 1 +--- + +Artifacts provides a versioned filesystem abstraction for storing and exchanging file trees. + +This page will describe how Artifacts stores versions, addresses filesystem content, and exposes the same underlying data through Workers, the REST API, and git-compatible interfaces. diff --git a/src/content/docs/artifacts/concepts/index.mdx b/src/content/docs/artifacts/concepts/index.mdx new file mode 100644 index 000000000000000..77572961314dace --- /dev/null +++ b/src/content/docs/artifacts/concepts/index.mdx @@ -0,0 +1,15 @@ +--- +title: Concepts +description: Learn how Artifacts works and how to use it effectively. +pcx_content_type: navigation +sidebar: + order: 3 + group: + hideIndex: true +--- + +import { DirectoryListing } from "~/components"; + +Use these concepts to understand the data model, versioning behavior, and operating model for Artifacts. + + diff --git a/src/content/docs/artifacts/examples/git-client.mdx b/src/content/docs/artifacts/examples/git-client.mdx new file mode 100644 index 000000000000000..4eb145e360a6f43 --- /dev/null +++ b/src/content/docs/artifacts/examples/git-client.mdx @@ -0,0 +1,11 @@ +--- +title: Git client +description: Example Artifacts integration with a git client. +pcx_content_type: example +sidebar: + order: 1 +--- + +This example will show how a git client can clone, fetch, or push against an Artifact-backed remote. + +Use it as a reference for local development workflows and repository-style synchronization. diff --git a/src/content/docs/artifacts/examples/index.mdx b/src/content/docs/artifacts/examples/index.mdx new file mode 100644 index 000000000000000..3d1bf8718bdaf5e --- /dev/null +++ b/src/content/docs/artifacts/examples/index.mdx @@ -0,0 +1,15 @@ +--- +title: Examples +description: Explore example Artifacts integrations. +pcx_content_type: navigation +sidebar: + order: 6 + group: + hideIndex: true +--- + +import { DirectoryListing } from "~/components"; + +Use these examples to see how Artifacts can fit into developer tools, clients, and execution environments. + + diff --git a/src/content/docs/artifacts/examples/opencode-plugin.mdx b/src/content/docs/artifacts/examples/opencode-plugin.mdx new file mode 100644 index 000000000000000..d7916f22ff45f04 --- /dev/null +++ b/src/content/docs/artifacts/examples/opencode-plugin.mdx @@ -0,0 +1,11 @@ +--- +title: OpenCode plugin +description: Example Artifacts integration with an OpenCode plugin. +pcx_content_type: example +sidebar: + order: 2 +--- + +This example will show how an OpenCode plugin can publish or consume Artifact versions as part of an automation or agent workflow. + +Use it as a reference for tool integrations that need durable file trees. diff --git a/src/content/docs/artifacts/examples/sandbox-sdk-artifacts.mdx b/src/content/docs/artifacts/examples/sandbox-sdk-artifacts.mdx new file mode 100644 index 000000000000000..7c56bb4dabe387c --- /dev/null +++ b/src/content/docs/artifacts/examples/sandbox-sdk-artifacts.mdx @@ -0,0 +1,11 @@ +--- +title: Sandbox SDK + Artifacts +description: Example Artifacts integration with Sandbox SDK. +pcx_content_type: example +sidebar: + order: 3 +--- + +This example will show how Sandbox SDK workloads can read from and write to Artifacts during isolated execution. + +Use it as a reference for build, analysis, and agent workflows that need shared versioned storage. diff --git a/src/content/docs/artifacts/get-started/index.mdx b/src/content/docs/artifacts/get-started/index.mdx new file mode 100644 index 000000000000000..214bef27e911b80 --- /dev/null +++ b/src/content/docs/artifacts/get-started/index.mdx @@ -0,0 +1,15 @@ +--- +title: Get started +description: Start using Artifacts with Workers or the REST API. +pcx_content_type: navigation +sidebar: + order: 2 + group: + hideIndex: true +--- + +import { DirectoryListing } from "~/components"; + +Start here to create, inspect, and version Artifacts from Cloudflare developer workflows. + + diff --git a/src/content/docs/artifacts/get-started/rest-api.mdx b/src/content/docs/artifacts/get-started/rest-api.mdx new file mode 100644 index 000000000000000..b3f0283310abf55 --- /dev/null +++ b/src/content/docs/artifacts/get-started/rest-api.mdx @@ -0,0 +1,11 @@ +--- +title: REST API +description: Create and manage Artifacts with the REST API. +pcx_content_type: get-started +sidebar: + order: 2 +--- + +Use this guide to create your first Artifact over HTTP by authenticating requests, uploading files, and reading Artifact versions through the REST API. + +This page will document the initial API workflow, required credentials, and first Artifact operations. diff --git a/src/content/docs/artifacts/get-started/workers.mdx b/src/content/docs/artifacts/get-started/workers.mdx new file mode 100644 index 000000000000000..21bfcc4a5bc58e1 --- /dev/null +++ b/src/content/docs/artifacts/get-started/workers.mdx @@ -0,0 +1,11 @@ +--- +title: Workers +description: Create and manage Artifacts from a Worker binding. +pcx_content_type: get-started +sidebar: + order: 1 +--- + +Use this guide to create your first Artifact from a Cloudflare Worker by writing files, reading versions, and promoting outputs into named artifacts. + +This page will document the minimal setup, binding configuration, and first read and write workflow for Artifacts in Workers. diff --git a/src/content/docs/artifacts/index.mdx b/src/content/docs/artifacts/index.mdx new file mode 100644 index 000000000000000..ef468c48224bb4e --- /dev/null +++ b/src/content/docs/artifacts/index.mdx @@ -0,0 +1,58 @@ +--- +title: Artifacts +description: Store, version, and share filesystem artifacts across Workers, APIs, and git-compatible tools. +pcx_content_type: overview +sidebar: + order: 1 + badge: + text: Beta +head: + - tag: title + content: Artifacts +--- + +import { CardGrid, Description, LinkTitleCard } from "~/components"; + + + +Store versioned filesystem artifacts that can be addressed through Workers, the REST API, and git-compatible workflows. + + + +Artifacts is in beta. Use Artifacts to persist file trees such as repositories, build outputs, checkpoints, and generated assets behind a versioned interface that works across Cloudflare developer tools. + + + + + Create your first Artifact with Workers or the REST API. + + + + Learn how Artifacts works and how to structure Artifact usage. + + + + Review the Workers binding, REST API, git support, and ArtifactsFS. + + + + Explore metrics and queries for understanding Artifact activity. + + + + See example integrations with git clients, OpenCode, and Sandbox SDK. + + + + Review pricing, limits, and changelog entries for Artifacts. + + + diff --git a/src/content/docs/artifacts/observability/index.mdx b/src/content/docs/artifacts/observability/index.mdx new file mode 100644 index 000000000000000..7345a273c1df3f5 --- /dev/null +++ b/src/content/docs/artifacts/observability/index.mdx @@ -0,0 +1,15 @@ +--- +title: Observability +description: Monitor Artifact usage with metrics and queries. +pcx_content_type: navigation +sidebar: + order: 5 + group: + hideIndex: true +--- + +import { DirectoryListing } from "~/components"; + +Use these pages to understand Artifact health, activity, and consumption patterns. + + diff --git a/src/content/docs/artifacts/observability/metrics.mdx b/src/content/docs/artifacts/observability/metrics.mdx new file mode 100644 index 000000000000000..84f83434689955a --- /dev/null +++ b/src/content/docs/artifacts/observability/metrics.mdx @@ -0,0 +1,11 @@ +--- +title: Metrics +description: Review the metrics exposed by Artifacts. +pcx_content_type: concept +sidebar: + order: 1 +--- + +Artifacts exposes metrics that help you understand storage growth, version activity, and request volume. + +This page will document the available metrics, how to interpret them, and where to view them. diff --git a/src/content/docs/artifacts/observability/queries.mdx b/src/content/docs/artifacts/observability/queries.mdx new file mode 100644 index 000000000000000..5ec808e740f668d --- /dev/null +++ b/src/content/docs/artifacts/observability/queries.mdx @@ -0,0 +1,11 @@ +--- +title: Queries +description: Query Artifact activity and usage data. +pcx_content_type: concept +sidebar: + order: 2 +--- + +Use queries to analyze Artifact activity, troubleshoot access patterns, and understand how versions are being produced and consumed. + +This page will document the available query surfaces and example analysis workflows. diff --git a/src/content/docs/artifacts/platform/changelog.mdx b/src/content/docs/artifacts/platform/changelog.mdx new file mode 100644 index 000000000000000..4b3afa486e0a740 --- /dev/null +++ b/src/content/docs/artifacts/platform/changelog.mdx @@ -0,0 +1,13 @@ +--- +title: Changelog +description: Review recent changes to Artifacts. +pcx_content_type: changelog +sidebar: + order: 3 +--- + +import { ProductChangelog } from "~/components"; + +{/* */} + + diff --git a/src/content/docs/artifacts/platform/index.mdx b/src/content/docs/artifacts/platform/index.mdx new file mode 100644 index 000000000000000..6373a10152c3d94 --- /dev/null +++ b/src/content/docs/artifacts/platform/index.mdx @@ -0,0 +1,15 @@ +--- +title: Platform +description: Review pricing, limits, and changelog information for Artifacts. +pcx_content_type: navigation +sidebar: + order: 7 + group: + hideIndex: true +--- + +import { DirectoryListing } from "~/components"; + +Use these pages to understand platform constraints and track product updates for Artifacts. + + diff --git a/src/content/docs/artifacts/platform/limits.mdx b/src/content/docs/artifacts/platform/limits.mdx new file mode 100644 index 000000000000000..0d942b2e347bad9 --- /dev/null +++ b/src/content/docs/artifacts/platform/limits.mdx @@ -0,0 +1,9 @@ +--- +title: Limits +description: Review Artifacts platform limits. +pcx_content_type: reference +sidebar: + order: 2 +--- + +This page will document the current limits for Artifact size, version count, requests, and related operational constraints. diff --git a/src/content/docs/artifacts/platform/pricing.mdx b/src/content/docs/artifacts/platform/pricing.mdx new file mode 100644 index 000000000000000..6ffa5781b5082fc --- /dev/null +++ b/src/content/docs/artifacts/platform/pricing.mdx @@ -0,0 +1,9 @@ +--- +title: Pricing +description: Review Artifacts pricing information. +pcx_content_type: reference +sidebar: + order: 1 +--- + +This page will document how Artifacts usage is billed, including any charges associated with storage, requests, or data transfer. diff --git a/src/icons/artifacts.svg b/src/icons/artifacts.svg new file mode 100644 index 000000000000000..c85cf737d3c7045 --- /dev/null +++ b/src/icons/artifacts.svg @@ -0,0 +1 @@ + From 291804ae10fc0116a62c78175d0944fcbe852e36 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Tue, 7 Apr 2026 16:35:24 -0400 Subject: [PATCH 2/3] [Artifacts] add get-started guides --- .../docs/artifacts/get-started/rest-api.mdx | 173 +++++++++- .../docs/artifacts/get-started/workers.mdx | 326 +++++++++++++++++- 2 files changed, 493 insertions(+), 6 deletions(-) diff --git a/src/content/docs/artifacts/get-started/rest-api.mdx b/src/content/docs/artifacts/get-started/rest-api.mdx index b3f0283310abf55..9d22d53fdebb08e 100644 --- a/src/content/docs/artifacts/get-started/rest-api.mdx +++ b/src/content/docs/artifacts/get-started/rest-api.mdx @@ -1,11 +1,178 @@ --- title: REST API -description: Create and manage Artifacts with the REST API. +description: Create a repo and token over HTTP. pcx_content_type: get-started sidebar: order: 2 +head: + - tag: title + content: Get started - REST API --- -Use this guide to create your first Artifact over HTTP by authenticating requests, uploading files, and reading Artifact versions through the REST API. +import { LinkCard } from "~/components"; -This page will document the initial API workflow, required credentials, and first Artifact operations. +Create and inspect your first Artifact repo with the REST API. + +In this guide, you will create a repo inside a namespace, fetch its metadata, retrieve its authenticated remote URL, and mint a read-only token for external tooling. + +## Prerequisites + +You need: + +- A Cloudflare account with access to Artifacts. +- An Artifacts namespace. This guide uses `default`. +- A control-plane JWT for the Artifacts API. + +## 1. Export your environment variables + +Set the variables used in the examples: + +```sh +export ARTIFACTS_BASE_URL="https://artifacts.cloudflare.dev" +export ARTIFACTS_NAMESPACE="default" +export ARTIFACTS_REPO="starter-repo" +export ARTIFACTS_JWT="" +``` + +Artifacts uses Bearer authentication for control-plane requests: + +```txt +Authorization: Bearer $ARTIFACTS_JWT +``` + +## 2. Create a repo + +Create a repo in your namespace: + +```bash +curl -X POST "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos" \ + -H "Authorization: Bearer $ARTIFACTS_JWT" \ + -H "Content-Type: application/json" \ + -d "{\"name\":\"$ARTIFACTS_REPO\",\"source\":\"rest-api-get-started\"}" +``` + +The response resembles the following: + +```json +{ + "id": "repo_123", + "name": "starter-repo", + "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo.git", + "token": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000", + "expiresAt": "" +} +``` + +The initial response includes an authenticated remote URL and a short-lived write token. You can use those values immediately with external tooling. + +## 3. Inspect the repo and remote URL + +Fetch the repo metadata: + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/$ARTIFACTS_REPO" \ + -H "Authorization: Bearer $ARTIFACTS_JWT" +``` + +Example response: + +```json +{ + "id": "repo_123", + "name": "starter-repo", + "createdAt": "", + "source": "rest-api-get-started", + "readOnly": false, + "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo.git" +} +``` + +If you only need the remote URL, call the dedicated endpoint: + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/$ARTIFACTS_REPO/remote" \ + -H "Authorization: Bearer $ARTIFACTS_JWT" +``` + +```json +{ + "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo.git" +} +``` + +## 4. Create and validate a token + +Create a read-only token for the repo: + +```bash +curl -X POST "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/tokens" \ + -H "Authorization: Bearer $ARTIFACTS_JWT" \ + -H "Content-Type: application/json" \ + -d "{\"repo\":\"$ARTIFACTS_REPO\",\"scope\":\"r\",\"ttl\":3600}" +``` + +Example response: + +```json +{ + "id": "tok_123", + "plaintext": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000", + "scope": "r", + "expiresAt": "" +} +``` + +Validate the token before handing it to another tool: + +```sh +export ARTIFACTS_TOKEN="" +``` + +```bash +curl -X POST "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/tokens/validate" \ + -H "Authorization: Bearer $ARTIFACTS_JWT" \ + -H "Content-Type: application/json" \ + -d "{\"repo\":\"$ARTIFACTS_REPO\",\"token\":\"$ARTIFACTS_TOKEN\"}" +``` + +```json +{ + "valid": true, + "scope": "r" +} +``` + +## 5. Delete the repo when you are done + +Remove the repo to clean up your test data: + +```bash +curl -X DELETE "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/$ARTIFACTS_REPO" \ + -H "Authorization: Bearer $ARTIFACTS_JWT" +``` + +```json +{ + "deleted": "starter-repo" +} +``` + +## Next steps + + + + + + diff --git a/src/content/docs/artifacts/get-started/workers.mdx b/src/content/docs/artifacts/get-started/workers.mdx index 21bfcc4a5bc58e1..a6a0d29b5f68a27 100644 --- a/src/content/docs/artifacts/get-started/workers.mdx +++ b/src/content/docs/artifacts/get-started/workers.mdx @@ -1,11 +1,331 @@ --- title: Workers -description: Create and manage Artifacts from a Worker binding. +description: Create a repo and token from a Worker. pcx_content_type: get-started sidebar: order: 1 +head: + - tag: title + content: Get started - Workers --- -Use this guide to create your first Artifact from a Cloudflare Worker by writing files, reading versions, and promoting outputs into named artifacts. +import { + LinkCard, + PackageManagers, + Render, + TypeScriptExample, + WranglerConfig, +} from "~/components"; -This page will document the minimal setup, binding configuration, and first read and write workflow for Artifacts in Workers. +Create your first Artifact from a Worker. + +In this guide, you will create a Worker, bind it to Artifacts through a service binding, and call the Artifacts API to create a repo, inspect it, and mint a read-only token. + +## Prerequisites + + + +You also need: + +- Access to the `artifacts` service in your Cloudflare account. +- A control-plane JWT for the Artifacts API. +- An Artifacts namespace. This guide uses `default`. + +## 1. Create a Worker project + +Create a new Worker project with C3: + + + + + +Move into the project directory: + +```sh +cd artifacts-worker +``` + +## 2. Add the Artifacts binding + +Add a service binding for Artifacts to `wrangler.jsonc`: + + + +```jsonc title="wrangler.jsonc" +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "artifacts-worker", + "main": "src/index.ts", + "compatibility_date": "$today", + "services": [ + { + "binding": "ARTIFACTS", + "service": "artifacts", + }, + ], +} +``` + + + +This exposes the service binding as `env.ARTIFACTS` in your Worker. + +Store your Artifacts control-plane JWT as a Worker secret: + +```sh +npx wrangler secret put ARTIFACTS_JWT +``` + +When prompted, paste your JWT. + +:::note + +Do not add the JWT directly to `wrangler.jsonc`. Store it as a secret so it does not end up in source control. + +::: + +If you are using TypeScript, regenerate your local binding types: + +```sh +npx wrangler types +``` + +## 3. Write code + +Replace `src/index.ts` with the following example: + + + +```ts +export interface Env { + ARTIFACTS: Fetcher; + ARTIFACTS_JWT: string; +} + +type RepoName = string; +type TokenScope = "rw" | "r"; + +interface CreateRepoRequest { + name: RepoName; + source?: string; + readOnly?: boolean; +} + +interface CreateRepoResponse { + id: string; + name: RepoName; + remote: string; + token: string; + expiresAt: string; +} + +interface GetRepoResponse { + id: string; + name: RepoName; + createdAt: string; + source: string | null; + readOnly: boolean; + remote: string; +} + +interface CreateTokenRequest { + repo: RepoName; + scope?: TokenScope; + ttl?: number; +} + +interface CreateTokenResponse { + id: string; + plaintext: string; + scope: TokenScope; + expiresAt: string; +} + +function createArtifactsClient( + fetcher: Fetcher, + opts: { baseUrl: string; jwt: string }, +) { + async function api(path: string, init: RequestInit = {}): Promise { + const headers = new Headers(init.headers); + headers.set("Authorization", `Bearer ${opts.jwt}`); + headers.set("Content-Type", "application/json"); + + const response = await fetcher.fetch(`${opts.baseUrl}${path}`, { + ...init, + headers, + }); + + if (!response.ok) { + const body = await response.text(); + throw new Error(body || `Artifacts API failed: ${response.status}`); + } + + return response.json(); + } + + return { + namespace(namespace: string) { + const base = `/v1/api/namespaces/${encodeURIComponent(namespace)}`; + + return { + repos: { + create(input: CreateRepoRequest) { + return api(`${base}/repos`, { + method: "POST", + body: JSON.stringify(input), + }); + }, + get(name: RepoName) { + return api( + `${base}/repos/${encodeURIComponent(name)}`, + ); + }, + }, + tokens: { + create(input: CreateTokenRequest) { + return api(`${base}/tokens`, { + method: "POST", + body: JSON.stringify(input), + }); + }, + }, + }; + }, + }; +} + +export default { + async fetch(request: Request, env: Env): Promise { + const url = new URL(request.url); + const client = createArtifactsClient(env.ARTIFACTS, { + baseUrl: "https://artifacts.cloudflare.dev", + jwt: env.ARTIFACTS_JWT, + }); + const artifacts = client.namespace("default"); + + if (request.method === "POST" && url.pathname === "/repos") { + const repo = await artifacts.repos.create({ + name: "starter-repo", + source: "workers-get-started", + }); + + return Response.json(repo); + } + + if (request.method === "GET" && url.pathname === "/repos/starter-repo") { + const repo = await artifacts.repos.get("starter-repo"); + return Response.json(repo); + } + + if (request.method === "POST" && url.pathname === "/tokens") { + const token = await artifacts.tokens.create({ + repo: "starter-repo", + scope: "r", + ttl: 3600, + }); + + return Response.json(token); + } + + return Response.json({ + message: "Use POST /repos, GET /repos/starter-repo, or POST /tokens.", + }); + }, +} satisfies ExportedHandler; +``` + + + +This example mirrors the public Artifacts schema: + +- `client.namespace("default")` scopes requests to one namespace. +- `repos.create()` creates a repo and returns its remote URL plus an initial write token. +- `tokens.create()` mints an additional token for downstream tooling. + +If your namespace is not `default`, replace that value in the example before you deploy. + +## 4. Develop locally + +Start the development server: + +```sh +npx wrangler dev +``` + +In a second terminal, create a repo: + +```sh +curl -X POST http://localhost:8787/repos +``` + +The response resembles the following: + +```json +{ + "id": "repo_123", + "name": "starter-repo", + "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo.git", + "token": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000", + "expiresAt": "" +} +``` + +Inspect the repo metadata: + +```sh +curl http://localhost:8787/repos/starter-repo +``` + +Create a read-only token for the repo: + +```sh +curl -X POST http://localhost:8787/tokens +``` + +## 5. Deploy and verify + +Deploy your Worker: + +```sh +npx wrangler deploy +``` + +Wrangler prints your `workers.dev` URL. Run the same requests against that URL to verify the Worker can create repos and tokens in Artifacts: + +```sh +curl -X POST https://artifacts-worker..workers.dev/repos +curl https://artifacts-worker..workers.dev/repos/starter-repo +curl -X POST https://artifacts-worker..workers.dev/tokens +``` + +## Next steps + + + + + + From 78d53a3bd658d0621ec082656e9dccb9cb5b2cfb Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Mon, 13 Apr 2026 10:10:21 -0400 Subject: [PATCH 3/3] [Artifacts] add API references and pricing --- src/content/docs/artifacts/api/rest-api.mdx | 482 ++++++++++++++++- .../docs/artifacts/api/workers-binding.mdx | 512 +++++++++++++++++- .../docs/artifacts/get-started/rest-api.mdx | 140 ++--- .../docs/artifacts/get-started/workers.mdx | 296 ++++------ .../docs/artifacts/platform/pricing.mdx | 7 + 5 files changed, 1153 insertions(+), 284 deletions(-) diff --git a/src/content/docs/artifacts/api/rest-api.mdx b/src/content/docs/artifacts/api/rest-api.mdx index edd586481610470..8843a5840bb59d5 100644 --- a/src/content/docs/artifacts/api/rest-api.mdx +++ b/src/content/docs/artifacts/api/rest-api.mdx @@ -1,11 +1,487 @@ --- title: REST API -description: Reference the Artifacts REST API. +description: Manage Artifacts repos and tokens over HTTP. pcx_content_type: reference sidebar: order: 2 +head: + - tag: title + content: REST API · Artifacts --- -Use the REST API to manage Artifacts over HTTP from external systems, automation, and custom clients. +import { LinkCard, MetaInfo, Type } from "~/components"; -This reference will document endpoints, request bodies, authentication requirements, and response formats. +Use the Artifacts REST API to manage repos, remotes, forks, imports, and tokens from external systems. + +## Base URL and authentication + +All routes are scoped to a namespace: + +```txt +/v1/api/namespaces/:namespace +``` + +Requests use Bearer authentication: + +```txt +Authorization: Bearer +``` + +The following examples assume: + +```sh +export ARTIFACTS_BASE_URL="https://artifacts.cloudflare.dev" +export ARTIFACTS_NAMESPACE="default" +export ARTIFACTS_REPO="starter-repo" +export ARTIFACTS_JWT="" +``` + +## Shared types + +```ts +export type NamespaceName = string; +export type RepoName = string; +export type Scope = "read" | "write"; +export type ArtifactToken = string; +export type Cursor = string; + +export interface RepoInfo { + id: string; + name: RepoName; + createdAt: string; + source: string | null; + readOnly: boolean; +} + +export interface RemoteRepoInfo extends RepoInfo { + remote: string; +} + +export interface TokenInfo { + id: string; + scope: Scope; + createdAt: string; + expiresAt: string; +} +``` + +## Repos + +### Create a repo + +Route: `POST /v1/api/namespaces/:namespace/repos` + +Request body: + +- `name` +- `readOnly` + +Response type: + +```ts +export interface CreateRepoRequest { + name: RepoName; + readOnly?: boolean; +} + +export interface CreateRepoResponse { + id: string; + name: RepoName; + remote: string; + token: ArtifactToken; + expiresAt: string; +} +``` + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" \ +--header "Content-Type: application/json" \ +--data '{ + "name": "starter-repo" +}' +``` + +```json +{ + "id": "repo_123", + "name": "starter-repo", + "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo.git", + "token": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000", + "expiresAt": "" +} +``` + +### List repos + +Route: `GET /v1/api/namespaces/:namespace/repos?limit=&cursor=` + +Query parameters: + +- `limit` +- `cursor` + +Response type: + +```ts +export interface ListReposQuery { + limit?: number; + cursor?: Cursor; +} + +export interface ListReposResponse { + repos: RepoInfo[]; + total: number; + cursor?: Cursor; +} +``` + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos?limit=20" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" +``` + +```json +{ + "repos": [ + { + "id": "repo_123", + "name": "starter-repo", + "createdAt": "", + "source": null, + "readOnly": false + } + ], + "total": 1 +} +``` + +### Get a repo + +Route: `GET /v1/api/namespaces/:namespace/repos/:name` + +Response type: + +```ts +export type GetRepoResponse = RemoteRepoInfo; +``` + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/$ARTIFACTS_REPO" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" +``` + +```json +{ + "id": "repo_123", + "name": "starter-repo", + "createdAt": "", + "source": null, + "readOnly": false, + "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo.git" +} +``` + +### Delete a repo + +Route: `DELETE /v1/api/namespaces/:namespace/repos/:name` + +Response type: + +```ts +export interface DeleteRepoResponse { + deleted: RepoName; +} +``` + +```bash +curl --request DELETE \ +"$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/$ARTIFACTS_REPO" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" +``` + +```json +{ + "deleted": "starter-repo" +} +``` + +### Fork a repo + +Route: `POST /v1/api/namespaces/:namespace/repos/:name/fork` + +Request body: + +- `name` +- `readOnly` + +Response type: + +```ts +export interface ForkRepoRequest { + name: RepoName; + readOnly?: boolean; +} + +export interface ForkRepoResponse extends CreateRepoResponse { + objects: number; +} +``` + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/$ARTIFACTS_REPO/fork" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" \ +--header "Content-Type: application/json" \ +--data '{ + "name": "starter-repo-copy" +}' +``` + +```json +{ + "id": "repo_456", + "name": "starter-repo-copy", + "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo-copy.git", + "token": "art_v1_89abcdef0123456789abcdef0123456789abcdef?expires=1760003600", + "expiresAt": "", + "objects": 128 +} +``` + +### Import a GitHub repository + +Route: `POST /v1/api/namespaces/:namespace/repos/:name/import` + +Request body: + +- `url` +- `branch` +- `readOnly` + +Response type: + +```ts +export interface ImportRepoRequest { + url: string; + branch?: string; + readOnly?: boolean; +} + +export type ImportRepoResponse = CreateRepoResponse; +``` + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/react-mirror/import" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" \ +--header "Content-Type: application/json" \ +--data '{ + "url": "facebook/react", + "branch": "main" +}' +``` + +```json +{ + "id": "repo_789", + "name": "react-mirror", + "remote": "https://.artifacts.cloudflare.dev/git/default/react-mirror.git", + "token": "art_v1_fedcba9876543210fedcba9876543210fedcba98?expires=1760007200", + "expiresAt": "" +} +``` + +## Tokens + +### List tokens for a repo + +Route: `GET /v1/api/namespaces/:namespace/repos/:name/tokens` + +Response type: + +```ts +export interface ListTokensResponse { + tokens: TokenInfo[]; +} +``` + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/$ARTIFACTS_REPO/tokens" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" +``` + +```json +{ + "tokens": [ + { + "id": "tok_123", + "scope": "read", + "createdAt": "", + "expiresAt": "" + } + ] +} +``` + +### Create a token + +Route: `POST /v1/api/namespaces/:namespace/tokens` + +Request body: + +- `repo` +- `scope` +- `ttl` + +Response type: + +```ts +export interface CreateTokenRequest { + repo: RepoName; + scope?: Scope; + ttl?: number; +} + +export interface CreateTokenResponse { + id: string; + plaintext: ArtifactToken; + scope: Scope; + expiresAt: string; +} +``` + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/tokens" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" \ +--header "Content-Type: application/json" \ +--data '{ + "repo": "starter-repo", + "scope": "read", + "ttl": 3600 +}' +``` + +```json +{ + "id": "tok_123", + "plaintext": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000", + "scope": "read", + "expiresAt": "" +} +``` + +### Validate a token + +Route: `POST /v1/api/namespaces/:namespace/tokens/validate` + +Request body: + +- `repo` +- `token` + +Response type: + +```ts +export interface ValidateTokenRequest { + token: ArtifactToken; + repo: RepoName; +} + +export interface ValidateTokenResponse { + valid: boolean; + scope?: Scope; +} +``` + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/tokens/validate" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" \ +--header "Content-Type: application/json" \ +--data '{ + "repo": "starter-repo", + "token": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000" +}' +``` + +```json +{ + "valid": true, + "scope": "read" +} +``` + +### Revoke a token + +Route: `POST /v1/api/namespaces/:namespace/tokens/revoke` + +Request body: + +- `token` +- `id` + +Provide either `token` or `id`. + +Response type: + +```ts +export interface RevokeTokenRequest { + token?: ArtifactToken; + id?: string; +} + +export interface RevokeTokenResponse { + revoked: true; +} +``` + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/tokens/revoke" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" \ +--header "Content-Type: application/json" \ +--data '{ + "id": "tok_123" +}' +``` + +```json +{ + "revoked": true +} +``` + +## Errors + +The API can return application errors and authentication errors. + +```ts +export interface ApiError { + error: string; +} + +export interface AuthError { + success: false; + errors: Array<{ + code: 10000; + message: string; + }>; +} +``` + +## Next steps + + + + + + diff --git a/src/content/docs/artifacts/api/workers-binding.mdx b/src/content/docs/artifacts/api/workers-binding.mdx index 3c5fe7d51497349..ff06a2758b05be9 100644 --- a/src/content/docs/artifacts/api/workers-binding.mdx +++ b/src/content/docs/artifacts/api/workers-binding.mdx @@ -1,11 +1,517 @@ --- title: Workers binding -description: Reference the Artifacts binding available inside Cloudflare Workers. +description: Call Artifacts from a Worker service binding. pcx_content_type: reference sidebar: order: 1 +head: + - tag: title + content: Workers binding · Artifacts --- -Use the Workers binding to create, inspect, and version Artifacts directly from a Worker. +import { + LinkCard, + MetaInfo, + Type, + TypeScriptExample, + WranglerConfig, +} from "~/components"; -This reference will document the available binding methods, parameters, and return values. +Use the Artifacts Workers binding to create, inspect, import, fork, and delete repos without making raw HTTP calls from your Worker. + +The binding is a Worker-to-Worker RPC surface. It returns repo handles for repo-scoped operations such as token management and forking. + +## Configure the binding + +Add the Artifacts binding to `wrangler.jsonc`: + + + +```jsonc title="wrangler.jsonc" +{ + "services": [ + { + "binding": "ARTIFACTS", + "service": "artifacts", + "entrypoint": "ArtifactsBinding", + }, + ], +} +``` + + + +Your Worker environment looks like this: + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +export interface Env { + ARTIFACTS: Service; +} +``` + +## Shared types + +These are the core public types used by the binding. + +```ts +export type NamespaceName = string; +export type RepoName = string; +export type Scope = "read" | "write"; +export type ArtifactToken = string; +export type Cursor = string; + +export interface RemoteRepoInfo { + id: string; + name: RepoName; + createdAt: string; + source: string | null; + readOnly: boolean; + remote: string; +} + +export interface CreateRepoResponse { + id: string; + name: RepoName; + remote: string; + token: ArtifactToken; + expiresAt: string; +} + +export interface ListReposResponse { + repos: Array<{ + id: string; + name: RepoName; + createdAt: string; + source: string | null; + readOnly: boolean; + }>; + total: number; + cursor?: Cursor; +} + +export interface CreateTokenResponse { + id: string; + plaintext: ArtifactToken; + scope: Scope; + expiresAt: string; +} + +export interface ValidateTokenResponse { + valid: boolean; + scope?: Scope; +} + +export interface TokenInfo { + id: string; + scope: Scope; + createdAt: string; + expiresAt: string; +} +``` + +## Binding surface + +```ts +export interface ArtifactsRepoHandle { + info(): Promise; + createToken(scope?: Scope, ttl?: number): Promise; + validateToken(token: ArtifactToken): Promise; + listTokens(): Promise<{ tokens: TokenInfo[]; total: number }>; + revokeToken(tokenOrId: string): Promise; + fork(target: { + name: RepoName; + namespace?: NamespaceName; + readOnly?: boolean; + }): Promise; +} + +export interface ArtifactsBinding { + create( + name: RepoName, + opts?: { readOnly?: boolean }, + ): Promise; + + get(name: RepoName): Promise; + + list(opts?: { limit?: number; cursor?: Cursor }): Promise; + + delete(name: RepoName): Promise; + + import( + name: RepoName, + source: { + url: string; + branch?: string; + headers?: Record; + readOnly?: boolean; + }, + ): Promise; +} +``` + +## Namespace methods + +### `create(name, opts?)` + +- `name` +- `opts.readOnly` +- Returns + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +async function createRepo(artifacts: Service) { + // Create the repo and capture the initial write token. + const created = await artifacts.create("starter-repo"); + + return { + name: created.name, + remote: created.remote, + initialToken: created.token, + }; +} +``` + + + +### `get(name)` + +- `name` +- Returns + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +async function getRepoHandle(artifacts: Service) { + // Resolve the handle once, then reuse it for repo-scoped methods. + const repo = await artifacts.get("starter-repo"); + + if (!repo) { + return null; + } + + return repo; +} +``` + + + +### `list(opts?)` + +- `opts.limit` +- `opts.cursor` +- Returns + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +async function listRepos(artifacts: Service) { + // Request a small page when you only need recent repo names. + const page = await artifacts.list({ limit: 10 }); + + return page.repos.map((repo) => repo.name); +} +``` + + + +### `delete(name)` + +- `name` +- Returns + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +async function deleteRepo(artifacts: Service) { + // Delete returns true when the repo was removed. + return artifacts.delete("starter-repo"); +} +``` + + + +### `import(name, source)` + +- `name` +- `source.url` +- `source.branch` +- `source.headers` +- `source.readOnly` +- Returns + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +async function importRepo(artifacts: Service) { + // Import a public GitHub repo into Artifacts. + const imported = await artifacts.import("react-mirror", { + url: "facebook/react", + branch: "main", + }); + + return imported.remote; +} +``` + + + +## Repo handle methods + +Use `await artifacts.get(name)` or the `repo` returned by `create()`, `import()`, or `fork()` to access repo-scoped methods. + +### `info()` + +- Returns + +Use `info()` when you need repo metadata, including the authenticated remote URL. The binding does not expose a separate `.remote()` method. + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +async function getRemoteUrl(artifacts: Service) { + const repo = await artifacts.get("starter-repo"); + if (!repo) { + return null; + } + + // Read the remote URL from repo metadata. + const info = await repo.info(); + return info?.remote ?? null; +} +``` + + + +### `createToken(scope?, ttl?)` + +- `scope` +- `ttl` +- Returns + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +async function mintReadToken(artifacts: Service) { + const repo = await artifacts.get("starter-repo"); + if (!repo) { + throw new Error("Repo not found"); + } + + // Create a short-lived read token for another tool. + return repo.createToken("read", 3600); +} +``` + + + +### `validateToken(token)` + +- `token` +- Returns + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +async function validateToken( + artifacts: Service, + token: string, +) { + const repo = await artifacts.get("starter-repo"); + if (!repo) { + throw new Error("Repo not found"); + } + + // Check whether the presented token is still valid. + return repo.validateToken(token); +} +``` + + + +### `listTokens()` + +- Returns + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +async function listRepoTokens(artifacts: Service) { + const repo = await artifacts.get("starter-repo"); + if (!repo) { + throw new Error("Repo not found"); + } + + // Inspect active tokens for cleanup or auditing. + const result = await repo.listTokens(); + return result.tokens; +} +``` + + + +### `revokeToken(tokenOrId)` + +- `tokenOrId` +- Returns + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +async function revokeToken( + artifacts: Service, + tokenId: string, +) { + const repo = await artifacts.get("starter-repo"); + if (!repo) { + throw new Error("Repo not found"); + } + + // Revoke by token ID or plaintext token value. + return repo.revokeToken(tokenId); +} +``` + + + +### `fork(target)` + +- `target.name` +- `target.namespace` +- `target.readOnly` +- Returns + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +async function forkRepo(artifacts: Service) { + const repo = await artifacts.get("starter-repo"); + if (!repo) { + throw new Error("Repo not found"); + } + + // Fork into a new repo and keep the returned handle. + const forked = await repo.fork({ + name: "starter-repo-copy", + }); + + return forked.remote; +} +``` + + + +## Worker example + +This example combines the binding methods in one Worker route. + + + +```ts +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; + +interface Env { + ARTIFACTS: Service; +} + +export default { + async fetch(request: Request, env: Env): Promise { + const url = new URL(request.url); + + if (request.method === "POST" && url.pathname === "/repos") { + // Create a repo and return its initial remote. + const created = await env.ARTIFACTS.create("starter-repo"); + return Response.json({ + name: created.name, + remote: created.remote, + }); + } + + if (request.method === "GET" && url.pathname === "/repos/starter-repo") { + const repo = await env.ARTIFACTS.get("starter-repo"); + if (!repo) { + return Response.json({ error: "Repo not found" }, { status: 404 }); + } + + // Fetch the current repo metadata, including its remote URL. + const info = await repo.info(); + return Response.json(info); + } + + if (request.method === "POST" && url.pathname === "/tokens") { + const repo = await env.ARTIFACTS.get("starter-repo"); + if (!repo) { + return Response.json({ error: "Repo not found" }, { status: 404 }); + } + + // Mint a short-lived read token for external tooling. + const token = await repo.createToken("read", 3600); + return Response.json(token); + } + + return Response.json( + { message: "Use POST /repos, GET /repos/starter-repo, or POST /tokens." }, + { status: 404 }, + ); + }, +} satisfies ExportedHandler; +``` + + + +## Next steps + + + + + + diff --git a/src/content/docs/artifacts/get-started/rest-api.mdx b/src/content/docs/artifacts/get-started/rest-api.mdx index 9d22d53fdebb08e..8c0cbc25bac0d32 100644 --- a/src/content/docs/artifacts/get-started/rest-api.mdx +++ b/src/content/docs/artifacts/get-started/rest-api.mdx @@ -1,6 +1,6 @@ --- title: REST API -description: Create a repo and token over HTTP. +description: Create an Artifact repo over HTTP. pcx_content_type: get-started sidebar: order: 2 @@ -11,17 +11,17 @@ head: import { LinkCard } from "~/components"; -Create and inspect your first Artifact repo with the REST API. +Create an Artifact namespace and repo with the REST API, then use a regular git client to push and pull content. -In this guide, you will create a repo inside a namespace, fetch its metadata, retrieve its authenticated remote URL, and mint a read-only token for external tooling. +In this guide, you will create a namespace, create a repo inside it, read back the repo remote URL, and use that remote with `git push` and `git clone`. ## Prerequisites You need: - A Cloudflare account with access to Artifacts. -- An Artifacts namespace. This guide uses `default`. - A control-plane JWT for the Artifacts API. +- A local `git` client. ## 1. Export your environment variables @@ -29,7 +29,7 @@ Set the variables used in the examples: ```sh export ARTIFACTS_BASE_URL="https://artifacts.cloudflare.dev" -export ARTIFACTS_NAMESPACE="default" +export ARTIFACTS_NAMESPACE="starter-namespace" export ARTIFACTS_REPO="starter-repo" export ARTIFACTS_JWT="" ``` @@ -40,139 +40,119 @@ Artifacts uses Bearer authentication for control-plane requests: Authorization: Bearer $ARTIFACTS_JWT ``` -## 2. Create a repo +## 2. Create a namespace -Create a repo in your namespace: +Create a namespace for your repos: ```bash -curl -X POST "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos" \ - -H "Authorization: Bearer $ARTIFACTS_JWT" \ - -H "Content-Type: application/json" \ - -d "{\"name\":\"$ARTIFACTS_REPO\",\"source\":\"rest-api-get-started\"}" +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" \ +--header "Content-Type: application/json" \ +--data "{\"name\":\"$ARTIFACTS_NAMESPACE\"}" ``` The response resembles the following: ```json { - "id": "repo_123", - "name": "starter-repo", - "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo.git", - "token": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000", - "expiresAt": "" + "name": "starter-namespace", + "createdAt": "" } ``` -The initial response includes an authenticated remote URL and a short-lived write token. You can use those values immediately with external tooling. - -## 3. Inspect the repo and remote URL +## 3. Create a repo -Fetch the repo metadata: +Create a repo inside that namespace: ```bash -curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/$ARTIFACTS_REPO" \ - -H "Authorization: Bearer $ARTIFACTS_JWT" +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" \ +--header "Content-Type: application/json" \ +--data "{\"name\":\"$ARTIFACTS_REPO\"}" ``` -Example response: +The response resembles the following: ```json { "id": "repo_123", "name": "starter-repo", - "createdAt": "", - "source": "rest-api-get-started", - "readOnly": false, - "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo.git" + "remote": "https://.artifacts.cloudflare.dev/git/starter-namespace/starter-repo.git", + "token": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000", + "expiresAt": "" } ``` -If you only need the remote URL, call the dedicated endpoint: +Copy the `remote` and `token` values into local shell variables: -```bash -curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/$ARTIFACTS_REPO/remote" \ - -H "Authorization: Bearer $ARTIFACTS_JWT" -``` - -```json -{ - "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo.git" -} +```sh +export ARTIFACTS_REMOTE="" +export ARTIFACTS_TOKEN="" ``` -## 4. Create and validate a token +## 4. Get the repo URL again -Create a read-only token for the repo: +Fetch the repo metadata when you need to recover the remote URL later: ```bash -curl -X POST "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/tokens" \ - -H "Authorization: Bearer $ARTIFACTS_JWT" \ - -H "Content-Type: application/json" \ - -d "{\"repo\":\"$ARTIFACTS_REPO\",\"scope\":\"r\",\"ttl\":3600}" +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/$ARTIFACTS_REPO" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" ``` -Example response: - ```json { - "id": "tok_123", - "plaintext": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000", - "scope": "r", - "expiresAt": "" + "id": "repo_123", + "name": "starter-repo", + "createdAt": "", + "source": null, + "readOnly": false, + "remote": "https://.artifacts.cloudflare.dev/git/starter-namespace/starter-repo.git" } ``` -Validate the token before handing it to another tool: +## 5. Push your first commit with git -```sh -export ARTIFACTS_TOKEN="" -``` - -```bash -curl -X POST "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/tokens/validate" \ - -H "Authorization: Bearer $ARTIFACTS_JWT" \ - -H "Content-Type: application/json" \ - -d "{\"repo\":\"$ARTIFACTS_REPO\",\"token\":\"$ARTIFACTS_TOKEN\"}" -``` +Create a local repository and push it to the Artifacts remote: -```json -{ - "valid": true, - "scope": "r" -} +```sh +mkdir artifacts-demo +cd artifacts-demo +git init -b main +printf '# Artifacts demo\n' > README.md +git add README.md +git commit -m "Initial commit" +git remote add origin "$ARTIFACTS_REMOTE" +git -c http.extraHeader="Authorization: Bearer $ARTIFACTS_TOKEN" push -u origin main ``` -## 5. Delete the repo when you are done +## 6. Pull the repo with a regular git client -Remove the repo to clean up your test data: +Clone the same repo into a second directory: -```bash -curl -X DELETE "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos/$ARTIFACTS_REPO" \ - -H "Authorization: Bearer $ARTIFACTS_JWT" +```sh +cd .. +git -c http.extraHeader="Authorization: Bearer $ARTIFACTS_TOKEN" clone "$ARTIFACTS_REMOTE" artifacts-clone +git -C artifacts-clone log --oneline -1 ``` -```json -{ - "deleted": "starter-repo" -} -``` +You should see the commit you pushed in the previous step. ## Next steps diff --git a/src/content/docs/artifacts/get-started/workers.mdx b/src/content/docs/artifacts/get-started/workers.mdx index a6a0d29b5f68a27..25cdadf86fb87fa 100644 --- a/src/content/docs/artifacts/get-started/workers.mdx +++ b/src/content/docs/artifacts/get-started/workers.mdx @@ -1,6 +1,6 @@ --- title: Workers -description: Create a repo and token from a Worker. +description: Create an Artifact repo from a Worker. pcx_content_type: get-started sidebar: order: 1 @@ -15,11 +15,12 @@ import { Render, TypeScriptExample, WranglerConfig, + Steps, } from "~/components"; -Create your first Artifact from a Worker. +Create an Artifact repo from a Worker and use a standard git client to push and pull content. -In this guide, you will create a Worker, bind it to Artifacts through a service binding, and call the Artifacts API to create a repo, inspect it, and mint a read-only token. +In this guide, you will create a Worker, bind it to Artifacts, create a repo through the Workers binding, and use the returned remote URL and token with `git push` and `git clone`. ## Prerequisites @@ -28,38 +29,41 @@ In this guide, you will create a Worker, bind it to Artifacts through a service You also need: - Access to the `artifacts` service in your Cloudflare account. -- A control-plane JWT for the Artifacts API. -- An Artifacts namespace. This guide uses `default`. +- A local `git` client. ## 1. Create a Worker project -Create a new Worker project with C3: + - +1. Create a new Worker project with C3: - + -Move into the project directory: + -```sh -cd artifacts-worker -``` +2. Move into the project directory: + + ```sh + cd artifacts-worker + ``` + + ## 2. Add the Artifacts binding -Add a service binding for Artifacts to `wrangler.jsonc`: +Open `wrangler.jsonc` and add the Artifacts service binding: @@ -73,6 +77,7 @@ Add a service binding for Artifacts to `wrangler.jsonc`: { "binding": "ARTIFACTS", "service": "artifacts", + "entrypoint": "ArtifactsBinding", }, ], } @@ -80,167 +85,51 @@ Add a service binding for Artifacts to `wrangler.jsonc`: -This exposes the service binding as `env.ARTIFACTS` in your Worker. - -Store your Artifacts control-plane JWT as a Worker secret: - -```sh -npx wrangler secret put ARTIFACTS_JWT -``` - -When prompted, paste your JWT. - -:::note - -Do not add the JWT directly to `wrangler.jsonc`. Store it as a secret so it does not end up in source control. - -::: +This exposes Artifacts as `env.ARTIFACTS` inside your Worker. If you are using TypeScript, regenerate your local binding types: -```sh -npx wrangler types -``` + -## 3. Write code +## 3. Write your Worker -Replace `src/index.ts` with the following example: +Replace `src/index.ts` with the following code: ```ts -export interface Env { - ARTIFACTS: Fetcher; - ARTIFACTS_JWT: string; -} - -type RepoName = string; -type TokenScope = "rw" | "r"; +import type { Service } from "cloudflare:workers"; +import type { ArtifactsBinding } from "artifacts"; -interface CreateRepoRequest { - name: RepoName; - source?: string; - readOnly?: boolean; -} - -interface CreateRepoResponse { - id: string; - name: RepoName; - remote: string; - token: string; - expiresAt: string; -} - -interface GetRepoResponse { - id: string; - name: RepoName; - createdAt: string; - source: string | null; - readOnly: boolean; - remote: string; -} - -interface CreateTokenRequest { - repo: RepoName; - scope?: TokenScope; - ttl?: number; -} - -interface CreateTokenResponse { - id: string; - plaintext: string; - scope: TokenScope; - expiresAt: string; -} - -function createArtifactsClient( - fetcher: Fetcher, - opts: { baseUrl: string; jwt: string }, -) { - async function api(path: string, init: RequestInit = {}): Promise { - const headers = new Headers(init.headers); - headers.set("Authorization", `Bearer ${opts.jwt}`); - headers.set("Content-Type", "application/json"); - - const response = await fetcher.fetch(`${opts.baseUrl}${path}`, { - ...init, - headers, - }); - - if (!response.ok) { - const body = await response.text(); - throw new Error(body || `Artifacts API failed: ${response.status}`); - } - - return response.json(); - } - - return { - namespace(namespace: string) { - const base = `/v1/api/namespaces/${encodeURIComponent(namespace)}`; - - return { - repos: { - create(input: CreateRepoRequest) { - return api(`${base}/repos`, { - method: "POST", - body: JSON.stringify(input), - }); - }, - get(name: RepoName) { - return api( - `${base}/repos/${encodeURIComponent(name)}`, - ); - }, - }, - tokens: { - create(input: CreateTokenRequest) { - return api(`${base}/tokens`, { - method: "POST", - body: JSON.stringify(input), - }); - }, - }, - }; - }, - }; +interface Env { + ARTIFACTS: Service; } export default { async fetch(request: Request, env: Env): Promise { const url = new URL(request.url); - const client = createArtifactsClient(env.ARTIFACTS, { - baseUrl: "https://artifacts.cloudflare.dev", - jwt: env.ARTIFACTS_JWT, - }); - const artifacts = client.namespace("default"); if (request.method === "POST" && url.pathname === "/repos") { - const repo = await artifacts.repos.create({ - name: "starter-repo", - source: "workers-get-started", - }); - - return Response.json(repo); - } + // Read the repo name from the request body so the route is reusable. + const body = (await request.json().catch(() => ({}))) as { + name?: string; + }; + const repoName = body.name ?? "starter-repo"; - if (request.method === "GET" && url.pathname === "/repos/starter-repo") { - const repo = await artifacts.repos.get("starter-repo"); - return Response.json(repo); - } + // Create the repo and return the remote plus initial write token. + const created = await env.ARTIFACTS.create(repoName); - if (request.method === "POST" && url.pathname === "/tokens") { - const token = await artifacts.tokens.create({ - repo: "starter-repo", - scope: "r", - ttl: 3600, + return Response.json({ + name: created.name, + remote: created.remote, + token: created.token, + expiresAt: created.expiresAt, }); - - return Response.json(token); } - return Response.json({ - message: "Use POST /repos, GET /repos/starter-repo, or POST /tokens.", + return new Response("Use POST /repos to create an Artifact repo.", { + status: 405, + headers: { Allow: "POST" }, }); }, } satisfies ExportedHandler; @@ -248,33 +137,28 @@ export default { -This example mirrors the public Artifacts schema: +This Worker does one job: create an Artifact repo and return the values your git client needs next. -- `client.namespace("default")` scopes requests to one namespace. -- `repos.create()` creates a repo and returns its remote URL plus an initial write token. -- `tokens.create()` mints an additional token for downstream tooling. +## 4. Create a repo through your Worker -If your namespace is not `default`, replace that value in the example before you deploy. +Start local development: -## 4. Develop locally + -Start the development server: +In a second terminal, create a repo through your Worker: -```sh -npx wrangler dev -``` - -In a second terminal, create a repo: - -```sh -curl -X POST http://localhost:8787/repos +```bash +curl http://localhost:8787/repos \ +--header "Content-Type: application/json" \ +--data '{ + "name": "starter-repo" +}' ``` The response resembles the following: ```json { - "id": "repo_123", "name": "starter-repo", "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo.git", "token": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000", @@ -282,50 +166,66 @@ The response resembles the following: } ``` -Inspect the repo metadata: +Copy the `remote` and `token` values into local shell variables: ```sh -curl http://localhost:8787/repos/starter-repo +export ARTIFACTS_REMOTE="" +export ARTIFACTS_TOKEN="" ``` -Create a read-only token for the repo: +## 5. Push your first commit with git + +Create a local repository and push it to Artifacts: ```sh -curl -X POST http://localhost:8787/tokens +mkdir artifacts-demo +cd artifacts-demo +git init -b main +printf '# Artifacts demo\n' > README.md +git add README.md +git commit -m "Initial commit" +git remote add origin "$ARTIFACTS_REMOTE" +git -c http.extraHeader="Authorization: Bearer $ARTIFACTS_TOKEN" push -u origin main ``` -## 5. Deploy and verify +This uses the remote URL returned by Artifacts and passes the token as an HTTP authorization header for the push. -Deploy your Worker: +## 6. Pull the repo with a regular git client + +Clone the same repo into a second directory: ```sh -npx wrangler deploy +cd .. +git -c http.extraHeader="Authorization: Bearer $ARTIFACTS_TOKEN" clone "$ARTIFACTS_REMOTE" artifacts-clone +git -C artifacts-clone log --oneline -1 ``` -Wrangler prints your `workers.dev` URL. Run the same requests against that URL to verify the Worker can create repos and tokens in Artifacts: +You should see the commit you pushed in the previous step. -```sh -curl -X POST https://artifacts-worker..workers.dev/repos -curl https://artifacts-worker..workers.dev/repos/starter-repo -curl -X POST https://artifacts-worker..workers.dev/tokens -``` +## 7. Deploy your Worker + +Deploy the Worker so you can create repos without running `wrangler dev`: + + + +Wrangler prints your `workers.dev` URL. Use the same `curl` request against that URL to create additional repos from production. ## Next steps diff --git a/src/content/docs/artifacts/platform/pricing.mdx b/src/content/docs/artifacts/platform/pricing.mdx index 6ffa5781b5082fc..374ba738ec3645c 100644 --- a/src/content/docs/artifacts/platform/pricing.mdx +++ b/src/content/docs/artifacts/platform/pricing.mdx @@ -7,3 +7,10 @@ sidebar: --- This page will document how Artifacts usage is billed, including any charges associated with storage, requests, or data transfer. + +Current Artifacts pricing is: + +| Usage type | $/unit | Included | +| ---------- | -------------------------- | ------------------------------ | +| Operations | $0.15 per 1,000 operations | First 10k included (per month) | +| Storage | $0.50/GB-mo | First 1 GB included |