Skip to Content
We are live but in Staging 🎉
Core Concepts

Core Concepts

The vocabulary behind every Dodil Git interaction.

Two planes, one host

Everything you do lands on one of two planes:

the git WIRE smart-HTTP: https://git.dodil.io/<repo_id>.git (clone / fetch / push) SSH: [email protected]:<repo_id>.git the REST CONTROL PLANE /api/v1/… — repos, PRs, checks, protection, (console, CLI, API) CI secrets, SSH keys

The wire moves objects; the control plane manages everything around them. Both authenticate through the same identity model — see Auth and Access.

Repositories

A repository has two names:

  • repo_id — an opaque UUIDv7, the permanent identity. Every API path and every clone URL keys by it (/api/v1/repos/{repo_id}, https://git.dodil.io/{repo_id}.git).
  • An address{org_slug}/{name}, the human handle. It is renameable and resolves back to the repo_id via GET /api/v1/orgs/{org_slug}/repos/{name}. The CLI accepts any of the three forms (repo_id, name, org/name) anywhere a <repo> argument appears.

Repos also carry a default branch (server default main; retarget any time to an existing branch), a state (active / deleted), and size/pack-count quotas.

Visibility

private (the default) or public. A public repo allows anonymous read — clone, code browse, PR reading — with no credential. Writing always requires one. Flipping visibility requires git.admin.

Pull requests

A PR proposes merging a source_branch into a target_branch. It carries a per-repo number, a state (openmerged or closed), the current head_sha, comments, reviews, and a checks_state rollup for the head commit.

  • Reviews are sha-pinned. A verdict (approve or request_changes) records the head sha it was cast on — its freshness stamp. Push new commits and the approval no longer matches the head; protection can dismiss stale approvals automatically.
  • Merging supports three methods: merge_commit, fast_forward, and squash. Pass expected_head_sha to make the merge fail if someone pushed in between.

CI checks

A check is one job’s report against one commit: a name (namespaced workflow/job), a state (queuedrunningpassed / failed / errored), an attempt counter, and a log. Per commit the API also returns a worst-of rollup (unspecified means no checks reported).

Runs start from files in the repo: push a workflow under .github/workflows/ or a .dodil/checks.yaml. Jobs declare needs dependencies, which the console renders as a graph. Logs stream live over SSE while a job runs and are flushed to durable storage when it finishes.

CI secrets are write-only values injected into jobs and masked in logs, at three scopes — organization, repository, and per-environment — with fetch-time precedence environment > repo > org. A read never returns a value, only names and timestamps.

Branch protection

A protection rule guards every branch matching a branch_pattern:

SettingEffect
required_approvalsPRs need N fresh approvals to merge
require_green_checksthe head commit’s rollup must be passed
block_force_pushnon-fast-forward pushes are rejected
block_deletionthe branch cannot be deleted
block_self_approvalthe PR author’s own approval doesn’t count
dismiss_stale_approvalsnew commits invalidate earlier approvals

Rules are upserted per pattern (PUT /api/v1/repos/{repo_id}/protection) and enforced on both the wire (force-push, deletion) and the control plane (merge).

Roles

Three IAM-granted tiers scope everything: git.admin (manage settings, visibility, protection, delete), git.editor (read + write — the default), git.viewer (read). Grants are org-wide or conditioned to a single repo — see Auth and Access.


See also