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..8843a5840bb59d5 --- /dev/null +++ b/src/content/docs/artifacts/api/rest-api.mdx @@ -0,0 +1,487 @@ +--- +title: REST API +description: Manage Artifacts repos and tokens over HTTP. +pcx_content_type: reference +sidebar: + order: 2 +head: + - tag: title + content: REST API · Artifacts +--- + +import { LinkCard, MetaInfo, Type } from "~/components"; + +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 new file mode 100644 index 000000000000000..ff06a2758b05be9 --- /dev/null +++ b/src/content/docs/artifacts/api/workers-binding.mdx @@ -0,0 +1,517 @@ +--- +title: Workers binding +description: Call Artifacts from a Worker service binding. +pcx_content_type: reference +sidebar: + order: 1 +head: + - tag: title + content: Workers binding · Artifacts +--- + +import { + LinkCard, + MetaInfo, + Type, + TypeScriptExample, + WranglerConfig, +} from "~/components"; + +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/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..8c0cbc25bac0d32 --- /dev/null +++ b/src/content/docs/artifacts/get-started/rest-api.mdx @@ -0,0 +1,158 @@ +--- +title: REST API +description: Create an Artifact repo over HTTP. +pcx_content_type: get-started +sidebar: + order: 2 +head: + - tag: title + content: Get started - REST API +--- + +import { LinkCard } from "~/components"; + +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 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. +- A control-plane JWT for the Artifacts API. +- A local `git` client. + +## 1. Export your environment variables + +Set the variables used in the examples: + +```sh +export ARTIFACTS_BASE_URL="https://artifacts.cloudflare.dev" +export ARTIFACTS_NAMESPACE="starter-namespace" +export ARTIFACTS_REPO="starter-repo" +export ARTIFACTS_JWT="" +``` + +Artifacts uses Bearer authentication for control-plane requests: + +```txt +Authorization: Bearer $ARTIFACTS_JWT +``` + +## 2. Create a namespace + +Create a namespace for your repos: + +```bash +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 +{ + "name": "starter-namespace", + "createdAt": "" +} +``` + +## 3. Create a repo + +Create a repo inside that namespace: + +```bash +curl "$ARTIFACTS_BASE_URL/v1/api/namespaces/$ARTIFACTS_NAMESPACE/repos" \ +--header "Authorization: Bearer $ARTIFACTS_JWT" \ +--header "Content-Type: application/json" \ +--data "{\"name\":\"$ARTIFACTS_REPO\"}" +``` + +The response resembles the following: + +```json +{ + "id": "repo_123", + "name": "starter-repo", + "remote": "https://.artifacts.cloudflare.dev/git/starter-namespace/starter-repo.git", + "token": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000", + "expiresAt": "" +} +``` + +Copy the `remote` and `token` values into local shell variables: + +```sh +export ARTIFACTS_REMOTE="" +export ARTIFACTS_TOKEN="" +``` + +## 4. Get the repo URL again + +Fetch the repo metadata when you need to recover the remote URL later: + +```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/starter-namespace/starter-repo.git" +} +``` + +## 5. Push your first commit with git + +Create a local repository and push it to the Artifacts remote: + +```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 +``` + +## 6. Pull the repo with a regular git client + +Clone the same repo into a second directory: + +```sh +cd .. +git -c http.extraHeader="Authorization: Bearer $ARTIFACTS_TOKEN" clone "$ARTIFACTS_REMOTE" artifacts-clone +git -C artifacts-clone log --oneline -1 +``` + +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 new file mode 100644 index 000000000000000..25cdadf86fb87fa --- /dev/null +++ b/src/content/docs/artifacts/get-started/workers.mdx @@ -0,0 +1,231 @@ +--- +title: Workers +description: Create an Artifact repo from a Worker. +pcx_content_type: get-started +sidebar: + order: 1 +head: + - tag: title + content: Get started - Workers +--- + +import { + LinkCard, + PackageManagers, + Render, + TypeScriptExample, + WranglerConfig, + Steps, +} from "~/components"; + +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, create a repo through the Workers binding, and use the returned remote URL and token with `git push` and `git clone`. + +## Prerequisites + + + +You also need: + +- Access to the `artifacts` service in your Cloudflare account. +- A local `git` client. + +## 1. Create a Worker project + + + +1. Create a new Worker project with C3: + + + + + +2. Move into the project directory: + + ```sh + cd artifacts-worker + ``` + + + +## 2. Add the Artifacts binding + +Open `wrangler.jsonc` and add the Artifacts service binding: + + + +```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", + "entrypoint": "ArtifactsBinding", + }, + ], +} +``` + + + +This exposes Artifacts as `env.ARTIFACTS` inside your Worker. + +If you are using TypeScript, regenerate your local binding types: + + + +## 3. Write your Worker + +Replace `src/index.ts` with the following code: + + + +```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") { + // 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"; + + // Create the repo and return the remote plus initial write token. + const created = await env.ARTIFACTS.create(repoName); + + return Response.json({ + name: created.name, + remote: created.remote, + token: created.token, + expiresAt: created.expiresAt, + }); + } + + return new Response("Use POST /repos to create an Artifact repo.", { + status: 405, + headers: { Allow: "POST" }, + }); + }, +} satisfies ExportedHandler; +``` + + + +This Worker does one job: create an Artifact repo and return the values your git client needs next. + +## 4. Create a repo through your Worker + +Start local development: + + + +In a second terminal, create a repo through your Worker: + +```bash +curl http://localhost:8787/repos \ +--header "Content-Type: application/json" \ +--data '{ + "name": "starter-repo" +}' +``` + +The response resembles the following: + +```json +{ + "name": "starter-repo", + "remote": "https://.artifacts.cloudflare.dev/git/default/starter-repo.git", + "token": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000", + "expiresAt": "" +} +``` + +Copy the `remote` and `token` values into local shell variables: + +```sh +export ARTIFACTS_REMOTE="" +export ARTIFACTS_TOKEN="" +``` + +## 5. Push your first commit with git + +Create a local repository and push it to Artifacts: + +```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 +``` + +This uses the remote URL returned by Artifacts and passes the token as an HTTP authorization header for the push. + +## 6. Pull the repo with a regular git client + +Clone the same repo into a second directory: + +```sh +cd .. +git -c http.extraHeader="Authorization: Bearer $ARTIFACTS_TOKEN" clone "$ARTIFACTS_REMOTE" artifacts-clone +git -C artifacts-clone log --oneline -1 +``` + +You should see the commit you pushed in the previous step. + +## 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/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..374ba738ec3645c --- /dev/null +++ b/src/content/docs/artifacts/platform/pricing.mdx @@ -0,0 +1,16 @@ +--- +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. + +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 | 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 @@ +