Skip to content

Git Gate and Security Modes

The git gate is a host-side bare git repository managed by terok-sandbox. When a project has an upstream configured, the gate mirrors it; without one, it initialises as a remoteless bare repo the container can still push to. The gate serves two purposes depending on the project's security mode:

  • Online mode — performance accelerator for cloning (the gate is not used for security; containers talk to upstream directly)
  • Gatekeeping mode — the default git origin for containers, directing agent pushes to the gate instead of upstream

Both modes can be paired with the gate.enabled knob; see Disabling the gate below.

Three Layers: Upstream → Gate → Tasks

Upstream (GitHub, GitLab, etc.)
Local gate (host-side mirror: STATE_ROOT/gate/<project>.git)
Task working copy (/workspace inside container)

Each task gets its own isolated repo, seeded from either the upstream (online) or the gate (gatekeeping).


Online Mode

The agent behaves like a normal developer:

  • CODE_REPO points to the upstream URL
  • Container can push branches directly to upstream
  • If a local gate exists, the initial clone is seeded from the gate server's HTTP endpoint for speed, then origin is repointed to upstream
  • Security relies on normal upstream auth (deploy key permissions, branch protections), not the gate

Gatekeeping Mode

Agent pushes are directed to the gate rather than upstream:

  • CODE_REPO = the gate server's HTTP endpoint (e.g., http://host:port/project.git)
  • The container's default origin points to the gate, not upstream
  • Humans review and promote changes from the gate to upstream

What the gate does and does not guarantee

The gate controls which remote is configured as origin. It does not technically prevent the agent from adding other remotes or making outbound network connections. If the agent knows an upstream URL (e.g. from git history or environment), it could attempt to reach it — the same way any online actor could attempt to access a public repository.

The gate is most effective when combined with:

  • The shield (egress firewall) — blocks outbound connections to hosts not on the allowlist
  • Per-project SSH keys — a convenience feature for accessing private repos from containers. These are ordinary deploy keys that only work if the user registers them on the upstream host; they do not add security on their own.

Options

SSH agent access (when SSH key is registered via terok project ssh-init): The vault's SSH signer is available even in gatekeeping mode. Useful for repos with private submodules. Ensure the key has no write access to upstream — otherwise the agent could push to upstream despite the gate configuration.

External remote (gatekeeping.expose_external_remote: true): Add the upstream URL as a remote named external in the container's git config. This is "relaxed gatekeeping" — the agent can see and interact with upstream, but origin still points to the gate. Use this when the agent needs to pull from upstream but you still want pushes to go through human review via the gate.

Upstream polling (gatekeeping.upstream_polling): TUI periodically checks if upstream has new commits using git ls-remote (cheap, refs only). Shows a notification when the gate is behind.

gatekeeping:
  upstream_polling:
    enabled: true           # default: true
    interval_minutes: 5     # default: 5

Auto-sync (gatekeeping.auto_sync): Automatically sync gate branches when upstream changes are detected. Opt-in (default: disabled).

gatekeeping:
  auto_sync:
    enabled: false
    branches:
      - main
      - dev

Gate Lifecycle

  1. terok project ssh-init <project> — generate a per-project SSH key (optional for public HTTPS repos). The key is only useful if the user registers it as a deploy key on the upstream remote.
  2. terok project gate-sync <project> — initialize or update the gate mirror (--force-reinit to recreate)
  3. Run tasks — online containers clone from gate then talk to upstream; gatekeeping containers use the gate as their default origin

Configuration Example

project:
  id: "myproject"
  security_class: "gatekeeping"

git:
  upstream_url: "git@github.com:org/repo.git"
  default_branch: "main"

gatekeeping:
  expose_external_remote: true

  upstream_polling:
    enabled: true
    interval_minutes: 5

  auto_sync:
    enabled: false
    branches:
      - main

Mental Model

Mode Container's default origin Gate role
Online Upstream directly Performance accelerator (clone seed only)
Gatekeeping Gate Review checkpoint — agent pushes to gate, humans promote to upstream
Relaxed gatekeeping Gate (+ upstream as external remote) Review checkpoint with upstream visibility

Defence in depth

The gate works best as one layer in a defence-in-depth strategy: gate (push destination control) + shield (egress firewall). No single layer is sufficient on its own.


Disabling the gate (gate.enabled)

The gate is an independent knob from security_class. Set gate.enabled: false in project.yml to stop terok from running a host-side mirror at all — the container then fetches directly from upstream via its own network path. Useful when the host has no path to the remote (firewall blocking SSH, corporate proxy) but the container does, or when you simply don't want the host keeping a local mirror.

project:
  id: cp2k
  security_class: online
git:
  upstream_url: git@github.com:user/cp2k.git
gate:
  enabled: false        # host never touches the remote

Four combinations

gate.enabled and upstream_url are orthogonal:

gate.enabled upstream_url Behaviour
true (default) set Host mirrors upstream; container clones from the mirror. Default, matches the table above.
true absent Host initialises a remoteless bare gate; the container still gets a remote to push to. Local-only scratch projects.
false set Host never touches the remote; the container fetches directly from upstream. Escape hatch for host-side network restrictions.
false absent No git plumbing at all; container workspace starts empty.

Collapse when upstream is absent

When upstream_url is absent, online and gatekeeping describe the same act — there is nothing to push beyond the gate. Both values are accepted and behave identically in that sub-case. The table above describes them distinctly only because they diverge once an upstream exists.

Incoherent combination (rejected)

security_class: gatekeeping with gate.enabled: false is rejected at project-load time. Gatekeeping is the gate-enforced mode — so disabling the gate in that mode is self-contradictory. Either set security_class: online to drop the gate, or keep gate.enabled: true.