Skip to main content
Documentation

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
bash

The two experiences#

SourceExperience
DeepSpaceThe 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.
GitHubManual 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 source records revision 1 immediately; the completed release records the same revision.
  • A deepspace push whose pack POST reaches the server, or a deploy whose durable release reservation is accepted with no GitHub remote → permanently claims DeepSpace source. This includes deploy --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, ... }
bash

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 push before the first GitHub deploy — that would claim DeepSpace source permanently. After the first GitHub deploy, every DeepSpace source verb refuses from the stored claim with source_managed_by_github and names the repository.

  • Do not create or replace origin unprompted. 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 main
    
    bash

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#