# Move your open-source Wren project to the cloud over plain git

> Source: <https://getwren.ai/post/wren-cloud-git-sync>
> Published: 2026-09-11 00:00:00+00:00

[The Wren Journal](/blog)

# Move your open-source Wren project to the cloud over plain git

Wren Git Sync syncs your open-source AI context layer as plain YAML and Markdown in a Git repo you own, authenticated with tokens that expire in 600 seconds.

Jasmine Nguyen

Updated: Sep 11, 2026

Published: Sep 11, 2026

**What is Git Sync?** Git Sync moves your open-source Wren project, the MDL context model and the knowledge that makes answers correct, into Wren Cloud as plain YAML and Markdown files in a git repository you own. You keep authoring with ordinary `git push` and `git pull`, and every operation authenticates with a token that expires in ten minutes, so no reusable git credential is ever stored on the machine or sent over the wire.

*Your context model and knowledge move to Wren Cloud as plain files in a git repo you own. After a one-time bind, you keep using the git you already know, and every push authenticates with a token that dies in ten minutes.*

## What you get

You built a Wren project in open source: models, an MDL context layer, and the knowledge that makes answers correct: glossary terms, metric definitions, business rules, NL→SQL pairs. Moving it to the cloud does three things:

- It becomes queryable. Once the project lands, a deploy step turns your model into something the cloud can serve queries against.
- You keep using plain git. There is no `wren push` , no`wren diff` , nothing new to learn. You bind the directory once; after that,`git push` and`git pull` do exactly what they always have.
- Your knowledge stays yours. Everything syncs as plain, open files in a repository you control: the same **semantic-layer-as-code** you already version. More on that next; it's the part most platforms get wrong.

That's the whole experience. The rest of this post is *why* it works the way it does, for the people who want to know before they trust it.

## Why a git-backed semantic layer prevents vendor lock-in

Most AI-BI platforms keep your semantic model, and everything the agent learns on top of it, inside their own database. It works fine until the day you want it somewhere else: there's no clean way to export it, diff it, or take it with you. The logic your team spent months encoding is real intellectual property, and it's sitting in a black box you don't hold the keys to.

Wren takes the opposite position. Git Sync writes your model and knowledge to your own git repository as plain, open files: the MDL context layer as YAML, and the knowledge base as Markdown and YAML in a committed `knowledge/` tree.

Because it's open-source MDL in plain YAML and Markdown on standard git, there is nothing proprietary between you and your own work. You can read it, review it in a pull request, diff two versions of a metric, feed it to another tool, or clone the whole thing and walk. It works with GitHub, GitLab, Bitbucket, or any git remote you already run. Wren Cloud is where your model becomes *queryable and collaborative*, but it was never where your knowledge is *trapped*. It was always yours, the same stance we took when we [rebuilt Wren AI around an open-source AI context layer](https://www.getwren.ai/post/genbi-for-ai-agents-open-context-layer): the value should sit in files you hold, not a vendor's database.

### Open repo vs. proprietary storage, at a glance

| Dimension | Proprietary database storage | Wren Git Sync (open repo) | 
|---|---|---|
| **Format** | Vendor's internal schema | Open MDL as YAML + knowledge as Markdown | 
| **Portability** | Export path is vendor-defined, often none | `git clone` and walk, any time | 
| **Version history** | Whatever the UI chooses to surface | Full git history: every commit, author, and diff | 
| **PR review workflow** | Not available | Standard pull requests on GitHub / GitLab / Bitbucket | 
| **Diffing changes** | Screenshot comparison | `git diff` two versions of a metric before you deploy | 
| **Backup custody** | Held by the vendor | In every clone your team already holds | 

This is what teams mean by **CI/CD for BI**: metric changes arrive as commits, get reviewed as pull requests, and promote from staging to production the same way application code does. It's also why "owning the meter" matters beyond this one feature. See [The Genie Meter Is On](https://www.getwren.ai/post/genie-meter-is-on-roi-of-owning-your-genbi) for the broader argument on why the ROI of GenBI comes down to what you actually own.

## How Git Sync works: repository structure & file types

