Best practices for Artifacts
Artifacts works best when you isolate work, scope access narrowly, keep metadata separate, and partition storage deliberately.
Use these patterns to structure repos for agents, automation, and shared systems.
Create one repo for each unit of autonomous work. If you have 10,000 agents, create 10,000 repos.
This keeps each agent's changes, failures, and cleanup lifecycle separate. It also avoids turning one shared repo into a hot spot for conflicts, large diffs, and accidental overwrites.
Use this pattern when you need to:
- isolate one agent's work from another agent's work
- hand off a repo to a single session or user application
- review, merge, archive, or delete work independently
Use branches only when collaborators share the same lifecycle and need to work on the same repository. Do not use one shared repo as a queue for many autonomous agents.
Repo names are unique within a namespace. If multiple agents need isolated copies of the same baseline repo in one namespace, do not reuse a short shared name such as docs-site.
Include stable identifiers in the repo name, such as the agent name, session ID, user ID, or workflow ID. A name like ${agentName}-${sessionId}-${repoName} is safer than ${repoName} because it avoids collisions and makes cleanup easier.
This example creates a unique repo name before creating the repo.
async function createRepoCopy(env, agentName, sessionId, repoName) { const uniqueRepoName = `${agentName}-${sessionId}-${repoName}`;
return env.ARTIFACTS.create(uniqueRepoName);}interface Env { ARTIFACTS: Artifacts;}
async function createRepoCopy( env: Env, agentName: string, sessionId: string, repoName: string,) { const uniqueRepoName = `${agentName}-${sessionId}-${repoName}`;
return env.ARTIFACTS.create(uniqueRepoName);}Start new repos from a trusted baseline when agents need the same starter files, prompts, or application structure. Forking from a reviewed repo is safer than copying files into every new repo by hand.
This keeps your starting point consistent and makes downstream diffs easier to review. It also lets you merge back only the results you want.
This example forks a reviewed baseline repo into a session-specific repo.
async function forkFromBaseline(env, sessionId) { const baseline = await env.ARTIFACTS.get("starter-repo"); const forked = await baseline.fork(`starter-repo-${sessionId}`, { description: `Fork for session ${sessionId}`, defaultBranchOnly: true, readOnly: false, });
return { name: forked.name, remote: forked.remote, };}interface Env { ARTIFACTS: Artifacts;}
async function forkFromBaseline(env: Env, sessionId: string) { const baseline = await env.ARTIFACTS.get("starter-repo"); const forked = await baseline.fork(`starter-repo-${sessionId}`, { description: `Fork for session ${sessionId}`, defaultBranchOnly: true, readOnly: false, });
return { name: forked.name, remote: forked.remote, };}Artifacts tokens are repo-scoped. Prefer read tokens for cloning, indexing, review, and retrieval.
Use write tokens only for the agent or system that must push changes. Give tokens short lifetimes, and re-issue a fresh token for each agent session.
This example uses the Workers binding to mint a short-lived read token for a repo.
Assume the caller is already authenticated and authorized before this route returns a token.
export default { async fetch(request, env) { const url = new URL(request.url); const repoName = url.searchParams.get("repo") ?? "starter-repo";
const repo = await env.ARTIFACTS.get(repoName); const token = await repo.createToken("read", 900);
return Response.json({ repo: repoName, scope: token.scope, expiresAt: token.expiresAt, token: token.plaintext, }); },};interface Env { ARTIFACTS: Artifacts;}
export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); const repoName = url.searchParams.get("repo") ?? "starter-repo";
const repo = await env.ARTIFACTS.get(repoName); const token = await repo.createToken("read", 900);
return Response.json({ repo: repoName, scope: token.scope, expiresAt: token.expiresAt, token: token.plaintext, }); },} satisfies ExportedHandler<Env>;Use the same pattern for write tokens only after your Worker authorizes a session that must push changes.
Do not issue one long-lived write token to every agent. Mint the narrowest token you can, for the shortest time you can.
Use git notes ↗ to attach prompts, model output, run IDs, or other harness metadata to a commit without changing the commit object or working tree.
This lets you use Artifacts as both the versioned filesystem for agent work and the source of truth for your agent harness. Your files stay focused on the work product, while the commit notes hold the surrounding execution context.
This example stores the user prompt and the assistant summary on the current commit, then reads the note back.
git notes add -m 'user: Add a best-practices section for unique repo names.' HEADgit notes append -m 'assistant: Added naming guidance and a code example.' HEADgit notes show HEADIf you sync repos between systems, remember that notes live on separate refs. Push and fetch refs/notes/* with the rest of your repo data when you want that metadata to travel with the repository.
Use namespaces to separate operating boundaries. Repo separation isolates units of work, while namespace separation isolates ownership, environments, and traffic patterns.
Do not keep every repo in one default namespace once usage grows. Split namespaces when you need clearer ownership or more room to scale within the request rate limits for each namespace.
| Use case | Example namespaces | Why |
|---|---|---|
| Environments | staging, prod | Keep test traffic and production traffic separate. |
| Team boundaries | sales, finance, devtools | Keep ownership, access, and cleanup policies distinct. |
| Traffic isolation | agents-batch, agents-realtime | Prevent one workload from consuming the limits of another workload. |
When one namespace becomes hot, shard new repos into additional namespaces instead of continuing to grow a single shared namespace.