# History and rewind

Read any folder or file as it was at a moment, even after a rename or delete, and put a whole folder back with one call that can itself be undone.

Every save is a version and every change is written to the [journal](/docs/changes), so a space remembers how it looked at any moment. You can read a folder as it was before an agent ran, get back a file that has since been renamed or deleted, and put a whole folder back.

## Moments

Anywhere that takes `as_of`, a moment can be:

| Form | Example | Means |
|---|---|---|
| A time | `2026-11-15T09:30:00Z` | The last change at or before that time |
| A duration ago | `-2h`, `-90m` | The same, counted back from now |
| A journal seq | `1042` | Exactly after that change |

Answers carry `Sidespace-As-Of-Seq`, the journal seq the moment resolved to. Ask again with that seq and you get exactly the same result, even if more changes happened at the same second.

## Read the past

```sh tab=CLI
sidespace ls "Film A" /Ep01 --as-of -2h                   # what the folder held two hours ago
sidespace get "Film A" /Ep01/script.md ./old.md --as-of -2h
```

```sh tab=curl
curl -H "Authorization: Bearer $SIDESPACE_TOKEN" \
  "$SIDESPACE_URL/v1/spaces/$SPACE/paths/Ep01/script.md?as_of=-2h"
curl -H "Authorization: Bearer $SIDESPACE_TOKEN" \
  "$SIDESPACE_URL/v1/spaces/$SPACE/nodes/$FOLDER/children?as_of=2026-11-15T09:30:00Z"
```

```ts tab=TypeScript
const then = await side.list("Film A", "/Ep01", { asOf: "-2h" });
const old = await side.download("Film A", "/Ep01/script.md", { asOf: "-2h" });
```

```python tab=Python
then = side.list("Film A", "/Ep01", as_of="-2h")
with side.open("Film A", "/Ep01/script.md", as_of="-2h") as r:
    old = r.read()
```

- `?as_of=` (or the `Sidespace-As-Of` header) works on `GET …/paths/{path}`, `GET …/nodes/{nid}` and `GET …/nodes/{nid}/children`. A path is looked up as it was then, so an old name still finds the file.
- A node shows the version that was current then; download it as usual with that `head_version_id`.
- A file deleted for good has no past. Everything else, including files in the trash, can be read.

## Rewind a folder

Rewind puts a folder back the way it was at a moment. Always look first with a dry run:

```sh tab=CLI
sidespace rewind "Film A" /Ep01 --to -2h --dry-run   # what would change
sidespace rewind "Film A" /Ep01 --to -2h
```

```sh tab=curl
curl -X POST -H "Authorization: Bearer $SIDESPACE_TOKEN" -H "Content-Type: application/json" \
  "$SIDESPACE_URL/v1/spaces/$SPACE/nodes/$FOLDER/rewind" -d '{"as_of": "-2h", "dry_run": true}'
```

```ts tab=TypeScript
const plan = await side.rewind("Film A", "/Ep01", "-2h", { dryRun: true });
for (const c of plan.changes) console.log(c.action, c.path, c.from_path ?? "");
```

```python tab=Python
plan = side.rewind("Film A", "/Ep01", "-2h", dry_run=True)
```

The answer lists each change:

| `action` | What happens |
|---|---|
| `restore` | The file's old content is saved as a new version. Files with the same bytes are left alone. |
| `undelete` | A file or folder that was deleted comes back from the trash |
| `move` | Something moved or renamed since goes back where it was. If the old name is taken, it gets a unique one and `note` says so. |
| `trash` | Something that wasn't there then goes to the trash |
| `missing` | A file that was there but has been deleted for good; it can't come back |

- A rewind runs in one transaction, and every step is an ordinary journaled change. So nothing is lost, and a rewind can itself be rewound.
- A dry run does the same work and rolls back, so it reports exactly what a real run would do.
- It needs the editor role on the folder and on everything it changes, and the `files:write` scope.
- Sidecars travel with their versions. Comments are not part of the folder and are not rewound.

## Over MCP and S3

- The MCP tools `list_folder` and `read_text` take `as_of`, and `rewind_folder` rewinds. Its description tells the agent to dry run first and show you. See [Use it in Claude](/docs/claude).
- Over the [S3 gateway](/docs/s3#versions-and-the-past), every bucket is versioned, and the `x-sidespace-as-of` header (or query parameter, for presigned URLs) lists and reads a bucket as it was.
