# Configuration reference

> `.rift.toml` schema: `version`, `hooks.postcreate` array, validation rules, and CLI/API hook skip flags.

- Repository: anomalyco/rift
- GitHub: https://github.com/anomalyco/rift
- Human docs: https://grok-wiki.com/public/docs/anomalyco-rift-ea3fd5dbf662
- Complete Markdown: https://grok-wiki.com/public/docs/anomalyco-rift-ea3fd5dbf662/llms-full.txt

## Source Files

- `crates/core/src/config.rs`
- `specs.md`
- `README.md`
- `crates/core/src/hook.rs`

---

---
title: "Configuration reference"
description: "`.rift.toml` schema: `version`, `hooks.postcreate` array, validation rules, and CLI/API hook skip flags."
---

Rift reads an optional `.rift.toml` file from the **source workspace root** during `rift create`. When hooks are enabled (the default), the file is parsed and validated before any copy starts; configured `hooks.postcreate` commands run sequentially in the new workspace after copy, Git preparation, and registry insertion. Use `--no-hooks` on the CLI or `hooks: false` in the JavaScript API to skip config loading and hook execution entirely.

## File location

| Property | Value |
| --- | --- |
| Filename | `.rift.toml` |
| Directory | Root of the source workspace resolved by `rift create` (the managed workspace whose `.rift` marker was found by upward search) |
| Required | No — a missing file yields an empty hook list |
| Copied to child | Yes — `.rift.toml` is not in the filtered-copy exclusion set and is copied into every new workspace |

<Info>
Config is read from the **source** (`from`), not from the destination. The child workspace receives a copy of the file; hooks execute against the new path.
</Info>

## Schema (version 1)

Rift supports exactly one configuration version. The on-disk format is TOML.

```toml
version = 1

[[hooks.postcreate]]
run = "pnpm install --frozen-lockfile"

[[hooks.postcreate]]
run = "pnpm run codegen"
```

### Top-level fields

<ParamField body="version" type="integer" required>
Must be exactly `1`. Any other value returns `invalid_config` with message `unsupported config version {n}`.
</ParamField>

<ParamField body="hooks" type="object">
Optional. Defaults to an empty object. Only `hooks.postcreate` is recognized under `hooks`.
</ParamField>

### `hooks.postcreate` entries

Each `[[hooks.postcreate]]` table defines one shell command. Tables are executed in file order.

<ParamField body="run" type="string" required>
Shell command string. Leading and trailing whitespace is trimmed. An empty string after trimming is rejected with `postcreate run cannot be empty`.
</ParamField>

<Warning>
Unknown keys at any level are rejected. For example, a `shell` field on a postcreate entry fails parsing with `invalid_config`.
</Warning>

## Validation rules

Validation runs only when `HookMode::Run` is active (hooks not skipped). A failing validation aborts `create` **before** the workspace copy begins.

| Rule | Error when violated |
| --- | --- |
| File must parse as valid TOML | `invalid_config` — serde/TOML parse message |
| `version` must be present and equal to `1` | `invalid_config` — missing field or `unsupported config version {n}` |
| No unknown top-level or nested keys (`deny_unknown_fields`) | `invalid_config` — serde unknown-field message |
| Each `run` must be non-empty after trim | `invalid_config` — `postcreate run cannot be empty` |
| Empty `hooks.postcreate` array | Valid — no hooks run |
| Missing `.rift.toml` file | Valid — treated as zero hooks |

```text
create (hooks enabled)
  │
  ├─ load + validate .rift.toml at source
  │     └─ InvalidConfig → abort (no destination created)
  │
  ├─ copy workspace → write .rift marker → Git prep → registry insert
  │
  └─ run hooks.postcreate[] sequentially in destination
        └─ HookFailed → workspace remains registered; create returns error
```

## Hook execution

Postcreate hooks run after the destination workspace exists on disk and is registered in the SQLite registry.

| Behavior | Detail |
| --- | --- |
| Working directory | Destination workspace root |
| Stdio | Inherited from the parent `rift create` process |
| Shell (Unix) | `sh -c "<run>"` |
| Shell (Windows) | `cmd /C "<run>"` |
| Order | Sequential; first failure stops remaining hooks |
| On failure | Destination directory and registry record are **kept**; `create` returns `hook_failed` |

### Hook environment variables