Git Sync writes the standard [Wren project layout](https://docs.getwren.ai/oss/guides/manage_project#project-layout) into your repo. The context model is YAML; the knowledge base is a committed folder of plain files:

A couple of things worth knowing about the formats:

- The source of truth is YAML, not JSON. Your models, relationships, and cubes all live as `*.yml` . The only JSON in a Wren project is`target/mdl.json` , the*compiled* MDL that`wren context build` produces (background on[how we designed the engine that compiles it](https://www.getwren.ai/post/how-we-design-our-semantic-engine-for-llms-the-backbone-of-the-semantic-layer-for-llm-architecture) ). It's build output, it's gitignore-able, and you don't commit it. What syncs is the human-readable YAML your team actually edits.
- Knowledge is plain and reviewable. Glossary terms, metric definitions, business rules, and NL→SQL pairs are stored as Markdown and YAML under `knowledge/` , so a change to how a metric is defined shows up as a readable diff in a pull request, not an opaque row in someone else's database.

## Why Git Sync runs over plain git

Many of our customers come to us after running open source first. The cloud plan adds managed hosting, collaboration, and the hardening that makes a deployment production-ready, but the jump from open source shouldn't cost you anything you already built, and it shouldn't ask you to learn a new way to work. Git is how we keep that promise: it's the version control your team already uses, and it makes your model portable by default.

Getting a local Wren project onto the cloud means talking to a git server, and a git server needs a credential to know who you are. That sounds trivial until you look at what the obvious options leave lying around.

Two of the credentials in our system are genuinely dangerous if they leak:

- The **org key** (`osk-` ) is valid across*every* project in your organization.
- The **project key** (`sk-` ) unlocks a whole project.

If either ends up in a file, a URL, or an OS keychain in reusable form, the blast radius is large and the exposure lasts until a human happens to notice. A personal access token pasted into a remote URL, or a long-lived secret cached by a credential helper, is exactly this failure mode: the credential that authenticates you is also the one an attacker walks away with.

So the requirement we set ourselves was narrow and load-bearing:

**Let git authenticate on every push and pull, without ever putting a long-lived, reusable credential on the wire or leaving one cached on the machine.**

That single commitment forces most of the design. The durable key has to live *somewhere* (the machinery needs it), but it must never be the thing git carries. So the durable key sits on disk locked down, and what git actually presents on each operation is a freshly minted token that dies in 600 seconds. A leak of that token is worth ten minutes and nothing else.

There's a second principle underneath the first: **don't wrap git that the server doesn't need.** Once a directory is bound to a cloud project, we hand the job straight back to native git. The server already enforces every bit of authentication and authorization; a `wren push` wrapper would add nothing except a second code path to keep honest forever. Bind the directory, then get out of the way.

## Getting your project onto the cloud

There are four commands that matter, and which one you reach for depends entirely on whether the cloud project already exists.

- `wren cloud create` : no cloud project yet. Create it, connect a data source, mint a project key, configure git, bind the directory, and push.
- `wren cloud auth add --project <id>` : validate a key against a specific project and write the git config. Doesn't touch your directory. (`wren cloud auth remove` undoes it.)
- `wren cloud link` : the cloud project already exists. Bind this directory to it. A one-time act.
- `wren cloud unlink` : remove the binding. The project is untouched; only this directory forgets it. It's also the first step of pointing a directory at a different project.

There is deliberately **no `push` and no `diff`**. Once bound, those are plain native git.

The step-by-step setup, with every flag and the exact git config the CLI writes, is in the [Git integration guide](https://docs.getwren.ai/cp/guide/integrations/git) in the Wren Cloud docs.

### Flow A: onboarding a project from nothing

You have a local Wren project whose YAML already compiles; nothing is in the cloud yet. Being a git repo was never the requirement here. `create` converts an existing Wren project; it doesn't scaffold one.

In practice:

`create` refuses to run until an org (`--org`), a data source (`--type` plus `--connection-info` or `--connection-info-file`), and a compiling Wren project are all in place. A project with no data source attached shows up as unfinished in the web app, and there's no way to attach one from the CLI afterward. The org key (`osk-`) is used for exactly the two setup calls inside `create` and is passed through rather than stored, because it's valid for every project in the org and writing it to disk would turn one leaked local file into an org-wide compromise. After that, every git operation mints its own 600-second token.

### Flow B: joining a project that already exists

A teammate created it in the UI, or you did earlier, and you have a local project to attach.

`auth add --project <id>` validates the key against that specific project and writes the git config, so you never have to know your org id. `link` then attaches the directory:

- an **empty** directory → a plain`git clone` ;
- a directory that **already has files** →`git init` in place, a commit, a remote, a fetch, and a`git merge --allow-unrelated-histories` ;
- either way, the local branch gets renamed to match the remote's default. Skip that and a client whose git still defaults to `master` against a remote on`main` ends up with a branch and an upstream that disagree:`git push origin HEAD` exits 0 having created a*second* remote branch nothing watches, and a plain`git push` afterward fails with "the upstream branch of your current branch does not match the name of your current branch."

That merge is not incidental. A newly created project's repository already has a commit of its own, so your local project and the remote start as two unrelated histories. Bind without reconciling them and the first `git push` is refused, which is exactly the moment a user reaches for `--force`. `link` runs the reconcile once against the remote's default branch so you never hit that. Everything after is ordinary `git pull`.

**Don't `git push --force` on a freshly bound project.** If a push is refused, let `link` reconcile the histories rather than forcing over them: a force push deletes content the server created for the project, and deploys can stop working as a result.

## Under the hood: authenticating without storing git credentials

Everything above is the experience. This is the machinery that makes the "no reusable credential at rest" guarantee hold. You don't need it to use Git Sync, but if you're the person who has to sign off on it, read on.

### A credential helper, not a wrapper

The mechanism is a standard git **credential helper**: a small program git invokes whenever it needs a password. On every git operation against the Wren host, git calls the helper and hands it the `protocol`, `host`, and `path` it's talking to. The helper:

1. reads the durable key (project- or org-scoped, whichever you added) from `~/.wren/cloud.yml` (mode`0600` ),
2. trades it at `POST /git-token` for a fresh 600-second Ed25519 JWT,
3. hands that JWT back to git as an HTTP Basic-auth password.

git uses the token, it expires, and it's never written down or reused.

The subtle part: the `path` git passes is what tells the helper which project to mint a token for. Nothing on the machine stores a "this directory belongs to that project" mapping. The git remote already *is* that binding, and git manages remotes for us. No per-directory state to keep in sync, ever.

### Three credentials, three lifetimes

The whole model turns on keeping these three straight:

| Credential | Lifetime | Stored where | Used by | 
|---|---|---|---|
| **org key**`osk-` | long-lived | passed through `create` 's setup calls;** not stored** by it | `create` , or any project in the org via`auth add` | 
| **project key**`sk-` | long-lived | `~/.wren/cloud.yml` ,`0600` | the helper, to buy git tokens | 
| **git token** (Ed25519 JWT) | **600 seconds** | **never on disk** , never reused | git itself, as a Basic-auth password | 

The org key is so broad that it never touches disk when `create` is the one using it: `create` passes it through for two API calls, mints a *project* key for the new project, stores that instead, and discards the org key. So a project you created ends up holding the narrower credential without your having to make one. (`auth add` accepts either level and stores whatever you give it; which one you use elsewhere is your call.) The git token is the disposable thing on the wire either way, sized to outlast one slow push rather than a working session.

## Things to know before you wire this into a team

Three consequences of building something stricter than the surrounding tooling assumes. None is a blocker; all are worth knowing up front.

Rotate secrets that ever landed in a commit; don't just delete them. We hand git an ephemeral credential instead of a durable one, which is why a leaked git token is worth 600 seconds instead of "until someone notices." But git's own semantics still apply: a `git clone` pulls the *full history*, not just the current files. A secret pasted into a knowledge note and later deleted, or a model since removed, is still reachable to anyone who can clone. "Deleted from the working tree" is not "gone from the repo." If something sensitive ever hit a commit, rotate it.

Design your CI around the token rate limit. Minting a fresh token on every operation costs a couple of database round trips to look up the project and validate the key. That's not the signing, which is microseconds, but it's still small enough that a person pushing occasionally will never notice it; it disappears into a push that already takes hundreds of milliseconds. Where it can bite is the rate limit: if you're throttled, the client backs off using the server's `Retry-After` header rather than any fixed budget, and because the limiter runs per-process, the ceiling you'll actually hit depends on how the service is deployed. A human doing normal work never reaches it; a CI pipeline firing many git operations in quick succession can, so build in backoff on 429s rather than sizing your pipeline around a fixed number.

git wants to cache, and we stop it. git calls `store` on every configured credential helper after every operation, and on macOS the `osxkeychain` helper is wired up by default. For an ordinary long-lived credential that caching is exactly right; for us it would persist the ephemeral token, the one thing our "never at rest" guarantee exists to prevent. Much of the git config we write is simply resetting the inherited helper chain so a cache nobody asked for isn't kept on your behalf.

## Frequently asked questions

**What file formats does Wren Git Sync store?**

Your MDL context model syncs as YAML (`models/<name>/metadata.yml`, `relationships.yml`, and `cubes/` for pre-aggregations), and your knowledge base syncs as Markdown and YAML under a committed `knowledge/` folder. The only JSON in a Wren project is `target/mdl.json`: compiled build output that isn't committed. Everything you author and review is plain, open, human-readable text.

**Can I trigger CI/CD pipelines on model changes?**

Yes. Because Git Sync is ordinary git, a `git push` is a normal push event your existing CI can react to: run validation on a pull request, diff a metric definition before it merges, and promote from staging to production the same way you ship application code. On the Wren side, a push to the bound branch queues a deploy that makes the updated model queryable.

**Does Git Sync work with GitHub, GitLab, and Bitbucket?**

Yes. Git Sync uses standard git against a repository you own, so it works with GitHub, GitLab, Bitbucket, or any git remote you already run. There's no proprietary transport: after a one-time bind, `git push` and `git pull` are the entire interface.

**Is any reusable git credential stored on my machine?**

No. The durable key (project- or org-scoped, depending on which you added) lives at `~/.wren/cloud.yml` with `0600` permissions and is only ever used to buy short-lived tokens. What git actually presents on each operation is a fresh Ed25519 JWT that expires in 600 seconds and is never written to disk or reused, so a leaked token is worth ten minutes and nothing more.

Ready to move your project? The [Git integration guide](https://docs.getwren.ai/cp/guide/integrations/git) walks through binding your first repository end to end.

### See governed GenBI in action

Watch short demos of Wren AI turning business questions into governed SQL, charts, and reusable GenBI apps.

[Watch product demos](/demos)

## Get the next deep dive in your inbox

Practical GenBI guides, product updates, and customer lessons from the Wren AI team. A couple of emails a month — no noise.
