Configuration
Graft repository mode is configured through the project-local .graft/config.toml.
project/ .graft/ config.tomlRepository Config
Section titled “Repository Config”The config stores repository format settings, object format, named remotes, and branch upstreams.
Example:
[core]repository_format_version = 2default_branch = "main"
[user]name = "Mayne"email = "me@example.com"
[extensions]object_format = "blake3"
[remotes.origin]type = "fs"root = "/srv/graft/app"
[branches.main]remote = "origin"merge = "refs/heads/main"Applications should normally update config through CLI commands instead of editing it by hand:
graft remote add origin fs:///srv/graft/appgraft branch --set-upstream-to origin/main mainConfigure the identity written into new commits and annotated tags with the Git-compatible keys:
graft config set user.name "Mayne"graft config set user.email "me@example.com"The default identity is Graft <graft@example.invalid>. Unset either key to
restore its default.
Embedded applications can use graft config ... --json to read or update
structured config entries without parsing terminal text.
Merge Policy
Section titled “Merge Policy”The optional [merge] section configures row-level merge behavior for SQLite
files. It can declare application semantic keys, generated columns, schema
resolvers, and safe SQLite internal resolvers.
[merge]default_semantic_keys = ["_id"]
[merge.semantic_keys]app_objects = ["id"]
[merge.internal_resolvers]sqlite_sequence = "sequence_max"index_btree = "reindex"
[merge.schema_resolvers]add_column = "alter_table_add_column"
[merge.generated_columns]app_objects = ["body_len"]See Merge Policy for the full resolver list, logical status values, conflict reasons, and validation policy.
Track Scope
Section titled “Track Scope”The optional [track] section defines which worktree paths enter repository
status and auto-staged commits by default. It describes which paths are part of
versioned state; it does not describe how file contents are stored.
[track]default_roots = ["data.sqlite", "settings.json", "attachments/**"]user_roots = ["notes.md"]Use default_roots during app initialization for developer-defined app-private
state, such as SQLite databases, config files, attachment directories, import
directories, or agent session directories. New files under those roots appear
in graft status; commits auto-stage configured roots.
Use user_roots for user-owned space content that the user explicitly opts
into versioning. Other ordinary files in the space do not pollute the app-state
view by default, but graft ls-files --others can still discover them.
File Storage
Section titled “File Storage”The optional [files] section controls storage strategy for non-SQLite files.
SQLite databases always use storage: "sqlite_snapshot". Binary files always
use storage: "external". Text files use storage: "inline" unless they match
an external path rule or exceed inline_text_threshold.
[files]inline_text_threshold = "1 MB"external_paths = ["assets/**", "attachments/**"]Use external_paths for app resource directories where even small files should
behave like payloads, for example images, imports, attachments, exports, models,
or generated media.
Worktree Materialization
Section titled “Worktree Materialization”The optional [worktree] section controls whether committed SQLite snapshots are written back to physical SQLite database files in the worktree.
[worktree]materialize_sqlite = trueThis is true by default. Checkout-style operations and merge operations that
apply, resolve, continue, or abort a candidate may materialize tracked SQLite
snapshots at their repo-relative database paths, such as data.sqlite or
sub-app/main.sqlite. Staging and commit record repository state without
replacing the live worktree file.
Set it to false only for integrations that intentionally want volume-only database paths and do not want ordinary SQLite files in the worktree.
Repository Storage
Section titled “Repository Storage”Repository-mode SQLite data is stored under:
.graft/store/This makes project data isolated. Two directories can both contain data.sqlite without sharing storage, refs, branches, remotes, index state, or materialized worktree files.
In repository mode:
- do not rely on environment variables for project identity
- do not use user-wide config for refs or remotes
- do keep project state under
.graft/
Remote URIs
Section titled “Remote URIs”Common URI shapes:
memoryfs:///absolute/paths3://bucket/prefixs3_compatible://bucket/prefixs3_compatible://bucket/prefix?endpoint=https://account.r2.cloudflarestorage.comhttps://host/namespace/repositorygraft+https://host/namespace/repositoryUse memory for tests, fs:// for a local or mounted directory, and s3:// or s3_compatible:// for object storage. S3-compatible services such as Cloudflare R2 and MinIO usually need an explicit endpoint query parameter.
Use the Git-style https://host/namespace/repository form for a Graft remote
service. graft+https is an explicit compatibility alias; graft+http is
available for local or trusted development only.
The exact remote type is stored in .graft/config.toml after parsing. For example:
[remotes.origin]type = "s3_compatible"bucket = "my-graft-bucket"prefix = "prod/app"endpoint = "https://account.r2.cloudflarestorage.com"S3 credentials are not stored in .graft/config.toml or in the remote URI. Configure them in the process environment, or with the standard AWS config and credentials files understood by the S3 client:
export AWS_ACCESS_KEY_ID="..."export AWS_SECRET_ACCESS_KEY="..."export AWS_REGION="auto"Use AWS_REGION=auto for Cloudflare R2. Graft’s remote URI parser currently accepts endpoint as the only S3 query parameter, so do not put region, access keys, or secret keys in the URI.