| 1 | --- |
| 2 | title: Quickstart |
| 3 | description: Go from an invite to a working Diffinery repository: passkey, CLI sign-in, first push, workflow run, pull request, release and an AI agent over MCP. |
| 4 | updated: 2026-10-25 |
| 5 | --- |
| 6 | |
| 7 | This guide takes you from an invite email to a repository with code, a passing workflow, a merged pull request, a first release and an AI agent connected over MCP. It takes about 15 minutes. Each step shows the web UI and, where it exists, the `diffinery` CLI and the REST API. |
| 8 | |
| 9 | The examples use the organization `acme`, the repository `acme/website`, the user `diffy` and the agent `review-bot`, on an instance at `https://diffinery.com`. Replace them with your own names and your instance's address. |
| 10 | |
| 11 | ## What you need |
| 12 | |
| 13 | - An account on a Diffinery instance, or an invite to one. To run your own instance, [deploy it](/docs/self-hosting/deploy) to your Cloudflare account and sign in as its first administrator. |
| 14 | - A browser that supports passkeys (current Chrome, Safari, Edge or Firefox). |
| 15 | - Git 2.26 or newer (`git --version`). |
| 16 | - For an instance someone else runs, an invite. An instance is invite-only by default: a site administrator, an organization owner or admin, or a repository admin sends it. On an instance with [open sign-up](/docs/accounts/passkeys-and-sign-in#open-sign-up), create an account at `/signup` instead. |
| 17 | |
| 18 | ## 1. Join with your invite and create a passkey |
| 19 | |
| 20 | Diffinery has no passwords. You sign in to the web UI with a passkey, which your device, phone or password manager stores and unlocks with your fingerprint, face or screen lock. |
| 21 | |
| 22 | 1. Open the invite link from the email (`https://diffinery.com/invite/...`). Invite links work for 14 days. |
| 23 | 2. On **Join Diffinery**, check the **Username** (filled in from your email address) and optionally add a **Display name**. Your repositories will live at `https://diffinery.com/<username>/...`. |
| 24 | 3. Click **Create a passkey and join** and complete your browser's prompt. |
| 25 | |
| 26 | You are signed in. If the invite was for an organization or a repository, you are now a member of it (or a collaborator on it) and land on its page; otherwise you land on your dashboard. Next time, sign in at `https://diffinery.com/login` with **Sign in with a passkey**. |
| 27 | |
| 28 | > **Note:** Add a second passkey on another device, or keep the passkey in a password manager that syncs, under **Settings → Passkeys and sessions**. See [Passkeys and sign-in](/docs/accounts/passkeys-and-sign-in). |
| 29 | |
| 30 | ## 2. Install the CLI and sign in |
| 31 | |
| 32 | Install the `diffinery` command-line client. On Linux and macOS: |
| 33 | |
| 34 | ```sh |
| 35 | curl -fsSL https://diffinery.com/install | bash |
| 36 | ``` |
| 37 | |
| 38 | On Windows, in PowerShell: |
| 39 | |
| 40 | ```powershell |
| 41 | powershell -c "irm https://diffinery.com/install.ps1 | iex" |
| 42 | ``` |
| 43 | |
| 44 | The script puts the CLI in `~/.diffinery` and adds it to your `PATH`. It uses your Node.js 22 or later when you have one, and otherwise installs its own copy. See [Install and sign in to the CLI](/docs/accounts/cli-sign-in). |
| 45 | |
| 46 | Sign in through your browser and let Git (and the GitHub CLI, if you have it) use the same sign-in: |
| 47 | |
| 48 | ```sh |
| 49 | diffinery setup # opens diffinery.com; click Authorize |
| 50 | diffinery whoami # prints diffy |
| 51 | ``` |
| 52 | |
| 53 | `diffinery setup` runs `diffinery login`, then `diffinery auth setup-git`, so `git clone`, `fetch` and `push` to diffinery.com use the sign-in, and `diffinery auth setup-gh` when `gh` is installed. With Claude Code installed, it also adds Diffinery's plugins to it ([CI status in Claude](/docs/connect/ci-status-in-claude)). You can run those steps yourself instead. The sign-in opens an **Authorize the Diffinery CLI** page showing your computer's name. Check it, keep **Write** access for this guide, and click **Authorize**. The terminal prints `✓ Logged in to https://diffinery.com as diffy`. No token to copy: the CLI gets its own, valid for 90 days and listed under **Settings → Access tokens**, and `diffinery logout` revokes it. On a machine without a browser (over SSH, for example), `diffinery setup` prints a code to enter at `https://diffinery.com/login/device` from any device instead. |
| 54 | |
| 55 | Working in VS Code, Cursor or Windsurf? Install the editor extension with `curl -fsSL https://diffinery.com/install-editor | bash`, then use **Git: Clone → Clone from Diffinery**. See [Clone in VS Code, Cursor and other editors](/docs/repositories/clone-in-your-editor). In IntelliJ IDEA, PyCharm or another JetBrains IDE, install the Diffinery plugin and use **Get from Version Control → Diffinery**: see [Clone in JetBrains IDEs](/docs/repositories/clone-in-jetbrains-ides). |
| 56 | |
| 57 | ## 3. Tokens for scripts and CI |
| 58 | |
| 59 | Scripts, CI and plain Git without the CLI authenticate with a personal access token. |
| 60 | |
| 61 | 1. Go to **Settings → Access tokens** (`https://diffinery.com/settings/tokens`). |
| 62 | 2. Under **Create a token**, enter a **Name** (for example `ci`), keep **Expires in (days)** at 90 (1 to 366), and pick a **Scope**: |
| 63 | |
| 64 | | Scope | Allows | |
| 65 | | --- | --- | |
| 66 | | **Read** | Clone, fetch and view only. Every change is refused (`this token needs the write scope`). | |
| 67 | | **Write** (default) | Push, issues, pull requests, releases, runs and creating repositories (up to the maintain role). | |
| 68 | | **Admin** | Everything you can do, including repository settings and secrets, and creating organizations, agents, invitations and other tokens. | |
| 69 | |
| 70 | Leave **Restrict to repositories** empty, or pick the repositories the script needs. |
| 71 | 3. Click **Create token** and copy the token. It starts with `dfy_` and is shown only once. |
| 72 | |
| 73 | Diffinery emails you whenever a token is created on your account, including by a CLI sign-in. See [Personal access tokens](/docs/accounts/tokens). |
| 74 | |
| 75 | The `DIFFINERY_TOKEN` and `DIFFINERY_URL` environment variables give the CLI a token without signing in, which is handy in CI; `diffinery login --token dfy_...` saves one. Without the CLI, Git asks for a username and password: enter any username and the token as the password. |
| 76 | |
| 77 | ## 4. Create a repository |
| 78 | |
| 79 | Pick one of these. |
| 80 | |
| 81 | **Web UI.** Click **+** in the header (or open `https://diffinery.com/new`). Choose the **Owner** (you or an organization such as `acme`), enter the **Repository name** `website`, an optional **Description**, the **Visibility** (**Private** is the default) and the **Default branch** (`main`). Click **Create repository**. |
| 82 | |
| 83 | **CLI.** |
| 84 | |
| 85 | ```sh |
| 86 | diffinery repo create website --org acme --private -d "Marketing site" |
| 87 | ``` |
| 88 | |
| 89 | **REST API.** |
| 90 | |
| 91 | ```sh |
| 92 | curl -X POST https://diffinery.com/api/v3/orgs/acme/repos \ |
| 93 | -H "Authorization: Bearer dfy_..." \ |
| 94 | -H "content-type: application/json" \ |
| 95 | -d '{"name": "website", "description": "Marketing site", "visibility": "private"}' |
| 96 | ``` |
| 97 | |
| 98 | Leave out `--org acme` (or use `POST /api/v3/user/repos`) to create it in your own account. See [Repositories](/docs/repositories/overview). |
| 99 | |
| 100 | ## 5. Push your code |
| 101 | |
| 102 | To bring over a repository that lives on GitHub, import it instead of pushing: **Settings → Imports → Import from GitHub** in the web UI, or `diffinery migrate github acme/website --to acme/website`. The import runs on the server and copies the history, issues and pull requests with their numbers, reviews, labels, releases, branch protection and variables (secret names only, never values); see [Migrate from GitHub](/docs/migrate-from-github). |
| 103 | |
| 104 | From an existing project: |
| 105 | |
| 106 | ```sh |
| 107 | cd website |
| 108 | git remote add origin https://diffinery.com/acme/website.git |
| 109 | git push -u origin main |
| 110 | ``` |
| 111 | |
| 112 | Or start from scratch: |
| 113 | |
| 114 | ```sh |
| 115 | mkdir website && cd website |
| 116 | echo "# website" > README.md |
| 117 | git init -b main |
| 118 | git add README.md |
| 119 | git commit -m "first commit" |
| 120 | git remote add origin https://diffinery.com/acme/website.git |
| 121 | git push -u origin main |
| 122 | ``` |
| 123 | |
| 124 | Open `https://diffinery.com/acme/website` to see the files (with syntax highlighting) and the rendered README. |
| 125 | |
| 126 | Every push is checked for leaked credentials, and so is every commit made in the web editor, through the REST API or over MCP. If you accidentally commit an API key or private key, the push is rejected with `Push blocked: possible secrets detected.` and nothing from it is stored: remove the secret from your commits and push again. See [Push protection](/docs/security/push-protection) and [Git over HTTPS](/docs/repositories/git). |
| 127 | |
| 128 | ## 6. Add a workflow and watch it run |
| 129 | |
| 130 | Diffinery runs GitHub Actions workflows from `.github/workflows/`. Add one: |
| 131 | |
| 132 | ```yaml |
| 133 | # .github/workflows/ci.yml |
| 134 | name: CI |
| 135 | |
| 136 | on: |
| 137 | push: |
| 138 | branches: [main] |
| 139 | pull_request: |
| 140 | |
| 141 | jobs: |
| 142 | test: |
| 143 | runs-on: ubuntu-latest |
| 144 | steps: |
| 145 | - uses: actions/checkout@v4 |
| 146 | - name: Show what we are building |
| 147 | run: | |
| 148 | echo "Event: ${{ github.event_name }}, commit: ${{ github.sha }}" |
| 149 | ls -la |
| 150 | ``` |
| 151 | |
| 152 | ```sh |
| 153 | git add .github/workflows/ci.yml |
| 154 | git commit -m "Add CI" |
| 155 | git push |
| 156 | ``` |
| 157 | |
| 158 | The push starts a run. Watch it: |
| 159 | |
| 160 | - **Web UI**: open the repository's **Actions** tab, click the run, then the `test` job. The log streams live while the job runs. |
| 161 | - **CLI**: run these inside your clone (the CLI finds the repository from the `origin` remote, or pass `-R acme/website`): |
| 162 | |
| 163 | ```sh |
| 164 | diffinery run list # newest runs first, with their ID |
| 165 | diffinery run watch <run-id> # follow it until it finishes; exits non-zero if it fails |
| 166 | diffinery run logs <job-id> # print a job's log |
| 167 | ``` |
| 168 | |
| 169 | - **API**: `GET https://diffinery.com/api/v3/repos/acme/website/actions/runs`. |
| 170 | |
| 171 | `runs-on: ubuntu-latest` runs in a fresh Ubuntu 24.04 container on Diffinery's hosted runners. The job reports a check named `CI / test` (workflow name, then job name) on the commit, which you can require in branch protection later. If you also run another CI system, it can report its results on the same commits as commit statuses or check runs through the GitHub-compatible REST API; see [Checks and statuses](/docs/collaboration/checks-and-statuses). If a workflow file has a syntax error, its run fails and shows the errors; `diffinery workflow validate` checks files before you push. See [Actions](/docs/actions/overview). |
| 172 | |
| 173 | Each job gets a `GITHUB_TOKEN` that follows GitHub's `permissions:` model. In a new repository it can only read the code and packages (`contents: read`, `packages: read`), which is all the checkout above needs. A workflow that comments on pull requests, pushes or publishes releases has to ask for more, for example: |
| 174 | |
| 175 | ```yaml |
| 176 | permissions: |
| 177 | contents: write |
| 178 | pull-requests: write |
| 179 | ``` |
| 180 | |
| 181 | A repository admin can also change the default. See [Workflow permissions](/docs/actions/workflow-permissions). |
| 182 | |
| 183 | ## 7. Open a pull request |
| 184 | |
| 185 | Make a change on a branch and push it: |
| 186 | |
| 187 | ```sh |
| 188 | git switch -c update-readme |
| 189 | echo "Our marketing site." >> README.md |
| 190 | git commit -am "Describe the project" |
| 191 | git push -u origin update-readme |
| 192 | ``` |
| 193 | |
| 194 | The push output includes a link: `Create a pull request for 'update-readme':` followed by the compare URL. |
| 195 | |
| 196 | **Web UI.** Open that link (or the repository's **Pull requests** tab, then **New pull request**). On the compare page, fill in: |
| 197 | |
| 198 | - **Title**, |
| 199 | - **Intent**: one or two sentences on what the change is meant to do, for human and AI reviewers, |
| 200 | - **Description** (Markdown), |
| 201 | |
| 202 | then click **Create pull request**. |
| 203 | |
| 204 | **CLI.** |
| 205 | |
| 206 | ```sh |
| 207 | diffinery pr create --title "Describe the project" \ |
| 208 | --intent "Add a one-line description of the site to the README." \ |
| 209 | --body "Small docs change." |
| 210 | ``` |
| 211 | |
| 212 | The pull request's `pull_request` workflow runs on a test merge of your branch into `main`, and its checks appear on the pull request. When the checks pass, merge it with **Merge pull request** (choose **Create a merge commit**, **Squash and merge** or **Rebase and merge (fast-forward)**), or: |
| 213 | |
| 214 | ```sh |
| 215 | diffinery pr merge 1 --squash --delete-branch |
| 216 | ``` |
| 217 | |
| 218 | Teammates review with **Approve** or **Request changes** on the pull request, or `diffinery pr review 1 --approve`. You can't approve your own pull request. To require reviews and green checks before merging, add a rule under **Settings → Branch protection**. See [Pull requests](/docs/collaboration/pull-requests) and [Branch protection](/docs/repositories/branch-protection). |
| 219 | |
| 220 | To skip the merge click from now on, open **Settings → Merge on green** once its checks have run: tick the checks and **Turn on auto-merge for pull requests I open**, and pull requests, yours and your agents', merge by themselves when the checks pass. See [Merge on green automatically](/docs/collaboration/merge-on-green). |
| 221 | |
| 222 | ## 8. Publish a release |
| 223 | |
| 224 | A release turns a tag into a version people can download, with release notes and attached files. |
| 225 | |
| 226 | **Web UI.** Open `https://diffinery.com/acme/website/releases` (or **Releases** in the sidebar of the repository page) and click **Draft a new release**. Under **Tag**, type a new tag such as `v0.1.0` (its **Target for a new tag** defaults to `main`), add a **Release title** and **Release notes**, optionally check **Generate release notes from merged pull requests (when the notes are empty)** and choose files under **Attach files**, then click **Publish release**. The release page also links to **Source code (zip)** and **Source code (tar.gz)** archives of the tag. |
| 227 | |
| 228 | **REST API** (GitHub-compatible, so Octokit and similar tools work too): |
| 229 | |
| 230 | ```sh |
| 231 | curl -X POST https://diffinery.com/api/v3/repos/acme/website/releases \ |
| 232 | -H "Authorization: Bearer dfy_..." -H "content-type: application/json" \ |
| 233 | -d '{"tag_name": "v0.1.0", "name": "First release", "generate_release_notes": true}' |
| 234 | ``` |
| 235 | |
| 236 | Publishing creates the tag if it doesn't exist and starts workflows that listen for `on: release`. See [Releases](/docs/collaboration/releases). |
| 237 | |
| 238 | To deploy from a workflow with its own secrets and branch rules, use an environment (`environment: production` on a job); see [Environments](/docs/actions/environments). |
| 239 | |
| 240 | ## 9. Connect an AI agent over MCP |
| 241 | |
| 242 | Agents are separate accounts for AI tools. They act on behalf of the person or organization that owns them, never get more access than their owner, can't push or merge to the default branch or protected branches, and their pull requests always need a human approval. |
| 243 | |
| 244 | ### Create the agent |
| 245 | |
| 246 | **Web UI.** Go to **Settings → Agents**. Under **Create an agent**, enter the **Agent username** `review-bot`, a **Display name**, the **Owner** (you or `acme`) and the **Maximum role** (keep **Write**). Click **Create agent**. Then open **Create a token for review-bot**, keep the defaults (**Scope** **Write**, 30 **Days**) and click **Create token**. Copy the token; it is shown once. |
| 247 | |
| 248 | Tokens can't create agents or tokens, so create agents in the web UI under **Settings → Agents**, then create the agent's token in its row (**Create a token for**). The token is shown once. An agent owned by `acme` can work in every `acme` repository, up to its maximum role. Creating an organization agent needs the owner or admin role in the organization. |
| 249 | |
| 250 | ### Point your MCP client at Diffinery |
| 251 | |
| 252 | Diffinery's MCP server is at `https://diffinery.com/mcp` (Streamable HTTP). Give the client the agent's token as a bearer token: |
| 253 | |
| 254 | ```json |
| 255 | { |
| 256 | "mcpServers": { |
| 257 | "diffinery": { |
| 258 | "type": "http", |
| 259 | "url": "https://diffinery.com/mcp", |
| 260 | "headers": { "Authorization": "Bearer dfy_..." } |
| 261 | } |
| 262 | } |
| 263 | } |
| 264 | ``` |
| 265 | |
| 266 | `diffinery mcp-config` prints this snippet with `${DIFFINERY_TOKEN}` in place of the token, which clients such as Claude Code expand from the environment. For Claude Code, save it as `.mcp.json` in your project and set `DIFFINERY_TOKEN` to the agent's token. |
| 267 | |
| 268 | Ask the agent something like "Use the diffinery tools: who am I, and what pull requests are open in acme/website?". It calls `whoami` and `list_pull_requests`. Agents can read and commit files without Git (`get_file`, `list_tree`, `commit_files`), open pull requests with an intent (`create_pull_request`), review, follow workflow runs and logs (`list_workflow_runs`, `get_job_logs`), publish releases (`create_release`, `upload_release_asset`), and start sandboxes to build and test (`sandbox_create`, `sandbox_exec`). See [MCP](/docs/agents/mcp) and the [MCP tool reference](/docs/reference/mcp-tools). |
| 269 | |
| 270 | ## Next steps |
| 271 | |
| 272 | - [Concepts](/docs/concepts): accounts, roles, tokens, visibility, Actions and agents in one page. |
| 273 | - [Migrate from GitHub](/docs/migrate-from-github): import a GitHub repository or organization from the web, the CLI, the API or MCP, then set its secrets again. |
| 274 | - [Merge on green automatically](/docs/collaboration/merge-on-green): pull requests merge by themselves once their checks pass. |
| 275 | - [Branch protection](/docs/repositories/branch-protection): require pull requests, reviews and checks, and [Checks and statuses](/docs/collaboration/checks-and-statuses) to report results from other CI systems. |
| 276 | - [Releases](/docs/collaboration/releases) and [Environments](/docs/actions/environments): ship versions and deploy them. |
| 277 | - [Organizations](/docs/accounts/organizations) and [Invitations](/docs/accounts/invitations): bring your team. |
| 278 | - [Secrets and variables](/docs/actions/secrets-and-variables): give workflows credentials. |
| 279 | - [Workflow syntax](/docs/actions/workflow-syntax) and [Compatibility with GitHub Actions](/docs/actions/compatibility). |
| 280 | - [Agents](/docs/agents/overview) and [Sandboxes](/docs/agents/sandboxes): put AI agents to work safely. |
| 281 | - [REST API](/docs/reference/rest-api) and the [CLI reference](/docs/reference/cli). |