3.3 Git remotes and the identity policy
Included from Basic tiertemplates
After this you can: configure a git remote with write-only credentials on a project, share one credential across remotes through the registry, rotate and retire it safely, and state the git identity policy the server applies before anyone may run from it.
Before you start — tick each one:
- Lesson 1.2 — Build and change your own (
opschain learn 1.2)
In the GUI: the Git remotes tab on a project page, beside Templates — its Bulk actions → Delete Git remotes… is the delete below, with the same refusals; the credential records live on the Operations area's Git credentials panel.
Lesson 3.1 needed one of these before its first change, and lesson 3.4's templates pin versions of one. A git remote is a repository your project can read: a name, a URL, and credentials the server uses to fetch it. Project and environment changes run actions straight from a remote; templates pin exact revisions of one for assets. This lesson is about the remote itself — and about the fact that configuring one and being allowed to run from it are two different authorities.
1. Configure a remote
A remote is project-scoped:
opschain git-remotes create acme \
--name infra \
--url https://git.example.com/acme/infra.git \
--username ci-bot --password-env ACME_GIT_TOKEN
created git remote acme/infra (01a13f0e-…-example)
The credential rules matter more than the flags:
- The URL names a repository on the network.
http://,https://,ssh://,git://or the scp-likegit@host:pathform. A filesystem path or afile://URL is refused before the server even probes it:
Error: the server answered invalid_argument: url: Url must be an http(s)://, ssh://, git:// or user@host:path remote
A server is one shared host — any repository its own user could read
would otherwise be one create away from any project editor. Only the
operator can lift this, with the deploy-authoritative
security.allow_local_git_remotes: true in the server's config.yaml
(opschain docs config; the hardened posture refuses it) — the setting
a lab server whose sample repositories live on its own disk runs with,
and nothing a production server needs.
- Secrets never go on the command line.
--password-envnames an environment variable holding the HTTP(S) password or token — the value itself never appears in argv, shell history or process listings. Interactive sessions can use--password-promptinstead, which reads the secret without echoing it. - SSH remotes (
ssh://…orgit@host:path) authenticate with--ssh-key-file <path>(the private key file) plus, for a protected key,--passphrase-env <var>. The server refuses a password on an SSH URL and a passphrase on an HTTP(S) one, with a named error saying which to use. - Credentials are write-only. The server stores them encrypted and
never returns their values —
showreports each one as set or unset:
opschain git-remotes show acme/infra
name: infra
id: 01a13f0e-…-example
project: acme
url: https://git.example.com/acme/infra.git
public_url: -
archived: false
disabled: false
fetch_interval: 900
last_fetched_at: 2026-10-12T08:15:00.000000Z
last_fetch_outcome: ok
last_fetch_error: -
consecutive_failures: 0
next_fetch_at: 2026-10-12T08:30:00.000000Z
floating_versions: 0
last_repin_at: -
credential_ref: -
credentials (stored values are never returned by the server):
user: set (masked)
password: set (masked)
passphrase: -
ssh_key: -
created_at: 2026-10-12T08:00:00.000000Z
updated_at: 2026-10-12T08:00:00.000000Z
-f remote.yaml supplies the non-secret attributes from a file
(name, url, public_url, user, periodic_fetch_interval);
credential keys in a file are refused, pointing you back at the flags.
2. Rotate, list, retire
Rotate a credential by sending a new one — unsupplied attributes keep their stored values:
opschain git-remotes update acme/infra --password-env ACME_GIT_TOKEN_NEW
updated git remote acme/infra
git-remotes list acme tables the remotes (the AUTH column names the
credential shape, never a value). Deleting is deliberate: without
--confirm the command only previews. The server refuses to delete a
remote that templates, changes or scheduled changes still reference; an
archived remote that finished changes reference is kept for the audit
trail and has its stored credentials removed instead.
opschain git-remotes delete acme/infra --confirm
deleted git remote acme/infra
3. One credential, many remotes — the recommended form
When several remotes share one real-world credential (a team's PAT, a deploy key), store it once in the shared git-credential registry and point each remote at it instead of repeating the values. The reference resolves at use time, so rotating the credential in one place applies to every referencing remote on its next fetch — one write, no remote touched. The same credential rules apply: the secret comes from an environment variable, a prompt or a key file, never argv.
opschain git-credentials create --project acme --name github-ci \
--kind https_basic --username ci-bot --password-env ACME_GIT_TOKEN
created git credential github-ci (https_basic, acme scope, 01a13f0e-…-example)
opschain git-remotes create acme --name infra \
--url https://git.example.com/acme/infra.git --credential-ref github-ci
created git remote acme/infra (01a13f0e-…-example)
Rotation is now one command — opschain git-credentials update github-ci --project acme --password-env ACME_GIT_TOKEN_NEW — and opschain git-credentials test github-ci --project acme --url <repository> proves
the new value reaches the repository before anything depends on it. A
credential lives in a project's registry (--project) or the
instance-global one (no --project, an administrator's write); a remote
may reference its own project's credentials or global ones, never another
project's. A remote never holds both — a reference beside inline values is
refused, --credential-ref "" on git-remotes update moves a remote
back to inline values, and a credential still referenced by remotes
refuses deletion, naming them. The registry is also the API's
GitCredentialService (opschain docs api-v2, with an extract action that
moves a remote's inline values into a named credential) and the GUI's Git
credentials panel; the resource is opschain docs resources/git_credential.
4. The git identity policy: configured is not usable
Configuring a remote and being allowed to RUN from it are two different
authorities. The git identity policy is a deny-all-by-default allow list
owned by the security_administrator role (never node settings): until
an entry covers your new remote, creating it succeeds with a warning
— "unusable until allow-listed" — and every attempt to use it (change
create, a workflow change step reaching it, a retry with
--refresh-git-rev, template-version create or sync) refuses with a
named error and a warn:security:git_remote_refused event. Remote
listings carry a per-user usable flag, so screens grey out remotes you
cannot use rather than hide them.
Walk this section as the everyday operator lesson 3.1 had you sign in
as — not as the bootstrap admin. The policy is an allow-list evaluated
for the acting user, and a superuser bypasses it exactly as it bypasses
path rules — a superuser is never denied — so from the admin's seat a
change from infra simply succeeds and the denial below is invisible.
As the operator, with a payload naming the new remote
(git_remote_name: infra, git_rev: main):
opschain runs create change acme/environments/dev --action hello -f payload-infra.yaml
Error: the server answered invalid_argument: git_remote_name: Git remote "infra" at rev "main" is denied by the git identity policy on /projects/acme — no allow-list entry covers this user; a security administrator (or superuser) can allow it with `opschain security git-policies put`
The refusal names the policy and the role that can lift it, and the
server records warn:security:git_remote_refused. Allow-listing is the
security administrator's act — opschain security git-policies put --project acme --remote infra — after which the same create succeeds.
Remotes that existed before the policy arrived were seeded allow-all
entries (marked seeded) so nothing broke at upgrade; a security
administrator reviews that queue with
opschain security git-policies list --seeded and allow-lists new
remotes with opschain security git-policies put — the full command
family is opschain docs cli/security-git-policies, and the role that
holds it is lesson 7.4.
Check yourself
- Create a remote and show it: how does
git-remotes showreport the password you supplied, and which event does the server record when a change tries to run from a remote the identity policy has not allow-listed?
What you learned
- A git remote is project-scoped; its credentials arrive by environment variable, prompt or key file — never argv, never a file — and the server reports them only as set or unset.
updaterotates in place,listshows the credential shape, anddeletepreviews until--confirm, refusing while anything still references the remote.- One shared credential can serve many remotes through
credential_ref, rotated in one place. - The git identity policy is deny-by-default: a configured remote is
unusable until a security administrator allow-lists it, and every
refused use lands a
warn:security:git_remote_refusedevent.
Next: lesson 3.4 — Templates and versions (opschain learn 3.4), or back to your track (opschain learn).