---
title: Cloud as source
description: Cloud projects are owned by Openship Cloud; your self-hosted instance is a gateway in front of it — plus how projects promote up and come back home.
---
When a project runs on **Openship Cloud** (our hosted service), the cloud is the one true copy — the
**canonical** owner. Its record, deployments, domains, environment variables, and logs all live on the
cloud, and the cloud runs its builds and lifecycle. Your self-hosted instance keeps **no local copy** of
that project. Instead it acts as a **gateway** — a thin pass-through that forwards requests for that project
up to the cloud and hands you back the answer.
Think of it like a mailbox on your street. The letters aren't stored in the mailbox; the mailbox just
carries them to and from the real post office. Your instance is the mailbox; Openship Cloud is the post
office.
A project lives in **exactly one place** — fully local, or fully on the cloud. Never half in each. That is
the whole idea behind this page.
## A gateway, not a mirror
Openship deliberately never keeps two copies of the same project. A mirror would mean two sources of truth
that constantly drift apart and need reconciling — and a bug on your box could corrupt cloud data. A gateway
avoids all of that: there is nothing to sync, because there is only ever one copy.
Your dashboard and CLI still talk **only to your local API**. When a request is for a cloud project, the
gateway quietly forwards it upstream and streams the response straight back — so the experience feels the
same whether a project is local or cloud.
Forwarding does **not** skip your rules. The local permission plane gates every call — checking the caller's
role and access — *before* anything is proxied. Only then does the gateway forward the request.
## How the gateway knows where a project lives
For every project-scoped request, the local API decides "serve this from my own database, or forward it to
the cloud?" in this order:
1. **A source hint.** The dashboard's project list tags each row as `local` or `cloud`, and carries that hint
on later requests (the `X-Project-Source` header) — the fast path, no database read.
2. **A local lookup.** No hint? If the project id exists in your local database, it's local.
3. **A cloud link.** No local row, but your organization has a linked cloud account? Then it's a cloud
project, and the request is forwarded.
4. **Otherwise, not found** — the same `404` a stranger's project id would get, so nothing leaks.
If the cloud can't be reached while forwarding, you get a `503` with code `CLOUD_UNREACHABLE` — not a `404`.
That distinction matters: the project *does* exist, the cloud is just temporarily out of touch.
## One cloud identity for the whole team
A cloud connection belongs to the **organization owner**, not to each person. When the gateway forwards a
call, it authenticates as the owner's Openship Cloud session (read server-side from encrypted storage — no
cookies or tokens ever travel from the browser).
So teammates do **not** each need their own cloud account. Your local instance decides *who is allowed* to
act (by their role), then proxies the action as the owner. Because the connection is org-owned, every member
sees the same "connected" state and the same list of cloud workspaces.
## Promote: sending a project up to the cloud
**Promote** moves a local project onto Openship Cloud so the cloud becomes its canonical home. Under the
hood it copies the project's whole **subgraph** (the project row plus everything attached to it —
deployments, domains, env, and so on) up to the cloud, and then removes the local rows so no shadow copy is
left behind.
There are two ways it happens:
- **Born-on-cloud (automatic).** When you deploy a still-local project to the cloud *and let the cloud
build it*, Openship promotes the project **first**, then runs the deploy upstream — so it becomes
cloud-canonical from its very first cloud deploy. If the copy-up fails, the promote stops **before**
anything local is torn down, leaving your project exactly as it was. (Choosing *build on this machine* is
the one exception — see [What your box still does](#what-your-box-still-does-for-a-cloud-project).)
- **Explicit transfer.** You can also promote on demand from the CLI or API (see
[Doing it yourself](#doing-it-yourself)).
Promote keeps the project's GitHub webhook. After promoting, a `git push` to the repo is forwarded to the
cloud copy and redeploys it *there* — so auto-deploy keeps working without any change on your side.
Bring-home reverses this: pushes are handled locally again.
Values encrypted with **your instance's** key (secret env vars) can't be decrypted by the cloud, so they're
stripped during promote. After a project is on the cloud, re-enter those secrets in the cloud project.
## Bring home: pulling a project back
**Bring-home** is the reverse: it exports the project's subgraph from the cloud, restores it into your local
database, clears the project's cloud link, and then tears down the leftover rows on the cloud side so a
future promote won't collide. From that moment the project is canonical-local again, and pushes to its
GitHub repo are handled locally instead of forwarded.
Bring-home moves the project's **data**. Reconnecting things like custom-domain DNS and bringing the app's
containers back up are operator follow-up steps, not part of the data transfer itself.
## What your box still does for a cloud project
For a cloud-owned project, your local instance does **zero** orchestration — with one opt-in exception:
**build locally, upload the artifact.** If you choose "build on this machine," your box builds the app with
its own credentials and uploads the result to an Openship Cloud workspace to run. In that case the project
stays local-canonical and is *not* promoted. Everything else — routing, TLS, running the containers —
belongs to the cloud. See [Runtime model](/docs/architecture/runtime-model) for how a runtime target is
chosen.
## On Openship Cloud itself
There's a neat symmetry here: **on the cloud service, every project is already canonical-local** — there is
nothing upstream to forward to. The exact same code runs on both sides; the gateway simply resolves
"local" when it *is* the cloud, so no request ever loops back on itself.
## Doing it yourself
Promote and bring-home are self-hosted-only actions, guarded by the `project:admin` permission. Your
organization also has to be **connected to Openship Cloud** first — a transfer on an unconnected org is
refused.
```bash
# Promote a local project up to Openship Cloud
openship project transfer to-cloud
# Bring a cloud project back to this instance
openship project transfer to-self-hosted
```
The matching API routes are `POST /api/projects/:id/transfer/to-cloud` and
`POST /api/projects/:id/transfer/to-self-hosted`. On promote, a transfer that copies up cleanly but can't
finish local cleanup returns a partial-success (`207`) so you can retry the cleanup — the cloud copy is
already authoritative.
- **`412`** — your org isn't connected to Openship Cloud yet.
- **`409`** — the project is already on the target side, *or* a leftover cloud copy / a name clash blocks the
promote. Openship never overwrites the other copy; it asks you to clean up or rename and retry.
- **`502`** — the cloud accepted the call but the transfer itself failed upstream.
## What next?