Each `run` command receives the process environment plus:

| Variable | Value |
| --- | --- |
| `RIFT_SOURCE` | Absolute path of the source workspace |
| `RIFT_DESTINATION` | Absolute path of the new workspace |
| `RIFT_ID` | ULID of the new workspace |
| `RIFT_PARENT_ID` | ULID of the immediate parent workspace |

<Note>
Environment variables are injected per hook step. They are not available during config parsing — only at command execution time.
</Note>

## Skipping hooks

Hook execution is controlled by `HookMode` in the core library. Both the CLI and JavaScript bindings map user-facing flags to this mode.

| Surface | Skip flag | Default | Effect when skipped |
| --- | --- | --- | --- |
| CLI | `--no-hooks` | hooks run | `HookMode::Skip` — no config load, no validation, no hook execution |
| JavaScript / FFI | `hooks: false` | `hooks: true` (omitted) | Same as CLI |
| Core `CreateOptions` | `hook_mode(HookMode::Skip)` | `HookMode::Run` | Same as above |

<RequestExample>

```bash
rift create --name parser-fix --no-hooks
```

</RequestExample>

<RequestExample>

```ts
import { create } from "rift-snapshot";

const workspace = create({
  from: process.cwd(),
  name: "parser-fix",
  hooks: false,
});
```

</RequestExample>

When hooks are skipped:

- `.rift.toml` is **not** read or validated, so an invalid config file does not block creation.
- The file is still copied into the child workspace as part of the normal copy operation.
- No `RIFT_*` environment variables are set because no commands run.

<Tip>
Use `--no-hooks` / `hooks: false` for fast workspace spin-up when you plan to run setup commands manually, or when the source config is temporarily invalid but you still need a copy.
</Tip>

## Error codes

Config and hook failures surface through the standard Rift error types.

### `invalid_config`

Returned when `.rift.toml` fails parsing or semantic validation while hooks are enabled.

| Field | Content |
| --- | --- |
| `code` (FFI/JS) | `invalid_config` |
| `path` | Absolute path to `.rift.toml` (e.g. `/projects/app/.rift.toml`) |
| `message` | Human-readable detail (TOML error, version mismatch, empty `run`, etc.) |

CLI message format: `invalid rift config at {path}: {message}`

### `hook_failed`

Returned when a postcreate command exits non-zero or cannot be started.

| Field | Content |
| --- | --- |
| `code` (FFI/JS) | `hook_failed` |
| `path` | Destination workspace path |
| `message` | Includes the failing `run` value and exit detail (e.g. `` `exit 7` exited with exit status: 7 ``) |

CLI message format: `postcreate hook failed at {path}: \`{command}\` {message}`

<Warning>
A hook failure does not roll back the created workspace. The child remains on disk and in the registry. Remove it with `rift remove` if the partial setup is unusable.
</Warning>

## Complete example

This configuration installs dependencies and runs codegen in every new workspace created from the source root:

```toml
version = 1

[[hooks.postcreate]]
run = "pnpm install --frozen-lockfile"

[[hooks.postcreate]]
run = "pnpm run codegen"
```

```bash
cd ~/code/app
rift create --name feature-x
```

Expected flow:

1. Rift validates `.rift.toml` at `~/code/app/.rift.toml`.
2. A filtered copy-on-write child is created under default storage (e.g. `~/code/.rifts/app/feature-x/`).
3. `.rift` marker is written, Git `HEAD` is detached if applicable, registry record is inserted.
4. `pnpm install --frozen-lockfile` runs in the child with `RIFT_DESTINATION` set to the child path.
5. On success, `pnpm run codegen` runs.
6. The child path is printed to stdout.

## Related pages

<CardGroup>
  <Card title="Postcreate hooks" href="/postcreate-hooks">
    Hook lifecycle, environment variables, failure behavior, and practical setup patterns.
  </Card>
  <Card title="Create workspaces" href="/create-workspaces">
    `rift create` flags including `--no-hooks`, copy modes, and storage options.
  </Card>
  <Card title="JavaScript API reference" href="/javascript-api">
    `create({ hooks })` signature, FFI protocol, and `RiftError` codes.
  </Card>
  <Card title="Error codes" href="/error-codes">
    Full `invalid_config` and `hook_failed` catalog with path-bearing variants.
  </Card>
</CardGroup>
