๐Ÿ”’ TouchQuill is in closed testing.Want to try it? Get in touch โ†’

How to think about TouchQuill

Before you type your first command, three ideas are worth internalizing. The rest of these docs keep coming back to them.

1. Content has an address, not a name

Every file, directory tree and commit is stored under an address computed from its content (a BLAKE3 hash). The consequences are practical: an identical texture used in two places is stored once; the server only sends objects you don't already have; and if anything gets corrupted in transit, the client notices because the hash won't match. You don't have to take integrity on faith โ€” you can compute it.

2. You don't merge binaries โ€” you take them

Textures, meshes and blueprints have no meaningful merge. Rather than pretend otherwise, TouchQuill builds locking into the core: whoever holds the lock commits; the server rejects a push touching a locked file from anyone else. The queue means "waiting for a file" no longer involves pinging people on Slack.

3. The change matters more than the commit

A commit is a technical record; the logical change (Change ID, ch-โ€ฆ) lives longer โ€” it survives fix-ups (amend), message edits and rebases. Reviews, locks and history refer to something stable. On top of that sits the operation log: almost anything can be undone with tq undo, including recovering unsaved work after an accidental restore.

Install and first commit

The client is a single tq binary (Linux, Windows, macOS). You only need the server โ€” tq-server โ€” once you want to share work.

# Linux: rpm package ยท Windows: installer ยท or build from source:
cargo build --release -p tq-cli
tq init MyGame
echo "asset" > Content/hero.txt
tq commit -m "first asset"
tq log        # ch-4f2a91  rev:1  first asset  (you)
tq status     # what changed since the last commit

Made a mistake? Nothing is lost:

tq restore    # back to the last committed state
tq undo       # ...and if restore itself was the mistake โ€” get your work back
This is not an editor-style undo: TouchQuill snapshots your working state into storage before every operation, so undo can bring back even uncommitted files.

Working with a team

tq clone https://vcs.studio.com MyGame     # fresh clone
tq login tqt_xxxxx                           # token from your admin
tq push
tq pull

Your identity on the server comes from the token, never from what the client claims โ€” which is why nobody can impersonate a teammate in history or in the audit log. Push is always fast-forward: if someone got there first, pull first.

Workflow: an artist's day

Morning: fresh state

Start by pulling the team's changes. If the project uses build flags, you can grab the last stable revision instead of the very latest โ€” one that's guaranteed to open in the editor.

tq pull            # or:
tq get stable      # last revision with green CI

Take the file

Before you open a binary in the editor โ€” lock it. If someone's already on it, you join the queue and get notified the moment it's yours.

tq lock Content/Characters/Hero.uasset
โœ“ Lock acquired.

Work and save

Commit in small steps โ€” commits are cheap (milliseconds even with large files). If something goes wrong, tq undo reverses the operation.

tq commit -m "new hero armor"

Ship and release the lock

After pushing, unlock โ€” the next person in the queue gets the file automatically, notification included.

tq push
tq unlock Content/Characters/Hero.uasset

Interrupted mid-work?

Don't force-commit an unfinished change โ€” shelve it. You can also share the shelf with a teammate for a second pair of eyes.

tq shelf create -m "armor wip" --expires 48h

Workflow: a programmer's task

A channel per task

A task channel inherits from main, but your commits don't affect anyone until you're done.

tq channel create task/GROM-447 --type task
tq channel switch task/GROM-447

Commit freely, fix boldly

amend folds a fix into the last commit, reword edits the message. The Change ID stays the same, so nothing gets lost.

tq commit -m "dodge system"
tq amend                    # fix-up on the same change

Stay current with main

integrate pulls in the parent's changes. A conflict won't stop you โ€” it gets recorded, and you decide when and how to resolve it.

tq integrate
tq conflicts                # what's pending
tq conflict resolve cfl-a1 --take theirs

Close the task

complete merges the channel into its parent and closes it. The task's history stays readable.

tq complete

Workflow: lead โ€” releases and order

A release channel with strict rules

No commits with unresolved conflicts land on a release, and only a chosen few can push. The policy lives in a versioned file โ€” changing it goes through history like everything else.

tq channel create release/1.0 --type release
# tq-channel.toml:
[permissions]
write = ["lead", "server-admin"]
integrate = ["lead"]

Lock disputes

Someone left a lock and went on vacation? Request a hand-over, or take it if you must โ€” every such action lands in the audit log, with the reason.

tq lock --request Content/Boss.uasset
tq steal Content/Boss.uasset     # requires lock.steal

Conflicts under control

Conflicts can be assigned to people and given deadlines. Overdue ones escalate on their own โ€” nothing drowns in the noise.

tq conflict assign cfl-a1 marta
tq conflict defer cfl-b2 --deadline 48h

Who did what

The audit log records administrative actions and permission-denied attempts. Verifying its integrity is one command.

tq-server audit log --denied
tq-server audit verify

Workflow: CI and builds

An account with exactly the rights it needs

