> ## Documentation Index
> Fetch the complete documentation index at: https://code.storage/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Resume Sandbox Work

> Restore the last session state in a new sandbox and reject writes from the old sandbox.

Use this pattern when a person or an agent must continue work in a new sandbox. The new sandbox
restores the last commit and continues on the same ephemeral branch.

This guide uses the branch layout from [Store Session State](/docs/guides/session-state).

## Load the last state

Store the repository ID, branch name, and current commit SHA in your backend. Update this record
after each successful state write.

When your backend starts a replacement worker, pass the stored values as environment variables.
`PIERRE_PRIVATE_KEY` holds the private key for your Code Storage organization. Keep this key in
trusted server code.

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import { GitStorage } from '@pierre/storage';

const repositoryId = process.env.REPOSITORY_ID;
const sessionId = process.env.SESSION_ID;
const tipSha = process.env.TIP_SHA;

const store = new GitStorage({
  key: process.env.PIERRE_PRIVATE_KEY,
  name: 'acme',
});

const repo = await store.findOne({ id: repositoryId });

const sessionBranch = `sessions/${sessionId}`;
```

The current SDK has no direct branch lookup method. If your backend loses the tip SHA, call
[`listBranches()`](/docs/reference/sdk/list-branches) and follow `nextCursor` until you find the branch.

## Restore the files

Use an archive when the sandbox only needs a file tree.

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
const archive = await repo.getArchiveStream({ ref: tipSha });

// Stream archive.body to the sandbox and extract the tar.gz response.
```

Use Git when the sandbox must create commits. Use a normal read URL to fetch a full SHA. The SHA
must remain reachable from an ephemeral ref.

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
const readUrl = await repo.getRemoteURL({
  permissions: ['git:read'],
  ttl: 600,
});
```

Pass `readUrl`, `sessionBranch`, and `tipSha` to the new sandbox as `READ_URL`, `SESSION_BRANCH`,
and `TIP_SHA`.

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
git clone --no-checkout "$READ_URL" work
git -C work fetch origin "$TIP_SHA"
git -C work switch -c "$SESSION_BRANCH" "$TIP_SHA"
```

Use the full 40-character SHA that the API returns. Do not use an abbreviated SHA or a revision
expression.

See [`getArchiveStream()`](/docs/reference/sdk/get-archive-stream) for archive filters. See
[Connect a Sandbox](/docs/guides/sandboxes#confine-a-sandbox-to-one-session-branch) for the separate push
URL.

## Reject stale writes

Write the next state only when the branch still has the tip that you restored.

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import { readFile } from 'node:fs/promises';

const updatedAuthSource = await readFile('src/auth.ts', 'utf8');

const nextState = await repo
  .createCommit({
    author: { email: 'worker@example.com', name: 'Sandbox worker' },
    commitMessage: 'Resume sandbox work',
    ephemeral: true,
    expectedHeadSha: tipSha,
    targetBranch: sessionBranch,
  })
  .addFileFromString('src/auth.ts', updatedAuthSource)
  .send();

console.log(nextState.commitSha);
```

If another sandbox wrote first, this call fails with the `precondition_failed` reason. Stop the old
sandbox. Do not retry until your backend assigns a new owner.

For Git pushes, keep `no-force-push` on the session branch. An old checkout cannot replace the new
commits with a non-fast-forward push.

## Record the handoff

Store `nextState.commitSha` as the current tip. Record which person, agent, and sandbox own the
session.

Use short URL lifetimes. Mint a new write URL for the replacement sandbox. Do not reuse the old
sandbox URL.
