# USourceControl documentation > Cloud source control for Unreal Engine 5. This file is the complete > documentation in one document, generated from the same content that > renders at https://usourcecontrol.com/docs. --- # Chapter 1. Getting started > Why game projects need different version control, what USourceControl is made of, and how to get your Unreal project into it. Source: https://usourcecontrol.com/docs/getting-started --- This chapter is about the ground floor. If you have never used version control, or you have used Git for code and found that it fell apart the moment you added a level, this is where to start. We begin with why game projects break the tools that work fine for source code, because that explains almost every design decision that follows. Then we cover what USourceControl is actually made of, the handful of words the rest of the book uses, and finally the practical business of installing the app and getting your first project into it. By the end you will have a project linked, a folder syncing, and enough vocabulary to read any other chapter in any order. --- ## 1.1 About version control for games > Why the tools built for source code struggle with Unreal projects, and what changes when your files are binary. Version control is a system that records changes to a set of files over time so you can recall any version later. That definition is old and it has not changed. What has changed is what people put under version control, and a modern Unreal Engine project is not what these systems were designed for. ## What source code assumes Git, and every tool shaped like it, rests on three assumptions. Files are text. Text files can be merged automatically when two people change different parts of them. And the whole history is small enough that every contributor can carry a complete copy of it. All three hold beautifully for a web application. None of them hold for a game. A `.umap` is not text. A `.uasset` is not text. When two people edit the same level, there is no line-by-line reconciliation to perform — the file is a serialized object graph, and the only two possible outcomes are "keep mine" and "keep theirs". And a single Nanite mesh can be larger than the entire history of a mature codebase, so cloning "the whole history" stops being a sensible default somewhere around your first cinematic. ``` TEXT BINARY MyCharacter.cpp MainLevel.umap Sarah ─┐ Sarah ─┐ ├──► merged, both kept ├──► one wins, Marcus ─┘ Marcus ─┘ the other is gone ``` *Two people edit a text file and both changes survive. Two people edit a level and one of them loses a day.* ## The workarounds, and what they cost The industry has three standard answers, and it is worth knowing what each one actually asks of you. **Git LFS** keeps the big files out of the repository and leaves pointer text behind. It works, in the sense that clones stop taking a week. But you have swapped one problem for a quota and a bandwidth bill, your artists still need the Git command line, and One File Per Actor turns a single level into thousands of tiny files that LFS was never tuned for. **Perforce** is the honest AAA answer and it is genuinely good at this. It is also a server you run, patch, secure, and back up, with licenses to buy and often a proxy or an edge server per studio location. For a team of four that is a part-time job nobody signed up for. **Cloud storage** — a synced folder — is what most small teams actually use, and it is the worst of the three. No history, no commit trail, no locking, and a conflicted copy is the only warning you get that a day has gone missing. ## What we do instead USourceControl starts from the constraint rather than working around it. Binary files cannot merge, so the answer is not a better merge algorithm — it is making sure two people never edit the same file at the same time in the first place. That is what [locking](https://usourcecontrol.com/docs/desktop-app/file-locking) is for, and it is why locking is a first-class feature here rather than an add-on. Large files are normal, not exceptional, so there is no pointer layer, no separate large-file store, and no quota. Storage and bandwidth are unlimited on every plan, including the free one, because metering them would push you back toward the behaviour that loses work — committing less often, keeping things out of source control, working from a folder on your desk. And the whole history does not live on your machine. You have the current state of your project plus whatever you ask for; everything else is one click away when you need it. That is what makes a hundred-gigabyte project openable on a laptop. > **INFO: This is a trade, and it is worth naming** > Centralized version control means that when you are offline you can keep working but you cannot commit, and it means the history lives with us rather than on every machine. In exchange you get locking that actually means something, no repository size to manage, and nothing to administer. For a game team that is almost always the better side of the trade. --- ## 1.2 What USourceControl is > Four pieces — a dashboard, a desktop app, a build runner, and a local agent — and which one you actually spend your day in. USourceControl is a cloud source control service for Unreal Engine 5 teams. There is no server for you to run. You install one application, point it at the folder your `.uproject` lives in, and that is the whole setup. Underneath, it is four pieces. Knowing which is which will make the rest of this book easier to follow, because almost every chapter is about one of them. ``` YOUR MACHINE USOURCECONTROL ┌────────────────────────────┐ ┌───────────────────────────┐ │ Unreal Editor │ │ Web dashboard │ │ │ │ │ projects · people · keys │ │ ▼ commits │ │ history · builds · plans │ │ Desktop app ────────────────────► │ │ │ ▲ pulls │ │ file versions · locks │ │ │ │ ◄───── │ tasks · releases │ │ Local agent (localhost) │ └───────────────────────────┘ └────────────────────────────┘ ▲ ▲ │ claims work, │ status · checkout · check-in │ uploads builds │ │ editor tooling ┌──────────────┐ coding agents │ Forge runner │ │ (a machine │ │ you own) │ └──────────────┘ ``` *The four pieces, and what talks to what.* ## The web dashboard This is the administrative half. Organizations, projects, members and their roles, project keys, billing, the browsable commit history, the task board, and the build queue all live here. You will visit it when you set something up and rarely in between. ## The desktop app This is where the work happens. It watches your project folder, tells you what changed and what your teammates pushed, takes and releases locks, commits, pulls, and — as of the Releases view — packages your game and uploads it to a store. Chapters 2 and 4 are almost entirely about this app. ## USourceControl Forge A separate application for a machine you dedicate to building. It claims work, materializes the exact committed state of a project, runs Unreal's own packaging toolchain, and publishes the result. It is optional, it is on every plan, and it is [chapter 5](https://usourcecontrol.com/docs/forge). ## The local agent A small HTTP API that the desktop app serves on localhost while it runs. It is how tooling on the same machine — an editor plugin, a coding agent, a script — asks the app to do things without holding any credentials of its own. Most people never touch it directly; see [section 8.3](https://usourcecontrol.com/docs/integrations/local-agent) if you want to. > **TIP: You can ignore three of the four** > If you install the desktop app and never open anything else, you have a complete, working version control setup. Everything else in this book is something you can add the day you need it. --- ## 1.3 Core concepts > Organizations, projects, commits, locks, branches, editions, plugins, and releases — the vocabulary the rest of the book assumes. Seven words carry most of the meaning in this product. You do not need to memorize them — every chapter reintroduces what it needs — but reading them once here will save you looking things up later. ## Organization The container for your team and your projects. You are the owner of the one you create. Members hold a role — owner, admin, or member — and are separately granted access to individual projects, which is what makes it safe to keep an unannounced title in the same organization as everything else. Billing is per seat at this level. ## Project One Unreal project, mapped to one folder on your machine: the one holding your `.uproject`. A project owns its files, commits, members, keys, tasks, and releases. ## Commit and the sequence number A commit is a set of file versions pushed together with a message. Every commit gets a **sequence number** that is unique and increasing within the project, so `#24` is unambiguously later than `#23`. There is no clock involved and no ordering to argue about. This matters more than it sounds. Sequence numbers are what make "everything that shipped since the last release" a range rather than a guess, and they are why a [changelog](https://usourcecontrol.com/docs/tasks/release-changelogs) can be derived rather than written. Committing a file adds a version. It never replaces one. Every version that has ever existed stays downloadable, which is what makes rolling back a click rather than an incident. ``` commit #21 #22 #23 #24 │ │ │ │ MainLevel.umap ● ● v3 ─► v4 SK_Hero.uasset ● ● v7 ─► v8 MyCharacter.cpp ● v2 nothing above is ever overwritten — v3 of MainLevel is still there, and still one click away ``` *A file accumulates versions; commits are project-wide and numbered in order.* ## Lock An exclusive claim on a file. Because binary game files cannot merge, working together means claiming a file before you open it rather than discovering the collision afterwards. A lock is visible instantly to the whole team in both the app and the dashboard. See [section 2.3](https://usourcecontrol.com/docs/desktop-app/file-locking). ## Branch A line of work that forks from `main` and is expected to merge back. Branches are for the ordinary case: a feature that will land, a risky change you want to keep away from everyone else until it works. ## Edition A permanent downstream copy of the game that receives everything from the main game and **never merges back** — a demo, or a per-platform version. If you have ever maintained a separate Perforce stream for a console SKU, this is that, without the maintenance. See [chapter 6](https://usourcecontrol.com/docs/editions). ## Plugin A subfolder of one project, promoted so it can be mounted into your others. A shared character rig, material library, or gameplay module lives in one place and flows out to everything that uses it. ## Release A packaged build of your game plus the version string it carries and the commit it was made from. Releases come from the desktop app or from Forge, and go to Steam, the Epic Games Store, or itch.io. --- ## 1.4 Installing the desktop app > Download, install, and understand how updates behave. There is one thing to install, and it does not need administrator rights. 1. **Download the installer** From the [download page](https://usourcecontrol.com/download). It installs per user, into your own profile. 2. **Run it** The app opens to a sign-in screen asking for a project key. Getting one is the next section. ## How updates work The app checks for a new version on launch and shows a banner when one exists. Nothing is downloaded or installed until you agree — a version control client that restarts itself mid-push would be a strange thing to trust — and an update never interrupts a sync, a commit, or a build in progress. > **INFO: Platforms** > Windows is supported on every plan. macOS and Linux builds, along with the desktop app's source code, are available to Enterprise customers. --- ## 1.5 Linking your first project > Create the project, generate a key, point the app at your .uproject folder, and take the first pull. The desktop app authenticates against a project rather than an account, using a **project key**. This is a small distinction with a useful consequence: a build machine, a contractor's laptop, and your own desktop can each hold a key scoped to exactly one project, and revoking one affects nothing else. 1. **Create the project on the web** Sign in to the dashboard, create an organization if you have not already, then create a project inside it. 2. **Generate a project key** Open the project and use the **Quick Setup** panel on the right. Keys start with `usc_` and are shown exactly once. Copy it somewhere safe; if you lose it, revoke it and generate another. 3. **Paste the key into the desktop app** The app validates it and shows which project and organization it belongs to, so you can tell immediately if you pasted the wrong one. 4. **Pick the project folder** Choose the folder containing your `.uproject`. The app recognizes an Unreal project and offers a `.gitignore` tuned for one. 5. **Take the first pull** If the project already has files, the app offers to download them before anything else happens. Accept it — this is what makes the first sync fast and, more importantly, correct. ## What to keep out USourceControl reads the `.gitignore` in your project root, in exactly the format you already know. Everything Unreal regenerates belongs in it. Committing `DerivedDataCache/` does not protect anything — it is derived, by definition — and it costs every teammate a long download on every pull. A reasonable starting point for an Unreal project ``` Binaries/ Build/ DerivedDataCache/ Intermediate/ Saved/ *.sln *.suo *.VC.db ``` The app writes something close to this for you and gives you an editor for the file inside Settings, so you can fix an over-broad rule without leaving the app. > **WARN: Keys are locked to a device** > The first machine to use a key claims it, and the key stops working anywhere else — checked on every request, not just at sign-in. That is what makes a leaked key useless to whoever leaked it. Each seat gets one active key per project on Free and two on Team, so a desktop and a laptop can both be authorized. If a machine changes hands, revoke and reissue from project settings. > **TIP: Bringing an existing project in** > Link the folder, let the app hash what is there, and commit everything as your first commit. There is no import step and no size limit to plan around. If the project is already in Git or Perforce, keep that history where it is and start fresh here — trying to replay years of binary history into a new system costs days and is almost never read again. --- ## Summary Game projects break the assumptions that source-code version control is built on: the files are binary, they cannot merge, and they are enormous. USourceControl takes that as the starting point rather than something to work around, which is why locking is central, why storage and bandwidth are unlimited, and why there is no repository for you to manage. You now have the vocabulary — organization, project, commit and its sequence number, lock, branch, edition, plugin, release — and a linked project on disk. That is everything the rest of the book builds on. Next is the desktop app in earnest: committing, pulling, locking, and getting a file back when you need it. --- # Chapter 2. The desktop app > The daily loop: see what changed, lock what you are editing, commit, pull, and get anything back that you need back. Source: https://usourcecontrol.com/docs/desktop-app --- This is the chapter you will actually use. Almost everything you do with USourceControl on an ordinary day happens in one window, and that window is deliberately small: two panels, one for what your team did and one for what you did. We start with the loop itself — commit, push, pull — then the thing that makes the loop safe on a game project, which is locking. After that comes getting work back when something goes wrong, and finally the three features you grow into: branches, shared plugins, and the settings that let the app keep working while its window is closed. ``` ┌───────────┬────────────────────┬───────────────────────┐ │ │ main ▾ Synced │ Changes 4 │ │ Project ├────────────────────┼───────────────────────┤ │ Tasks │ ↓ 3 files to pull │ ☑ SK_Hero.uasset │ │ Releases │ │ ☑ MainLevel.umap │ │ Settings │ HISTORY │ ☑ P_Smoke.uasset │ │ Plugins │ #24 Yuki 2h │ ☑ MyCharacter.cpp │ │ │ #23 Marcus 5h ├───────────────────────┤ │ │ #22 Sarah 1d │ message… Push (4) │ └───────────┴────────────────────┴───────────────────────┘ nav THEIR WORK YOUR WORK ``` *The two panels, and what each is for.* --- ## 2.1 Recording changes > Pick the files, write a message worth reading, link the tasks it closes, and push. The right-hand panel lists every file that differs from what is committed. It updates as you work — the app watches the folder — so by the time you finish a modelling session the list is already correct and you have nothing to scan for. Each row carries a path, a status, and, if someone holds the file, a padlock with their name. | Status | Means | | --- | --- | | Modified | The file exists upstream and your copy differs | | New | The file has never been committed | | Outdated | A teammate pushed a newer version and yours is behind | ## Choosing what goes in Every changed file is ticked by default, on the theory that most of the time you want to push what you did. Untick anything you are not ready to share. On a long list, `Ctrl+F` opens a filter. There is no staging area to learn. A commit is the files that are ticked when you press the button, and that is the whole model. ## Writing the message The commit box is a formatting editor rather than a plain text field. Bold, italic, strikethrough, inline code, bullet lists, and colour are on the toolbar above it, with `Ctrl+B` and `Ctrl+I` where your fingers expect them. `Shift+Enter` starts a new line; `Enter` commits. This exists because commit messages on a game project are read by people who are not programmers. "Fixed lighting" is not a useful message to an artist wondering whether to pull; a short list of what actually moved is. The formatting survives all the way through to [Discord and Slack](https://usourcecontrol.com/docs/integrations/chat-notifications), so the message you write is the message your team reads in the channel. ## Linking the tasks it finishes The task button on the toolbar attaches cards from the board, with a **done** tick for the ones this commit actually finishes. You can also just write `closes #12` in the message. Either way the card moves and the work becomes eligible for the next release's changelog — see [section 3.2](https://usourcecontrol.com/docs/tasks/link-commits-to-tasks). ## What pushing actually does Press **Push (n)** and each file is hashed with SHA-256 before it leaves your machine. If the server already holds those exact bytes — because a teammate pushed the same asset, or because you are re-committing a file whose contents did not really change — nothing is uploaded for it. On a project full of duplicated source art this is the difference between a two-minute push and a twenty-minute one. Uploads run in parallel, sized to what your connection can carry, and the commit is only recorded once every byte has arrived. A push that is interrupted halfway does not produce half a commit. The same hash is checked on the way back down, so what you push is byte-for-byte what your team receives. Not "probably", not "a file of the same size" — the same bytes. ## Deleting files Right-click a file and choose **Mark for deletion**. It is removed in your next commit, disappears for everyone on their next pull, and stays restorable from history indefinitely. Deletion here is a soft delete; nothing you commit is ever truly destroyed. > **TIP: Commit more often than feels necessary** > There is no repository to bloat and no storage bill to worry about, so the usual reason to batch up changes does not apply. A commit per meaningful step gives you somewhere precise to roll back to, and a history someone can actually read six months from now. --- ## 2.2 Pulling your team's work > How incoming changes are classified, what the pull card is telling you, and why your local edits are never overwritten. The left panel is your team's side of the project. The header shows which branch you are on and when the app last synced, with a refresh button beside it. Below that sits the incoming card, and below that the commit history. ## Reading the incoming card The card summarizes what is waiting — "3 files to pull, 2 outdated, 1 conflict" — and expands into the list. Three things can be true of an incoming file: - **Outdated** — a teammate committed a newer version and your copy is untouched. Pulling replaces it. This is the ordinary case and it needs no thought from you. - **Conflict** — a teammate committed a newer version *and* you have also changed the file locally. Pulling will not overwrite you. The file is held back until you decide. - **Removed** — the file was deleted upstream. It is deleted locally only if your copy is byte-identical to the committed one; if you had changed it, it stays. ``` teammate pushes a new version │ ┌─────────────┴─────────────┐ │ │ you changed it too you didn't touch it │ │ ▼ ▼ CONFLICT OUTDATED held back, nothing pull replaces it on disk is touched silently ``` *How a file arrives at each of the three incoming states.* > **WARN: A pull never overwrites local work** > This is a hard rule, not a heuristic. A file you have edited is never replaced by a pull, and a file whose contents differ from the committed version is never deleted by one. If both sides changed, you are told, and the bytes on your disk stay exactly as they are until you choose. ## Resolving a conflict Because the files are binary, resolving means choosing, and the app gives you both options plainly: keep yours and push it over the top, or discard yours and take theirs. There is no third answer and no tool that can invent one. The way to have fewer of these is not a better conflict view — it is [locking](https://usourcecontrol.com/docs/desktop-app/file-locking), which is the next section. ## Why syncs stay fast on huge projects A naive sync hashes every file in the project every time, which on a hundred-thousand-file World Partition project is minutes of disk churn before anything useful happens. Instead the app keeps an on-disk hash cache and watches the filesystem while it is running, so a routine sync costs roughly what actually changed rather than what the project contains. Reopening the app the next morning does not re-hash the world; it asks the operating system what moved while it was away and picks up from there. --- ## 2.3 File locking > Claim a level or Blueprint before you open it, so nobody can save over your day. If you take one habit away from this book, take this one. Unreal's binary files do not merge. Two people editing the same level means one of them loses the work, and no version control system — not this one, not Perforce, not anything — can reconcile it afterwards. The only real fix is to make the collision impossible, and that means claiming the file before you open it. ## Taking a lock 1. **Right-click the file and choose Lock** You can select several files first and lock them together — the whole level plus the Blueprints you are about to touch. 2. **Everyone sees it immediately** The file shows a padlock with the holder's name and face in the desktop app, and in the project's file browser on the web. Nobody has to ask in chat who has the level. 3. **Release it when you push** Right-click and choose **Release lock**. The habit worth building is lock, work, commit, release — in that order, every time. ``` Sarah Marcus │ │ ├── locks MainLevel.umap ──────────────────► sees the padlock │ and Sarah's face ├── opens it in the editor │ ├── works picks a different task ├── commits │ └── releases the lock ─────────────────────► padlock clears ``` *The lock is a signal, not a filesystem permission — but a signal everyone can see is enough.* ## Locks are advisory, and that is the point A lock does not make the file read-only on anyone's disk. It could — and some systems do that — but a hard lock turns every forgotten release into someone else being blocked at 2am with nobody around to clear it. What a lock does is make the situation visible before it becomes expensive. In practice that is what prevents the collision: nobody opens a level they can see is checked out. > **TIP: Lock the level, not the whole folder** > With One File Per Actor, a level is thousands of small files and two people can genuinely work in the same level on different actors. Lock what you are editing rather than the directory around it, and OFPA does what it was designed to do. > **INFO: On every plan** > File locking is included everywhere, Free included. The dashboard's Files card shows how many files a project has locked at a glance. --- ## 2.4 History and undoing things > Reverting local changes, bringing back an old version, and reading what a commit actually did. Everything in this section is non-destructive. That is worth saying first, because the reason people lose work is usually that they were afraid to try the recovery. ## Throwing away local changes Right-click a changed file and choose **Revert**. Your local edits go away and the committed version comes back. This touches only your machine and only that file — nobody else sees anything. ## Going back to an older version This is the one people come to version control for, and it is three clicks. 1. **Open the file's history** Click the file, or right-click and choose **History**. You get every version it has ever had, with the commit, the author, and the date behind each one. 2. **Pick a version** Download it somewhere to inspect it first if you want to be sure, or restore it directly. 3. **Commit the restore** A restore is an ordinary change to your working folder, so it becomes an ordinary commit. The version you rolled back *from* is still in history — you can always roll forward again. ``` before: v1 ── v2 ── v3 ── v4 ── v5 (current, broken) restore v3: after: v1 ── v2 ── v3 ── v4 ── v5 ── v6 (= v3's bytes) ▲ ▲ what you wanted what you now have v5 is still there ``` *Restoring v3 writes a new version. Nothing is removed, so the mistake is still recoverable in both directions.* ## Reading a commit Click any commit in the left panel to see exactly which files it touched and what happened to each. The same view exists in the dashboard under a project's **Commits** tab, which is the one to send to someone who does not have the desktop app open. > **TIP: Recovering a file someone deleted** > Deletions are soft. Open the project's history in the dashboard, find the commit that removed the file, and restore that version. The file comes back with its full history intact. --- ## 2.5 Branches > Take a line of work away from main, then bring it back without leaving anyone's assets behind. Branching here follows the shape people already know: `main` is the trunk, a branch forks from it, and a branch is expected to come back. The branch selector sits at the top left of the project view. 1. **Create a branch** Open the branch selector and create one. It forks from the current state of `main`. Nothing is copied and nothing is duplicated in storage — a branch that has changed nothing costs nothing. 2. **Work on it** Commits you push while the branch is selected land on the branch, and pulls give you the branch's files. The selector always shows where you are, because pushing a week of work to the wrong line is a bad afternoon. 3. **Merge back** Merging applies the branch's file versions onto `main` as a new commit. ## Why merging works on binary files Because the unit being merged is a file version, not a diff. Merging a branch means "for each file the branch changed, make main's current version the branch's version". There is no reconstruction step and therefore nothing to reconstruct incorrectly. An asset that only ever existed on the branch arrives whole. The limitation is the honest one: if `main` and the branch both changed the same binary file, someone has to choose. That is the same choice as a conflict on pull, for the same unavoidable reason. > **TIP: Branch or edition?** > If the work is coming back to the main game, you want a branch. If you are building a demo, or a version of the game for one console that will diverge permanently and never merge back, you want an [edition](https://usourcecontrol.com/docs/editions/editions) — and you want it from the start, because an edition is designed to receive main's changes forever while a branch is not. --- ## 2.6 Shared plugins > Promote a subfolder to a plugin, mount it into your other projects, and update it deliberately. Studios accumulate things worth reusing: a character controller, a material library, a dialogue system. The usual fate of those things is a copy in every project, three of which have diverged and one of which has the bug fix. A plugin is a subfolder of one project, promoted so other projects can mount it. The source of truth stays in one place and flows outward. ``` CombatKit (lives in ToolsProject) │ ┌────────────┼────────────┐ ▼ ▼ ▼ MyGame SpaceGame Prototype /Plugins/ /Plugins/ /Plugins/ CombatKit CombatKit CombatKit edits flow down, never back up ``` *One plugin, one home, mounted into as many projects as you like. Changes flow one way.* ## Creating one The app detects folders containing a `.uplugin` and offers to promote them. Once promoted, the subtree syncs one way: from the plugin's home project out to everywhere it is mounted. ## Updates are explicit, and this is deliberate Syncing a project **checks** its plugins and queues what is available, exactly the way incoming file changes are queued. Nothing is written into your working tree until you say so. A silent plugin update is one of the worst things that can happen mid-session: your project changes underneath you, in code you did not write, while you are debugging something else. The Plugins button in the sidebar carries a badge when a mounted plugin is behind its source, and it waits for you. ## Sharing across organizations A plugin can be granted to another organization by slug — useful for a contractor, a partner studio, or a toolkit you sell. The consumer pays for their own storage of it, and revoking the grant cascades: the plugin stops flowing immediately. > **INFO: Plan limits** > Plugins are unlimited on every paid plan. Free is a single project, which has nothing to mount a plugin into, so plugins are the feature that separates Free from Team. --- ## 2.7 Settings, tray, and background mode > Close-to-tray, start with the system, the ignore-file editor, and where the build toolchain lives. ## Closing to the tray Closing the window hides the app to the system tray rather than quitting it. That is deliberate on three counts: the [local agent](https://usourcecontrol.com/docs/integrations/local-agent) keeps serving, so editor tooling and coding agents keep working; a queued build keeps running; and the next launch is instant because nothing had to shut down. A real quit is the **Quit** item in the tray menu, and even that stops to ask if an operation is in progress. ## Starting with the system **Start with system** adds a login item that boots the app straight to the tray with no window. Worth turning on for a machine that also runs builds, and for anyone who wants the agent available the moment they sit down. ## The ignore-file editor Settings includes an editor for the project's `.gitignore`, with an Unreal preset. Fixing an over-broad rule is a thing you do about twice a year and it should not require leaving the app to do it. ## Build toolchain paths Under **Releases → Settings** you point the app once at the tools it shells out to: `steamcmd.exe` for Steam, `BuildPatchTool` for the Epic Games Store, and `butler.exe` for itch.io. These are machine-wide — you have one steamcmd, not one per project — alongside the default artifact folder and a build time limit. [Chapter 4](https://usourcecontrol.com/docs/packaging) covers what they are for. > **WARN: Store credentials never leave your machine** > Every store key, secret, and password is encrypted with your operating system's keychain and stored locally. They are never sent to our servers, and never written onto a command line where another process on the machine could read them. If your operating system cannot encrypt them, the app refuses to store them rather than storing them weakly. --- ## Summary The loop is small: see what changed, lock what you are about to edit, commit with a message someone can read, push, pull. Everything else in this chapter is a variation on it. The two habits worth building are locking before you open a binary file, and committing more often than feels necessary. The first prevents the only class of data loss the system cannot undo for you. The second costs nothing here and gives you somewhere precise to go back to. You also know that nothing is destructive — reverts, restores, and deletes are all recoverable — and that branches exist for work that comes back, while [editions](https://usourcecontrol.com/docs/editions) exist for work that does not. The next chapter is about the other half of a project: knowing what everyone is supposed to be doing, and turning that into release notes without writing them. --- # Chapter 3. Tasks and planning > A board per project, commits that close cards, and release notes derived from what actually shipped. Source: https://usourcecontrol.com/docs/tasks --- Most teams keep their work in one tool and their code in another, and spend a surprising amount of energy keeping the two in sync by hand. Someone finishes a feature, pushes it, and then remembers to go and drag a card. At release time someone else scrolls through a month of commits trying to reconstruct what changed. The board in USourceControl is not trying to replace a full project management suite. It exists because it sits next to the commits, and that adjacency buys two things nothing else can: a card can be closed by the commit that finished it, and a release's changelog can be derived from the cards its commits closed. This chapter covers the board itself, the link between commits and cards, how a changelog gets built out of that link, and the two AI-assisted paths in and out: speaking tasks into existence, and handing them to a coding agent. --- ## 3.1 The board > To Do, Doing, Done, and Blocked, in the desktop app and on the web, backed by the same rows. Every project has a board. Open it from **Tasks** in the desktop app's sidebar, or from the **Tasks** button on the project page in the dashboard. Both write the same rows, so a card someone drags in the browser moves in the app. ## The four columns | Column | Means | | --- | --- | | To Do | Agreed, not started | | Doing | Someone is on it right now | | Done | Finished. A commit that closes a card lands it here and then archives it | | Blocked | Cannot proceed. Say why in the card | Four columns is a deliberate ceiling. A board with twelve states is a board nobody updates, and an out-of-date board is worse than no board because people trust it. ## Cards A card has a number, a title, a description, and an optional assignee. Numbers are per project and increase, so `#12` in one project has nothing to do with `#12` in another — the same rule commits follow. The assignee list is exactly the set of people who can open the project: organization owners and admins implicitly, plain members through their project membership. You cannot assign work to somebody who cannot open the board it is on, which sounds obvious and is the kind of thing that silently breaks in most tools. ## Ordering Drag a card and the client tells the server which two cards you dropped it between. The server works out the new rank from that. The alternative — the client computing a position and sending it — breaks the moment two people drag at once, because each of them computed against a board that was already stale. Describing the drop by its neighbours cannot go wrong that way. > **INFO: On every plan** > The board, its commit links, the generated agent prompt, and the release changelog are on every plan including Free. Voice capture is the one paid part, for the reason given in [section 3.4](https://usourcecontrol.com/docs/tasks/voice-capture). --- ## 3.2 Linking commits to tasks > Mention a card with #12, finish it with closes #12, and let the board keep up with the work. There are two ways to connect a commit to a card, and the difference between them is the whole point of this section. | You write | It means | What happens | | --- | --- | --- | | `#12` | This commit touched task 12 | The commit is listed on the card | | `closes #12` | This commit finished task 12 | The card closes, leaves the board for the archive, and goes into the next release's notes | A commit message that does both ``` Enemy stagger pass Tuned the hit reaction curves and rebuilt the montage. #31 Also fixes the T-pose on death, closes #28 ``` The commit bar's task picker does the same thing without you typing anything: attach the cards, then tick **done** on the ones this commit actually finishes. Cards already sitting in Done are listed too, and ticking one assumes this is the push that finishes it. > **WARN: Only closed tasks reach the changelog** > A mention says a commit went near a card. Only a resolved link says the work is finished. Conflating the two would put half-built features in front of players in your release notes, so the changelog lists resolved links and nothing else. ## Closed cards leave the board When a push closes a card, the card is archived. It disappears from the board, keeps its number and its commits, and still appears in the release notes for the build it shipped in. Nothing is deleted. This is what keeps a board readable after six months. A Done column that only ever grows is a list of everything anyone has ever finished, and within a few weeks it is the biggest column on the screen and nobody reads it. The board should be what is left to do. Everything archived is one click away. **Archive** at the top of the board opens the list, newest first, with the commit that closed each card and a **Restore** button that puts it back at the bottom of its column. You can also archive a card by hand from its dialog, for the ones that leave the board without a commit finishing them. > **INFO: Archiving never touches the changelog** > Release notes are built from commits, not from what is currently on the board. Archiving a card, restoring it, or archiving it again cannot change what a past release says it shipped. ## Why the commit decides, not the column A card dragged to Done but never pushed did not ship. A card closed, reopened, and closed again shipped once. The board is a statement of intent and it is allowed to be optimistic; the commit history is a record of fact. Linking through the commit means the changelog can be built from facts while the board stays a comfortable, human, draggable thing. You do not have to be careful with the board for the release notes to be right. ``` drag a card ─────────────────────────► board says Done (intent) push "closes #12" ──► commit #24 ──► link recorded │ at seq 24 ▼ changelog for the next release (fact) ``` *The board is intent, the history is fact, and the link between them is what makes release notes derivable.* --- ## 3.3 Changelogs from tasks > A release's notes are the tasks its commits closed since the last release, not a list of commits. Release notes assembled from commit messages read like a repository, because they are one. "Fix null deref in AISpawner" is not a line any player wants, and the two lines they *do* want are buried among forty of those. So the changelog is built from tasks instead. When you cut a release, USourceControl works out which commits are in it and lists the tasks those commits closed. You get notes written in the language of the work. ## What defines the range - The **start** is the commit sequence number of the previous release on the same lineage. Both release paths count: a Forge build and a desktop publish are equally a release, and consulting only one would re-list everything the other already shipped. - The **end** is the commit the build was made from. - The **lineage** is the branch or edition being released. ``` seq 19 20 21 22 23 24 25 │ │ │ │ release closes closes │ closes 1.2 #31 #28 │ #35 ▲ ▼ └──────────── range ──────────┘ 1.3 release notes: · Enemy stagger pass (#31) · Fix T-pose on death (#28) · New main menu layout (#35) ``` *The changelog for 1.3 is the tasks closed by commits in the range, not the commits themselves.* ## Why lineage is not optional Sequence numbers are project-global. A console edition that has been frozen for certification for six weeks still shares its numbering with the main game, which has moved on. A range without a lineage filter would therefore sweep up everything the main game did during the freeze and put it in the console build's notes. So the lineage is part of the range, always, and an unresolvable one is an error rather than a quiet fallback to main — answering with the main game's changelog looks like a valid answer while being exactly the wrong one. The changelog appears on the release in the desktop app's Releases view, ready to paste into a store page or an announcement. --- ## 3.4 Voice capture > Say what needs doing, read the transcript, tick the cards you want. Paid plans. Playtests generate notes faster than anyone can type them. You are watching someone play, things are going wrong in real time, and the choice is between looking at the player and writing it down. Voice capture lets you talk and get task cards back. ## The reviewed path 1. **Record** Press the microphone on the board and say what needs doing. One long take is fine — it is better, actually, than trying to speak in discrete items. 2. **Read the transcript** You get the text back before anything else happens. Fix it, trim it, or throw it away. 3. **Tick the cards you want** The approved text becomes proposed cards. Nothing reaches the board until you accept it, and you can edit a proposal before you do. The two gates are load-bearing. Transcription and extraction are separate steps with a person in between, so a bad recording costs one call to discover rather than two, and the text that reaches extraction is text you have read. The output is always proposals — nothing appears on a shared board because a model thought it should. ``` 🎙 audio │ ▼ transcribe transcript ──► YOU read / edit / discard │ ▼ extract proposals ──► YOU tick the ones you want │ ▼ board ``` *Two automated steps, two human gates, then the board.* ## The global shortcut The desktop app can also register a system-wide shortcut: press it, talk, press it again, and cards appear on the active project's board with no review step at all. It is off by default. Skipping the review is the whole value during a playtest, so the trade is paid on the way out instead: the overlay names every card it created and offers **Undo**. > **INFO: The shortcut needs a modifier** > Accelerators must include Ctrl, Alt, or Cmd, or be a function key. The shortcut is registered with the operating system once and applies everywhere, so a bare `Shift+A` would eat every capital A you typed on the machine, in every application. > **WARN: Voice capture is the one paid part of tasks** > It is the only part of the task system that costs money every time it is used — a transcription call and a model call, both on our side. Everything else about tasks is on every plan, Free included, because it is our own code and gating it would make the free tier worse for no saving. See [pricing](https://usourcecontrol.com/pricing). --- ## 3.5 Handing tasks to a coding agent > Copy a prompt that tells an agent what to build and lets it move the cards as it works. Select cards on the board and use the agent action. You get a prompt containing the tasks, their descriptions, and the project path, ready to paste into a coding agent working in that repository. The prompt is shown before it is copied, never copied silently. You should read what you are about to hand to something that will edit your project. ## Write-back The prompt generated from the desktop app also tells the agent how to move the cards itself: set a task to `doing` when it starts, `done` when it finishes, `blocked` with a note when it cannot proceed. The agent talks to the desktop app's [local agent server](https://usourcecontrol.com/docs/integrations/local-agent) over localhost. The effect is that you can watch an agent work on the same board your team uses, rather than reading a wall of terminal output and guessing how far along it is. What the agent does, in its own words ``` $ POST /v1/tasks/update {"taskId": "...", "status": "doing"} 200 $ POST /v1/tasks/update {"taskId": "...", "status": "blocked", "note": "needs the art for the new HUD"} 200 ``` > **INFO: The prompt never contains a token** > The agent's bearer token rotates on every app launch, so an embedded one would be stale by the time anyone used it. The prompt tells the agent to read `~/.usourcecontrol/agent.json` instead. The prompt copied from the web omits write-back entirely, because a browser cannot promise there is a desktop app running. > **INFO: Task endpoints keep working during a sync** > Unlike the file operations, the agent's task endpoints are not blocked while a sync or a push is running. Refusing them would silence an agent's progress for the length of a big pull, which is exactly when you most want to know what it is doing. --- ## Summary The board is four columns and a card, which is all a board needs to be. What makes it worth using is what sits next to it: commits that close cards with `closes #12`, and a changelog that comes out of those closures rather than out of somebody's memory on release day. The distinction to hold on to is intent versus fact. Drag cards freely — the board is allowed to be aspirational. The release notes are built from commits, so they stay true regardless. Both AI paths keep a person in the loop by design: voice capture proposes and you accept, and an agent's prompt is shown to you before it is copied. Next: turning a commit into something a player can actually run. --- # Chapter 4. Packaging and publishing > Package your Unreal project with the engine you already have, then send the build to Steam, the Epic Games Store, or itch.io. Source: https://usourcecontrol.com/docs/packaging --- The day you first ship should not be the day you learn a build script. In practice it usually is: packaging works on the programmer's machine, nobody else can reproduce it, the upload is a command nobody has run before, and something goes wrong at 11pm on a Thursday. This chapter is about removing that. The desktop app packages your project with the Unreal install you already have, on the machine you already own, and uploads the result to a store from the same window you commit in. There is no build service to configure and no second copy of your engine to license. We cover packaging first, then each of the three stores in turn — Steam, the Epic Games Store, and itch.io — and finish with the parts that keep a shipping habit sane: auto-publish, version numbers, and knowing what went where. > **INFO: Included on every plan** > Packaging and store publishing are on Free too. They run on your hardware with your engine and your store accounts, so there is nothing for us to meter and nothing to charge for. The same reasoning applies to [Forge](https://usourcecontrol.com/docs/forge). --- ## 4.1 Packaging a build > Pick a version, a platform, and a configuration, and let Unreal's own toolchain do the work. Open **Releases** in the desktop app and press **New build**. What runs underneath is Unreal's own `RunUAT BuildCookRun` against your own engine — the same thing you would type by hand, with the arguments worked out for you and the output made readable. ## The build form | Field | What it does | | --- | --- | | Version | The version string this build carries. Pre-filled with the next one after your last successful build, and it warns you if the number collides with a build you already made | | Write this version into the project | Sets `ProjectVersion` in `DefaultGame.ini` before packaging, so the number inside the build matches the number you shipped. On by default | | Platform | Windows, Linux, or macOS. Pick more than one and each becomes its own build | | Configuration | Development (fastest, includes debug tooling), Test (profiling, no editor), or Shipping (what you send to a store) | | Engine | Auto-detected from your installs. The form warns you when the version your project requires is not present, rather than failing forty minutes in | | Include debug symbols (PDBs) | Ships the symbols alongside the build | | Stage the prerequisites installer | Packages the runtime installer your game needs into the build. Read [4.3](https://usourcecontrol.com/docs/packaging/publish-to-epic) before turning this off | | When the build succeeds | Tick a store to upload every successful build there without another click. See [4.5](https://usourcecontrol.com/docs/packaging/auto-publish-and-versions) | ## Building more than one thing Selecting several platforms or configurations queues a matrix. Builds run one at a time, in order, and the queue survives you closing the window — the app keeps working from the tray, which is the point of [close-to-tray](https://usourcecontrol.com/docs/desktop-app/settings-and-tray). ## When a build fails It will. The useful question is how long it takes you to find out why. The output log is in the app and tailed live, and failures are classified rather than dumped. A missing engine, a compile error, a cook error, and a machine that ran out of disk are four different messages with four different fixes, and being told which one you have is worth more than four thousand lines of log. A build that stops producing output entirely is detected and stopped, rather than left to hold the queue overnight. > **TIP: The clean-tree gate** > A project can require a clean working tree before packaging, so every build is reproducible from a commit. With it on, the build button tells you which files are in the way instead of quietly building something that exists on no one else's machine. Turn it on under Releases → Settings the first time you ship something you might have to reproduce. --- ## 4.2 Publishing to Steam > Point USourceControl at steamcmd once, fill in your app and depot, and upload from the same window you commit in. Steam uploads go through SteamPipe, which means `steamcmd`. You set it up once per machine and then never think about it again. 1. **Set up steamcmd** Under **Releases → Settings**, point the app at your `steamcmd.exe`, then run the warm-up it offers. steamcmd updates itself the first time it runs, and you do not want to discover that in the middle of your first upload. 2. **Fill in the Steam target** Under **Releases → Stores**, open Steam and enter your **App ID**, **Depot ID** (usually your App ID plus one), and **Steam username**. 3. **Choose a branch** Set **Beta branch** to upload to a branch such as `playtest` rather than the default one. Leaving it empty targets the default branch, which is the one players are on. 4. **Decide how you sign in** Leave the password empty to sign in by hand the first time, or save one so uploads can run unattended. If your account uses the mobile authenticator, save its shared secret too. 5. **Upload** Pick a finished build and publish. The progress bar is real: the app reads steamcmd's own output and reports the actual phase and percentage. > **WARN: Uploaded is not live** > USourceControl reports a build as **uploaded**. Setting a build live on Steam's default branch is an action in Steamworks and it stays yours. We will not tell you something shipped when it has not — that is a message you would only find out was wrong from your players. > **INFO: Your password is never on a command line** > A saved Steam password is encrypted with your operating system's keychain and typed into steamcmd's own prompt when it asks for it. It is never passed as an argument, where any process on the machine could read it out of the process list, and it never reaches us. --- ## 4.3 Publishing to the Epic Games Store > BuildPatchTool, your organization and product IDs, and the prerequisite installer that decides whether the game starts at all. 1. **Set up BuildPatchTool** Under **Releases → Settings**, point the app at BuildPatchTool. You get it from the Epic Developer Portal. 2. **Fill in the Epic target** Under **Releases → Stores**, enter your **Organization ID**, **Product ID**, and **Artifact ID**, then the **Launch executable** and any launch arguments (usually none). 3. **Add BPT credentials** The **BPT client ID** and **client secret** come from the Developer Portal. The secret is encrypted with your keychain and passed to BuildPatchTool through the environment, never on the command line. 4. **Upload** Publish a finished build. As with Steam, promoting it to players stays a step you take in Epic's own portal. ## Prerequisites: read this one This is the single most expensive thing to get wrong when shipping to Epic, and it is invisible from your own machine. > **WARN: Epic installs nothing unless your build says so** > Steam installs the Visual C++ redistributable for you from the depot configuration. Epic does not. A build uploaded without prerequisite information crashes before the splash screen on any machine that does not already have the VS 2015-2022 x64 runtime — which is most machines that have never run an Unreal game. Your development machine has that runtime, because Visual Studio put it there years ago. So does every machine at your studio. The failure only appears on a clean install, which is exactly the population your store page is aimed at. USourceControl therefore turns prerequisites **on** by default and fills in Unreal's own installer. Two things have to line up: the build form's **Stage the prerequisites installer** option packages the installer into the build, and the Epic store form declares it to the launcher. | Field | What it is | | --- | --- | | Prerequisite IDs | A version token. The launcher skips IDs a machine already has, so adding a prerequisite means changing this list — reusing an old ID means the new installer never runs | | Display name | Shown to the player while the prerequisite installs | | Installer path | Relative to the build root, and it must exist inside the staged build. This is the check that catches a project packaged without prerequisites | | Installer arguments | Passed to the installer, normally a silent-install flag | ``` DECLARED NOT DECLARED launcher reads prereq ids launcher has nothing to read │ │ machine missing them? │ │ │ yes ├──► runs the installer │ │ │ no └──► skips │ │ │ ▼ ▼ game starts game exits before the splash screen, on any machine without the VC++ runtime ``` *What the Epic launcher does with a build that declares prerequisites, and one that does not.* Turning prerequisites off requires you to write down why. That is not bureaucracy — it is a note to whoever is debugging a wave of "the game won't start" reports six months from now, telling them this was a decision rather than an oversight. > **TIP: Verify on a machine that has never built anything** > A clean Windows image, or a virtual machine with nothing installed on it. Your workstation cannot reproduce this failure and will happily tell you the build is fine. --- ## 4.4 Publishing to itch.io > Butler, a target, and a channel. The shortest publishing path there is. itch.io is the easiest of the three by a wide margin, and it is the one to start with if you have never shipped anything. Two fields and an upload. 1. **Set up butler** Under **Releases → Settings**, point the app at `butler.exe`. You can get it from the itch.io app or from itch's own download page. 2. **Fill in the itch target** Under **Releases → Stores**, set **Target** to `yourstudio/yourgame` and **Channel** to something like `windows-beta`. The channel is how itch tells your uploads apart — one per platform, or one per audience. 3. **Upload** Publish a finished build. Butler reports genuine progress, which the app reads and shows. > **INFO: itch does go live** > Unlike Steam and Epic, an itch.io channel updates for players as soon as the upload completes. There is no promotion step. The app says so in the auto-publish hint before you turn it on, because "uploaded" and "live" mean different things on different stores and you should not have to remember which is which. --- ## 4.5 Auto-publish, versions, and history > Send every successful build to a store without a second click, and keep track of what went where. ## Auto-publish The build form's **When the build succeeds** row lets you tick the stores a successful build should be sent to. The upload starts the moment packaging finishes, for every build in a matrix, with nothing else to press. A store you have not finished configuring shows as **Set up** rather than as something you can tick by mistake and discover an hour later. This is what turns shipping a playtest build from an evening's work into a thing you do because it is Tuesday. ## Version numbers The version box is pre-filled with the next version after your last successful build, and it warns you if the number you typed has already been built for that platform and configuration. Shipping two different builds under one version is easy to do and genuinely hard to undo: bug reports arrive tagged with a version that describes two different sets of bytes, and there is no way to tell them apart afterwards. ## Publish history and logs Every upload is recorded — which build, which store, when, and how it ended. The full log is readable in the app, and successful uploads carry a deep link to the store page you sent them to. If an upload stalls, the app notices and says so rather than sitting on a progress bar that has not moved in twenty minutes. > **INFO: Provenance** > A build records the commit it was made from. That is what makes a release's [changelog](https://usourcecontrol.com/docs/tasks/release-changelogs) possible, and what lets you answer, months later, which state of the project a given build came from — which is the first question anyone asks about a crash report. --- ## Summary Packaging runs on your machine with your engine, so it is on every plan and there is nothing to configure beyond pointing at the tools once. The build form's defaults are the ones you want: Shipping, your project's engine, the version after the last one, and the prerequisites installer staged. Each store has one thing worth remembering. Steam: a saved password is typed into steamcmd's prompt, never onto a command line, and "uploaded" is not "live". Epic: declare your prerequisites, or the game will not start on a clean machine. itch: it goes live immediately. Turn on auto-publish once the setup is done. A build you have to remember to upload is a build that ships late. Everything in this chapter runs on the machine in front of you. The next chapter moves it to a machine that does nothing else. --- # Chapter 5. Forge, the build runner > Put a machine to work: it claims commits, packages them, tests them, and publishes the result without anyone sitting there. Source: https://usourcecontrol.com/docs/forge --- Chapter 4 got a build out of the machine in front of you. That is the right first step and it stops scaling at roughly the moment two people need a build on the same afternoon, or the moment someone notices that the build always happens on the same person's computer and always while they wanted to be doing something else. Forge is a separate application you install on a machine you own. It polls for work, materializes the exact committed state of a project, runs the packaging toolchain, and publishes the result. It is what continuous integration means for a game project, without a CI service that charges by the minute for compute you already own. This chapter covers what it is, how to enroll a machine, the three kinds of pipeline and the tests you can turn on without writing any, and what happens when you let it build every commit. --- ## 5.1 What Forge is > A standalone app for a build machine, so packaging stops being something a person does. **USourceControl Forge** is its own application with its own installer. It turns a machine you own into a build runner: it claims queued work, downloads exactly the committed state the build asked for, runs Unreal's `RunUAT BuildCookRun`, zips and uploads the artifact, and publishes the release. It is separate from the desktop app on purpose. A build machine has no reason to run a sync client, and someone using the sync client has no reason to be carrying a build runner around. ``` queue runner (your machine) │ ├─ claim ──────► materialize commit #24 │ │ │ ▼ │ RunUAT BuildCookRun │ │ │ ▼ │ validate · tests · smoke │ │ │ ▼ ◄──── publish ─── zip + upload artifact │ ▼ deploy to Steam (optional) ``` *What a runner does between claiming a job and publishing a release.* ## Why it is on every plan Forge runs on your hardware with your engine. There is no cloud compute for us to meter, so there is nothing to charge for. It is included everywhere, Free included. ## Incremental workspaces The naive version of a build runner downloads the whole project every time. On a hundred-gigabyte game that is an hour of network before the first line compiles, every single build. Instead a runner keeps its workspace between builds and records which commit sequence number it materialized. The next build asks the server for the delta since that number, so only files that actually changed are transferred. The correctness work sits underneath that: a stat pass invalidates a baseline that has drifted on disk, downloads are verified by hash as they stream in, and any mismatch falls back to a full download rather than building something subtly wrong. A fast build that is occasionally wrong is worse than a slow one. > **TIP: What makes a good build machine** > Fast storage matters more than core count for cook times, and disk space matters more than either — a workspace, an intermediate directory, and a few archived artifacts add up quickly. An old workstation with a big SSD is a better runner than a new laptop. --- ## 5.2 Enrolling a runner > Name the machine, copy the token once, install the app, paste it in. 1. **Open Builds in the dashboard** The Runners panel lists every machine enrolled in your organization and whether it is online. 2. **Add a build runner** Give the machine a name you will recognize — `studio-build-01` beats `runner 2` the day you need to walk over and look at one. You get an enrollment token starting with `forge_`, shown exactly once. 3. **Install USourceControl Forge** Download it from the same panel and install it on the build machine. 4. **Paste the token** The runner enrolls, heartbeats every few seconds, and reports which platforms it can actually target. That last part matters more than it looks. Because a runner reports its real capabilities, the pipeline form will not offer you a platform whose build would fail an hour into the queue — you find out that nobody can build for Linux when you are configuring it, not when you are waiting for it. > **WARN: Revoking is immediate** > Revoking a runner kills its token at once, and a build in progress on that machine stops. That is the behaviour you want when a machine is lost, stolen, or leaves the studio with a contractor. ## Running it in the background Like the desktop app, Forge closes to the tray rather than quitting, and can start with the system straight into the tray. A build machine that reboots for updates comes back building instead of waiting for someone to notice. --- ## 5.3 Pipelines > Three kinds — package, compile check, editor binaries — plus validation and tests that need no code. A pipeline is a saved recipe: what to build, for which platform and configuration, with which engine, and what to do when it is done. Create one from a project's row under **Builds → New pipeline**. ## The three kinds | Kind | Produces | What it is for | | --- | --- | --- | | Package | A full cooked, packaged build | The thing you ship | | Compile check | Nothing but a verdict | Catching a broken commit in minutes rather than on Friday | | Editor binaries | Compiled editor binaries | So designers and artists never have to compile C++ themselves | Choosing check or editor switches the pipeline to build on every commit in Development, because that is what those two are for and there is no reason to make you set it twice. The editor-binaries pipeline is the one teams underestimate. On a project with C++, every artist who pulls a code change either compiles it themselves or is blocked. Building the binaries once, centrally, removes an entire category of "it doesn't open for me". ## Testing without writing a test harness Four checks are switches on the pipeline rather than code you have to write: - **Data validation** — runs Unreal's own asset validation across the project. - **Blueprint compile sweep** — compiles every Blueprint and fails the build on any that no longer compile. This catches the classic case where a C++ signature change silently breaks a Blueprint nobody opened. - **Boot smoke test** — launches the packaged game and fails if it does not reach a running state. Crude, and it catches an enormous proportion of real shipping failures. - **Automation tests** — runs the named Unreal automation tests, if you have written any. None of these require a test engineer. They are the difference between finding out on a build machine and finding out from a player. ## What happens when it finishes A package pipeline zips the artifact, uploads it, and publishes a downloadable release. It can also deploy straight to a Steam app, depot, and branch, and post the result to a Discord channel. Build steps, durations, and the tailed log are visible in the dashboard while it runs, and progress is measured against how long that pipeline usually takes rather than against a guess. --- ## 5.4 Building every commit > Turn a pipeline's auto-build on and every push becomes a build. Every pipeline has an **Auto-build** switch. With it on, a push to the pipeline's branch queues a build automatically. With it off, builds start when someone presses **Build now**. The live queue in the dashboard shows what is waiting, what is running on which machine, and how far through it is. ## A setup that works You do not need to build everything on every commit, and on a game project you actively should not — a full cook is measured in tens of minutes and a queue that never drains tells you nothing. The arrangement that works for most teams is two pipelines: - A **compile check** on auto-build, so a commit that breaks the build is caught within minutes of landing and the person who broke it is still thinking about it. - A **package** pipeline you trigger by hand, when you actually want something to play. Add **editor binaries** on auto-build the first time an artist waits on a compile. Add a nightly package once the team is large enough that somebody always wants yesterday's build. > **TIP: The value is the speed of the signal** > A broken commit found in eight minutes costs eight minutes. The same commit found on Thursday costs an afternoon of bisecting and an apology. That gap is the entire argument for building on every commit, and it is why the cheap check pipeline matters more than the expensive package one. --- ## Summary Forge is a machine you own, running the engine you already have, doing the packaging nobody wants to do by hand. Enrolling one is a name and a token; the runner reports what it can build so you never configure something that cannot run. Workspaces persist and transfer only the delta since the last build, with hash verification and a full-download fallback, so repeat builds are fast without being occasionally wrong. Start with a compile check on auto-build and a package pipeline you press yourself. Turn on data validation, the Blueprint sweep, and the boot smoke test — they cost you nothing and they catch the failures that otherwise reach players. Next: shipping more than one version of the same game. --- # Chapter 6. Editions and release trains > Ship a demo and a console version of the same game without maintaining copies of it, then release everywhere in one step. Source: https://usourcecontrol.com/docs/editions --- At some point one game becomes several things: the game, a demo for a festival, a Switch version with different textures, a build for a publisher under NDA. The traditional answer is a copy — a separate stream, a separate repository, a folder called `MyGame_Demo_FINAL` — and the traditional consequence is that the copy drifts, and six months later a fix that landed in the main game never reached the demo anybody is still playing. An **edition** is that relationship expressed properly: a permanent downstream line of the game that keeps receiving the main game's work and never sends anything back. This chapter covers the two shapes an edition takes, how scope decides what an edition even contains, how the main game's work reaches it, and how to ship all of them on the same commit without one store going live hours before another. > **INFO: Unlimited on every plan** > An edition shares every unchanged asset with the main game — the same stored bytes, referenced twice — so it costs almost nothing to keep. Editions are unlimited everywhere, Free included. --- ## 6.1 Demo and platform editions > A permanent downstream copy of your game: changes flow one way, and it never merges back. An edition receives everything from the main game and sends nothing back, forever. Two shapes ride the same machinery, and which one you pick sets sensible defaults for everything else. | Kind | Starts as | Default scope | For | | --- | --- | --- | --- | | Demo | Empty | Deny — you add what belongs | A cut-down slice for a festival or a store demo | | Platform | A full mirror of the main game | Allow — you exclude what must not ship | The whole game for one console or store | The defaults are opposite because the mistakes are opposite. A demo that accidentally contains the whole game leaks your unannounced content; a platform edition that accidentally contains nothing ships an empty build. Starting each one from the safe side means the failure mode of forgetting to configure it is "too little" for a demo and "everything" for a platform, which is the right way round. Create an edition from a project's **Editions** tab in the dashboard. It forks from the current state of the main game. ``` MAIN GAME │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ Steam demo Switch PlayStation (demo) (platform) (platform) own scope own overrides own version, own version own version own cert cycle ────────── one-way, always ────────── ``` *Work flows down from the main game forever. Nothing flows back up, and the product enforces that.* ## Owning a file Editing a file inside an edition forks it. The edition now owns that file, and stops receiving the main game's changes to it — permanently, until you release it. That is exactly right for a console-specific override: you changed the texture settings for Switch precisely because you do not want the main game's version anymore. It is a bad surprise if you did not mean it, because the consequence is invisible — nothing breaks, work simply stops arriving. So the commit bar warns you before the first write to a file that is about to become edition-owned, and names the files. It is the only moment where that consequence can be made visible. > **WARN: An edition never merges back** > That is the point of it, and the product refuses the operation for both kinds. A console edition merging back would drag platform overrides into the game everyone else is building — the same class of accident that separate streams exist to prevent. --- ## 6.2 Scope: what an edition contains > Include and exclude rules decide what is materialized. Content outside the scope never reaches a workspace or a build machine. Scope is a list of path rules on the edition. A demo starts denying everything and you add what belongs in it; a platform edition starts allowing everything and you exclude what must not ship there. A demo's scope: the first level, the shared systems, nothing else ``` + Content/Maps/Tutorial/ + Content/Characters/Hero/ + Content/Core/ + Config/ everything else is denied by default ``` > **WARN: Scope is a boundary, not a filter** > Excluded content is not materialized anywhere: not in the edition's workspace, not in a manifest, and not on the machine that builds it. An unannounced level that is out of a demo's scope is not merely hidden from a list — it is not on the disk of the computer that cooks the demo. This is the difference between a feature and a guarantee. A UI filter can be got around; content that was never transferred cannot leak from a build machine, cannot be found in a shipped package by someone with a hex editor, and cannot be pulled by a contractor who only works on the demo. ## Restricted editions A console or NDA edition can be restricted to named members. Access is enforced on the server for every path that can reach it — the board, the API, manifests, releases, every branch parameter — rather than by hiding things in the interface. A denial answers exactly the same 404 as an edition that does not exist. That is deliberate: a 403 tells you there is something there to be denied, and "which unannounced platforms is this studio working on" is precisely the question the restriction exists to refuse. > **TIP: Set scope before you invite anyone** > Scope decides what a person's machine downloads. Getting it right first means a contractor brought in for the demo never has a copy of the full game on their laptop, which is a much stronger position than trusting them to delete it later. --- ## 6.3 Integrating from the main game > Bring the main game's newer work into an edition. Files the edition owns are parked for a person to decide. Integration walks the commits the main game has made since the last integration and applies everything the edition does not own. It can run on a schedule or on demand. For most files this is silent and boring, which is the goal: a bug fix in a shared system lands in the main game and turns up in the demo and both console editions without anyone doing anything. ## Divergences The interesting case is when the main game changes a file the edition owns — you overrode the Switch texture settings, and now someone has changed the base ones. > **INFO: Divergences never block, and are never resolved for you** > Integration applies everything else and parks that file for review. It does not stop, and it does not guess. An integration that halts on the first conflict means nothing integrates for a week; an integration that picks a side silently means someone's console override disappears without a trace. ``` main commits since last integration │ ├── file the edition does NOT own ──► applied │ └── file the edition OWNS ──────────► parked for review │ stays until a person answers it — it does not expire with this run ``` *Integration applies what it can and leaves a standing question behind for what it can't.* The pending list is a standing query, not a report from one integration run. It does not scroll away, and tomorrow's integration does not clear it. Reviewing a divergence means deciding whether the edition takes the main game's new version or keeps its own, and both are one action. --- ## 6.4 Release trains > Pin one commit, build every edition from it, and hold every store upload until they are all green. Once you have a main game and three editions, "ship 1.2" stops being one action and becomes four, spread over however long the slowest one takes. In the meantime players on the fastest store have a version nobody else has, and your support channel finds out before you do. A release train is that whole operation as one object. It pins a single commit of the main game, integrates that commit into every participating edition, builds each one, and then waits. ## Nothing uploads until everything is ready A build that belongs to a train withholds its deploy. Left to itself, the Steam build would deploy as the last step of packaging and reach players hours before the Switch build finished — which is the exact thing a train exists to prevent. Publishing is then one confirmed step that releases every store at once. ``` pin commit #412 │ ├─► integrate ─► build main / Steam ✓ ready ├─► integrate ─► build Switch ✓ ready ├─► integrate ─► build PlayStation ⏳ building └─► integrate ─► build demo ✓ ready │ gate: closed ─────────────┘ │ all green ─┴─► PUBLISH (one confirmed step) ``` *Every build must be green before anything uploads.* ## Versions stay per edition Certification cycles put consoles out of lockstep, and a train that forced one version string across every platform would be unusable the first time a submission came back with a fix request. So versions remain per edition. What a train guarantees is a shared **commit**, not a shared number — which is the thing that actually matters when you are trying to work out whether two builds contain the same fix. > **INFO: Console stores stop at "awaiting manual upload"** > Nintendo, Sony, and Microsoft have no upload API. A console target in a train reaches a terminal success state meaning the build is ready for you to submit through their portal. It is not a failure and it does not hold the train open. Claiming otherwise would have a studio believe their build shipped when it had not. --- ## Summary An edition is a permanent downstream line of the game — a demo or a platform version — that keeps receiving the main game's work and never merges back. That single constraint is what makes it maintenance-free where a copied repository is not. Scope is the guarantee worth understanding: excluded content is never materialized, so it cannot leak from a build machine or a contractor's laptop. Ownership is the consequence worth watching: editing a file in an edition stops the main game's changes to it arriving, permanently, which is why the commit bar warns you the first time. Integration applies everything it can and parks what it cannot, forever, rather than blocking or guessing. And a release train holds every store until every build is green, on one shared commit, with versions still per edition because cert cycles do not care about your schedule. The next chapter steps back from the daily work to the things you set up once: people, keys, and paying for it. --- # Chapter 7. Administering a team > The things you set up once: projects and their history, people and their roles, keys, the audit trail, and billing. Source: https://usourcecontrol.com/docs/web-dashboard --- Everything so far has been about doing the work. This chapter is about the scaffolding around it: who can see what, how machines authenticate, what gets recorded, and what it costs. It all lives in the web dashboard, and most of it you will touch once when you set the team up and then only when somebody joins or leaves. That is deliberate — administration that needs constant attention is administration nobody does. --- ## 7.1 Projects, files, and history on the web > Browse what is committed, see who has what locked, and read any commit, from a browser. A project's page is the shared view of it — the link you send to somebody who needs to see something without installing anything. The Files card browses everything committed, showing the current version of each file and how many are locked right now. The Recent commits card is the last few pushes, and the **Commits** tab is the whole history, with every file a commit touched. ## The tabs - **Tasks** — the [board](https://usourcecontrol.com/docs/tasks/task-board). - **Commits** — the full history. - **Branches** — the project's [branches](https://usourcecontrol.com/docs/desktop-app/branches). - **Editions** — demo and platform [editions](https://usourcecontrol.com/docs/editions/editions). - **Releases** — published builds people can download. - **Plugins** — what this project mounts, and what it publishes. - **Settings** — name, icon, keys, notifications, and the clean-tree gate. > **INFO: The dashboard reads, the app writes** > You commit from the desktop app, not from the browser. The dashboard is for looking at what is there, deciding who can see it, and paying for it. That split is why the dashboard never needs your project folder. --- ## 7.2 Members, roles, and access > Three organization roles, plus per-project membership on top of them. Access is two layers. An organization role says what you can administer; project membership says what you can open. | Role | Can | | --- | --- | | Owner | Everything, including billing and deleting the organization | | Admin | Manage projects, members, keys, and settings | | Member | Work in the projects they have been added to | Owners and admins reach every project in the organization. A plain member reaches a project only once they have been added to it. That second layer is what makes it safe to keep an unannounced title in the same organization as everything else, and it is what lets you bring in a contractor for one project without handing them the studio. ``` ORGANIZATION My Studio ├─ Sarah owner ──────────── every project ├─ Marcus admin ──────────── every project ├─ Yuki member ──┐ └─ Alex member ──┼──┐ │ │ PROJECTS │ │ ├─ MyGame ◄────┘ │ ├─ SpaceGame ◄───────┘ └─ Unannounced (Sarah and Marcus only) ``` *Two layers of access: the role you hold, and the projects you are in.* ## Seats A user is anyone with access to at least one project. Your seat count follows your member count automatically — adding someone adds a seat, removing them removes it — so there is no separate licence pool to reconcile and no way to end up paying for seats nobody is using. > **TIP: Remove people the day they leave** > It reduces the bill immediately and it revokes their access immediately. There is no reason to keep a departed contractor's seat, and no penalty for adding them back if they return. --- ## 7.3 Project keys > How the desktop app and the API authenticate, why keys are locked to a device, and how to rotate one. A project key authenticates a client — the desktop app, a script, a build agent — against exactly one project. Keys are prefixed `usc_`, shown once at creation, and stored by us only as a hash, so a leaked database does not leak anyone's key. ## Device locking The first machine to use a key claims it, and the key stops working anywhere else. This is checked on every request, not just at sign-in. The practical consequence is that a key pasted into a chat message, committed to a public repository, or read off a screen in a stream is useless to whoever finds it — it is already bound to a machine that is not theirs. Each seat gets one active key per project on Free and two on Team, so one person can work from a desktop and a laptop. > **INFO: Losing a key is not an emergency** > Revoke it in project settings and generate another. Nothing on any machine is deleted when a key is revoked — the app simply stops being able to reach the server until you give it a working one. A version control client that wiped your working folder on an authentication failure would be a far more dangerous thing than a lost key. > **TIP: Name your keys** > `sarah-laptop` and `studio-build-01` tell you what a revocation will break. `key 3` tells you nothing, and you will be reading it at the moment you least want a puzzle. --- ## 7.4 The audit log > Who did what, when: commits, uploads, downloads, access, and membership changes. Studio and above. The audit log records the events that matter for accountability: commits, uploads, downloads, access grants and denials, and membership changes. Filter it by project, member, and event type, and export it when somebody needs it in a spreadsheet. Most teams never need this. The ones who do usually need it suddenly — a publisher's security review, an NDA question about who accessed a restricted [edition](https://usourcecontrol.com/docs/editions/edition-scope), or working out how a file came to be deleted. > **INFO: Plan requirement** > Audit logs are included on Team and Enterprise. Free is the only plan without them. See [pricing](https://usourcecontrol.com/pricing). --- ## 7.5 Billing and plans > Per seat, monthly or annual, with tax handled at checkout and a portal you control. Billing is per seat at the organization level. **Billing** in the dashboard shows your plan and seat count, and links into the customer portal where invoices, payment methods, and cancellation live. ## Changing plan - Upgrades take effect immediately and are prorated. - Downgrades apply at the end of the billing cycle, so you keep everything until then. - Annual billing is paid upfront and gives you two months free. > **INFO: Nothing is stranded by a downgrade** > Dropping to a plan with a lower limit never deletes anything. Content over the limit becomes read-only rather than disappearing, so a downgrade is always recoverable by upgrading again. Your files are yours, and a billing event is not a reason to lose any of them. Every new subscription carries a 30-day money-back guarantee. Move a real project across, and if it is not right for your team, say so within 30 days of the first payment. See the [refund policy](https://usourcecontrol.com/refunds) and [pricing](https://usourcecontrol.com/pricing). --- ## Summary Access is two layers: an organization role for what you can administer, and project membership for what you can open. That is what keeps an unannounced title safe in the same organization as everything else. Keys are per project, hashed, and locked to the first machine that uses them, which makes a leaked key useless. Revoking one never touches any files. Seats follow your member count, downgrades never delete anything, and the audit log is there on Team for the day a publisher asks. One chapter left: connecting USourceControl to everything else you run. --- # Chapter 8. Integrations and automation > Push events into Discord and Slack, subscribe to them from your own service, and drive the desktop app from your own tooling. Source: https://usourcecontrol.com/docs/integrations --- No tool is the only tool. Your team already lives in a chat channel, you may already run a dashboard or a pipeline of your own, and sooner or later somebody will want to script something. This chapter is the four ways out. Chat notifications are the ready-made version for Discord and Slack. Outbound webhooks are for driving your own service. The local agent is how tooling on the same machine drives the desktop app. And the REST API is everything else. They are ordered by how much work each one is: the first takes two minutes, the last is a programming task. --- ## 8.1 Discord and Slack notifications > Paste an incoming webhook URL into a project and your channel sees every push as it happens. This is the highest-value two minutes in the product. A channel that shows every push — who, which branch, and the message as written — replaces an entire genre of "has anyone pushed the thing yet". 1. **Create an incoming webhook** In Discord: **Channel Settings → Integrations → Webhooks → Copy Webhook URL**. In Slack: create an incoming webhook for the channel you want. 2. **Paste it into the project** In the dashboard, open the project's **Settings → Notifications** and add the URL. The provider is detected from it, so there is nothing to choose. 3. **Push something** The channel gets the commit as it lands. > **INFO: Formatting survives the trip** > Commit messages carry formatting, and each provider gets what it can render. Bold, italics, code, lists, and colour come through in Discord; Slack has no coloured text, so a coloured message tints the message bar instead. What you wrote in the commit bar is what the team reads. Notifications are configured per project, so a team channel and a publishing channel can watch different games — or the same game at different volumes. --- ## 8.2 Outbound webhooks > Subscribe your own service to what happens in your organization, with signed deliveries and a retry log. Outbound webhooks are organization-level. Open **Webhooks** in the dashboard to add an endpoint, choose the events it should receive, and inspect every delivery — payload, response, and retries — from the same screen. ## Events | Event | Fires when | | --- | --- | | `commit.created` | A commit is pushed | | `branch.merged` | A branch is merged | | `release.published` | A release is published | | `build.queued` | A Forge build is queued | | `build.started` | A runner claims a build | | `build.succeeded` | A build finishes successfully | | `build.failed` | A build fails | | `member.invited` | Someone is invited to the organization | | `member.removed` | Someone is removed | | `plugin.shared` | A plugin is shared | | `plugin.granted` | Another organization is granted a plugin | | `plugin.mounted` | A plugin is mounted into a project | Deliveries are signed, retried on failure, and replayable, so a receiver that was down for an hour does not lose the hour. > **TIP: Webhooks or chat notifications?** > If you want a message in Discord or Slack, use [chat notifications](https://usourcecontrol.com/docs/integrations/chat-notifications) — it is two minutes and it formats the commit properly. Use webhooks when something has to *happen*: a deploy, a ticket, a dashboard, a build in another system. --- ## 8.3 The local agent > A localhost API inside the desktop app that editor tooling and coding agents can drive. While the desktop app is running it serves a small HTTP API on localhost, and only on localhost. It is how tooling on the same machine asks the app to do things without holding any cloud credentials of its own. That indirection is the design. An editor plugin that talked to our servers directly would need its own key, its own device binding, its own retry logic, and its own copy of the sync rules. Talking to the running app instead means it inherits all of that for free, and there is exactly one implementation of "what does a pull do" on the machine. ## Discovery The app writes `~/.usourcecontrol/agent.json` — `%USERPROFILE%\.usourcecontrol\agent.json` on Windows — containing the port it is listening on and a bearer token. The token is regenerated on every launch, so anything talking to the agent must read the file rather than remember a value. Asking the agent about a file ``` $ PORT=$(node -p "require(process.env.HOME + '/.usourcecontrol/agent.json').port") $ TOKEN=$(node -p "require(process.env.HOME + '/.usourcecontrol/agent.json').token") $ curl -sS -X POST "http://127.0.0.1:$PORT/v1/status" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"paths":["Content/Maps/MainLevel.umap"]}' {"files":[{"path":"Content/Maps/MainLevel.umap","status":"modified", "lockedBy":"Sarah Chen","outOfDate":false}]} ``` ## What it exposes | Endpoint | Does | | --- | --- | | `POST /v1/status` | Status, hashes, locks, and out-of-date state for paths | | `POST /v1/checkout` | Take a lock | | `POST /v1/checkin` | Upload and commit | | `POST /v1/revert` | Restore the committed version | | `POST /v1/sync` | Get latest for the whole project | | `POST /v1/history` | A file's revisions | | `POST /v1/download-version` | Fetch one revision to a temporary file | | `POST /v1/tasks` | Create task cards | | `POST /v1/tasks/update` | Move a card, or add a note | > **INFO: This is what the Unreal editor plugin is built on** > An in-editor source control provider for Unreal is in development as a thin client over exactly this interface. The interface itself is live today, so anything the plugin will do, your own tooling can already do. For coding agents specifically, see [section 3.5](https://usourcecontrol.com/docs/tasks/coding-agents). --- ## 8.4 The REST API > Sync, upload, commit, list versions, and restore, authenticated with a project key. Everything the desktop app does over the network, it does through a documented REST API you can call yourself. Requests authenticate with a [project key](https://usourcecontrol.com/docs/web-dashboard/project-keys) and are scoped to that key's project — a key cannot reach a project it was not issued for, no matter what id you put in the path. The natural uses are the ones that do not fit a desktop app: onboarding a machine from a script, pulling a specific revision inside somebody else's CI, wiring a build agent that is not Forge, or exporting your own data on your own schedule. ## The shape of an upload Uploads are three steps rather than one, because the bytes never pass through our application servers: you reserve a version and get a presigned URL, you PUT the file straight to storage, and you confirm. The commit then references the versions you created. ``` you api storage │ │ │ ├── initiate ──────►│ │ │◄── presigned url ─┤ │ │ │ ├── PUT bytes ───────────────────────────►│ │ │ ├── confirm ───────►│ │ │ │ │ ├── commit ────────►│ { fileId, version }│ ``` *Uploading a file through the API. The bytes go direct to storage.* The full endpoint reference, with curl and TypeScript for every call, is [Appendix A](https://usourcecontrol.com/docs/api). > **INFO: Versioning** > The API is versioned under `/api/v1`. Additive changes happen without notice; anything that would break a caller gets a new version. --- ## Summary Four ways out, in increasing order of effort. Chat notifications take two minutes and are worth doing today. Webhooks drive your own systems, with signed, retried, replayable deliveries. The local agent lets tooling on the machine drive the desktop app without credentials of its own. The REST API is everything else. That is the end of the book. If you started at the introduction and read to here, you have seen the whole product: the daily loop, the board and the changelog it produces, packaging and publishing, automated builds, editions and release trains, and the administration around all of it. The reference for every endpoint is next, in Appendix A.