Source control
Source latches permanently on first source use: how DeepSpace and GitHub source work and what each release records.
On this page
Every app has exactly one authoritative Git repository, and you never declare it with a setting. A new app starts unclaimed. Its first source-producing action latches one source permanently: a deploy from a checkout with a GitHub remote claims that GitHub repository; a first deepspace push, or a deploy without a GitHub remote, claims DeepSpace source. There is no setter, unclaim, or transfer.
To see where an app stands:
npx deepspace app source --json # read-only: reports, never changes
npx deepspace status --json # sourceInference: what the NEXT deploy will do
The two experiences#
| Source | Experience |
|---|---|
| DeepSpace | The packaged default. Commit-first: deploy publishes the attached clean branch automatically, and the space remote, clone/push/pull, workspaces, activity, releases, and rollback all work together. |
| GitHub | Manual Git ownership. A new app's first deploy from a checkout whose selected remote points at GitHub permanently claims that repository. Deploy ships the local working tree and performs no Git read, write, or credential verification. |
Under GitHub source, dirty and unpushed bytes are valid deploy inputs. The release records the claimed repository, source revision, and a dirty flag (releases shows GitHub · owner/repo, dirty worktree), but carries no DeepSpace commit id; rollback uses the retained deployment bundle. Commit discipline is yours — the platform respects that.
How the first source use decides#
An unclaimed app is decided once, at the first source latch:
- A deploy whose durable release reservation is accepted with a selected GitHub remote → permanently claims the observed repository.
app sourcerecords revision 1 immediately; the completed release records the same revision. - A
deepspace pushwhose pack POST reaches the server, or a deploy whose durable release reservation is accepted with no GitHub remote → permanently claims DeepSpace source. This includesdeploy --no-push: skipping source sync for that release does not leave authority undecided.
A claimed app ignores remotes entirely — whichever provider it claimed, that is the answer forever.
app source is read-only#
npx deepspace app source # report: claimed source, or unclaimed
npx deepspace app source --json # { source: {...} | null, revision, ... }
The old setter forms (app source github, app source deepspace) refuse with source_inferred; the retired public POST /source answers a 410 tombstone. Only the server's first-use paths can write source. The revision counter appears in --json surfaces (revision on app source, sourceRevision on release rows); on new apps it changes once, at the claim, and a latching deploy's release carries that winning revision.
Working under GitHub source#
-
Push with ordinary Git. Branches, tags, PRs — all normal.
-
Deploy ships the working tree, dirty bytes included; the release ledger labels dirty releases so a rollback never picks between clean and dirty blind.
-
Do not run
deepspace pushbefore the first GitHub deploy — that would claim DeepSpace source permanently. After the first GitHub deploy, every DeepSpace source verb refuses from the stored claim withsource_managed_by_githuband names the repository. -
Do not create or replace
originunprompted. Wiring a repo to GitHub is the developer's call — and on an unclaimed app, adding or removing a GitHub remote changes what the next deploy does. When asked, use ordinary Git:git remote add origin git@github.com:<org>/<repo>.git git push -u origin mainbash
The refusal the source verbs share#
push, pull, clone, and the workspace verbs all refuse GitHub-owned apps from the permanent registry claim with one code, source_managed_by_github:
This app uses GitHub source (
owner/repo). Use normal Git/GitHub for source operations…
--json carries repository and appId alongside the code. The refusal ships no executable action: which Git command to run is the developer's call.
A review branch is not a second trunk#
A claimed DeepSpace-source app may still keep a review branch on GitHub — that is fine because remotes no longer change the claim. On an unclaimed app a GitHub remote is not inert: the next deploy whose durable release reservation is accepted claims GitHub permanently. One repository is the truth; the other may hold a copy for a purpose, but it is not a second authoritative trunk.
CI deploys#
For CI, inspect the current flags with npx deepspace auth login --help and npx deepspace deploy --help rather than copying stale snippets. Store credentials in the CI secret manager — never in argv, repository files, or workflow logs. A CI checkout's selected remote decides an unclaimed app's first deploy: keep the intended GitHub remote to claim GitHub, or remove it only when the first release should claim DeepSpace. A depth-1 DeepSpace-source clone refuses shallow_repo; unshallow it before deploying.
Legacy declared-source apps#
Apps that declared a source under v0.25 and earlier keep their recorded provider; a recorded GitHub app behaves exactly like a first-release-claimed one (same refusals, same manual deploys), and deploy removes its stale space remote for it. The transfer machinery those versions documented is gone.
See also#
- Workspaces — the DeepSpace-source collaboration surface
- App identity — what stays put regardless of source;
app init --new-idto fork - Deployment — what a release records under each source
- Command reference:
app source