CI gets a PAT token limited to specific permissions โ€” even if it leaks, it can't do anything beyond its scope.

tq-server user pat ci-build --user ci --scopes repo.read,build-flag.set

Build and mark

After a successful build, CI flags the revision. That flag means "this version works".

tq build-flag set --rev rev:128 --flag ci --state ok

The team takes stable

An artist never has to land on a programmer's broken commit: get stable fetches the newest revision with all required flags green.

tq get stable

Channels

A channel is a line of work โ€” the counterpart of a branch, but with a type and a policy. The type carries sensible defaults: a release rejects conflicted commits, a task has an owner and a lifecycle (create โ†’ integrate โ†’ complete), personal is your sandbox.

The virtual channel is worth knowing: a view onto a slice of the repo. Your art team can get just Content/ โ€” as far as they're concerned the code doesn't exist, yet their commits land in shared history with the invisible files preserved.

tq channel create artview --type virtual --include "Content/..." --exclude "Source/..."

Locks

The model is simple: one owner, everyone else queues. Locks have an inactivity timeout (4 h by default) โ€” if you forget to release one and go home, the team isn't blocked overnight. You can ask the owner to hand a lock over (--request) or pass yours to a specific person (--give).

The server enforces locks at push: if the channel policy says *.uasset requires a lock, a push touching such a file without one is rejected with a clear message. Offline work is covered too โ€” declare a lock locally, sync when you're back; if two people took the same file offline, a dispute blocks both sides until a lead decides. Nobody silently overwrites anyone's work.

Conflicts

In most systems a conflict is a wall: the merge stops and you drop everything to defuse it. In TouchQuill a conflict is a stored object (cfl-โ€ฆ) with full context: both versions, the common ancestor, who and when. Integration always completes; you resolve conflicts when you have room for them โ€” or a lead delegates them.

tq conflict resolve cfl-a1 --take theirs   # accept their version
tq conflict resolve cfl-a1 --merge         # three-way merge via plugin (if the format has a differ)

Change ID

A commit hash changes with every fix-up โ€” a Change ID doesn't. The distinction sounds subtle, but in practice it means a review comment saying "fix this in ch-4f2a91" stays valid after your amend and rebase. The operation log (tq op log) completes the picture: every operation leaves a trace and most can be undone.

Lens

Classic blame answers "who changed line 40 of file X". Lens answers the question you actually have: "what happened to this function?" โ€” even when the file was renamed and the function moved between modules. Cosmetic edits (formatting, comments) don't pollute the result.

tq lens blame --symbol calculate_damage
CREATED   ch-6f9  rev:1   krzysztof  "first version"
MODIFIED  ch-a12  rev:3   marta      "damage buff"
MOVED     ch-c73  rev:5   krzysztof  (Weapon.rs โ†’ Combat.rs)

Best practices

A few habits that make the biggest difference in practice:

Migrating from Git and Perforce

Both migrations preserve history: authors, timestamps, messages. Change IDs are derived deterministically from the source, and Lens works on imported history from day one. A sensible order: migrate a copy, verify it, switch the team over, and keep the old repo read-only for a transition period.

tq migrate from-git --repo /path/to/repo --branch main
tq migrate from-p4  --port perforce:1666 --user krzysztof --path //depot/...
tq migrate verify   --repo /path/to/repo

Server and permissions

The server exposes gRPC (clients), REST (integrations) and SSE (a live event feed). Authentication switches on with the first user you add โ€” before that it runs in open mode, handy for trying things out.

tq-server --data /var/tq --listen 0.0.0.0:7470 --tls-cert cert.pem --tls-key key.pem
tq-server user add krzysztof --role developer

Seven built-in roles (from viewer to server-admin), custom roles cloned with permissions subtracted, path restrictions for contractors ("only Content/Weapons/..."), scoped PATs for automation, and per-channel overrides. Add regional replication (locks stay on the primary โ€” one source of truth), server-to-server mTLS, and one-command backups.

Command reference

CommandDescription
tq init / clone / setupcreate / clone / configure a repo
tq add / edit / status / diffworktree changes
tq commit / amend / rewordrecord a revision (stable Change ID)
tq log / show / cat / restorehistory and navigation
tq rebase / revertrebase / inverse revision
tq op log / undo / redooperation log and undo
tq channel โ€ฆcreate / switch / list / policy
tq integrate / completeintegrate and close a channel
tq lock / unlock / locksqueue-based locks, request / give
tq conflict(s) โ€ฆresolve / assign / defer
tq shelf โ€ฆshelve work, share, TTL
tq lens blame / symbolssymbol history
tq build-flag / get stablebuild flags and the stable revision
tq workspace โ€ฆsave / sync / offline / mount / presence
tq migrate from-git / from-p4history import
tq fsmonitor start / stop / statusfile-change daemon (Linux / Windows / macOS)
tq notify โ€ฆinbox, DND, digests
tq plugin โ€ฆWASM plugins: extractor / differ
tq admin obliterate / channelpermanent content removal, permission overrides

The technical specification and architecture decision records live in the repository.