# Git transport support matrix: canonical source of truth (SEN-9 PR 5). # # Every entry here is a claim about one exercised combination, not an # independently-tested dimension: a PAT test, a tag test, and a GitLab # observation do not jointly prove "GitLab + PAT + tag" unless something # actually ran that exact combination. `docs/features/git-transport-support.mdx` # is generated from this file (backend/scripts/git-support-matrix/render.js); # a backend test (git-support-matrix.test.ts) enforces byte-for-byte parity # and validates every rule below. Internal test paths, PR numbers, and Linear # IDs never appear on the published page; they live here instead. # # Schema (schema: matrix-v1): # implementation_baseline: the git commit this file's claims describe. # claims[]: one entry per exercised (transport, ref, auth, host, ca, # node_path[, port]) combination. # - support: supported | unsupported | unverified # - supported requires evidence with outcome: success # - unsupported requires evidence with outcome: rejected (a reproducible # refusal, not silence; an unsupported claim with no evidence is as # unreproducible as an unproven supported one) # - unverified forbids evidence entirely # - evidence.kind: automated (a handle into a test file + exact test # title) or live (a pointer into docs/git-transport-attestations.yaml). # Every evidence record repeats all six combination dimensions, and the # validator requires them to match the claim exactly: a direct-proxy # result proves the direct-proxy combination, nothing else. # limitations[]: named gaps, each with its operator-facing consequence. # error_model[]: the transport-facing error codes this matrix covers # (TransportFacingCode plus REF_DELETED and FILE_NOT_FOUND), each with # its HTTP status and one-line meaning. GitOps plan-lifecycle codes # (STALE_PLAN, PLAN_FINGERPRINT_REQUIRED, PLAN_BLOCKED, LEGACY_PENDING, # PLAN_UNAVAILABLE, OPERATION_IN_FLIGHT) belong to reconciliation, not # transport, and are listed separately so the partition is visible. # attestations: pointer to the retained live-evidence file. version: '1' schema: 'matrix-v1' implementation_baseline: 79b86ddcd4aefdd6941f098e35990ab397b13c72 attestations: docs/git-transport-attestations.yaml claims: # ===== Automated: real git, real TLS/SSH, against a local fixture server ===== # host: generic, node_path: local for all of these: no proxy hop, no # branded host. Branded-host and non-local node-path claims live below, # pending or drawn from the live attestation pass. - id: https-pat-branch-system-generic-local transport: https ref: branch auth: pat host: generic ca: system node_path: local support: supported qualifiers: - History rewritten on this ref after resolution is detected and rejected as non-fast-forward, not silently deployed. evidence: kind: automated outcome: success handles: - file: backend/src/__tests__/git-transport-auth.integration.test.ts title: clones a private repo end-to-end with a valid token - file: backend/src/__tests__/git-transport-auth.integration.test.ts title: treats a linear branch advance as a fast-forward - file: backend/src/__tests__/git-transport-auth.integration.test.ts title: treats a multi-commit branch advance as a fast-forward - file: backend/src/__tests__/git-transport-auth.integration.test.ts title: rejects rewritten history as non-fast-forward - id: https-pat-tag-system-generic-local transport: https ref: tag auth: pat host: generic ca: system node_path: local support: supported qualifiers: - Proven for both an annotated tag (peeled through its ^{} commit) and a lightweight tag. evidence: kind: automated outcome: success handles: - file: backend/src/__tests__/git-transport-auth.integration.test.ts title: resolves and fetches an annotated tag through the peeled commit - file: backend/src/__tests__/git-transport-auth.integration.test.ts title: resolves and fetches a lightweight tag - id: https-pat-sha-system-generic-local transport: https ref: sha auth: pat host: generic ca: system node_path: local support: supported qualifiers: [] evidence: kind: automated outcome: success handles: - file: backend/src/__tests__/git-transport-auth.integration.test.ts title: resolves and fetches a pinned commit SHA - id: https-none-branch-persource-generic-local transport: https ref: branch auth: none host: generic ca: per-source node_path: local support: supported qualifiers: - Also proven to survive a same-origin redirect (the repository relocating on the same host). evidence: kind: automated outcome: success handles: - file: backend/src/__tests__/git-private-ca.integration.test.ts title: clones a private-CA HTTPS repo when the per-source CA PEM is supplied - file: backend/src/__tests__/git-redirect.integration.test.ts title: resolves a ref through an unauthenticated same-host redirect - id: https-pat-branch-persource-generic-local transport: https ref: branch auth: pat host: generic ca: per-source node_path: local support: supported qualifiers: - Proven through a same-origin redirect; the token is forwarded to the relocated path and never offered to a different host. evidence: kind: automated outcome: success handles: - file: backend/src/__tests__/git-redirect.integration.test.ts title: resolves a ref through an authenticated same-host redirect and sends the token to the relocated path - id: ssh-deploy-key-branch-na-generic-local-nonstandard-port transport: ssh ref: branch auth: deploy-key host: generic ca: not-applicable node_path: local port: nonstandard support: supported qualifiers: - Full round trip (resolve, fetch, and content verification) proven at this port. evidence: kind: automated outcome: success handles: - file: backend/src/__tests__/git-transport-ssh.integration.test.ts title: resolves and fetches over SSH with a deploy key and trusted host key - file: backend/src/__tests__/git-transport-ssh.integration.test.ts title: 'resolves over ssh:// with a nonstandard port' - id: ssh-deploy-key-branch-na-generic-local-default-port transport: ssh ref: branch auth: deploy-key host: generic ca: not-applicable node_path: local port: default support: supported qualifiers: - Ref resolution proven at the default port; the full content fetch is proven only at a nonstandard port (see the sibling claim), not separately re-run here. evidence: kind: automated outcome: success handles: - file: backend/src/__tests__/git-transport-ssh.integration.test.ts title: resolves over scp-style URL on the default SSH port # ===== Live: named Git hosts, external SaaS and self-hosted ===== # Evidence lives in docs/git-transport-attestations.yaml. A row stays # `unverified` (no evidence permitted) until a live pass actually exercises # it; nothing here is extrapolated from a different host or node_path. # Live-attested 2026-09-01 (see the attestation ids referenced below). - id: https-none-branch-system-github-local transport: https ref: branch auth: none host: github ca: system node_path: local support: supported qualifiers: - Only a public, read-only repository is exercised; GitHub does not receive a token or an SSH deploy key from this attestation. evidence: kind: live outcome: success attestation: att-2026-09-01-github-branch - id: https-none-tag-system-github-local transport: https ref: tag auth: none host: github ca: system node_path: local support: unverified qualifiers: - Only a public, read-only repository is exercised; GitHub does not receive a token or an SSH deploy key from this attestation. - Not attested: no small, stable, publicly tagged GitHub repository was found for this pass within a reasonable search. - id: https-none-sha-system-github-local transport: https ref: sha auth: none host: github ca: system node_path: local support: supported qualifiers: - Only a public, read-only repository is exercised; GitHub does not receive a token or an SSH deploy key from this attestation. evidence: kind: live outcome: success attestation: att-2026-09-01-github-sha - id: https-pat-branch-system-github-local transport: https ref: branch auth: pat host: github ca: system node_path: local support: unverified qualifiers: - Not attested: requires a real GitHub Personal Access Token, which this pass does not hold. - id: ssh-deploy-key-branch-na-github-local transport: ssh ref: branch auth: deploy-key host: github ca: not-applicable node_path: local support: unverified qualifiers: - Not attested: requires a real GitHub-registered SSH deploy key, which this pass does not hold. - id: https-pat-branch-persource-gitea-local transport: https ref: branch auth: pat host: gitea ca: per-source node_path: local support: supported qualifiers: - Attested against a private repository on a disposable, self-signed Gitea instance; the self-signed certificate is trusted via the per-source custom CA field, not system trust. - A wrong token against this private repository was separately confirmed to classify as an authentication failure, and a wrong SSH host key as a host-key mismatch. evidence: kind: live outcome: success attestation: att-2026-09-01-gitea-https-branch - id: https-pat-tag-persource-gitea-local transport: https ref: tag auth: pat host: gitea ca: per-source node_path: local support: supported qualifiers: - Annotated tag, resolved through its peeled commit. evidence: kind: live outcome: success attestation: att-2026-09-01-gitea-https-tag - id: https-pat-sha-persource-gitea-local transport: https ref: sha auth: pat host: gitea ca: per-source node_path: local support: supported qualifiers: [] evidence: kind: live outcome: success attestation: att-2026-09-01-gitea-https-sha - id: ssh-deploy-key-branch-na-gitea-local transport: ssh ref: branch auth: deploy-key host: gitea ca: not-applicable node_path: local support: supported qualifiers: - Host key fetched and trusted through Sencho's own probe endpoint, exactly as an operator would. evidence: kind: live outcome: success attestation: att-2026-09-01-gitea-ssh-branch - id: ssh-deploy-key-tag-na-gitea-local transport: ssh ref: tag auth: deploy-key host: gitea ca: not-applicable node_path: local support: supported qualifiers: - Lightweight tag. evidence: kind: live outcome: success attestation: att-2026-09-01-gitea-ssh-tag - id: ssh-deploy-key-sha-na-gitea-local transport: ssh ref: sha auth: deploy-key host: gitea ca: not-applicable node_path: local support: supported qualifiers: [] evidence: kind: live outcome: success attestation: att-2026-09-01-gitea-ssh-sha - id: https-pat-branch-persource-forgejo-local transport: https ref: branch auth: pat host: forgejo ca: per-source node_path: local support: supported qualifiers: - Attested against a private repository on a disposable, self-signed Forgejo instance; the self-signed certificate is trusted via the per-source custom CA field, not system trust. - A wrong token against this private repository was separately confirmed to classify as an authentication failure, and a wrong SSH host key as a host-key mismatch. evidence: kind: live outcome: success attestation: att-2026-09-01-forgejo-https-branch - id: https-pat-tag-persource-forgejo-local transport: https ref: tag auth: pat host: forgejo ca: per-source node_path: local support: supported qualifiers: - Annotated tag, resolved through its peeled commit. evidence: kind: live outcome: success attestation: att-2026-09-01-forgejo-https-tag - id: https-pat-sha-persource-forgejo-local transport: https ref: sha auth: pat host: forgejo ca: per-source node_path: local support: supported qualifiers: [] evidence: kind: live outcome: success attestation: att-2026-09-01-forgejo-https-sha - id: ssh-deploy-key-branch-na-forgejo-local transport: ssh ref: branch auth: deploy-key host: forgejo ca: not-applicable node_path: local support: supported qualifiers: - Host key fetched and trusted through Sencho's own probe endpoint, exactly as an operator would. evidence: kind: live outcome: success attestation: att-2026-09-01-forgejo-ssh-branch - id: ssh-deploy-key-tag-na-forgejo-local transport: ssh ref: tag auth: deploy-key host: forgejo ca: not-applicable node_path: local support: supported qualifiers: - Lightweight tag. evidence: kind: live outcome: success attestation: att-2026-09-01-forgejo-ssh-tag - id: ssh-deploy-key-sha-na-forgejo-local transport: ssh ref: sha auth: deploy-key host: forgejo ca: not-applicable node_path: local support: supported qualifiers: [] evidence: kind: live outcome: success attestation: att-2026-09-01-forgejo-ssh-sha - id: https-none-branch-system-gitlab-local transport: https ref: branch auth: none host: gitlab ca: system node_path: local support: supported qualifiers: - Only a public, read-only repository is exercised; GitLab does not receive a token or an SSH deploy key from this attestation. evidence: kind: live outcome: success attestation: att-2026-09-01-gitlab-branch - id: https-none-tag-system-gitlab-local transport: https ref: tag auth: none host: gitlab ca: system node_path: local support: supported qualifiers: - Only a public, read-only repository is exercised; GitLab does not receive a token or an SSH deploy key from this attestation. evidence: kind: live outcome: success attestation: att-2026-09-01-gitlab-tag - id: https-none-sha-system-gitlab-local transport: https ref: sha auth: none host: gitlab ca: system node_path: local support: supported qualifiers: - Only a public, read-only repository is exercised; GitLab does not receive a token or an SSH deploy key from this attestation. evidence: kind: live outcome: success attestation: att-2026-09-01-gitlab-sha - id: https-none-branch-system-bitbucket-local transport: https ref: branch auth: none host: bitbucket ca: system node_path: local support: supported qualifiers: - Only a public, read-only repository is exercised; Bitbucket does not receive a token or an SSH deploy key from this attestation. evidence: kind: live outcome: success attestation: att-2026-09-01-bitbucket-branch - id: https-none-tag-system-bitbucket-local transport: https ref: tag auth: none host: bitbucket ca: system node_path: local support: unverified qualifiers: - Only a public, read-only repository is exercised; Bitbucket does not receive a token or an SSH deploy key from this attestation. - Not attested: the public fixture repository used for this pass carries no tags. - id: https-none-sha-system-bitbucket-local transport: https ref: sha auth: none host: bitbucket ca: system node_path: local support: supported qualifiers: - Only a public, read-only repository is exercised; Bitbucket does not receive a token or an SSH deploy key from this attestation. evidence: kind: live outcome: success attestation: att-2026-09-01-bitbucket-sha # ===== Distribution: does the same combination hold off the local node ===== # Scoped to host: github (a real branded host reachable over public egress) # rather than the self-hosted fixtures above: the fleet's inbound firewall # only opens ports 22 and 1852, so a remote or Pilot node cannot reach a # container port newly published on the hub, but every node has unrestricted # outbound egress to the public internet. - id: https-none-branch-system-github-direct-proxy transport: https ref: branch auth: none host: github ca: system node_path: direct-proxy support: supported qualifiers: - Proves the direct-proxy path forwards and executes the fetch on the target node; it does not by itself prove any other host, ref, auth, or CA combination on this path. evidence: kind: live outcome: success attestation: att-2026-09-01-github-direct-proxy - id: https-none-branch-system-github-pilot transport: https ref: branch auth: none host: github ca: system node_path: pilot support: supported qualifiers: - Proves the Pilot dial-out path forwards and executes the fetch on the target node; it does not by itself prove any other host, ref, auth, or CA combination on this path. evidence: kind: live outcome: success attestation: att-2026-09-01-github-pilot limitations: - id: no-git-lfs title: No Git LFS statement: Compose and env files tracked via Git LFS are rejected rather than silently fetched as pointer stubs. Commit plain files instead. - id: no-submodules title: No submodules statement: Submodule contents are not fetched. Inputs and build contexts that reference submodule contents are refused with an actionable message; a warning is shown when .gitmodules is present. - id: no-sparse-partial-clone title: No sparse or partial clone statement: Every fetch materializes the complete repository at the resolved commit (shallow, single-branch); there is no sparse or partial clone for large monorepos. - id: no-github-app-authentication title: No GitHub App authentication statement: Authentication is Personal Access Token or SSH deploy key only. GitHub App installation tokens are not supported. - id: no-provider-pull-request-revisions title: No provider pull/merge request revisions statement: Sources track a branch, a tag, or a pinned commit SHA. A provider-specific pull request or merge request revision (for example GitHub's refs/pull/N/head) is not a supported ref shape. - id: outbound-target-restrictions title: Outbound target restrictions statement: Repository targets that resolve to loopback, link-local, multicast, or selected special-use addresses are refused before any request is sent. Private hosts on an operator's own LAN or VPN are not affected by this restriction. - id: branch-tag-name-collision title: Branch/tag name collisions statement: A bare ref name resolves as a branch first, then as a tag. If an operator renames a branch and a tag of the same name later appears, the source silently starts resolving the tag instead. A ref name that is also a valid 40 or 64 character hex string resolves as a commit SHA before either lookup. error_model: - code: REPO_NOT_FOUND label: Repository not found status: 404 meaning: The repository does not exist, or (indistinguishably, matching GitHub's own private-repo masking) exists but is private and no usable credential was supplied. - code: AUTH_FAILED label: Authentication failed status: 400 meaning: The Git host rejected the supplied credential. Mapped to 400, never 401, so an upstream Git-host auth failure never triggers the dashboard's own session logout. - code: SSH_HOST_KEY_FAILED label: SSH host key mismatch status: 400 meaning: The server's SSH host key does not match the fingerprint trusted for this source. - code: REF_NOT_FOUND label: Ref not found status: 404 meaning: The configured branch, tag, or commit SHA does not exist on the remote. - code: REF_DELETED label: Ref deleted or rewritten status: 404 meaning: A ref that previously resolved to a commit no longer matches that history (deleted, force-pushed, or superseded by a same-named tag). - code: UNSUPPORTED_REF label: Commit not reachable on this host status: 400 meaning: A pinned commit SHA that the Git host will not serve because it is not advertised by any branch or tag tip. - code: RATE_LIMITED label: Rate limited status: 429 meaning: The Git host throttled the request (an HTTP 429, or a sideband message naming a rate limit or abuse-detection mechanism). Wait and retry; there is no automated backoff yet. - code: NETWORK_TIMEOUT label: Network timeout status: 504 meaning: A connect, fetch, or DNS-resolution timeout, or a target the host actively refused. - code: GIT_ERROR label: Git error status: 400 meaning: Any other transport failure not covered above (invalid URL, disallowed target, TLS/certificate problem, oversized repository, canceled fetch), classified with a specific operator-facing message. - code: FILE_NOT_FOUND label: File not found status: 404 meaning: A configured compose or env file path does not exist on the resolved commit. reconciliation_only_codes: # Not part of this transport matrix; listed so the partition of # GitSourceErrorCode is visible and auditable. Owned by the GitOps # change-plan lifecycle, not by repository transport. - STALE_PLAN - PLAN_FINGERPRINT_REQUIRED - PLAN_BLOCKED - LEGACY_PENDING - PLAN_UNAVAILABLE - OPERATION_IN_FLIGHT